Search code examples
c#dllimportunmanagedwchar-tinteropservices

Marshal unmanaged variables in c#


I am importing an unmanaged dll on my C# code. The .h file describes methods as shown bellow

DLL_API int __stdcall SetConfiguration(IN char* configuration);

DLL_API int  __stdcall GetErrorMessage_UTF16(
    INOUT int* errorGroup, 
    INOUT char* errorCode, /*It must be allocated by the caller and its length must be at least 10 bytes, otherwise the function will crash*/
    INOUT wchar_t *messageText, 
    IN int bufferLength);

I've managed to call the SetConfiguration method

[DllImport(DllPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern int SetConfiguration([In] MarshalAs(UnmanagedType.LPStr)] string configuration);

Supplying a simple C# string did the job.

Now i am trying to GetErrorMessage_UTF16 method with no success. So, how do i allocate the length of errorCode and how do i declare the wchar_t *messageText variable?

I've tried something like this, but it doesn't seem to work

[DllImport(DllPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern int GetErrorMessage_UTF16(
    [In, Out] int errorGroup, 
    [In, Out] [MarshalAs(UnmanagedType.LPStr)] string errorCode, 
    [In, Out] [MarshalAs(UnmanagedType.LPStr)] StringBuilder messageText, 
    [In] int bufferLength);

Whatever i've tried i get

Exception thrown at 0x0f5d1235 (Mydll.dll) in Mydll.exe: 0xc0000005: Access violation writing location 0x067d1000

If there is a handler for this exception, the program may be safely continued.

Solution

  • OK, i've found the answer

    [DllImport(DllName, EntryPoint = "GetErrorMessage_UTF16", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    public static extern int GetErrorMessage_UTF16(out int errorGroup, IntPtr errorCode, IntPtr messageText, int bufferLength);
    

    For errorCode and messageText create a pointer and then allocate the size.

    IntPtr errorCode = Marshal.AllocHGlobal(10);
    IntPtr messageText = Marshal.AllocHGlobal(someBufferLength * sizeof(char));
    

    Don't forget to free the allocated space when the variables are not needed.