4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
1

Неуправляемая dll (delphi) не может менять свои данные

24.10.2013, 12:29. Показов 1281. Ответов 15
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Начну с вопроса: если неуправляемый код сам выделяет память и создает объект, доступен ли он при следующих вызовах неуправляемого кода?

Есть некая dll, от которой известно описание объектов и заголовков функций. При помощи вызова одной из функций dll сама создаёт в памяти некую сложную структуру, и вызовами других функций вносит изменения в поля структуры.
Первый вызов с созданием структуры (и её начальным наполнением) удалось наладить, возвращается указатель на красивую структуру.
Но дальше проблемы - при вызове последующих функций dll ругается на неопределенные ошибки (видимо Exception), и данные в структуре не меняет.

Цитаты из кода:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    [StructLayout(LayoutKind.Sequential)]
    public class uInfo
    {
        public int Adr1;
        public int Adr2;
        public string PostAddress;
 
        public int SharedLen;
        public IntPtr Shared;
 
        public int PayLen;
        public IntPtr Pay;
 
        public int CntLen;
        public IntPtr Cnt;
 
        public int MonthPay;
 
        public IntPtr CntData1;
        public int CntData1Len;
        public IntPtr CntData2;
        public int CntData2Len;
 
        public IntPtr Internal1;
        public IntPtr Internal2;
    }
C#
1
2
3
4
5
        [DllImport(@"k.dll", CallingConvention = CallingConvention.StdCall)]
        static extern int GetAccount(int Adr1, int Adr2, ref uInfo info);
 
        [DllImport(@"k.dll", CallingConvention = CallingConvention.StdCall)]
        static extern int FillCharges(ref uInfo info, int mode);
C#
1
2
3
            uInfo inf = null;
            int res = GetAccount(Adr1, Adr2, ref inf);
            int res2 = FillCharges(ref inf, 1);
В результате res = 0 (Успех), по указателю вытаскивается красивая структура вся в правильных данных.
А res2 = -1 (общая ошибка, как по описанию dll).
Дело вряд ли в самой функции FillCharges, вместо неё испробовано ещё с десяток предлагаемых в dll функций, с разными входными параметрами и проч.
У кого есть опыт работы с неуправляемым кодом, прошу поделиться соображениями.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.10.2013, 12:29
Ответы с готовыми решениями:

C# и неуправляемая Dll
Возник вопрос. При работе с подключенной следующим образом неуправляемой dll возникает exception...

Borland C++ Builder 6.0 не может найти свои же dll файлы
Недавно установил Borland C++ Builder 6.0 . Всё бы хорошо но при компиляции проэкта пишет что не...

Неуправляемая DLL библиотека. Импорт, создание класса библиотеки
По курсовой нужно воспользоваться неуправляемой dll. Сразу скажу, возможно много где ошибаюсь в...

Функция может менять глобальные данные и сохраняются ли эти изменения
Например функция в ходе работы записывает что то в глобальную переменную, которая не является ни...

15
Эксперт Python
4630 / 2050 / 361
Регистрация: 17.03.2012
Сообщений: 10,131
Записей в блоге: 6
24.10.2013, 12:59 2
Цитата Сообщение от Slyfish Посмотреть сообщение
Начну с вопроса: если неуправляемый код сам выделяет память и создает объект, доступен ли он при следующих вызовах неуправляемого кода?
В общем сказать нельзя. Но если этот объект не замочили, между первым и вторым вызовом, то должен быть доступен.

Добавлено через 2 минуты
Да, вопрос - там у вас точно объект?
Если вы без COM-интерфейсов и т.п. читаете в C# дельфийскую структуру, может, там record?
А record-ы дельфи мочит, при уходе из области видимости.
0
4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
24.10.2013, 13:05  [ТС] 3
А кто мочит объекты в неуправляемой памяти? GC ведь туда не лезет, как я понимаю.
При отладке, по указателю все данные видны.
Может маршалинг после преобразования ответа и ref параметров блокирует все результаты деятельности неуправляемого метода?
И кто подскажет, может какие дебарегы дополнительные есть, чтобы понять проблему...
У кого был подобный опыт многократных вызовов неуправляемого кода?

Там record, dll его создаёт и возвращает указатель. Для освобождения памяти предусмотрена отдельная процедура.
0
Эксперт Python
4630 / 2050 / 361
Регистрация: 17.03.2012
Сообщений: 10,131
Записей в блоге: 6
24.10.2013, 13:14 4
Цитата Сообщение от Slyfish Посмотреть сообщение
А кто мочит объекты в неуправляемой памяти? GC ведь туда не лезет, как я понимаю.
Ну мало ли. Например, интерфейсные объекты удаляются при обнулении ссылок, тоже своеобразная сборка мусора. Дотнетовский GC, конечно, не залезает.

