毒龙的程序人生

BASIC, C, C++, C#, Qt, 程序人生

统计

常用链接

留言簿(12)

积分与排名

blog

常用链接

阅读排行榜

评论排行榜

关于C#中实现两个应用程序消息通讯的问题

  最近项目中需要在两个应用程序之间通讯,这里的两个程序是在一台机器上,看了csdn上的一篇文章如何在C#用WM_COPYDATA消息来实现两个进程之间传递数据》,原理是讲清楚了,但使起来很不爽,决定自己封装一下,满足项目需要就行。

  注意这里发送消息的函数:public static void SendMessage(string destProcessName, int msgID, string strMsg),只能发送一个msgID和一个strMsg, 接收方只接收了strMsg,我的项目中已经够用了,你如果需要读msgID,还需要改一下。客户端代码请参考csdn上的原文自己写吧。

/*
 进程之间通讯的几种方法: 
 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。常用的方法有  
 (1)使用内存映射文件  
 (2)通过共享内存DLL共享内存  
 (3)使用SendMessage向另一进程发送WM_COPYDATA消息  
 
 比起前两种的复杂实现来,WM_COPYDATA消息无疑是一种经济实惠的一种方法.  
 WM_COPYDATA消息的主要目的是允许在进程间传递只读数据。Windows在通过WM_COPYDATA消息传递期间,不提供继承同步方式。
 SDK文档推荐用户使用SendMessage函数,接受方在数据拷贝完成前不返回,这样发送方就不可能删除和修改数据:  
 
 这个函数的原型及其要用到的结构如下:  
 SendMessage(hwnd, WM_COPYDATA, wParam, lParam);    
 其中:
 WM_COPYDATA对应的十六进制数为0x004A  
 wParam设置为包含数据的窗口的句柄。
 lParam指向一个COPYDATASTRUCT的结构:  
 typedef  struct  tagCOPYDATASTRUCT
 {  
             DWORD  dwData;  //用户定义数据  
             DWORD  cbData;  //数据大小  
             PVOID  lpData;  //指向数据的指针  
 } COPYDATASTRUCT;  
 该结构用来定义用户数据。  
 
 具体过程如下:  
 首先,在发送方,用FindWindow找到接受方的句柄,然后向接受方发送WM_COPYDATA消息。
 接受方在DefWndProc事件中处理这条消息。由于中文编码是两个字节, 所以传递中文时候字节长度要搞清楚。  
 protected  override  void  DefWndProc(ref  System.Windows.Forms.Message  m)  {  
     switch(m.Msg)  {  
         case  WinMessageUtil.WM_COPYDATA:  
               string str = WinMessageUtil.ReceiveMessage(ref m);
               break;
         default:  
               break;  
     }
     base.DefWndProc(ref  m);  
 }  
*/


using System;
using  System.Runtime.InteropServices;  
using System.Diagnostics;

namespace Speeding.Util
{

    
//WM_COPYDATA消息所要求的数据结构
    public  struct  CopyDataStruct  
    
{  
        
public  IntPtr  dwData;  
        
public  int  cbData;  

        [MarshalAs(UnmanagedType.LPStr)]  
        
public  string  lpData;  
    }
  
    
    
/// <summary>
    
/// 本类封装了一些进程间通讯的细节
    
/// </summary>

    public class WinMessageUtil
    
{
        
public const  int  WM_COPYDATA  =  0x004A;  

        
//通过窗口的标题来查找窗口的句柄
        [DllImport("User32.dll",EntryPoint="FindWindow")]  
        
private  static  extern  int  FindWindow(string  lpClassName, string  lpWindowName);  

        
//在DLL库中的发送消息函数
        [DllImport("User32.dll",EntryPoint="SendMessage")]  
        
private  static  extern  int  SendMessage
            (  
            
int  hWnd,                        // 目标窗口的句柄  
            int  Msg,                        // 在这里是WM_COPYDATA
            int  wParam,                    // 第一个消息参数
            ref  CopyDataStruct  lParam        // 第二个消息参数
            );  
 
        
/// <summary>
        
/// 发送消息,只能传递一个自定义的消息ID和消息字符串,想传一个结构,但没成功
        
/// </summary>
        
/// <param name="destProcessName">目标进程名称,如果有多个,则给每个都发送</param>
        
/// <param name="msgID">自定义数据,可以通过这个来决定如何解析下面的strMsg</param>
        
/// <param name="strMsg">传递的消息,是一个字符串</param>

