Here is the Delphi DLL code:
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.
The Dll function MsgEncode will allocmem to pOut param, and the BlockFree is used to free th开发者_运维技巧e memory which alloced by MsgEncode.
My question is: How can I use this dll in C#? I'm a newbie in C#.
I'm going to take your question at face value, with a few provisos:
- Whether you are using a Unicode Delphi or not is essential to know for interop code using
PChar
becausePChar
floats betweenAnsiChar
andWideChar
depending on the version of Delphi. I've assumed that you use Unicode Delphi. If not then you'd need to change the string marshalling at the P/Invoke side. - I've modified your DLL code. I've removed the length parameters and am working on the assumption that that you are only going to let trusted code call this DLL. Untrusted code could produce buffer overruns but you aren't going to let untrusted code run on your machine, are you?
- I've also changed
BlockFree
so that it can receive an untyped pointer. There's no need for it to be types asPChar
, it's just callingFree
.
Here's the modified Delphi code:
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.
And here's the C# code on the other side:
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);
}
}
}
This should get you started. Since you are new to C# you are going to need to do quite a bit of reading up on P/Invoke. Enjoy!
Note that C# string data is Unicode, so if you proceed with this Delphi code using PChar there will be a hidden conversion from PChar to PWideChar performed in the PInvoke call. (Conversion meaning allocation of another memory buffer and copying of all the data to the new buffer) If you intend for this Delphi code to be used with C# and you care about performance, you should change your Delphi code to operate on PWideChar data instead.
There is another reason to use PWideChar instead of PChar: Delphi allocates the OleString type using the Win32 SysAllocString allocator, as per COM requirements. This means the recipient of the string is capable of deallocating it using the Win32 API.
If you aren't actually processing text in your function but are using PChar as a surrogate for an array of arbitrary byte values, you can get away with that on the unmanaged side of the call but not on the managed side. If it is byte data, it should be declared as array of byte to avoid charset or char size conversions.
On the C# side of the house, you will need to use PInvoke to call the unmanaged Delphi DLL function. See pinvoke.net for details about how to annotate the call in C# to get PInvoke to take care of the buffer allocation automatically. Find a Win32 API function that passes PChar (or PWideChar) params similar to your function and then search PInvoke.net for the PInvoke declaration to use in managed code.
Edited
Sorry, I did not seen that you export BlockFree function also.
The rule of thumb is: always allocate and free memory in the same module; if you allocate memory in Dll, it should be freed in the same Dll also.
So if you free memory with BlockFree you allocate and free memory in the same module, that is OK.
Note that Delphi string and PChar types are version-dependent - they are ANSI prior Delphi 2009 and UNICODE in Delphi 2009 and after.
精彩评论