Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

ASP.NET web app calling Delphi DLL on IIS webserver, locks up when returning PChar string

Ask Question

Works fine if I don't return anything, or I return an integer. But if I try to return a PChar, ie..

result := PChar('')  or   result:= PChar('Hello')

The web app just freezes up and I watch its memory count gradually get higher and higher in task manager.

The odd thing is that the DLL works fine on the VStudio debug server, or through a C# app. The only thing I can think of that would make a difference is that the IIS server is running in 64bit Windows.

It doesn't appear to be a compatability issue though because I can successfully write to text files and do other things from the DLL... I just can NOT return a PChar string.

Tried using PWideChar, tried returning 'something\0', tried everything I could think of. No luck unfortunately.

[DllImport("TheLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern string SomeFunction();
string result = SomeFunction();

delphi:

library TheLib;
function SomeFunction() : PChar export; stdcall;
begin
return PChar('');
exports
    SomeFunction

Dampsquid's analysis is correct so I will not repeat that. However, I prefer a different solution that I feel to be more elegant. My preferred solution for such a problem is to use Delphi Widestring which is a BSTR.

On the Delphi side you write it like this:

function SomeFunction: Widestring; stdcall;
begin
  Result := 'Hello';

And on the C# side you do it like this:

[DllImport(@"TheLib.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string SomeFunction();

And that's it. Because both parties use the same COM allocator for the memory allocation, it all just works.

Update 1

@NoPyGod interestingly points out that this code fails with a runtime error. Having looked into this I feel it to be a problem at the Delphi end. For example, if we leave the C# code as it is and use the following, then the errors are resolved:

function SomeFunction: PChar; stdcall;
begin
  Result := SysAllocString(WideString('Hello'));

It would seem that Delphi return values of type WideString are not handled as they should be. Out parameters and var parameters are handled as would be expected. I don't know why return values fail in this way.

Update 2

It turns out that the Delphi ABI for WideString return values is not compatible with Microsoft tools. You should not use WideString as a return type, instead return it via an out parameter. For more details see Why can a WideString not be used as a function return value for interop?

I have to wait till monday to try your solution on IIS at work, but the reasoning behind it sounds solid to me. One thing though.. I've been testing the code at home, and I can't get the return value to work, it just gives an exception 'Attempted to read or write protected memory.'. I am however able to achieve what I want to do by using an 'out' variable and passing the result that way. Do you know why I would be unable to return? I am marshalling the result just as in your code. Is it because it's Delphi 2007? I am fine to use the 'out' trick, but I'd like to know why it's necessary. – NoPyGod Feb 18, 2012 at 22:40 @kobik The pinvoke marshaller will do that for you. So you don't need to. What's more, since the C# declaration does not use IntPtr, you could not get hold of a native pointer on which to call SysFreeString. – David Heffernan Feb 19, 2012 at 19:03

You cannot return a string like that, the string is local to the function and will be freed as soon as he function returns leaving the returned PChar pointing to an invalid location.

you need to pass in a pointer to be filled within the DLL, dynamically create the string and free it back in the c# code or create a static buffer in yout DLL and return that.

By far the safest way is to pass a pointer into the function ie

function SomeFunction( Buffer: PChar; MaxLength: PInteger ): wordbool; stdcall;
  // fill in the buffer and set MaxLength to length of data

you should set MaxLength to the sixe of the buffer before calling your dll so that the dll can check there is enough space for the data to be returned.

What would the code look like on the c# side? Because it looks the same here excluding the maxlength param. And why doee the code work outside of iis? Thanks – NoPyGod Feb 17, 2012 at 12:04 I guess u make a typo, should Pchar be some variable name. Look forward to trying this when i get to a computer. – NoPyGod Feb 17, 2012 at 12:07 You could return a BSTR and let the marshaller handle things. That would be my preferred option. – David Heffernan Feb 17, 2012 at 12:18 yep sorry made a mistake buffer is var pchar is type. not done much c# recently so cant really comment on that, as to it working outside iis, thats just because the memeory pointed to still contains the string although it is no longer valid, its just lucky that it hasnt been overwritten yet. – Dampsquid Feb 17, 2012 at 12:18

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.