Problems with DllImport and a Delphi native DLL.

T

Thomas Due

Hi,

I manage an rather old application in which we have some fairly complex (ugly) Delphi code. This is Delphi 6 we're talking about.

Among all this Delphi code there is method for formating a print layout for slip printers. Pass a few parameters to this method and it returns a Delphi string which contains the entire slip, ready for sending to the slip printer.

Now, we're slowly migrating to .NET and am in need of printing this slip from a .NET application. However instead og re-inventing the wheel for generating this slip, we'd like to create a simple DLL in Delphi which is then consumed by the .NET application.

However, this has turned out to pose a few problems.

=====================================================
My first attempt at this:

-------------------------
Delphi method prototype:
------------------------
function GetSlipData(
ARegNo: integer;
APrinterID: integer;
ASlipNo: integer) : string; stdcall;

-------------------------
C# DllImport declaration:
-------------------------
[DllImport("SlipPrint.dll",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static extern string GetSlipData(
int regNo,
int printerId,
int slipId);

This results in a

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

Access violation exception.

So, I research and discover that I can't apparently return strings to .NET application through p/invoke, as "strings are immutable". Fair enough, I try using a StringBuilder instead.

=====================================================
My second attempt at this:

-------------------------
Delphi method prototype:
------------------------
function GetSlipData(
AEkspNr: integer;
APrinterID: integer;
ASlipNo: integer;
var output : string) : integer; stdcall;

-------------------------
C# DllImport declaration:
-------------------------
[DllImport("SlipPrint.dll",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
internal static extern int GetSlipData(
int regNo,
int printerId,
int slipId,
[MarshalAs(UnmanagedType.LPStr)]StringBuilder output);

Now I don't get any exceptions, the method returns the correct length of the string, but the StringBuilder only contains five characters.

You see, the string may very well contain null characters, so how on earth do I solve this problem?

How do I pass a string that may contain null characters from a Delphi DLL to a .NET application?

--
Thomas Due
Posted with XanaNews version 1.18.1.6

"If you want a friend in Washington, get a dog."
-- Harry S. Truman
 
M

Mathieu Cartoixa

Thomas Due a écrit :
Hi,

I manage an rather old application in which we have some fairly complex (ugly) Delphi code. This is Delphi 6 we're talking about.

Among all this Delphi code there is method for formating a print layout for slip printers. Pass a few parameters to this method and it returns a Delphi string which contains the entire slip, ready for sending to the slip printer.

Now, we're slowly migrating to .NET and am in need of printing this slip from a .NET application. However instead og re-inventing the wheel for generating this slip, we'd like to create a simple DLL in Delphi which is then consumed by the .NET application.

However, this has turned out to pose a few problems.

=====================================================
My first attempt at this:

-------------------------
Delphi method prototype:
------------------------
function GetSlipData(
ARegNo: integer;
APrinterID: integer;
ASlipNo: integer) : string; stdcall;

-------------------------
C# DllImport declaration:
-------------------------
[DllImport("SlipPrint.dll",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static extern string GetSlipData(
int regNo,
int printerId,
int slipId);

This results in a

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

Access violation exception.

From what I can remember, strings in Pascal (Delphi) are prefixed by
their length, and are allocated (and freed) by specific functions (in
the ShareMem unit).
This conflicts with the definition of strings in .NET, which are null
('\0') terminated, and allocated (and freed) by the .NET environment.

In your case, you want the delphi code to allocate a string, and the
..NET environment (GC) to free it automatically. So you have to choose a
string format that is recognized by both sides : BSTR.

1. Make a wrapper in Delphi for your function that return a BSTR (cf.
http://groups.google.com/group/borland.public.delphi.objectpascal/msg/da2ff657886892ff).
2. Change your return type attribute to [MarshalAs(UnmanagedType.BStr)]
or [MarshalAs(UnmanagedType.AnsiBStr)] (you will have to determine this
by yourself).

That is all, your original BSTR will be automatically freed by the .NET
environment, calling the proper system API.
So, I research and discover that I can't apparently return strings to .NET application through p/invoke, as "strings are immutable". Fair enough, I try using a StringBuilder instead.

I am pretty sure you can. The truth is elsewhere...
=====================================================
My second attempt at this:

-------------------------
Delphi method prototype:
------------------------
function GetSlipData(
AEkspNr: integer;
APrinterID: integer;
ASlipNo: integer;
var output : string) : integer; stdcall;

-------------------------
C# DllImport declaration:
-------------------------
[DllImport("SlipPrint.dll",
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
internal static extern int GetSlipData(
int regNo,
int printerId,
int slipId,
[MarshalAs(UnmanagedType.LPStr)]StringBuilder output);

Now I don't get any exceptions, the method returns the correct length of the string, but the StringBuilder only contains five characters.

You see, the string may very well contain null characters, so how on earth do I solve this problem?

How do I pass a string that may contain null characters from a Delphi DLL to a .NET application?

See my answer above. Had there been no null character in your string,
you would very likely have got an exception as well.

Mathieu
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top