none
return WCHAR** from C++ DLL to C# RRS feed

  • Question

  • I have a DLL function

    WCHAR** GetDevicesesNames()
    {
    	WCHAR **names = new WCHAR*[CountDevices]; 
    	for (UINT32 i = 0; i < CountDevices; i++)
    		ppDevices[0]->GetAllocatedString(
    			MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, 
    			&names[i], 
    			NULL
    			);
    	return names;
    }
    How to work with return value in C#
    Saturday, September 15, 2012 8:51 AM

Answers

  • Hello Ramirag,

    1. The return value should be declared as an IntPtr.

    2. The GetDevicesesNames() API may be declared in C# as :

    [DllImport("RamiragDLL.dll", CallingConvention=CallingConvention.StdCall)]
    public static extern IntPtr GetDevicesesNames();

    3. Hence the "names" array of WCHAR pointers will be received as an IntPtr in C#.

    4. This IntPtr by itself cannot be converted directly into an array of managed strings. Each WCHAR pointer inside "names" must be converted one by one to a managed string. Thereafter, each WCHAR pointer must be freed. And after that, the "names" array itself must be freed.

    5. Furthermore, in the C++ code, the array of WCHAR pointers that "names" points to must not be allocated via the C++ new operator. It must be allocated using either CoTaskMemAlloc() or LocalAlloc().

    6. This is because the "names" array is returned to the caller (C# code) and so it is "owned" by the client code. Being the owner of the returned array, the onus is on this client code to free the memory of this array (as mentioned in point 4).

    7. C# code can only free unmanaged memory using either :

    7.1 Marshal.FreeCoTaskMem() (which calls CoTaskMemFree(), the conjugal of CoTaskMemAlloc()).

    7.2 Or Marshal.FreeHGlobal() (which calls LocalFree(), the conjugal of LocalAlloc()).

    8. The C# code below demonstrate the points mentioned above :

    static void DoTest()
    {
        IntPtr ppStr = GetDevicesesNames();
        
        string [] ManagedStrArray;
    
        MarshalUnmananagedStrArray2ManagedStrArray(ppStr, 4, out ManagedStrArray);
    }

    8.1 The code above calls GetDevicesesNames() to receive "names" as an IntPtr.

    8.2 Thereafter, MarshalUnmananagedStrArray2ManagedStrArray() (code provided below) is called to convert "names" into a managed array of strings and also to free the unmanaged strings inside "names" and also to free the "names" array itself.

    9. The following is the code for MarshalUnmananagedStrArray2ManagedStrArray() :

    // This method transforms an array of unmanaged character pointers (pointed to by pUnmanagedStringArray)
    // into an array of managed strings.
    //
    // This method also destroys each unmanaged character pointers and will also destroy the array itself.
    static void MarshalUnmananagedStrArray2ManagedStrArray
    (
      IntPtr pUnmanagedStringArray, // Pointer to an array of unmanaged WCHAR pointers.
      int StringCount,  // The total number of WCHAR*s in pUnmanagedStringArray.
      out string[] ManagedStringArray  // Receipient of the array of managed strings.
    )
    {
        // First allocate an array of IntPtrs.
        IntPtr[] pIntPtrArray = new IntPtr[StringCount];
        // Also allocate an array of managed strings using
        // the ManagedStringArray parameter.
        ManagedStringArray = new string[StringCount];
    
        // Copy the WCHAR*s from pUnmanagedStringArray to 
        // the array of IntPtrs (pIntPtrArray).
        Marshal.Copy(pUnmanagedStringArray, pIntPtrArray, 0, StringCount);
    
        // Then, one by one, convert each of the WCHAR*s 
        // inside the pIntPtrArray array into a managed string.
        // Simultaneously, clear away the WCHAR* using
        // Marshal.FreeCoTaskMem() - this is why we must use
        // CoTaskMemAlloc() and cannot use the C++ new nor
        // the C malloc().
        for (int i = 0; i < StringCount; i++)
        {
            ManagedStringArray[i] = Marshal.PtrToStringUni(pIntPtrArray[i]);
            Marshal.FreeCoTaskMem(pIntPtrArray[i]);
        }
        
        // Finally, free the entire buffer of unmanaged
        // WCHAR*s.
        Marshal.FreeCoTaskMem(pUnmanagedStringArray);
    }

    9.1 Please refer to the self-explanatory comments above for the working details of the function.

    10. I have previously written an article that expounds on something similar. Please refer to :

    Returning an Array of Strings from C++ to C# Part 1

    (http://limbioliong.wordpress.com/2011/08/14/returning-an-array-of-strings-from-c-to-c-part-1/)

    - Bio.



    Please visit my blog : http://limbioliong.wordpress.com/

    • Marked as answer by Ramirag Sunday, September 16, 2012 8:07 AM
    Saturday, September 15, 2012 3:56 PM