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

转载 2013年12月03日 10:17:42

/**************************************************************************************************

转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/6804157

**********************************************************************************************************************/

进程间通信最简单的方式就是发送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下编译通过):

  1. #include <windows.h>  
  2. #include <time.h>  
  3. #include <conio.h>  
  4. #include <stdio.h>  
  5. int main()  
  6. {  
  7.     const char szDlgTitle[] = "RecvMessage";  
  8.   
  9.     HWND hSendWindow = GetConsoleWindow ();  
  10.     if (hSendWindow == NULL)  
  11.         return -1;  
  12.     HWND hRecvWindow = FindWindow(NULL, szDlgTitle);  
  13.     if (hRecvWindow == NULL)  
  14.         return -1;  
  15.   
  16.     char szSendBuf[100];  
  17.     time_t  timenow;  
  18.     COPYDATASTRUCT CopyData;  
  19.   
  20.     for (int i = 0; i < 10; i++)  
  21.     {  
  22.         time(&timenow);  
  23.         sprintf(szSendBuf, "%s", ctime(&timenow));//注意,ctime()返回的字符串后面带了'\n'  
  24.         CopyData.dwData = i;  
  25.         CopyData.cbData = strlen(szSendBuf);  
  26.         szSendBuf[CopyData.cbData - 1] = '\0';  
  27.         CopyData.lpData = szSendBuf;  
  28.   
  29.         SendMessage(hRecvWindow, WM_COPYDATA, (WPARAM)hSendWindow, (LPARAM)&CopyData);  
  30.         printf("%s\n", szSendBuf);  
  31.         Sleep(1000);  
  32.     }  
  33.     return 0;  
  34. }  

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

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

  1. #include "stdafx.h"  
  2. #include "resource.h"  
  3. #include <stdio.h>  
  4. BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);  
  5. int APIENTRY WinMain(HINSTANCE hInstance,  
  6.                      HINSTANCE hPrevInstance,  
  7.                      LPSTR     lpCmdLine,  
  8.                      int       nCmdShow)  
  9. {  
  10.     // TODO: Place code here.  
  11.     DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);  
  12.     return 0;  
  13. }  
  14. BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
  15. {  
  16.     const char szDlgTitle[] = "RecvMessage";  
  17.     static HWND s_hEditShowRecv;  
  18.   
  19.     switch (message)  
  20.     {  
  21.     case WM_INITDIALOG:  
  22.         SetWindowText(hDlg, szDlgTitle);  
  23.         s_hEditShowRecv = GetDlgItem(hDlg, IDC_EDIT_RECVMESSAGE);  
  24.         return TRUE;  
  25.   
  26.     case WM_COMMAND:  
  27.         switch (LOWORD(wParam))  
  28.         {  
  29.         case IDOK:  
  30.         case IDCANCEL:  
  31.             EndDialog(hDlg, LOWORD(wParam));  
  32.             return TRUE;  
  33.         }  
  34.         break;  
  35.   
  36.     case WM_COPYDATA:  
  37.         {  
  38.             COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;  
  39.             char szBuffer[300];  
  40.   
  41.             memset(szBuffer, 0, sizeof(szBuffer));  
  42.             sprintf(szBuffer, "dwData:%d cbData:%d\r\nlpData:0x%08x = %s\r\n\r\n",   
  43.                 pCopyData->dwData, pCopyData->cbData,   
  44.                 (PVOID)pCopyData->lpData, (char*)pCopyData->lpData);  
  45.             //在编辑框中追加数据  
  46.             SendMessage(s_hEditShowRecv, EM_SETSEL, (WPARAM)-1, (LPARAM)-1); // (0, -1)表示全选, (-1,任意)表示全不选  
  47.             SendMessage(s_hEditShowRecv, EM_REPLACESEL, FALSE, (LPARAM)szBuffer);  
  48.             SendMessage(s_hEditShowRecv, EM_SCROLLCARET, 0, 0);  
  49.         }  
  50.         return TRUE;  
  51.     }  
  52.     return FALSE;  
  53. }  

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

 

 

 

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