        public static void SendMessage(string destProcessName, int msgID, string strMsg) 
        
{
            
if(strMsg == nullreturn;

            
//按进程名称查找,同名称的进程可能有许多,所以返回的是一个数组
            Process []foundProcess = Process.GetProcessesByName(destProcessName);
            
foreach(Process p in foundProcess) 
            
{
                
int toWindowHandler = p.MainWindowHandle.ToInt32();
                
if(toWindowHandler  !=  0)  
                
{  
                     CopyDataStruct  cds;  
                    cds.dwData  
=  (IntPtr)  msgID;   //这里可以传入一些自定义的数据,但只能是4字节整数      
                    cds.lpData  =  strMsg;            //消息字符串
                    cds.cbData  =  System.Text.Encoding.Default.GetBytes(strMsg).Length + 1;  //注意,这里的长度是按字节来算的

                    
//发送方的窗口的句柄, 由于本系统中的接收方不关心是该消息是从哪个窗口发出的,所以就直接填0了
                    int fromWindowHandler = 0;    
                    SendMessage(toWindowHandler,  WM_COPYDATA,  fromWindowHandler,  
ref  cds);  
                }

            }

        }


        
/// <summary>
        
/// 接收消息,得到消息字符串
        
/// </summary>
        
/// <param name="m">System.Windows.Forms.Message m</param>
        
/// <returns>接收到的消息字符串</returns>

        public static string ReceiveMessage(ref  System.Windows.Forms.Message  m)
        
{
            CopyDataStruct  cds 
= (CopyDataStruct) m.GetLParam(typeof(CopyDataStruct));  
            
return cds.lpData;
        }


    }
  

    
}

------------------------------------------------------------------------

posted on 2004-10-24 09:48 毒龙 阅读(2901) 评论(8)  编辑 收藏 所属分类: C#编程

评论

#1楼  2004-10-24 10:11 hys [未注册用户]

我觉得程序间通讯用TCP/IP好点,只要知道IP,port便可以
另外,两个成讯可以在一台电脑上,也可以在不同的电脑上。

灵活性比较大。

我一直想构思一个通讯网关平台。   回复  引用    

#2楼  2004-10-24 16:32 风的种子      

Why not remoting?   回复  引用  查看    

#3楼  2004-10-25 08:38 大坏蛋      

不要封装了,Remoting已经提供了个IPCcHannel(其实也是封装),不过用Remoting来做,方便很多,我的Blog上有IpcChannel的例子.   回复  引用  查看    

#4楼 [楼主] 2004-10-25 14:03 毒龙      

因为我的项目中要求几个应用程序之间需要通讯,所以不想用remoting,而且性能也不错,以后有可能可以试一试remoting   回复  引用  查看    

#5楼  2005-07-29 11:13 小黑 [未注册用户]

我在使用这种方法时,遇到些麻烦:
我要做的是c++发送数据给c#,下面结构中lpData并不是字串,而是一般的数据,可能包含数据0
typedef struct tagCOPYDATASTRUCT
{
DWORD dwData; //用户定义数据
DWORD cbData; //数据大小
PVOID lpData; //指向数据的指针
} COPYDATASTRUCT;

在c#中接收时lpData是string型的,接收数据是有问题的,不能把所有数据完整接收
//WM_COPYDATA消息所要求的数据结构
public struct CopyDataStruct
{
public IntPtr dwData;
public int cbData;

[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}

我所采用的是比较笨的办法,用c++做动态库,提供返回byte[]型号的数据,c#中调用得到byte[]就可以了

不知道各位大侠有什么好的办法?   回复  引用    

#6楼 [楼主] 2005-07-29 15:05 毒龙      

To 小黑:
我们这从C#给VC++发消息很好使,我对VC++不熟,我们经验是事先设计好通讯消息的格式   回复  引用  查看    

#7楼  2005-09-28 14:14 kongxx      

好东西,收藏之   回复  引用  查看    

#8楼  2006-11-03 02:03 meng[匿名] [未注册用户]

TO 我们经验是事先设计好通讯消息的格式

你们是使用什么样的消息格式通信?   回复  引用