Вызов DLL Delphi от C# приводит к неожиданным результатам

Как правило, вы позволяете базе данных генерировать ключ в виде числового значения, а затем форматируете его в своей программе.

Скажем, у вас есть автоматически сгенерированный первичный ключ из вашей базы данных, и сгенерированный ключ 18435876, вы можете отформатировать его в java, чтобы он был «1843-5876».

8
задан Doug Hays 5 February 2009 в 21:14
поделиться

7 ответов

Delphi использует так называемое fastcall соглашение о вызовах по умолчанию. Это означает, что компилятор пытается передать параметры функции в регистрах ЦП и только использует стек, если существует больше параметров, чем бесплатные регистры. Например, использование Delphi (EAX, EDX, ECX) для первых трех параметров к функции.
В Вашем коде C# Вы на самом деле используете stdcall соглашение о вызовах, которое дает компилятору команду передавать параметры через стек (в обратном порядке, т.е. последний параметрический усилитель продвинут сначала) и позволять очистке вызываемого стек.
Напротив, вызов cdecl, используемый компиляторами C/C++, вынуждает вызывающую сторону к очистке стек.
Просто удостоверьтесь, что Вы используете то же соглашение о вызовах с обеих сторон. Stdcall главным образом используется, потому что он может использоваться почти везде и поддерживается каждым компилятором (API Win32 также используют эту конвенцию).
Обратите внимание, что fastcall не поддерживается.NET так или иначе.

22
ответ дан 5 December 2019 в 04:43
поделиться

jn является правильным. Прототипа функции, столь же данного, нельзя легко назвать непосредственно от C#, пока это находится в Delphi register соглашение о вызовах. Любой необходимо записать a stdcall функция обертки для него - возможно, в другом DLL, если у Вас нет источника - или необходимо получить людей, которые поддерживают функцию для изменения ее соглашения о вызовах на stdcall.

Обновление: Я также вижу, что первым аргументом является строка Delphi. Это не что-то, что C# может предоставить также. Это должен быть PChar вместо этого. Кроме того, важно согласиться, является ли функцией Ansi или Unicode; если DLL записан с Delphi 2009 (или позже), то это - Unicode, иначе это - Ansi.

16
ответ дан 5 December 2019 в 04:43
поделиться

Я никогда не делал этого, но пытаюсь изменить Ваш код на:

function CreateCode(SerialID : String;
    StartDateOfYear, YearOfStartDate, YearOfEndDate, DatePeriod : Word;
    CodeType,RecordNumber,StartHour,EndHour : Byte) : PChar; stdcall;
    external 'CreateCodeDLL.dll';

Отметьте дополнительный stdcall.

Edit2: Поскольку Вы видите от других ответов Вас или должны сделать изменение выше или записать обертку dll, который делает то же самое.

1
ответ дан 5 December 2019 в 04:43
поделиться

Возвращаемое значение могло бы быть другой проблемой. Это - вероятно, любой утечка памяти (Они выделяют буфер на "куче" и никогда не освобождают ее), или доступ к уже свободной памяти (Они возвращают локальный бросок строковой переменной PChar).

Возврат строк (или переменная измерил данные в целом) от функции до другого модуля проблематичен в целом.

Одно решение (используемый winapi) состоит в том, чтобы потребовать, чтобы вызывающая сторона передала в буфере и его размере. Недостаток этого - то, что, если буфер является слишком маленьким, функциональные сбои и вызывающая сторона должны назвать его снова с более крупным буфером.

Другое возможное решение состоит в том, чтобы выделить буфер от "кучи" в функции и возвратить его. Затем необходимо экспортировать другую функцию, которую вызывающая сторона должна использовать для освобождения выделенной памяти снова. Это гарантирует, что память освобождена тем же временем выполнения, которое выделило ее.

При передаче (Delphi) строковый параметр между различным (не Borland) языки, вероятно, невозможен. И даже между модулями Delphi Вы для обеспечения обоих модулей используете тот же экземпляр диспетчера памяти. Обычно это означает добавлять, "использует ShareMem" в качестве первого использования ко всем модулям. Другим различием является соглашение о вызовах "регистр", который является fastcall конвенцией, но не идентичный с fastcall использованием компиляторов MS.

Совершенно другое решение могло перекомпилировать Delphi dll с одним из компиляторов Delphi.net. Сколько работы, которая является, зависит от их кода.

2
ответ дан 5 December 2019 в 04:43
поделиться

Создайте обертку COM в Delphi и вызове, которые в Вашем C# кодируют через interop. Вуаля.. простой в использовании от C# или любой другой будущей платформы.

1
ответ дан 5 December 2019 в 04:43
поделиться

В то время как Вы просите, чтобы они изменили соглашение о вызовах, необходимо также попросить, чтобы они изменили первый параметр так, чтобы это не была "строка". Заставьте их использовать указатель на (завершенный пустым указателем) символ или массив widechar вместо этого. Используя строки Delphi как параметры DLL плохая идея даже без добавленной сложности попытки достигнуть межъязыковой совместимости. Кроме того, строковая переменная будет или содержать ASCII или содержание Unicode, в зависимости от которой версии Delphi они используют.

0
ответ дан 5 December 2019 в 04:43
поделиться

На днях я шутил, пытаясь узнать о созыве конвенций, и написал несколько приёмов для конвертации между различными из них. Вот один для StdCall>FastCall.

typedef struct
{
    USHORT ParameterOneOffset;  // The offset of the first parameter in dwords starting at one
    USHORT ParameterTwoOffset;  // The offset of the second parmaeter in dwords starting at one
} FastCallParameterInfo;



    __declspec( naked,dllexport ) void __stdcall InvokeFast()
{
    FastCallParameterInfo paramInfo;
    int functionAddress;
    int retAddress;
    int paramOne, paramTwo;
    __asm
    {
        // Pop the return address and parameter info.  Store in memory.
        pop retAddress;
        pop paramInfo;
        pop functionAddress;

        // Check if any parameters should be stored in edx                          
        movzx ecx, paramInfo.ParameterOneOffset;     
        cmp ecx,0;
        je NoRegister;  

        // Calculate the offset for parameter one.
        movzx ecx, paramInfo.ParameterOneOffset;    // Move the parameter one offset to ecx
        dec ecx;                                    // Decrement by 1
        mov eax, 4;                                 // Put 4 in eax
        mul ecx;                                    // Multiple offset by 4

        // Copy the value from the stack on to the register.
        mov ecx, esp;                               // Move the stack pointer to ecx
        add ecx, eax;                               // Subtract the offset.
        mov eax, ecx;                               // Store in eax for later.
        mov ecx, [ecx];                             // Derefernce the value
        mov paramOne, ecx;                          // Store the value in memory.

        // Fix up stack
        add esp,4;                                  // Decrement the stack pointer
        movzx edx, paramInfo.ParameterOneOffset;    // Move the parameter one offset to edx
        dec edx;                                    // Decrement by 1
        cmp edx,0;                                  // Compare offset with zero
        je ParamOneNoShift;                         // If first parameter then no shift.

    ParamOneShiftLoop:
        mov ecx, eax;
        sub ecx, 4;
        mov ecx, [ecx]
        mov [eax], ecx;                             // Copy value over
        sub eax, 4;                                 // Go to next 
        dec edx;                                    // decrement edx
        jnz ParamOneShiftLoop;                      // Loop
    ParamOneNoShift:
        // Check if any parameters should be stored in edx                          
        movzx ecx, paramInfo.ParameterTwoOffset;     
        cmp ecx,0;
        je NoRegister;  

        movzx ecx, paramInfo.ParameterTwoOffset;    // Move the parameter two offset to ecx
        sub ecx, 2;                                 // Increment the offset by two.  One extra for since we already shifted for ecx
        mov eax, 4;                                 // Put 4 in eax
        mul ecx;                                    // Multiple by 4

        // Copy the value from the stack on to the register.
        mov ecx, esp;                               // Move the stack pointer to ecx
        add ecx, eax;                               // Subtract the offset.
        mov eax, ecx;                               // Store in eax for later.
        mov ecx, [ecx];                             // Derefernce the value
        mov paramTwo, ecx;                          // Store the value in memory.           

        // Fix up stack
        add esp,4;                                  // Decrement the stack pointer
        movzx edx, paramInfo.ParameterTwoOffset;    // Move the parameter two offset to ecx
        dec edx;                                    // Decrement by 1
        cmp edx,0;                                  // Compare offset with zero
        je NoRegister;                              // If first parameter then no shift.
    ParamTwoShiftLoop:
        mov ecx, eax;
        sub ecx, 4;
        mov ecx, [ecx]
        mov [eax], ecx;                             // Copy value over
        sub eax, 4;                                 // Go to next 
        dec edx;                                    // decrement edx
        jnz ParamTwoShiftLoop;                      // Loop


    NoRegister:
        mov ecx, paramOne;                          // Copy value from memory to ecx register
        mov edx, paramTwo;                          // 
        push retAddress;
        jmp functionAddress;
    }
}

}

1
ответ дан 5 December 2019 в 04:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: