2

I have native DLL library wrote in Delphi. I want to refer to this lib from C# and get returned string.

Function

function GetCode (aUser,aPassword: PAnsiChar): PAnsiChar; stdcall;

C# code

[DllImport("C:\\library.dll", CallingConvention = CallingConvention.StdCall,
        CharSet = CharSet.Ansi)]
private static extern String GetCode([MarshalAs(UnmanagedType.LPStr)]string aUser, [MarshalAs(UnmanagedType.LPStr)]string aPassword);

public String getCode(String login, String password) {
        return GetCode(login, password);
}

Trying call function Application exits with code -1073740940 (0xc0000374).

Do you have any expirence with it? Thank you for help

  • 1. do you get a proper exception message when calling this under a debugger? 2. is the function exported? 3. is the function name mangled? 4. how are you supposed to free the return value? – Lucas Trzesniewski Nov 05 '16 at 16:58
  • 1. No, the application ends with an NTSTATUS error code. 2. Yes it is, otherwise a managed exception would have been raised. 3. See 2. 4. Very good question! – David Heffernan Nov 05 '16 at 17:01
  • @DavidHeffernan you're right, I should have googled for 0xc0000374 first. (I wanted to make sure OP didn't miss a managed exception by not running under the debugger) – Lucas Trzesniewski Nov 05 '16 at 17:14
  • @LucasTrzesniewski 4.) If you have control over the DLL you can implement an additional function to free the value. For example a `FreeCode` function (with a pointer to the string as parameter) could be called from C# code after it has finished working with the string (eg. copied it to another variable). – quasoft Nov 05 '16 at 17:27
  • @quasoft yes that's one way. Another way is to pass a buffer and a size to the function so it can fill it. You need to know the maximum string length beforehand though. P/Invoke lets you supply a `StringBuilder` for this, you just need to initialize it with the capacity you need. – Lucas Trzesniewski Nov 05 '16 at 18:04
  • @LucasTrzesniewski Yes thats right, but I dont have possible to implement anything to this library. There is no any function to free memory or function's arguments for pointer. Thanks for help. – Juliusz Hajdacki Nov 09 '16 at 19:37

1 Answers1

5

With a return value of string, the marshaller assumes responsibility for freeing the returned string. It does so with a call to CoTaskMemFree. No doubt your string was not allocated on the COM heap. Hence the error, which is the NTSTATUS error code of STATUS_HEAP_CORRUPTION.

Your p/invoke should return an IntPtr instead to avoid this:

[DllImport("C:\\library.dll", CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Ansi)]
private static extern IntPtr GetCode(string aUser, string aPassword);

To get the string, pass the return value to Marshal.PtrToStringAnsi.

public string getCode(string login, string password) 
{
    IntPtr retval = GetCode(login, password);
    string result = Marshal.PtrToStringAnsi(retval);
    // how do we deallocate retval
    return result;
}

Note also that I remove the superfluous MarshalAs attributes. CharSet.Ansi takes care of the input argument marshalling.

You still need to work out how to deallocate the returned memory. We cannot tell how it was allocated. You'll need to contact the developer of the Delphi code, or read the source. Do not be surprised if the native Delphi heap was used, which leaves you in a bind. Using the COM heap by calling CoTaskMemAlloc is actually not a bad option, and then your original p/invoke return value of string would be fine.

Other options include exporting a deallocator or asking the caller to allocate the buffer. There are a great many examples on the web of how to implement the various options.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Allocation and deallocation can be handle by using `WideString` in Delphi and `UnmanagedType.BStr` in C#. However do not use this as the return value but as a parameter (see http://stackoverflow.com/questions/9349530/why-can-a-widestring-not-be-used-as-a-function-return-value-for-interop) – Remko Nov 06 '16 at 20:38
  • Thank you very much :-) This works great, but I have problems with deallocation of returned memory. I have used Marshal.FreeHGlobal(retval); but it still makes application crash (after closing) and process never die. Do you have any idea ? – Juliusz Hajdacki Nov 09 '16 at 17:51