views:

2099

answers:

8

I have a Delphi DLL with a function defined as:

function SubmitJobStringList(joblist: tStringList; var jobno: Integer): Integer;

I am calling this from C#. How do I declare the first parameter as a tStringList does not exist in C#. I currently have the declaration as:

[DllImport("opt7bja.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int SubmitJobStringList(string[] tStringList, ref int jobno);

But when I call it I get a memory access violation exception.

Anyone know how to pass to a tStringList correctly from C#?

+11  A: 

You'll most likely not have any luck with this. The TStringList is more than just an array, it's a full-blown class, and the exact implementation details may differ from what is possible with .NET. Take a look at the Delphi VCL source code (that is, if you have it) and try to find out if you can rebuild the class in C#, and pass it with the help of your best friend, the Interop Marshaller. Note that even the Delphi string type is different from the .NET string type, and passing it without telling the marshaller what he should do, he will pass it as a char-array, most likely.

Other than that, I would suggest changing the Delphi DLL. It's never a good thing to expose anything Delphi-specific in a DLL that is to be used by non-Delphi clients. Make the parameter an array of PChar and you should be fine.

OregonGhost
A: 

I am not exactly clear your way of using delphi and C#. It seems you have created a Win32 DLL which you want to call from C#. Offcourse you must be using PInvoke for this.

I would suggest that you create a .NET DLL using your source code since complete porting of VCL is available. I can further elaborate if you wish....

Hemant
A: 

In theory, you could do something like this by using pointers (casting them as the C# IntPtr type) instead of strongly typed object references (or perhaps wrapping them in some other type to avoid having to declare unsafe blocks), but the essential catch is this: The Delphi runtime must be the mechanism for allocating and deallocating memory for the objects. To that end, you must declare functions in your Delphi-compiled DLL which invoke the constructors and destructors for the TStringList class, you must make sure that your Delphi DLL uses the ShareMem unit, and you must take responsibility for incrementing and decrementing the reference count for your Delphi AnsiStrings before they leave the DLL and after they enter it, preferably also as functions exported from your Delphi DLL.

In short, it's a lot of work since you must juggle two memory managers in the same process (the .NET CLR and Delphi's allocators) and you must both manage the memory manually and "fool" the Delphi memory manager and runtime. Is there a particular reason you are bound to this setup? Could you describe the problem you are trying to solve at a higher level?

Mihai Limbășan
A: 

As Hemant Jangid said, you should easily be able to do this by compiling your code as a .NET dll and then referring to that assembly in your c# project.

Of course, you'll only be able to do this if the version of Delphi you have has Delphi.NET.

Shunyata Kharg
+2  A: 

If this is your DLL, I'd rewrite the function to accept an array of strings instead. Avoid passing classes as DLL parameters.

Or, if you really want to use a TStringList for some reason, Delphi's VCL.Net can be used from any .Net language.

An old example using TIniFile: http://cc.codegear.com/Item/22691

The example uses .Net 1.1 in Delphi 2005. Delphi 2006 and 2007 support .Net 2.0.

Bruce McGee
A: 

Thanks everyone for the reply's.

I don't control the DLL, it's written by a third party and I know its definately an older version of Delphi that can't compile a .NET DLL. I'm going to ask them to change the parameter to accept an array of PChars as suggested by OregonGhost.

Andy
+1  A: 

If you don't control the DLL and they can't or won't change it, you could always write your own Delphi wrapper in a separate DLL with parameters that are more cross-language friendly.

Having a class as a parameter of a DLL function really is bad form.

Bruce McGee
A: 

I don't know a lot about c#, but a technique that I use for transporting stringlists across contexts is using the .text property to get a string representing the list, then assigning that property on "the other side".

It's usually easier to get a string over the wall that it is a full blown object.