views:

281

answers:

2

I need to send a string from C# to a C++ WindowProc. There are a number of related questions on SO related to this, but none of the answers have worked for me. Here's the situation:

PInvoke:
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, string lParam);

C#:
string lparam = "abc";
NativeMethods.User32.SendMessage(handle, ConnectMsg, IntPtr.Zero, lparam);

C++:
API LRESULT CALLBACK HookProc (int code, WPARAM wParam, LPARAM lParam)
{       
    if (code >= 0)
    {
        CWPSTRUCT* cwp = (CWPSTRUCT*)lParam;
                ...
        (LPWSTR)cwp->lParam   <-- BadPtr
                ...
    }

    return ::CallNextHookEx(0, code, wParam, lParam);
}

I've tried a number of different things, Marshalling the string as LPStr, LPWStr, also tried creating an IntPtr from unmanaged memory, and writing to it with Marshal.WriteByte.

The pointer is the correct memory location on the C++ side, but the data isn't there. What am I missing?

+2  A: 

For C++ LPWSTR or LPSTR parameters you need to use the C# StringBuilder in your DllImport.

For C++ LPCWSTR or LPCSTR parameters you need to use the C# string in your DllImport.

Brian R. Bondy
Can you give an example?
Nayan
+1  A: 

Make sure that your SendMessage call is occurring in the expected, synchronous manner and that your NativeMethods class maps the proper Win32 call (Send vs. PostMessage.) If this isn't correct it's possible that by the time your message is processed on the C++ end, you've left the scope of your C# method and any local variables created on the stack are gone resulting in your bad pointer.

Stack and heap considerations for cross-thread calls: Threads have their own stacks but share the heap. Stack-allocated variables in one thread will not be visible in another. The string type is an odd duck in .NET. It is an Object-derived, reference type but made to look and feel like a value type in code. So perhaps passing a pointer to heap-allocated data ought to work. That's where StringBuilder, as a heap-allocated reference type, comes in.

Paul Sasik
I'm definitely using SendMessage, so I shouldn't be returning to C# until after the message is processed.
jaws
Is this a cross-thread call? Threads have their own stacks but share the heap. Stack-allocated variables in one thread will not be visible in another. The string type is an odd duck in .NET. It is Object-derived, reference type but made to look and feel like a value type. So passing a pointer to heap-allocated data ought to work. Perhaps that's where StringBuilder comes in.
Paul Sasik
Yes it is cross-thread, that makes total sense.
jaws