您的位置:首页 > 编程语言 > C语言/C++

进程通信之一 使用WM_COPYDATA C++及C#实现(转)

2014-03-12 22:06 579 查看
进程间通信最简单的方式就是发送WM_COPYDATA消息。本文提供C++及C#程序相互通信的二种实现方式。这样消息的接收端可以用C++实现,发送端可以用C++或C#实现。

 
 

发送WM_COPYDATA消息:

SendMessage(接收窗口句柄, WM_COPYDATA, (WPARAM)发送窗口句柄, (LPARAM)&CopyData);

 
 

其中的CopyData为COPYDATASTRUCT结构类型,该结构定义如下:

typedef struct tagCOPYDATASTRUCT {

DWORD dwData;  // Specifies data to be passed to the receiving application.

DWORD cbData;  //Specifies the size, in bytes, of the data pointed to by the lpData member.

 PVOID lpData;    // Pointer to data to be passed to the receiving application. can be NULL.

} COPYDATASTRUCT, *PCOPYDATASTRUCT;

注意:该消息只能由SendMessage()来发送,而不能使用PostMessage()。因为系统必须管理用以传递数据的缓冲区的生命期,如果使用了PostMessage(),数据缓冲区会在接收方(线程)有机会处理该数据之前,就被系统清除和回收。此外如果lpData指向一个带有指针或某一拥有虚函数的对象时,也要小心处理。

 
 

如果传入的句柄不是一个有效的窗口或当接收方进程意外终止时,SendMessage()会立即返回,因此发送方在这种情况下不会陷入一个无穷的等待状态中。

 
 

返回值问题,MSDN上说如果接收方处理了,返回TRUE,否则返回FALSE,但是本人在实验时,都是返回0(接收方已经处理)。

 
 

接收WM_COPYDATA消息:

只要用COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;就可以了。接收方应认为这些数据是只读的。

 
 

由于发送方在接收方处理WM_COPYDATA消息完毕前都是处于等待中,所以接收方应当尽快处理WM_COPYDATA消息。

 
 

以一个简单的例子来说明如何使用WM_COPYDATA消息,有二个程序,一个用来发送表示当前时间信息的字符串,另一个接收数据后显示到编辑框中。例子中有几点要注意:

1.如何得到当前控制台窗口句柄?VS2008下可以直接使用HWND GetConsoleWindow(void);函数。

2.使用char *ctime(const time_t *timer);将一个time_t类型转化成一个字符串时,函数会在字符串末尾加下'\n',因为发送前要将这个'\n'去掉。

 
 

发送消息的程序代码(VS2008下编译通过):

[cpp]
view plaincopyprint?

#include <windows.h>   

#include <time.h>   

#include <conio.h>   

#include <stdio.h>   

int main()  

{  

    const char szDlgTitle[] = "RecvMessage";  

  
 
    HWND hSendWindow = GetConsoleWindow ();  

    if (hSendWindow == NULL)  

        return -1;  

    HWND hRecvWindow = FindWindow(NULL, szDlgTitle);  

    if (hRecvWindow == NULL)  

        return -1;  

  
 
    char szSendBuf[100];  

    time_t  timenow;  

    COPYDATASTRUCT CopyData;  

  
 
    for (int i = 0; i < 10; i++)  

    {  

        time(&timenow);  

        sprintf(szSendBuf, "%s", ctime(&timenow));//注意,ctime()返回的字符串后面带了'\n'   

        CopyData.dwData = i;  

        CopyData.cbData = strlen(szSendBuf);  

        szSendBuf[CopyData.cbData - 1] = '\0';  

        CopyData.lpData = szSendBuf;  

  
 
        SendMessage(hRecvWindow, WM_COPYDATA, (WPARAM)hSendWindow, (LPARAM)&CopyData);  

        printf("%s\n", szSendBuf);  

        Sleep(1000);  

    }  

    return 0;  

}  

#include <windows.h>

#include <time.h>

#include <conio.h>

#include <stdio.h>

int main()

{

    const char szDlgTitle[] = "RecvMessage";

 

    HWND hSendWindow = GetConsoleWindow ();

    if (hSendWindow == NULL)

        return -1;

    HWND hRecvWindow = FindWindow(NULL, szDlgTitle);

    if (hRecvWindow == NULL)

        return -1;

 

    char szSendBuf[100];

    time_t timenow;

    COPYDATASTRUCT CopyData;

 

    for (int i = 0; i < 10; i++)

    {

        time(&timenow);

        sprintf(szSendBuf, "%s", ctime(&timenow));//注意,ctime()返回的字符串后面带了'\n'

        CopyData.dwData = i;

        CopyData.cbData = strlen(szSendBuf);

        szSendBuf[CopyData.cbData - 1] = '\0';

        CopyData.lpData = szSendBuf;

 

        SendMessage(hRecvWindow, WM_COPYDATA, (WPARAM)hSendWindow, (LPARAM)&CopyData);

        printf("%s\n", szSendBuf);

        Sleep(1000);

    }

    return 0;

}

