Как использовать Delphi Dll (с типом PChar) в C # - PullRequest
3 голосов
/ 23 февраля 2011

Вот код DLL Delphi:

library Project2;

uses
  SysUtils,
  Classes;

{$R *.res}

function SimpleConv(const s: string): string;
var
  i: Integer;
begin
  Result := '';
  for i := 1 to Length(s) do
    if Ord(S[i]) < 91 then
      Result := Result + S[i];
end;

function MsgEncode(pIn: pchar; InLen: Integer; var pOut: pchar; var OutLen: Integer): Boolean; stdcall;
var
  sIn: string;
  sOut: string;
begin
  SetLength(sIn, InLen);
  Move(pIn^, sIn[1], InLen);

  sOut := SimpleConv(sIn);   // Do something

  OutLen := Length(sOut);
  GetMem(pOut, OutLen);
  Move(sOut[1], pOut^, OutLen);
  Result := OutLen > 0;
end;

procedure BlockFree(Buf: pchar); stdcall;
begin
  if assigned(Buf) then
     FreeMem(Buf);
end;

exports
  MsgEncode,
  BlockFree;

begin
end.

Функция Dll MsgEncode будет выделять для параметра pOut, а BlockFree используется для освобождения памяти, выделенной MsgEncode.

Myвопрос: Как я могу использовать эту DLL в C #?Я новичок в C #.

Ответы [ 3 ]

9 голосов
/ 23 февраля 2011

Я собираюсь принять ваш вопрос за чистую монету с несколькими оговорками:

  • Независимо от того, используете ли вы Unicode Delphi или нет, важно знать, что для кода взаимодействия используется PChar, поскольку PChar находится в диапазоне от AnsiChar до WideChar в зависимости от версии Delphi. Я предположил, что вы используете Unicode Delphi. Если нет, то вам нужно изменить сортировку строк на стороне P / Invoke.
  • Я изменил ваш код DLL. Я удалил параметры длины и работаю в предположении, что вы позволите доверенному коду вызывать эту DLL. Ненадежный код может привести к переполнению буфера, но вы не позволите запустить ненадежный код на вашем компьютере, не так ли?
  • Я также изменил BlockFree, чтобы он мог получать нетипизированный указатель. Нет необходимости, чтобы это были типы как PChar, он просто вызывает Free.

Вот модифицированный код Delphi:

library Project2;

uses
  SysUtils;

{$R *.res}

function SimpleConv(const s: string): string;
begin
  Result := LowerCase(s);
end;

function MsgEncode(pIn: PWideChar; out pOut: PWideChar): LongBool; stdcall;
var
  sOut: string;
  BuffSize: Integer;
begin
  sOut := SimpleConv(pIn);
  BuffSize := SizeOf(Char)*(Length(sOut)+1);//+1 for null-terminator
  GetMem(pOut, BuffSize);
  FillChar(pOut^, BuffSize, 0);
  Result := Length(sOut)>0;
  if Result then
    Move(PChar(sOut)^, pOut^, BuffSize);
end;

procedure BlockFree(p: Pointer); stdcall;
begin
  FreeMem(p);//safe to call when p=nil
end;

exports
  MsgEncode,
  BlockFree;

begin
end.

А вот код C # с другой стороны:

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        [DllImport("project2.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool MsgEncode(string pIn, out IntPtr pOut);

        [DllImport("project2.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        public static extern void BlockFree(IntPtr p);

        static void Main(string[] args)
        {
            IntPtr pOut;
            string msg;
            if (MsgEncode("Hello from C#", out pOut))
                msg = Marshal.PtrToStringAuto(pOut);
                BlockFree(pOut);
        }
    }
}

Это должно помочь вам начать. Поскольку вы новичок в C #, вам нужно немного почитать о P / Invoke. Наслаждайтесь!

4 голосов
/ 23 февраля 2011

Обратите внимание, что строковыми данными C # является Unicode, поэтому, если вы продолжите работу с этим кодом Delphi с использованием PChar, в вызове PInvoke будет выполнено скрытое преобразование из PChar в PWideChar.(Преобразование означает выделение другого буфера памяти и копирование всех данных в новый буфер). Если вы намереваетесь использовать этот код Delphi с C # и заботитесь о производительности, вам следует изменить код Delphi для работы с данными PWideChar.

Есть и другая причина использовать PWideChar вместо PChar: Delphi выделяет тип OleString с помощью распределителя Win32 SysAllocString в соответствии с требованиями COM.Это означает, что получатель строки может освободить ее с помощью Win32 API.

Если вы на самом деле не обрабатываете текст в своей функции, а используете PChar в качестве суррогата для массива произвольных значений байтов, выможет сойти с рук на неуправляемой стороне вызова, но не на управляемой стороне.Если это байтовые данные, их следует объявлять как массив байтов, чтобы избежать преобразований charset или size char.

На стороне C # дома вам понадобится PInvoke для вызова неуправляемой функции DLL Delphi.См. pinvoke.net для получения подробной информации о том, как аннотировать вызов в C #, чтобы PInvoke автоматически позаботился о выделении буфера.Найдите функцию Win32 API, которая передает параметры PChar (или PWideChar), аналогичные вашей функции, а затем найдите в PInvoke.net объявление PInvoke для использования в управляемом коде.

1 голос
/ 23 февраля 2011

Отредактированный

Извините, я не видел, чтобы вы также экспортировали функцию BlockFree.

Практическое правило: всегда выделяйте и освобождайте память в одном и том же модуле; если вы выделяете память в Dll, она также должна быть освобождена в той же самой Dll.

Так что, если вы освобождаете память с помощью BlockFree, вы выделяете и освобождаете память в том же модуле, это нормально.

Обратите внимание, что строки Delphi и типы PChar зависят от версии - это ANSI до Delphi 2009 и UNICODE в Delphi 2009 и после.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...