[csharp] view plaincopy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Threading;  
  5. using System.Runtime.InteropServices;  //[DllImport("user32.dll")]中DllImport的命名空间  
  6.   
  7. namespace UseWMCOPYDATA  
  8. {  
  9.     class Program  
  10.     {  
  11.         static void Main(string[] args)  
  12.         {  
  13.             string strDlgTitle = "RecvMessage";   
  14.   
  15.             //接收端的窗口句柄  
  16.             IntPtr hwndRecvWindow = ImportFromDLL.FindWindow(null, strDlgTitle);  
  17.             if (hwndRecvWindow == IntPtr.Zero)  
  18.             {  
  19.                 Console.WriteLine("请先启动接收消息程序");  
  20.                 return;  
  21.             }  
  22.   
  23.             //自己的窗口句柄  
  24.             IntPtr hwndSendWindow = ImportFromDLL.GetConsoleWindow();  
  25.             if (hwndSendWindow == IntPtr.Zero)  
  26.             {  
  27.                 Console.WriteLine("获取自己的窗口句柄失败,请重试");  
  28.                 return;  
  29.             }  
  30.   
  31.             for (int i = 0; i < 10; i++)  
  32.             {  
  33.                 string strText = DateTime.Now.ToString();  
  34.                 //填充COPYDATA结构  
  35.                 ImportFromDLL.COPYDATASTRUCT copydata = new ImportFromDLL.COPYDATASTRUCT();  
  36.                 copydata.cbData = Encoding.Default.GetBytes(strText).Length; //长度 注意不要用strText.Length;  
  37.                 copydata.lpData = strText;                                   //内容  
  38.   
  39.                 ImportFromDLL.SendMessage(hwndRecvWindow, ImportFromDLL.WM_COPYDATA, hwndSendWindow, ref copydata);  
  40.   
  41.                 Console.WriteLine(strText);  
  42.                 Thread.Sleep(1000);  
  43.             }  
  44.   
  45.         }  
  46.     }  
  47.   
  48.     public class ImportFromDLL  
  49.     {  
  50.         public const int WM_COPYDATA = 0x004A;  
  51.   
  52.         //启用非托管代码  
  53.         [StructLayout(LayoutKind.Sequential)]   
  54.         public struct COPYDATASTRUCT   
  55.         {  
  56.             public int dwData;    //not used  
  57.             public int cbData;    //长度  
  58.             [MarshalAs(UnmanagedType.LPStr)]  
  59.             public string lpData;   
  60.         }  
  61.   
  62.         [DllImport("User32.dll")]  
  63.         public static extern int SendMessage(  
  64.             IntPtr hWnd,     // handle to destination window   
  65.             int Msg,         // message  
  66.             IntPtr wParam,    // first message parameter   
  67.             ref COPYDATASTRUCT pcd // second message parameter   
  68.         );  
  69.   
  70.         [DllImport("User32.dll", EntryPoint = "FindWindow")]  
  71.         public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);  
  72.   
  73.         [DllImport("Kernel32.dll", EntryPoint = "GetConsoleWindow")]  
  74.         public static extern IntPtr GetConsoleWindow();  
  75.   
  76.     }  
  77. }  

 

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

 

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

 

/**************************************************************************************************

转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/6804157

**********************************************************************************************************************/

附比较有用的评论:

5楼 万个函数千个类 2013-01-19 11:07发表 [回复]
很简洁的示例,收益。。
楼主不知道能否有时间讲些使用
HWND_BROADCAST进行进程间通信的精彩的例子。
谢谢!
Re: MoreWindows 2013-01-19 15:28发表 [回复]
回复softfox:呵呵,广播确实也是一种简洁的进程通信手段。等完成毕业论文后会来讲解的。欢迎您浏览博客中其它文章。
3楼 王林森 2012-09-01 00:10发表 [回复] [引用] [举报]
发送端和接收端都定义相同的消息!

发送端:
用FindWindow查找到接收端,获得它的窗口类指针,借由GetWindowThreadProcessId获得接收端的进程ID,并打开该接收进程OpenProcess。为该接收进程申请虚拟内存VirtualAllocEx,并进行数据的写入WriteMemory通过SendMessage发送虚拟内存的指针给接收端,等待接搜端接收完成后(例如可以sleep(100))释放虚拟内存

接收端:
获得进程GetCurrentProcess,读取虚拟内存ReadProcessMemory,显示!
Re: MoreWindows 2012-09-01 13:31发表 [回复] [引用] [举报]
回复wlsgzl:这种也可以的。

相关文章推荐

【Django】settings

作者:Django 团队译者:weizhong2004@gmail.com翻译开始日期:2006-04-04翻译完成日期:2006-04-04修订日期:2006-05-06原文版本:2789Djang...

JZOJ 3853. 【NOIP2014八校联考第2场第2试9.28】帮助Bsny(help)

DescriptionBsny的书架乱成一团了,帮他一下吧! 他的书架上一共有n本书,我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3;30,...

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

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

C++ WM_COPYDATA 实现进程通信

完整工程可以到:http://download.csdn.net/detail/zy_dreamer/5385153 下载 基于MFC 用于存储数据的自定义结构体: struct MSG_S...

进程通信之一使用WM_COPYDATA

http://www.cnblogs.com/morewindows/archive/2011/09/23/2186294.html   进程间通信最简单的方式就是发送WM_COPYDATA消息。...
  • sergery
  • sergery
  • 2012年02月14日 01:51
  • 527

进程通信——使用WM_COPYDATA消息通信

3.4  使用WM_COPYDATA消息通信对于少量数据可以用WM_COPYDATA方便地实现通信。由于SendMessage()是阻塞的,只有接收方响应了消息,SendMessage()才能返回,否...

WM_COPYDATA消息进程通信

  • 2017年06月06日 17:06
  • 3.52MB
  • 下载

Windows进程通信之消息和WM_COPYDATA

  • 2014年08月03日 22:40
  • 412KB
  • 下载

跨进程通信之WM_COPYDATA

一、MSDN提供: #define WM_COPYDATA 0x004A 数据结构: typedef struct tagCOPYDATASTRUCT...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:进程通信之一 使用WM_COPYDATA C++及C#实现
举报原因:
原因补充:

(最多只允许输入30个字)