views:

137

answers:

4

Hello, I am working with an application in C# that need to send a message to a C++ application.

I imported

[DllImport("user32.dll")]
        public static extern IntPtr SendMessage(
              int hWnd,      // handle to destination window
              uint Msg,       // message
              IntPtr wParam,  // first message parameter
              IntPtr lParam   // second message parameter
              );

but now, my problem is that I need to passe string to wParam and lParam. I tried unsafe code, but it seems string just doesnt work like the rest of variables. How can I achieve that? Thanks.

A: 

Passing the address of the string would involve unsafe code, since Win32 API calls expect addresses (in a C/C++ native envrion). What windows message are you sending that requires a string in wParam or lParam?

Daniel
A custom message. And I cant passe the adresse of the string. It just doesnt want to build it.
Wildhorn
+9  A: 

The declaration is wrong, the wParam and lParam arguments are IntPtr, not long.

There is a complication because you are trying to send strings. What matters if the target window is Unicode enabled or not. There are two versions of SendMessage, SendMessageA() and SendMessageW(). The former needs to be used if the program is dated and uses 8-bit character strings rather than UTF-16 encoded strings.

You can find out by using Spy++. Use the finder tool to select the window of the application. In the General property tab, you'll see "Window proc". It will say (Unicode) if the window is Unicode enabled. If you don't see it then the strings have to be translated to 8-bit characters.

To generate the string pointers you need to pass, you can use Marshal.StringToHGlobalAnsi or StringToHGlobalUni (respectively 8-bit and Unicode). You can however play a trick to let the P/Invoke marshaller translate the string for you. Saves you the hassle of having to free the strings after the call. For the Ansi version, you can declare the API function like this:

    [DllImport("user32.dll", CharSet = CharSet.Ansi, EntryPoint = "SendMessageA", ExactSpelling = true)]
    private static extern IntPtr SendMessageStrings(IntPtr hWnd, int msg, string wParam, string lParam);

And the Unicode version like this:

    [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW", ExactSpelling = true)]
    private static extern IntPtr SendMessageStrings(IntPtr hWnd, int msg, string wParam, string lParam);

One final note: this will not work as-is if the window belongs to another application, you'll crash it. The pointer values you pass are only valid in your own process, not in the C++ process. To work around that, you have to allocate memory in the target process so that the pointer is valid. That requires OpenProcess to get a handle to the process, VirtualAllocEx() to allocate memory in the target process, big enough to store the string, WriteProcessMemory to write the string. Now you can call SendMessage(), use a version that is declared with IntPtr for the wParam and lParam arguments, pass the value you got from VirtualAllocEx. Next use VirtualFreeEx() to release the memory and CloseHandle to clean up. Or keep the memory around for the next time if you do this often.

Quite a lot of P/Invoke to get wrong there. Not to mention security issues, WriteProcessMemory requires admin privileges, UAC elevation is required.

Hans Passant
About your "final note", do you have some code example? Because I am quite lost right now. And yes, the target application is another application (like I said, it goes from a C# app to a C++ app)I have the target application process, my problem is passing the string to it.
Wildhorn
Hans Passant
A: 

We ended up using "WmCpyDta_d.dll" to deal with all that.

Wildhorn