以下是Delphi DLL代码:
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将allocmem分配给pOut param,而BlockFree用于释放由MsgEncode分配的内存。
我的问题是:如何在C#中使用此dll?我是C#的新手。
答案 0 :(得分:9)
我将以面值来回答您的问题,并附带几个附带条件:
PChar
来了解互操作代码,因为PChar
在AnsiChar
和WideChar
之间浮动,具体取决于Delphi的版本。我假设您使用Unicode Delphi。如果没有,那么你需要在P / Invoke端更改字符串编组。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上进行相当多的阅读。享受!
答案 1 :(得分:4)
请注意,C#字符串数据是Unicode,因此如果您使用PChar继续使用此Delphi代码,则会在PInvoke调用中执行从PChar到PWideChar的隐藏转换。 (转换意味着分配另一个内存缓冲区并将所有数据复制到新缓冲区)如果您打算将此Delphi代码与C#一起使用并且关心性能,则应更改Delphi代码以对PWideChar数据进行操作。
使用PWideChar而不是PChar还有另一个原因:Delphi根据COM要求使用Win32 SysAllocString分配器分配OleString类型。这意味着字符串的接收者能够使用Win32 API解除分配它。
如果您实际上没有在函数中处理文本,而是使用PChar作为任意字节值数组的代理,那么您可以在调用的非托管端而不是托管端侥幸逃脱。如果是字节数据,则应将其声明为字节数组,以避免字符集或字符大小转换。
在房子的C#端,你需要使用PInvoke来调用非托管的Delphi DLL函数。有关如何在C#中注释调用以使PInvoke自动处理缓冲区分配的详细信息,请参阅pinvoke.net。找到一个Win32 API函数,它传递类似于你的函数的PChar(或PWideChar)参数,然后搜索PInvoke.net以获取在托管代码中使用的PInvoke声明。
答案 2 :(得分:1)
<强>被修改强>
抱歉,我没有看到你也导出了BlockFree函数。
经验法则是:始终在同一模块中分配和释放内存;如果你在Dll中分配内存,它也应该在同一个Dll中释放。
因此,如果您使用BlockFree释放内存,则可以在同一模块中分配和释放内存,这没关系。
请注意,Delphi字符串和PChar类型取决于版本 - 它们是Delphi 2009之前的ANSI和Delphi 2009及之后的UNICODE。