Страницы

Поиск по вопросам

среда, 25 декабря 2019 г.

Вызов функции из dll написанной на Delphi из C#

#c_sharp #delphi


Есть DLL написанная на Delphi. Исходников от нее у меня нет. Есть описание. Перепробовал
много способов все пишут "Attempted to read or write protected memory" 

вот сама функция из dll:

procedure getFastPhases(n: Integer; alpha: array of Real; step_t: Real; var 
  nt: Integer; var ps: array of Integer);


Входные данные: n, alpha, step_t 

Выходные данные: nt, ps

Как вызвать эту функцию из C#?
    


Ответы

Ответ 1



Правила написание dll на Delphi: Сразу оговорось что описанные ниже правила применимы для "крос-языковой" dll, но я очень советую если не знаете что делаете, то применять их и в случаее когда верна схема приложение(delphi) - dll(delphi). Что можно передавать безопасно: [const, var, out]Целочисленные(возможно только надо быть осторожным с int64/uint64 в 32 битной dll, но я не уверен) [const, var, out]Дробные single и double(real лучше не использовать) [const, var, out]Любые указатели на элементарные типы, структуры и статические массивы [const, var, out]WideString Что можно возращать безопасно: Любые целочисленные Любые указатели Перадача структур: Со структурами нужно соблюдать осторожность и понимать что они могут передаваться как указатель в регистре или полностью в регистре значение. Также не мало важно выравнивание. Перерача строк, массивов: Никогда не передавайте и не возвращайте строки или массивы. Эти типы являются управляемыми в контексте языка Delphi, и если вы попытаетесь передавать эти типы в лучшем случаем получите AV или утечку. В худшем случае приложение может и схлопнуться. Исключения: Вы должны обеспечить обработку исключений внутри dll и ни когда не допускать их наружу. Исключения являются классами Delphi и другие языки о них ни чего не знают, из за этого поведение при исключениях может быть не предсказуемым. Память: Если вы возвращаете какой либо указатель, то вы должны обеспечить функцию в вашей dll котороя освободит эту память, тогда вызывающий после использования вашего указателя сможет его освободить. Иначе буду утечки Примеры: Код dll type PDblArray = ^TDblArray; TDblArray = array[0 .. 0] of Double; // можно вместо 0 .. 0 написать 0 .. 100000, но это может привести к печальным последтсвия если объявить переменную с таким типом // пример входного массива procedure DllTest1(const InputArray: PDblArray; InputArraySize: Integer); stdcall; var I: Integer; begin try for I := 0 to InputArraySize - 1 do begin // some calculation end; except // some handling end; end; // пример выходного массива procedure DllTest2(var OutputArray: PDblArray; var OutputArraySize: Integer); stdcall; var I: Integer; Size: Integer; begin try Size := 100; GetMem(OutputArray, Sizeof(Double) * Size); OutputArraySize := Size; for I := 0 to OutputArraySize - 1 do begin // some calculation end; except // some handling end; end; // пример освобождения памяти для выходного массива procedure DllTestFree(var OutputArray: PDblArray); stdcall; var Size: Integer; begin try FreeMem(OutputArray); OutputArray := nil; except // some handling end; end; Код C# [DllImport("TestDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] public static extern void DllTest1(double[] InputArray, int InputArraySize); [DllImport("TestDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] public static extern void DllTest2(ref IntPtr OutputArray, ref int OutputArraySize); [DllImport("TestDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] public static extern void DllTestFree(ref IntPtr OutputArray); Если dll не ваша: Напиши свою длл обертку соблюдая правила и все буде хорошо PS: Для шарпа под рукой ни чего нет поэтому код проверял только на Delphi. И вот хорошие описание для шарпа как надо преобразовывать передачу массиов в шарпе

Ответ 2



Код Delphi type Tr = array[0..50000] of real; Ta = array[0..2000] of Integer; {$R *.res} procedure getFastPhases(const n:Integer; const alpha:Tr; var step_t:Real; var nt:Integer; var ps:Ta); stdcall; begin end; exports getFastPhases; Код C# [DllImport("Project2.dll", CallingConvention = CallingConvention.StdCall)] public static extern void getFastPhases(int n, double[] alpha, ref double stept, ref int nt, int[] p); private void GetFast() { int n = 0; double[] alph = new double[50000]; double step = 0; int nt = 0; int[] p = new int[2000]; getFastPhases(n, alph, ref step, ref nt, p); } Для работы с динамическим массивом используем указатель Код Delphi type Tr = array[0..50000] of real; Ta = array of Integer; {$R *.res} procedure getFastPhases(const n:Integer; const alpha:Tr; var step_t:Real; var nt:Integer; var ps:Ta); stdcall; var h:ta; begin SetLength(h, 5); nt:=Length(h); h[0]:=19; h[1]:=99; h[3]:=89; h[4]:=79; ps:=h; end; exports getFastPhases; Код C# [DllImport("Project2.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] public static extern void getFastPhases(int n, double[] alpha, ref double stept, ref int nt, ref IntPtr p); private void GetFast() { int n = 0; double[] alph = new double[50000]; double step = 0; int nt = 0; IntPtr p1 = Marshal.AllocHGlobal(4); getFastPhases(n, alph, ref step, ref nt, ref p1); int[] p = new int[nt]; Marshal.Copy(p1, p, 0, p.Length); } Функция должна быть помечена "Соглашением о вызове" calling convention это обязательное условие иначе невозможен вызов функции(возможен только из Delphi)

Комментариев нет:

Отправить комментарий