Использовать DLL, скомпилированную в Delphi 7 в C #

2

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

Функции, экспортируемые этой 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 с С#.

Спасибо.

  • 1
    Ошибается, длинные строки нулевые байта в Delphi, так что можно ввести отливку их PChar и использовать их как строки C.
  • 0
    @ Mghie, нет, ОП не так. Вы правы насчет длинных строк, но в примере кода используются короткие строки.
Показать ещё 2 комментария
Теги:
dll

3 ответа

6
Лучший ответ

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

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);
6

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

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

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

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

Edit:

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

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

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

  • 0
    «Тогда вы писали, что у вас нет исходного кода для этой DLL». Я не могу найти, где по пути, который ОП сказал, что ...... Кстати, включение DLL, хранящихся в качестве ресурсов, действительно здорово. ;-)
  • 0
    В связанном предыдущем вопросе: «У меня нет исходного кода для этой DLL, поэтому я должен адаптировать свою программу на C к этой DLL, а не наоборот».
2

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

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, получите указатель на первый char ShortString и сделайте копию (в данном случае я сделал с CopyMemory) в PAnsiChar. И поместите нуль в свое место.

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

  • 0
    "return PChar" - но на какую память должен указывать PChar?
  • 0
    Некоторый код выше

Ещё вопросы

Сообщество Overcoder
Наверх
Меню