Monday, October 31, 2011

Passing Unicode strings between C# and C/C++ dynamic link libraries

Passing simple data types like int, boolean, double between managed C# code and unmanaged C/C++ code is fairly straightforward. However, passing Unicode strings between them is a little more complicated. To simplify the coding effort, wrapper functions can be written to hide away the complexity as illustrated in the examples below.

Example 1: Passing from C# to C/C++
If the C/C++ function has the following signature (where the second parameter is the Unicode string to be passed to the C++ function):

HRESULT SetGpsData( HANDLE hClient, const char* szNmeaSentence );

Then the following wrapper functions can be used:

[DllImport("myDynamicLinkLib.dll")]
private static extern int SetGpsData(IntPtr hClient, IntPtr szNmeaSentence);
public static int SetGpsData(IntPtr hClient, string szNmeaSentence)
{
    IntPtr pszNmeaSentence = IntPtr.Zero;
    pszNmeaSentence = Marshal.StringToHGlobalUni(szNmeaSentence);
    int hr = SetGpsData(hClient, pszNmeaSentence);
    Marshal.FreeHGlobal(pszNmeaSentence);
    return hr;
}

Inside the wrapper function, the Unicode string has to be copied to unmanaged memory using the Marshal class' StringToHGlobalUni method first and then passed as a pointer to the C/C++ function. Finally, free up the unmanaged memory after you are done with the FreeHGlobal method.

To use the public wrapper function, simply do the following:

IntPtr hClient = IntPtr.Zero;
string szNmea = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47";
int ret = SetGpsData( hClient, szNmea);

Example 2: Passing from C/C++ to C#
If a C/C++ function has the following signature (where the second parameter is the Unicode string value to be returned by the function):

BOOL GetTheString( HRESULT hr, LPWSTR szName, INT maxChar );

Then the wrapper functions in C# can be written as:

[DllImport("myDynamicLinkLib.dll")]
private static extern bool GetTheString(int hr, IntPtr szName, int maxChar);
public static bool GetTheString(int hr, out string szName)
{
    bool ret = false;
    IntPtr pSzName = IntPtr.Zero;
    pSzName = Marshal.AllocHGlobal(260);
    szName = string.Empty;
    if (hr != 0)
    {
        bool status = GetTheString(hr, pSzName, 260);
        if (status)
        {
            szName = Marshal.PtrToStringUni(pSzName);
        }
    }
    Marshal.FreeHGlobal(pSzName);
    return ret;
}
In this example, inside the wrapper function, unmanaged memory for the Unicode string is allocated first using the Marshal class' AllocHGlobal method. Then the pointer to that memory is passed to the C/C++ function, which will then write the string value in the allocated memory. In managed C# code, the Unicode string value is copied from the unmanaged memory using the PtrToStringUni method. As above, remember to free up the allocated memory using the FreeHGlobal method.

To use this wrapper function, simply do the following:

IntPtr hClient = IntPtr.Zero;
string szFromDLL = string.Empty;
bool ret = GetTheString( hClient, out szFromDLL);

No comments:

Post a Comment