接收消息程序代码(VC6.0下编译通过):

程序中的IDC_EDIT_RECVMESSAGE为编辑框的ID。

[cpp]
view plaincopyprint?

#include "stdafx.h"   

#include "resource.h"   

#include <stdio.h>   

BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);  

int APIENTRY WinMain(HINSTANCE hInstance,  

                     HINSTANCE hPrevInstance,  

                     LPSTR     lpCmdLine,  

                     int       nCmdShow)  

{  

    // TODO: Place code here.   

    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);  

    return 0;  

}  

BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  

{  

    const char szDlgTitle[] = "RecvMessage";  

    static HWND s_hEditShowRecv;  

  
 
    switch (message)  

    {  

    case WM_INITDIALOG:  

        SetWindowText(hDlg, szDlgTitle);  

        s_hEditShowRecv = GetDlgItem(hDlg, IDC_EDIT_RECVMESSAGE);  

        return TRUE;  

  
 
    case WM_COMMAND:  

        switch (LOWORD(wParam))  

        {  

        case IDOK:  

        case IDCANCEL:  

            EndDialog(hDlg, LOWORD(wParam));  

            return TRUE;  

        }  

        break;  

  
 
    case WM_COPYDATA:  

        {  

            COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;  

            char szBuffer[300];  

  
 
            memset(szBuffer, 0, sizeof(szBuffer));  

            sprintf(szBuffer, "dwData:%d cbData:%d\r\nlpData:0x%08x = %s\r\n\r\n",   

                pCopyData->dwData, pCopyData->cbData,   

                (PVOID)pCopyData->lpData, (char*)pCopyData->lpData);  

            //在编辑框中追加数据   

            SendMessage(s_hEditShowRecv, EM_SETSEL, (WPARAM)-1, (LPARAM)-1); // (0, -1)表示全选, (-1,任意)表示全不选   

            SendMessage(s_hEditShowRecv, EM_REPLACESEL, FALSE, (LPARAM)szBuffer);  

            SendMessage(s_hEditShowRecv, EM_SCROLLCARET, 0, 0);  

        }  

        return TRUE;  

    }  

    return FALSE;  

}  

#include "stdafx.h"

#include "resource.h"

#include <stdio.h>

BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow)

{

    // TODO: Place code here.

    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);

    return 0;

}

BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

    const char szDlgTitle[] = "RecvMessage";

    static HWND s_hEditShowRecv;

 

    switch (message)

    {

    case WM_INITDIALOG:

        SetWindowText(hDlg, szDlgTitle);

        s_hEditShowRecv = GetDlgItem(hDlg, IDC_EDIT_RECVMESSAGE);

        return TRUE;

 

    case WM_COMMAND:

        switch (LOWORD(wParam))

        {

        case IDOK:

        case IDCANCEL:

            EndDialog(hDlg, LOWORD(wParam));

            return TRUE;

        }

        break;

 

    case WM_COPYDATA:

        {

            COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;

            char szBuffer[300];

 

            memset(szBuffer, 0, sizeof(szBuffer));

            sprintf(szBuffer, "dwData:%d cbData:%d\r\nlpData:0x%08x = %s\r\n\r\n",

                pCopyData->dwData, pCopyData->cbData,

                (PVOID)pCopyData->lpData, (char*)pCopyData->lpData);

            //在编辑框中追加数据

            SendMessage(s_hEditShowRecv, EM_SETSEL, (WPARAM)-1, (LPARAM)-1); // (0, -1)表示全选, (-1,任意)表示全不选

            SendMessage(s_hEditShowRecv, EM_REPLACESEL, FALSE, (LPARAM)szBuffer);

            SendMessage(s_hEditShowRecv, EM_SCROLLCARET, 0, 0);

        }

        return TRUE;

    }

    return FALSE;

}

运行结果如下 (先启动接收消息程序再运行发送消息程序):

 


 
 

 
 

有的时候,发送消息程序用C#实现起来更加方便,因此在这也提供了用C#实现的例子发送消息程序供大家参考:

[csharp]
view plaincopyprint?

using System;  

using System.Collections.Generic;  

using System.Text;  

using System.Threading;  

using System.Runtime.InteropServices;  //[DllImport("user32.dll")]中DllImport的命名空间   

  
 
