在.net中基于Windows消息的IPC实现
2011年01月14日
一、什么是IPC
IPC(Inter process Communication)就是“进程间通讯”。我们都知道,在windows系统中,各个应用程序(进程)之间常常需要交换、传递数据,这就要解决进程间的数据通信问题。在最初的16位Windows3.x系统中,所有Windows应用程序共享单一地址,任何进程都能够对这一共享地址空间的数据进行读写操作。
随着Windwos98、Windows NT、Windows2000等32位的操作系统的出现,每个进程都有自己的地址空间,一个Windows进程不能存取另一个进程的私有数据,也就是说,虽然两个进程可以用具有相同值的指针寻址,但所读写的只是它们各自的数据,这样就减少了进程之间的相互干扰。
二、如何实现IPC
那么在windows当前系统下,如何实现进程通讯呢?其实有很多方法,如:
1、 剪贴板Clipboard
2、 DDE(动态数据交换)
3、 内存映像
4、 消息管道
5、 邮件槽
6、 Socket
7、 RPC
8、 串行/并行通信(Serial/Parallel Communication)
9、 COM/DCOM
10、Windows消息
三、基于Windows消息的IPC
现在让我们进入今天我们要讲的主题:“基于Windows消息的IPC实现”。
在这里,我假定大家对Windows消息机制都有很好的理解,所以我就不在这上面费太多的墨水了。我们直接看看Windows消息是怎么样实现进程间通讯的。我们首先看看Windows的消息常数:
WM_COPYDATA=0x004A// 当一个应用程序传递数据给另一个应用程序时发送此消息。
这就是我们要的。下面我们来看看如何利用它来实现IPC。
让我们先看看几个API函数,没有它们,我们没有办法将数据发送出去。
1、 PostMessage
函数功能:该函数将一个消息放入(寄送)到与指定窗口创建的线程相联系消息队列里,不等待线程处理消息就返回。消息队列里的消息通过调用GetMessage和PeekMessage取得。
函数原型:B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
参数
hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:
HWND.BROADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口。消息不被寄送到子窗口。
NULL:此函数的操作和调用参数dwThread设置为当前线程的标识符PostThreadMessage函数一样。
Msg:指定被寄送的消息。
wParam:指定附加的消息特定的信息。
IParam:指定附加的消息特定的信息。
返回值:如果函数调用成功,返回非零值:如果函数调用失败,返回值是零。若想获得更多的错误信息,请调用GetLastError函数。
2、 SendMessage
函数功能:该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。而函数PostMessage不同,将一个消息寄送到一个线程的消息队列后立即返回。
函数原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
参数:
hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。
Msg:指定被发送的消息。
wParam:指定附加的消息指定信息。
IParam:指定附加的消息指定信息。
返回值:返回值指定消息处理的结果,依赖于所发送的消息。
3、 RegisterWindowMessage
函数功能:RegisterWindowMessage函数定义一个新的窗口消息,该消息保证在整个系统范围内是唯一的。调用SendMessage或PostMessage函数时可以使用该函数返回的消息值。
函数原型:UINT RegisterWindowMessage(lpsz)
参数:
lpsz指向一个以NULL结束的字符串,该字符串指定待登记的消息。
返回值:若成功地登记了消息,返回值是一个消息标识符。该标识符值的范围在0XC000到0XFFFF之间,否则,返回值为0。
我们现在在C#中声明这些API函数:
[DllImport("user32")]
internal static extern int RegisterWindowMessage(string lpString);
[DllImport("user32")]
internal static extern int PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32")]
internal static extern int PostMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32")]
internal static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
然后定义一些我们需要的常数:
internal const int WM_COPYDATA = 0x004A; //当一个应用程序传递数据给另一个应用程序时发送此消息
internal const int WM_DESTROY = 0x0002; //窗体被销毁
internal const int WM_CREATE = 0x0001; //应用程序创建一个窗口
internal const int WM_QUERYENDSESSION = 0x0011; //当用户选择结束对话框或程序自己调用ExitWindows函数
internal static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
我们还需要一个传送数据的结构:
/**////
///发送WM_COPYDATA消息的数据结构
///
internal struct COPYDATASTRUCT
...{
/**////
///用户自定义数据
///
internal IntPtr dwData;
/**////
///数据长度
///
internal int cbData;
/**////
///数据首地址指针
///
internal IntPtr lpData;
}
现在我们来看看具体如何实现:
1、 我们首先创建一个类,让它从Form类继承,因为我们需要一个窗体,然后对它的消息进行处理:
internal class WinMsg : Form
...{
private string _messageString;
private List _windowList;
private int _message;
private int _intHandler;
private bool _isConnected;
}
2、 然后定义构造函数,在构造函数里注册一个消息通道值,并发出一个广播通知其它在这个通道的窗口。
internal WinMsg(string messageString)
...{
_messageString = messageString;
_isConnected = false;
_intHandler = Handle.ToInt32();
_windowList = new List();
_message = Win32.RegisterWindowMessage(_messageString);//注册一个消息通道
int errCode = Win32.PostMessage(Win32.HWND_BROADCAST, _message, Win32.CONNECTION, _intHandler);//向此通道内所有的窗口广播自己的句柄
if (errCode == 0)
...{
throw new Win32Exception(errCode);//发生错误,抛出异常
}
else
...{
_isConnected = true;
}
}
3、 重写基类的WndProc函数,处理接收到的消息:
protected override void WndProc(ref Message m)
...{
if (m.Msg == _message)//接收到广播消息,进行处理
...{
int LParam = m.LParam.ToInt32();
int WParam = m.WParam.ToInt32();
if (LParam != 0 && LParam != _intHandler)
...{
if (WParam == Win32.DISCONNECTION)
...{
_windowList.Remove(m.WParam);//将对方窗口的句柄从列表中删除
}
else
...{
if (WParam==Win32.CONNECTION)
...{
Win32.PostMessage(m.LParam, _message, Win32.REVERSION, _intHandler);
}
_windowList.Add(m.LParam);//将对方窗口的句柄存入列表中
}
}
return;
}
switch (m.Msg)
...{
case Win32.WM_COPYDATA://接收到其它窗口发送过来的数据
...{
COPYDATASTRUCT data = new COPYDATASTRUCT();
data = (COPYDATASTRUCT)m.GetLParam(data.GetType());
byte[] message = new byte[data.cbData];
Marshal.Copy(data.lpData, message, 0, data.cbData);//从非托管内存中将数据复制到我们的byte数组中。
Anyzler(m.WParam, message);//在这里处理接收到的数据。
}
break;
case Win32.WM_DESTROY:
case Win32.WM_QUERYENDSESSION://窗口被关闭,向其它窗口广播通知从队列中删除自己
Win32.PostMessage(Win32.HWND_BROADCAST, _message, Win32.DISCONNECTION, _intHandler);
base.WndProc(ref m);//调用基类的消息处理函数。
break;
default:
base.WndProc(ref m); //调用基类的消息处理函数。
break;
}
}
4、 定义发送消息的函数:
internal void Send(byte[] message)
...{
if (_isConnected)
...{
int length = message.Length;
IntPtr ptr = Marshal.AllocHGlobal(length);//申请一块非托管内存
Marshal.Copy(message, 0, ptr, length);//将要发送的数据封送到非托管内存
COPYDATASTRUCT data = new COPYDATASTRUCT();
data.dwData = IntPtr.Zero;//这里可以随意定义。
data.cbData = length;//传递要发送的数据的长度
data.lpData = ptr;//传递要发送的数据的地址指针
//向其它所有窗口发送数据,这里不能发广播消息。必须一个一个发送。
foreach (IntPtr window in _windowList)
...{
Win32.SendMessage(window, Win32.WM_COPYDATA, this.Handle, ref data);
}
Marshal.FreeHGlobal(ptr);//释放这块非托管内存,这行一定要写上,不然会内存泄漏。
}
}
发表评论
-
Python GUI编程-了解相关技术[整理]
2012-01-20 09:39 1151Python GUI编程-了解相关技术[整理] 2011年0 ... -
Python笔记――python简介、特点、安装及helloworld
2012-01-20 09:39 665Python笔记――python简介 ... -
给C++程序员的Python初学指南 / Guide to Python for C++ Programmers
2012-01-20 09:39 735给C++程序员的Python初学指南 / Guide to P ... -
深入Python3 (Dive Into Python3)笔记11--文件
2012-01-20 09:39 935深入Python3 (Dive Into Python ... -
eclipse开发python+qt
2012-01-20 09:39 1103eclipse开发python+qt 2011年05月12日 ... -
穿越亚细亚行笺[18国家和地区旅行攻略]-转
2012-01-19 14:42 939穿越亚细亚行笺[18国家 ... -
外贸找客户的几十种方法,总有一种适合你
2012-01-19 14:42 1700外贸找客户的几十种方法,总有一种适合你 2011年08月20 ... -
办签证
2012-01-19 14:42 493办签证 2011年11月17日 去越南属于出国,必须办理 ... -
告别光棍有新法
2012-01-19 14:42 540告别光棍有新法 2011年02月28日 越 ... -
MFC消息处理和消息映射的概念
2012-01-17 04:27 652MFC消息处理和消息映射的概念 2011年08月25日 ... -
Windows SDK编程(Delphi版) 之 消息处理
2012-01-17 04:27 501Windows SDK编程(Delphi版) ... -
VC消息映射
2012-01-17 04:27 577VC消息映射 2010年08月14 ... -
VC++日记(非模态,自定义消息,窗体移动……)
2012-01-17 04:27 999VC++日记(非模态,自定义消息,窗体移动……) 2011年 ... -
根在故乡
2012-01-16 03:17 659根在故乡 2011年12月06日 ... -
我心中完美男人的形象
2012-01-16 03:17 555我心中完美男人的形象 2011年12月18日 一 ... -
■反击关于盗墓一点也不腐つ
2012-01-16 03:17 595■反击关于盗墓一点也不腐つ 2012年01月07日 首先 ...
相关推荐
这是我的Blog文章《在.NET中基于Windows消息的IPC实现》一文的源码例子。
IPC通道是建立在Windows IPC系统上层的远程通道。假如你熟悉编写远程通信应用程序的话,新的IPC通道对于你来说就很容易了。IPC通道和其他远程通道都非常相似,只是在有些功能函数上有差异。最显著的差异在于IPC通道...
Andrid系统中基于Binder的IPC流程框架分析_V1.0_201407251755.pdf
\基于Socket的IPC消息平台设计 Socket技术 消息平台 里面有对你有用的信息的
Android 基于Socket 的IPC通信
一个C#.NET Remoting示例 ChannelServices.RegisterChannel(new TcpChannel(9090), true); RemotingConfiguration.ApplicationName = "HelloServiceApplication"; RemotingConfiguration....
轻量级库,用于通过.NET Core中IPC的命名管道和匿名管道进行方法调用。 支持与回调的双向通信。 客户端到服务器的呼叫 var pipeServer = new PipeServer ( new NetJsonPipeSerializer (), " mypipe " , () => ...
最近在玩golang,于是自己写了golang版本的ipc,用于实现进程间通信。此外还实现了,Java版的client和golang的server进行连接。此版本可以用于linux,也可以用于windows,测试已通过。如果使用有问题,可以给我发...
内涵完整的windows与RTX之间通讯的完整代码,通过共享内存等IPC对象实现。
XP的IPC入侵与2000最大的不同在于来访者的权限问题, 运行这个程序后就可以像入侵2000一样入侵xp. 程序会打开所有盘的默认共享(c$d$....) bin目录附入侵xp的所有命令. 运行后有些杀毒软件会提示“危险操作”, ...
Aeron.NET - 高效可靠的UDP单播, UDP组播和IPC消息传输 - Aeron的.NET移植
亲测可用的基于Linux消息队列的简易聊天室(C语言)(附源代码)采用客户-服务器结构,其中服务器实现各个用户的登录并存储相关信息,客户端通过服务器端获取当前登录用户信息,然后各客户进程通过消息队列实现双向通信...
详细叙述了GB28181在IPC上实现细节,让开发工程师可以轻易上手的文档,网上找的,很不错喔
IPC analysis on android with a demo (基于IPC实例分析android IPC机制) 代码分析,请参考:http://blog.csdn.net/safrans/article/details/6272652
文章介绍一种基于IPC+PLC的燃天然气热处理炉微机温控系统的硬件结构和软件设计,论述实现这一系统遇到的具体问题及解决方法。该系统的应用表明:IPC+PLC是一种开发周期短、成本低、可靠性和准确度高的温控措施,对其它...
基于 PC 的自动化:使用 SIMATIC IPC427C 实现自动化pdf,基于 PC 的自动化:使用 SIMATIC IPC427C 实现自动化
基于IPC-7351B的表贴器件PCB封装设计.pdf
基于Windows的进程间通信(IPC)的一些代码示例_C++_C_下载.zip
一种基于IPC_RT LINUX数控系统控制面板的设计及实现.pdf
IPC是一个C 库,它使用Windows上的共享内存提供进程间通信