How to Modify Managed Strings in Native Code?

Want to modify a managed string passed to a native function? I collected two ways of doing it. Either you can use System::Text::StringBuilder mechanism or you can use the array method mentioned at https://msdn2.microsoft.com/en-us/library/s04yfy1s(VS.80).aspx

You can pass the string that you want to modify as a one-element string array to the native code and modify the array, or you can pass a StringBuilder variable and modify it.

In the array mechanism, construct a native function that looks like below:

void NativeFunction(PWSTR lpszString[])
{
lpszString[0] = L"Hello World"; // Modifies the string
}

The managed code to P/Invoke this should be as below:

    class Program
    {
        [DllImport("NativeDll.dll")]
        static extern int NativeFunction([In][Out][MarshalAs(UnmanagedType.LPArray,ArraySubType=UnmanagedType.LPWStr, SizeConst=1)] String[] str); 

        static void Main(string[] args)
        {
            String[] myArr = new String[] { "One" }; 

            NativeFunction(myArr); // Call the Native Method

            System.Console.WriteLine(myArr[0].ToString()); // Should Print "HelloWorld"
        }
    }

In the above, we are DllImporting the native function by marshalling the parameter as an in,out,LPWStr array of size 1.

Note that this requires declaring the native function parameter as an array of strings (instead of single string). This should be fine as long as we have the flexibility to change the native function prototype. If the native function is an API Callback (e.g. GetWindowText) then we cannot use this method.

In such cases using StringBuilder is the recommended approach. If we have a native method declaration as below,

void GetComputerName (PWSTR lpszString, int nBufLen)
{      
wcsncpy(lpszString, L"Home-PC", nBufLen); // Modifies the string
}

In the managed code, use the following DllImport options:

        [DllImport("NativeDll.dll")]
        static extern void GetComputerName ([In][Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, [In] int nBufLen);

        static void Main(string[] args)
        {
            StringBuilder ComputerName = new StringBuilder();

            ComputerName.EnsureCapacity(1024);

            fnWin32DLL(ComputerName, 1024); // Call the Native Method 

            System.Console.WriteLine(ComputerName.ToString()); // Should Print "Home-PC"
        }

In the managed code, declare the string parameter as an In,Out, StringBuilder and pass a StringBuilder variable (after ensuring a minimum buffer size with EnsureCapacity() function to avoid buffer access problems in the native code).