namespace UseWMCOPYDATA  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            string strDlgTitle = "RecvMessage";   

  
 
            //接收端的窗口句柄   

            IntPtr hwndRecvWindow = ImportFromDLL.FindWindow(null, strDlgTitle);  

            if (hwndRecvWindow == IntPtr.Zero)  

            {  

                Console.WriteLine("请先启动接收消息程序");  

                return;  

            }  

  
 
            //自己的窗口句柄   

            IntPtr hwndSendWindow = ImportFromDLL.GetConsoleWindow();  

            if (hwndSendWindow == IntPtr.Zero)  

            {  

                Console.WriteLine("获取自己的窗口句柄失败,请重试");  

                return;  

            }  

  
 
            for (int i = 0; i < 10; i++)  

            {  

                string strText = DateTime.Now.ToString();  

                //填充COPYDATA结构   

                ImportFromDLL.COPYDATASTRUCT copydata = new ImportFromDLL.COPYDATASTRUCT();  

                copydata.cbData = Encoding.Default.GetBytes(strText).Length; //长度 注意不要用strText.Length;   

                copydata.lpData = strText;                                   //内容   

  
 
                ImportFromDLL.SendMessage(hwndRecvWindow, ImportFromDLL.WM_COPYDATA, hwndSendWindow, ref copydata);  

  
 
                Console.WriteLine(strText);  

                Thread.Sleep(1000);  

            }  

  
 
        }  

    }  

  
 
    public class ImportFromDLL  

    {  

        public const int WM_COPYDATA = 0x004A;  

  
 
        //启用非托管代码   

        [StructLayout(LayoutKind.Sequential)]   

        public struct COPYDATASTRUCT   

        {  

            public int dwData;    //not used   

            public int cbData;    //长度   

            [MarshalAs(UnmanagedType.LPStr)]  

            public string lpData;   

        }  

  
 
        [DllImport("User32.dll")]  

        public static extern int SendMessage(  

            IntPtr hWnd,     // handle to destination window    

            int Msg,         // message   

            IntPtr wParam,    // first message parameter    

            ref COPYDATASTRUCT pcd // second message parameter    

        );  

  
 
        [DllImport("User32.dll", EntryPoint = "FindWindow")]  

        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);  

  
 
        [DllImport("Kernel32.dll", EntryPoint = "GetConsoleWindow")]  

        public static extern IntPtr GetConsoleWindow();  

  
 
    }  

}  

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Runtime.InteropServices; //[DllImport("user32.dll")]中DllImport的命名空间

 

namespace UseWMCOPYDATA

{

class Program

{

static void Main(string[] args)

{

string strDlgTitle = "RecvMessage";

 

//接收端的窗口句柄

IntPtr hwndRecvWindow = ImportFromDLL.FindWindow(null, strDlgTitle);

if (hwndRecvWindow == IntPtr.Zero)

{

Console.WriteLine("请先启动接收消息程序");

return;

}

 

//自己的窗口句柄

IntPtr hwndSendWindow = ImportFromDLL.GetConsoleWindow();

if (hwndSendWindow == IntPtr.Zero)

{

Console.WriteLine("获取自己的窗口句柄失败,请重试");

return;

}

 

for (int i = 0; i < 10; i++)

{

string strText = DateTime.Now.ToString();

//填充COPYDATA结构

     ImportFromDLL.COPYDATASTRUCT copydata = new ImportFromDLL.COPYDATASTRUCT();

copydata.cbData = Encoding.Default.GetBytes(strText).Length; //长度 注意不要用strText.Length;

copydata.lpData = strText; //内容

 

ImportFromDLL.SendMessage(hwndRecvWindow, ImportFromDLL.WM_COPYDATA, hwndSendWindow, ref copydata);

 

Console.WriteLine(strText);

Thread.Sleep(1000);

}

 

}

}

 

public class ImportFromDLL

{

public const int WM_COPYDATA = 0x004A;

 

//启用非托管代码

[StructLayout(LayoutKind.Sequential)]

public struct COPYDATASTRUCT

{

public int dwData; //not used

public int cbData; //长度

[MarshalAs(UnmanagedType.LPStr)]

public string lpData;

}

 

[DllImport("User32.dll")]

public static extern int SendMessage(

IntPtr hWnd,    // handle to destination window

int Msg,    // message

IntPtr wParam,  // first message parameter

ref COPYDATASTRUCT pcd // second message parameter

);

 

[DllImport("User32.dll", EntryPoint = "FindWindow")]

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

 

[DllImport("Kernel32.dll", EntryPoint = "GetConsoleWindow")]

public static extern IntPtr GetConsoleWindow();

 

}

}

 
 

运行结果如下 (先启动接收消息程序再运行发送消息程序):



 
 

 下一篇《进程通信之二
管道技术第一篇
输入输出的重定向》示范了程序输入输出的重定向,以及如何用管道来完成进程之间的通信。

 
 

原文地址:/article/1392209.html

 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: