Reputation: 1957
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 the memory which alloced by MsgEncode.
My question is: How can I use this dll in C#? I'm a newbie in C#.
Upvotes: 3
Views: 14930
Reputation: 613441
I'm going to take your question at face value, with a few provisos:
PChar
because PChar
floats between AnsiChar
and WideChar
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.BlockFree
so that it can receive an untyped pointer. There's no need for it to be types as PChar
, it's just calling Free
.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!
Upvotes: 10
Reputation: 36092
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.
Upvotes: 4
Reputation: 27493
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.
Upvotes: 1