Цитата Сообщение от Slyfish Посмотреть сообщение
И кто подскажет, может какие дебарегы дополнительные есть, чтобы понять проблему...
Ну, тут только дельфийским дебаггером эту dll отлаживать.

Цитата Сообщение от Slyfish Посмотреть сообщение
Там record, dll его создаёт и возвращает указатель.
Тогда вполне возможно, что этот рекорд удаляется при выходе из процедуры, где создаётся. Позаботьтесь, чтобы не удалялся.
Например, перенесите в более высокую область видимости.
Или выделите для него память вручную, чтобы самому контролировать удаление (оператор new).

Добавлено через 2 минуты
Цитата Сообщение от Slyfish Посмотреть сообщение
public class uInfo
Да, кстати, почему тут класс, если из dephi приходит record? Это ведь оно, я так понимаю?
0
4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
24.10.2013, 14:44  [ТС] 5
Цитата Сообщение от dondublon Посмотреть сообщение
Да, кстати, почему тут класс, если из dephi приходит record? Это ведь оно, я так понимаю?
Да, это оно. Был там и struct, эффект тот же. Это я пытался пробовать варианты возврата указателя на структуру. Это ОНО вообще достойно отдельной темы. Две недели добивался как правильно сделать маршалинг. В этом record'е сплошные указатели на массивы, причем иногда указатели на массивы других record'ов.

Цитата Сообщение от dondublon Посмотреть сообщение
Тогда вполне возможно, что этот рекорд удаляется при выходе из процедуры, где создаётся.
Как я писал выше, стартовый метод dll возвращает указатель на созданную структуру, и есть финальный метод, для освобождения этой структуры. Из этого я делаю вывод, что dll сама выделяет память под структуру, и освобождает только при вызове финального метода. Сама dll предусматривает работу с ней внешней системы в виде вызова многих отдельных методов, которые работают со структурой.

Delphi
1
2
3
4
5
6
  PInfo = ^TInfo;
  TInfo = record // Специальная структура
    Adr1: Integer; // Первый код
    Adr2: Integer; // Второй код
    ...
  end;
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
function GetAccount(Adr1, Adr2: Integer; var Info: PInfo): Integer; stdcall;
{
  Назначение:
    Поиск данных и заполнение спец.структуры
  Параметры:
    Adr1: Первый код
    Adr2: Второй код
    Info: Указатель на спец.структуру
  Результат: см. описание ошибок в файле Types.pas
  Примечание:
    Info создается средствами библиотеки и должен быть освобожден
    вызовом функции FreeAccount
}
0
Почетный модератор
Эксперт .NET
8721 / 3673 / 404
Регистрация: 14.06.2010
Сообщений: 4,513
Записей в блоге: 9
24.10.2013, 14:54 6
Slyfish, приведите определения функций и структур на Delphi, обычно такое поведение из-за некорректного объявления. Также было бы хорошо увидеть саму DLL.
Отлаживать можно через Windbg + SOS(EX) для работы с .NET'ом.
0
Эксперт Python
4630 / 2050 / 361
Регистрация: 17.03.2012
Сообщений: 10,131
Записей в блоге: 6
24.10.2013, 14:57 7
Цитата Сообщение от Slyfish Посмотреть сообщение
Как я писал выше, стартовый метод dll возвращает указатель на созданную структуру, и есть финальный метод, для освобождения этой структуры.
Увы, это не ответ на вопрос - а не убивается ли ваша структура раньше времени.
Цитата Сообщение от Slyfish Посмотреть сообщение
Из этого я делаю вывод, что dll сама выделяет память под структуру, и освобождает только при вызове финального метода.
Ну это ведь только так предполагается, верно?

Напишите, как вы создаёте вашу структуру. Не описание типа, а само создание, внутри GetAccount, как именно вы выделяете под неё память.

Если как-то так:
Delphi
1
2
3
4
5
function GetAccount(): TInfo;
begin
    result.Addr1 := ...
   ... возвращаем result
end;
или так:
Delphi
1
2
3
4
5
6
7
function GetAccount(): TInfo;
var
   info: TInfo;
begin
    info.Addr1 := ...
    result := info;
end;
то не удивлюсь, что структура таки убивается.
0
4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
24.10.2013, 15:28  [ТС] 8
Привожу выдержки из описания dll

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
type
  TVSut = packed record
    Code: Word;
    Value: Integer;
  end;
  PVSutArray = ^TVSutArray;
  TVSutArray = array[0..0] of TVSut;
 
  TCntData = record
    Tar: Integer;
    Rash: Integer;
    Sum: Integer;
  end;
 
  PCnt = ^TCnt;
  TCnt = record
    Vid: Integer;
    SubVid: Integer;
    Prec: Integer;
    Rash: Integer;
    Sum: Integer;
    Number, P, D1, D2: Integer;
    Normal: TCntData;
    Higher: TCntData;
    Value: PChar;
//    Number: Integer;
  end;
  PCntArray = ^TCntArray;
  TCntArray = array[0..0] of TCnt;
 
  TPay = record
    Vid: Integer;
    Sal: Integer;
    Nac: Integer;
    Sum: Integer;
  end;
  PPayArray = ^TPayArray;
  TPayArray = array[0..0] of TPay;
 
  PInfo = ^TInfo;
  TInfo = record // Специальная структура
    Adr1: Integer; // Первый код
    Adr2: Integer; // Второй код
    PoAddr: PChar;
    SharedLen: Integer;
    Shared: PVSutArray;
    PayLen: Integer;
    Pay: PPayArray;
    CntLen: Integer;
    Cnt: PCntArray;
    MPay:Integer;
    CntData:array [0..1] of PCntArray;
    Internal1:Pointer;
    Internal2:Pointer;
  end;
  // Допустимо модифицировать первые SharedLen записей по адресу Shared
  // Остальные поля являются служебными
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function GetAccount(Adr1, Adr2: Integer; var Info: PInfo): Integer; stdcall;
{
  Назначение:
    заполнение спец.структуры
  Параметры:
    Adr1: Первый код
    Adr2: Второй код
    Info: Указатель на спец.структуру
  Результат: см. описание ошибок в файле Types.pas
  Примечание:
    Info создается средствами библиотеки и должен быть освобожден
    вызовом функции FreeAccount
}
 
function FreeAccount(var Info: PInfo): Integer; stdcall;
{
  Назначение:
    Освобождение памяти, занятой спецструктурой
  Параметры:
    Info: Указатель на спец.структуру
  Результат: см. описание ошибок в файле Types.pas
}
 
function FillCharges(Info: PInfo; Mode: Integer): Integer; stdcall;
{
  Назначение:
    Заполнение
  Параметры:
    Info: Указатель на спец.структуру
    Mode: Режим
      0 - нуль
      ...
  Результат: см. описание ошибок в файле Types.pas
}
 
function SetConsumptionNum(Info: PInfo; Vid: Integer; Number: Integer; Rashod: Integer): Integer; stdcall;
{
  Назначение:
    Установить данные
  Параметры:
    Info: Указатель на спец.структуру
    Vid: Вид
    Number: Номер
    Rashod: Данные
  Результат: см. описание ошибок в файле Types.pas
}
Функций вроде SetConsumptionNum более десятка, с небольшими отличиями по числу параметров. Все занимаются внесениями разных изменений в спец.структуру. Саму dll выложить наверное не имею права, так как предоставлена мне на ограниченных условиях.

Добавлено через 1 минуту
Цитата Сообщение от dondublon Посмотреть сообщение
Напишите, как вы создаёте вашу структуру. Не описание типа, а само создание, внутри GetAccount, как именно вы выделяете под неё память.
Увы, dll предоставлена в виде черного ящика, исходного кода нет.
0
Эксперт Python
4630 / 2050 / 361
Регистрация: 17.03.2012
Сообщений: 10,131
Записей в блоге: 6
24.10.2013, 15:32 9
Цитата Сообщение от Slyfish Посмотреть сообщение
Увы, dll предоставлена в виде черного ящика, исходного кода нет.
Ну, тогда что-то сказать сложно.
Если у них такое солидное описание, и другие пользовались и не жаловались - видимо, всё нормально там у них с памятью.
0
4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
24.10.2013, 15:37  [ТС] 10
Цитата Сообщение от dondublon Посмотреть сообщение
Ну, тогда что-то сказать сложно.
Если у них такое солидное описание, и другие пользовались и не жаловались - видимо, всё нормально там у них с памятью.
Не только другие не жаловались, мы сами на Delphi 7 писали работу с этой dll. И всё прекрасно. Теперь вот встала необходимость сделать сетевую версию, и решили уходить от древностей. Тем более что на Delphi 7 с сетью тоже какие-то заморочки нам насыпались.
0
Почетный модератор
Эксперт .NET
8721 / 3673 / 404
Регистрация: 14.06.2010
Сообщений: 4,513
Записей в блоге: 9
24.10.2013, 15:48 11
Смущает вот этот момент
Цитата Сообщение от Slyfish Посмотреть сообщение
CntData:array [0..1] of PCntArray;
Цитата Сообщение от Slyfish Посмотреть сообщение
C#
1
2
3
4
public IntPtr CntData1;
public int CntData1Len;
public IntPtr CntData2;
public int CntData2Len;
Откуда взялись public int CntDataXLen ? Если Вы к этим данным не обращаетесь, то по идее ничего страшно произойти не должно, т.к. структура получается больше по размеру, плохо было бы если б меньше.

