Использовать DLL, скомпилированную в Delphi 7 в C # - PullRequest
2 голосов
/ 18 июня 2009

Мне нужно использовать DLL (Hardware ID Extractor), созданную в Delphi 7, в моем приложении C #.

Функции, экспортируемые этой DLL:

Экспортируемые функции:

// CPU
function GetCPUSpeed: Double;
function CPUFamily: ShortString; { Get cpu identifier from the windows registry }
function GetCpuTheoreticSpeed: Integer; { Get cpu speed (in MHz) }
function IsCPUIDAvailable: Boolean; Register;
function GetCPUID (CpuCore: byte): ShortString;
Function GetCPUVendor: ShortString;

// RAM
function MemoryStatus (MemType: Integer): cardinal; { in Bytes }
function MemoryStatus_MB (MemType: Integer): ShortString; { in MB }

// HDD
function GetPartitionID (Partition : PChar): ShortString; { Get the ID of the specified patition. Example of parameter: 'C:' }
function GetIDESerialNumber(DriveNumber: Byte ): PChar; { DriveNr is from 0 to 4 }

Я знаю (очевидно), что строки в Delphi не заканчиваются нулем и являются байтовыми (ASCII). Но я понятия не имею, как сопоставить эти строки Delphi с C #.

Спасибо.

Ответы [ 3 ]

6 голосов
/ 18 июня 2009

Проблема заключается в том, как вы спроектировали свои экспортируемые функции. Библиотеки DLL - это (помимо прочего) механизм предоставления кода таким образом, чтобы его можно было использовать из приложений (или других библиотек DLL), написанных на разных языках программирования.

Посмотрите на Windows API, есть много функций, возвращающих текст. К сожалению, это делается по-разному, в Windows API нет реального стандарта. Однако я совершенно уверен, что вы не найдете ни одной функции, которая возвращает «строку» (строку Pascal или C (с нулевым символом в конце)), как вы это сделали. Причина проста: это сделает очень трудным или даже невозможным использование разных языков программирования для разных модулей. Ваша DLL будет выделять память для строки, и как только вызывающая сторона завершит работу со строкой, ей потребуется освободить память. Таким образом, оба модуля должны совместно использовать менеджер для этого фрагмента памяти. Как программа C # может использовать вашу DLL, если видит, что они используют совершенно разные менеджеры памяти?

Обычный способ получить строковое значение из DLL - это вызвать функцию с предварительно выделенным буфером и длиной буфера в параметрах, позволить функции заполнить этот буфер содержимым строки и позволить результату функции обозначать успех или неудачу. , Например, пропуск буфера недостаточного размера будет одной из причин сбоя. Разные функции Windows API справляются с этим по-разному, проще всего для вас, вероятно, определить максимальную длину строки с помощью константы, например, MAX_PATH.

Чтобы решить вашу проблему, вы должны взять одну функцию Windows API, которую вы знаете, как вызывать из C #, и которая возвращает строковый результат в буфер, в качестве примера. Смоделируйте ваши экспортированные функции после этого примера, и их должно быть легко вызвать из программы на C #.

Edit:

Я вспомнил, что видел подобный вопрос ( Как импортировать функцию из DLL, созданной в Delphi? ) некоторое время назад, и теперь, когда я посмотрел, я обнаружил, что она также принадлежит вам. .

Тогда вы писали, что у вас нет исходного кода для этой DLL, поэтому об изменении экспортируемых функций не может быть и речи. То, что вы могли бы сделать, это создать DLL-оболочку (или COM-объект) в Delphi, вызвать оригинальный HardwareIDExtractor.dll и предоставить разумный API поверх него. Это позволит вам одновременно предоставлять версии экспортируемых функций AnsiChar и WideChar.

Если вы хотите уменьшить беспорядок (две DLL необходимы вместо одной), вы также можете поместить исходный HardwareIDExtractor.dll в качестве ресурса в вашу DLL-оболочку, как описано в этой публикации блога: Использование DLL, хранящихся в качестве ресурсов в программах Delphi

6 голосов
/ 18 июня 2009

Вот пример того, как вы можете объявить функцию GetCPUSpeed ​​в C #:

class Program
{
    [DllImport("the_name_of_the_delphi_library.dll")]
    public static extern double GetCPUSpeed();

    static void Main(string[] args)
    {
        double cpuSpeed = GetCPUSpeed();
        Console.WriteLine("CPU speed: {0}", cpuSpeed);
    }
}

И есть другие объявления, которые вы можете попробовать:

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string CPUFamily();

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern int GetCpuTheoreticSpeed();

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern bool IsCPUIDAvailable();

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetCPUID(byte cpuCore);

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetCPUVendor();

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern uint MemoryStatus(int memType);

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string MemoryStatus_MB(int memType);

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetPartitionID(char partition);

[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetIDESerialNumber(byte driveNumber);
2 голосов
/ 19 июня 2009

Если у вас есть источник dll D7, вы можете изменить экспортированную функцию, чтобы сделать Функция ShortString для возврата PChar. Вы можете создавать новые функции, которые вызывают исходные и выполняют приведение типов - это самый простой путь. Если вы следуете по этому пути, сделайте то же самое с типами Integer и Cardinal (приведите их к LongInt и LongWord соответственно). Некоторый код ниже (я думаю, что маги, такие как г-н Хаусладен, имеют более элегантный подход, но это работает :-))

var
  SInput: ShortString;
  POutput: PAnsiChar;
  Tam: Integer;
  pt: Pointer;
begin
  SInput := 'Alphabet'; //ShortString
  pt := @SInput[1]; // Points to the first char of the string;
  Tam := (Length(SInput))+1; // Size the string
  POutput := StrAlloc(tam); // Allocate;
  CopyMemory(POutput,pt,tam); // Do the copy
  POutput[tam-1] := #0; // Put the null to finish
  MessageBox(0, POutput, 'Algo', MB_ICONWARNING or MB_OK);
  StrDispose(POutput);

end;

Это работает, потому что внутренне ShortString является массивом [0..255] из Char. У меня нет D2009 под рукой, чтобы увидеть, должен ли SizeOf (char) быть изменен на SizeOf (AnsiChar). НО принцип тот же: выделите PAnsiChar, получите указатель на первый символ ShortString и сделайте Copy (в данном случае я сделал это с CopyMemory) для PAnsiChar. И поместите нуль на его место.

Или вы можете пойти по пути mghie's , создать обертку и выполнить там броски.

...