zoukankan      html  css  js  c++  java
  • <转载>在.NET中基于Windows消息的IPC实现

    一、什么是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);
    我们还需要一个传送数据的结构:
    ///<summary>
    ///发送WM_COPYDATA消息的数据结构
    ///</summary>
    internal struct COPYDATASTRUCT
    {
    ///<summary>
    ///用户自定义数据
    ///</summary>
    internal IntPtr dwData;
    ///<summary>
    ///数据长度
    ///</summary>
    internal int cbData;
    ///<summary>
    ///数据首地址指针
    ///</summary>
    internal IntPtr lpData;
    }
    现在我们来看看具体如何实现:
    1、 我们首先创建一个类,让它从Form类继承,因为我们需要一个窗体,然后对它的消息进行处理:
    internal class WinMsg : Form

    {

    private string _messageString;

    private List<IntPtr> _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<IntPtr>();
    _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);
    //释放这块非托管内存,这行一定要写上,不然会内存泄漏。
    }
    }
    好了,主要代码就是这些了!附上我的例子源代码 https://files.cnblogs.com/ChangTan/28135640622.rar
  • 相关阅读:
    c#中value是什么意思
    javascript json转为 go struct 小工具代码
    android greendao的外部封装不太友好。
    redis 内存泄露
    Robolectric 配置
    android studio 代理配置
    python 写文件,utf-8问题
    go 的 time ticker 设置定时器
    FQ记(nexus7 2代 恢复出厂设置,然后重启,因为被墙,卡住了!)
    lua https request 调用
  • 原文地址:https://www.cnblogs.com/ChangTan/p/2050491.html
Copyright © 2011-2022 走看看