И да, uInfo должен быть классом (как Вы определили в самом начале), т.к. var в Delphi говорит что передаваемое значение может быть изменено, если использовать структуры тогда в методе GetAccount нужно использовать IntPtr, но это так не заметку...
1
4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
24.10.2013, 16:01  [ТС] 12
Я, видимо, так и не смог правильно описать подходящую структуру в управляемом коде. Здесь надо использовать SizeConst=2, или подобное? Как я уже писал, маршалинг этой структуры достоин отдельной темы.
Но пока до этой структуры дело не доходит, разве не так? С целью проверить, что происходит, я пока достаю остальные данные из созданной структуры, игнорируя этот кусок. Да и при отладке уже видно, что данные не меняются.

Добавлено через 3 минуты
Delphi
1
CntData:array [0..1] of PCntArray;
По моим представлениям, это должно в памяти быть как два указателя (по 4 байта). На практике я не знаю, как Delphi хранит массивы с заданной размерностью, и методом подбора по отладчику описывал этот кусок, чтобы посмотреть его начинку.
0
Почетный модератор
Эксперт .NET
8721 / 3673 / 404
Регистрация: 14.06.2010
Сообщений: 4,513
Записей в блоге: 9
24.10.2013, 16:07 13
Судя по структуре всё правильно, за исключением CntDataX. В Delphi если не ошибаюсь это array [0..1] массив из 2х элементов, в C# это можно заменить двумя IntPtr:
C#
1
2
3
4
5
6
7
        public int MonthPay;
 
        public IntPtr CntData1;
        public IntPtr CntData2;
 
        public IntPtr Internal1;
        public IntPtr Internal2;
еще бы я попробовал public string PostAddress заменить на public IntPtr PostAddress, но не думаю что это как-то отобразиться на работе.

Без отладки довольно трудно сказать в чем дело, была бы DLL тогда можно было бы попробовать посмотреть где именно происходит ошибка выполнения и почему.
Что еще можно попробовать - сравнить размеры структур и смещения полей в Delphi (работая в самой среде) и в C# (с помощью Marshal.SizeOf и Marshal.OffsetOf)
0
4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
24.10.2013, 16:18  [ТС] 14
NickoTin, Вы полагаете, несовпадение полей или размеров всей структуры в управляемом коде всё же может влиять на работу dll со своими "родными" данными, которые она только что создала сама?

Провел эксперимент. Выключим маршалинг совсем:

C#
1
2
3
IntPtr inf = IntPtr.Zero;;// вместо uInfo
int res = GetAccount(Adr1, Adr2, ref inf);
int res2 = FillCharges(ref inf, 1);
Итог - тот же. Первый вызов нормальный (0 и заполняет данные), второй ничего не меняет и ошибается (-1, нет изменений). Попробую другие функции, вдруг FillCharges(ref inf, 1) не устраивает... но кажется так я уже пробовал.
0
Почетный модератор
Эксперт .NET
8721 / 3673 / 404
Регистрация: 14.06.2010
Сообщений: 4,513
Записей в блоге: 9
24.10.2013, 23:14 15
Slyfish, согласен, некорректное предположение, с учетом того что с самими данными Вы не работаете напрямую. Кроме отладки я больше не могу ничего предложить.
0
4 / 0 / 0
Регистрация: 24.10.2013
Сообщений: 8
25.10.2013, 12:57  [ТС] 16
Решение найдено. Увы, банальная невнимательность. Кто читал тему, тоже не прошел этот тест

Delphi:
function GetAccount(Adr1, Adr2: Integer; var Info: PInfo): Integer; stdcall;
function FillCharges(Info: PInfo; Mode: Integer): Integer; stdcall;

C#:
uInfo inf = null;
int res = GetAccount(Adr1, Adr2, ref inf);
int res2 = FillCharges(ref inf, 1);

При вызове FillCharges получился указатель на указатель, хотя требуется просто сам указатель.
0
25.10.2013, 12:57
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
25.10.2013, 12:57
Помогаю со студенческими работами здесь

Получить данные через указатель из dll на delphi
Здравствуйте, помогите пожалуйста! имеется dll, написанная на delphi, где есть процедура: ...

Кто может поделиться файлами ogg.dll, vorbis.dll и vorbisfile.dll - 32-х и 64-битными версиями?
Движок перевожу на платформу Win64 и нужно, чтобы разрядность ЕХЕ и DLL совпадали, а в интернете...

После загрузки приложения картинки должны менять свои координаты случайным образом
Расположите на форме таймер и несколько изображений (массив изображений) Разработайте программный...

Как сделать, чтобы пользователи могли вводить или менять свои пароли в окне приветствия удаленного компьютера?
Есть RemoteAPP/RDP на win2012 R2, active directory, и клиенты на win10 Home. Когда у пользователя...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru