1

I have a WPF application that needs to interface with another application.

This application has about 20 custom Windows Messages (WM_USER+50...WM_USER+70).

Summary of what I'm trying to accomplish:

WPF Application -> SendMessage -> ThirdParty application

The problem I have is that all of the Messages are CUSTOM messages. Therefore I have to implement my own data marshaling.

See http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950(v=vs.85).aspx

It seems that the process I need to go through is:

  1. Grab the process and open it for all access. User32.GetWindowThreadProcessId(windowHandle, out pId);

    // Open the process with all access
    someprocess = OpenProcess((0x1F0FFF), false, (int)pId);

  2. Allocate a buffer in the process: IntPtr buffer = VirtualAllocEx( hProcess, IntPtr.Zero, 1024, 0x1000, 0x04 );

  3. Fill up some sort of struct that will be written to the buffer created in #2?

  4. Copy #3 to the remote buffer is #2? WriteProcessMemory??

  5. Send the custom message ( SendMessage(windowhandle, customMsg, 0, buffer from #2?)

  6. Read the struct back in from the remote process buffer into a local buffer

  7. Marshal this data to a managed type. (This is a C# .Net application)

I could really use some insight. I haven't had much luck thus far. I think the part that I'm most stuck on is what type of struct to send to the WriteProcessMemory?

tronious
  • 1,547
  • 2
  • 28
  • 45
  • 3
    You could use `WM_COPYDATA` and let it do data marshaling for you. – jamesdlin Apr 30 '13 at 19:35
  • If you're doing something that's been done a zillion times before (two processes communicating) and you find yourself using debugging functions (OpenProcess, WriteProcessMemory, etc.) then this is a very clear sign that you're doing it wrong. [Windows has many ways of doing Interprocess Communication](http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574%28v=vs.85%29.aspx). – arx Apr 30 '13 at 20:07
  • I understand making two processes communicate, but am I wrong in my understanding that data marshaling has to be customized when dealing with CUSTOM messages? These are all WM_USER+X messages. – tronious Apr 30 '13 at 20:17
  • You are right, you can't send custom data with a WM_USER message. So you need to use some other mechanism to send the data. And now you're sending the data using some other mechanism, what do you need the custom message for? Custom messages are a bad choice. Since you're already using messages, `WM_COPYDATA` would be a good choice, as everybody else has noted. – arx Apr 30 '13 at 20:24
  • Thanks for the response. While this is now working using ReadProcessMemory and WriteProcessMemory. I will look into wm_copydata. I'm concerned, however, that I still need to be able to write data to and from. Also custom messaging is not my choice. I'm stuck with it for better or worse. Need to make it work as it"s what is available to me for integration. Thanks for the replies. Will select an answer after I investigate wm_copydata – tronious Apr 30 '13 at 23:12

2 Answers2

4

WM_COPYDATA is definitely the easiest way to do this. WM_COPYDATA lets you send two distinct items of data to another process - a DWORD value, and an arbitrarily-sized chunk of data. So for your implementation you would probably do something like this:

COPYDATASTRUCT cds;
cds.dwData = WM_USER + 50; // the "message" you want to send
cds.cbData = sizeof(MyDataForMessage50); // the size of the chunk of data
cds.lpData = lpMessage50Data; // a pointer to the chunk of data
SendMessage(hwndTarget, WM_COPYDATA, reinterpret_cast<WPARAM>(hwndSender),
            reinterpret_cast<LPARAM>(&cds));

Note that hwndTarget is the target window in the other process, and hwndSender is a window in the sending process. The target window receives the same parameters and so can use wParam to learn who sent the message, and can therefore send a reply if needed.

In the WndProc at the receiving end:

if (uMsg == WM_COPYDATA)
{
    HWND hwndSender = reinterpret_cast<HWND>(wParam);
    LPCOPYDATASTRUCT pcds = reinterpret_cast<LPCOPYDATASTRUCT>(lParam);
    DWORD dwCustomMsg = pcds->dwData;
    LPVOID pCustomData = pcds->lpData;
    DWORD dwCustomDataSize = pcds->cbData;

    // do something with the custom message

    // return TRUE to indicate message received
    return TRUE;
}

Also note the important note in the docs for WM_COPYDATA:

The receiving application should consider the data read-only. The lParam parameter is valid only during the processing of the message. The receiving application should not free the memory referenced by lParam. If the receiving application must access the data after SendMessage returns, it must copy the data into a local buffer

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • Question regarding wm_copydata...i have no access to the target code. This is a third party integration. Wouldn't they need to have some handler on their end to process that incoming wm_copydata message? I can request zero changes to the target – tronious Apr 30 '13 at 23:29
  • As @tronious said, the other code cannot be modified. Plus there is nothing special about WM_COPYDATA, it is a convenience but it just does the marshaling that the question asks about, nothing more. – Sam Hobbs Jan 15 '17 at 00:32
  • @user34660 dude, that was 4 years ago – Jonathan Potter Jan 15 '17 at 06:30
  • Yes, I know that, @JonathanPotter. Did you know that Stackoverflow endeavors to be a resource for use by many people forever? That is why they attempt to prevent duplicate questions. Also they attempt to include all relevant information in answers instead of portions of answers in links. So for the future people should know that WM_COPYDATA does nothing special that we can't do on our own. – Sam Hobbs Jan 15 '17 at 20:04
  • 1
    @user34660 So why not write up an answer to that effect? More useful than a snide comment. – Jonathan Potter Jan 15 '17 at 20:46
  • @JonathanPotter, I see no problem with anything I said. I do get snide comments from others but I don't see any of that in my initial comment. We need to keep comments minimal so I am not likely to reply further here. – Sam Hobbs Jan 15 '17 at 21:18
  • 1
    @user34660 nothing wrong except that it was inaccurate. OP did not say code could not be modified. Consensus in comments (four years ago) was that WM_COPYDATA was the solution. Last thing OP said was they were going to investigate it. Then 4 years later you pop up offering no new information. Write up an answer if you think you have something useful to add. – Jonathan Potter Jan 15 '17 at 21:25
  • @JonathanPotter, you are correct that the question is unclear. It was clear for me when I read it that it is likely the other application cannot be modified so if it were me **I would have asked for clarification**. The fact that there was consensus in the comments four years ago makes it more important to provide the additional information. – Sam Hobbs Jan 15 '17 at 21:33
  • 1
    @user34660 I didn't say the question was unclear, I said your comment was inaccurate. Anyway, looking forward to reading your alternative answer. – Jonathan Potter Jan 15 '17 at 21:34
-1

Sample code for Sending message:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
    class Program
    {
        public const int WM_COPYDATA = 0x4A;
        public const UInt32 WM_COMMAND = 0x0111;
        public const UInt32 IDM_MENU_SECUREDISCONNECT = 305;
        public const UInt32 PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
        [StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lpData;
        }
        [DllImport("User32.dll", EntryPoint = "SendMessage", SetLastError = true)]
        public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
        private const string MARKER = "MARKER\n";
        static void Main(string[] args)
        {
            IntPtr destWnd = (IntPtr)int.Parse(args[0]);
          string packedargs = DAZZLE_MARKER + String.Join("\n", args[1]);
            /
            byte[] sarr = System.Text.Encoding.Unicode.GetBytes(packedargs);
            int len = sarr.Length;
            COPYDATASTRUCT CopyDataStruct;
            CopyDataStruct.dwData = (IntPtr)100;
            CopyDataStruct.cbData = (len + 1) * 2;
            CopyDataStruct.lpData = packedargs;
            int result = SendMessage(destWnd, WM_COPYDATA, 0, ref CopyDataStruct);
        }
    }
}
scx
  • 45
  • 1
  • 8