views:

308

answers:

2

Here's the native (Delphi 7) function:

function Foo(const PAnsiChar input) : PAnsiChar; stdcall; export;
var
  s : string;
begin
    s := SomeInternalMethod(input);
    Result := PAnsiChar(s);
end;

I need to call this from C#, but the name of the dll is not known at compile time - so I must use LoadLibrary to get to it.

This is what my C# code looks like so far:

[DllImport("kernel32.dll")]
public extern static IntPtr LoadLibrary(String lpFileName);

[DllImport("kernel32.dll")]
public extern static IntPtr GetProcAddress(IntPtr handle, string funcName);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate string FooFunction(string input);

...

IntPtr dllHandle = LoadLibrary(dllName);
IntPtr fooProcAddr = GetProcAddress(dllHandle, "Foo");

FooFunction foo = (FooFunction)Marshal.GetDelegateForFunctionPointer(
    fooProcAddr, typeof(FooFuncion)
);

string output = foo(myInputString);

Now, this actually works - at least, the delphi code receives the string correctly, and the C# code receives the output string.

However, I've noticed some weirdness when debugging the delphi code when it's called from the C# code - the debugger skips lines when it shouldn't..

And I'm concerned that I'm leaking memory - is anyone cleaning up those PChars?

Can anyone give me some feedback / advice on how this should be done?

+3  A: 

The only reasonable thing that you can do is trash this function and rewrite it. There is no way this is ever going to work. s is a local string variable of the Foo() function, so the memory the string occupies will be freed when you leave Foo(). The pointer you return points to an invalid memory location, which by chance still contains the string data. If you use a memory manager that clears the memory when pointers to it are freed it won't even contain the data any more. If memory is reused it will contain something else, if the block containing that chunk of memory is released you will get an AV.

There are more questions here on StackOverflow how to return character sequence data from a DLL. Either use a string type that is compatible with the way the Windows API does business, a COM string, or pass a preallocated buffer to your function and fill that with data. In the latter case you can use the same way of using your function like with every similar API function.

mghie
Thanks, I was confused because it actually worked - I thought maybe that PChar cast was doing something clever like allocating memory.
Blorgbeard
Can you point me to a resource on "the way the Windows API does business"? I don't want to have to preallocate a buffer, because the length of the string returned will vary wildly.
Blorgbeard
OK, I'm trying this approach: http://stackoverflow.com/questions/1699736/how-can-i-return-a-pchar-from-a-dll-function-to-a-vb6-application-without-risking/1699889#1699889
Blorgbeard
I couldn't explain it any better than Rob Kennedy did in his answer to the linked question: http://stackoverflow.com/questions/1699736/how-can-i-return-a-pchar-from-a-dll-function-to-a-vb6-application-without-risking/1701864#1701864. But there are some more if you search for "c#" and "dll" in the [delphi] tag. The question are different, but it always boils down to the same things: don't allocate and free memory in different modules unless you use the same memory manager (impossible with Delphi and C#). Use basic types only. And don't return pointers to things that are no longer valid.
mghie
And it's no problem to preallocate a buffer, if you a) declare a maximum size for returned strings (like `MAX_PATH`), or b) you use a two step algorithm, with the first step returning only the necessary buffer size, and the second with a buffer of at least that size. Rob explains it quite well.
mghie
+2  A: 

For memory leak detection you can use the open source FastMM4 memory manager for Delphi.

"FastMM is a lightning fast replacement memory manager for Embarcadero Delphi Win32 applications that scales well in multi-threaded applications, is not prone to memory fragmentation, and supports shared memory without the use of external .DLL files."

It is great for speed, leak checking and memory sharing between dll's.

Also very useful is the FastMM4 Options Interface which helps to configure FastMM4.

mjustin
Cheers, I'll take a look at that
Blorgbeard