c# 进程间同步实现 进程之间通讯的几种方法

进程之间通讯的几种方法:
常用的方法有:
1.使用内存映射文件
2.通过共享内存DLL共享内存
3.使用SendMessage向另一进程发送WM_COPYDATA消息.

比起前两种的复杂实现来,WM_COPYDATA消息无疑是一种经济实惠的一中方法.(ZT)

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事件中,来处理这条消息.由于中文编码是两个字节,所以传递中文时候字节长度要搞清楚.
体代码如下:
//---------------------------------------------------
//发送方:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
namespace WinFormSendMsg
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private System.ComponentModel.Container components = null;
const int WM_COPYDATA = 0x004A;
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(184, 24);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(128, 21);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "textBox1";
//
// button1
//
this.button1.Location = new System.Drawing.Point(344, 16);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(112, 32);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(536, 142);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.button1,
this.textBox1});
this.Name = "Form1";
this.Text = "发送方窗体";
this.ResumeLayout(false);
}
static void Main()
{
Application.Run(new Form1());
}
[DllImport("User32.dll",EntryPoint="SendMessage")]
private static extern int SendMessage(
int hWnd, // handle to destination window
int Msg, // message
int wParam, // first message parameter
ref COPYDATASTRUCT lParam // second message parameter
);

[DllImport("User32.dll",EntryPoint="FindWindow")]
private static extern int FindWindow(string lpClassName,string
lpWindowName);

private void button1_Click(object sender, System.EventArgs e)
{
int WINDOW_HANDLER = FindWindow(null,@"接收方窗体");
if(WINDOW_HANDLER == 0)
{
}
else
{
byte[] sarr = System.Text.Encoding.Default.GetBytes(this.textBox1.Text);
int len = sarr.Length;
COPYDATASTRUCT cds;
cds.dwData = (IntPtr) 100;
cds.lpData = this.textBox1.Text;
cds.cbData = len + 1;
SendMessage(WINDOW_HANDLER, WM_COPYDATA, 0, ref cds);

}
}
}
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)] public string lpData;
}

}

//---------------------------------------------------
//接受方
//---------------------------------------------------
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
namespace WindowsFormGetMsg
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox textBox1;
private System.ComponentModel.Container components = null;
const int WM_COPYDATA = 0x004A;
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(176, 32);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(160, 21);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "textBox1";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(432, 266);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.textBox1});
this.Name = "Form1";
this.Text = "接收方窗体";
this.ResumeLayout(false);
}
static void Main()
{
Application.Run(new Form1());
}
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
switch(m.Msg)
{
case WM_COPYDATA:
COPYDATASTRUCT mystr = new COPYDATASTRUCT();
Type mytype = mystr.GetType();
mystr =(COPYDATASTRUCT)m.GetLParam(mytype);
this.textBox1.Text =mystr.lpData;
break;
default:
base.DefWndProc(ref m);
break;
}
}
}
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)] public string lpData;

 

 

 

C#下的进程间的通讯 -- 共享内存篇
来自: jinhong618@ 奴隶 日期: 2007-8-3 22:43:02 全文阅读: 499 分类: .Net (C#)
【摘要】
这篇文章早就想发了,可是一直没有时间整理,今天总算发上来了。

上次发了利用发消息实现的C#进程间的通讯,这次又使用共享内存了,他们应用范围是不同的,共享内存适用于共享大量数据的情况。
本文章利用了前面的一篇.net 1.1 下实现的信号量的类,在.net 1.1 下实现,如果在.net 2.0 下实现的话就用不到我的那个信号量的类了,因为这个类在.net 2.0是提供的。

【全文】

首先还是定义非托管调用,如下:


const  int  INVALID_HANDLE_VALUE  =  - 1 ;
const  int  PAGE_READWRITE  =  0x04 ;
   //共享内存
   [ DllImport ( "Kernel32.dll" , EntryPoint = "CreateFileMapping" )]
   private  static  extern  IntPtr  CreateFileMapping ( IntPtr  hFile ,  //HANDLE hFile,
    UInt32  lpAttributes , //LPSECURITY_ATTRIBUTES lpAttributes,  //0
    UInt32  flProtect , //DWORD flProtect
    UInt32  dwMaximumSizeHigh , //DWORD dwMaximumSizeHigh,
    UInt32  dwMaximumSizeLow , //DWORD dwMaximumSizeLow,
    string  lpName //LPCTSTR lpName
    );

   [ DllImport ( "Kernel32.dll" , EntryPoint = "OpenFileMapping" )]
   private  static  extern  IntPtr  OpenFileMapping (
    UInt32  dwDesiredAccess , //DWORD dwDesiredAccess,
    int  bInheritHandle , //BOOL bInheritHandle,
    string  lpName //LPCTSTR lpName
    );

   const  int  FILE_MAP_ALL_ACCESS  =  0x0002 ;
   const  int  FILE_MAP_WRITE  =  0x0002 ;

   [ DllImport ( "Kernel32.dll" , EntryPoint = "MapViewOfFile" )]
   private  static  extern  IntPtr  MapViewOfFile (
    IntPtr  hFileMappingObject , //HANDLE hFileMappingObject,
    UInt32  dwDesiredAccess , //DWORD dwDesiredAccess
    UInt32  dwFileOffsetHight , //DWORD dwFileOffsetHigh,
    UInt32  dwFileOffsetLow , //DWORD dwFileOffsetLow,
    UInt32  dwNumberOfBytesToMap //SIZE_T dwNumberOfBytesToMap
    );

   [ DllImport ( "Kernel32.dll" , EntryPoint = "UnmapViewOfFile" )]
   private  static  extern  int  UnmapViewOfFile ( IntPtr  lpBaseAddress );

   [ DllImport ( "Kernel32.dll" , EntryPoint = "CloseHandle" )]
   private  static  extern  int  CloseHandle ( IntPtr  hObject );
然后分别在AB两个进程中定义如下两个信号量及相关变量;
   private  Semaphore  m_Write ;   //可写的信号
   private  Semaphore  m_Read ;   //可读的信号
   private  IntPtr  handle ;      //文件句柄
   private  IntPtr  addr ;        //共享内存地址
   uint  mapLength ;             //共享内存长


定义这两个信号量是为读写互斥用的。
在A进程中创建共享内存:

m_Write  =  new  Semaphore ( 1 , 1 , "WriteMap" );
m_Read  =  new  Semaphore ( 0 , 1 , "ReadMap" );
mapLength  =  1024 ;
IntPtr  hFile  =  new  IntPtr ( INVALID_HANDLE_VALUE );   
handle  =  CreateFileMapping ( hFile , 0 , PAGE_READWRITE , 0 , mapLength , "shareMemory" );
addr  =  MapViewOfFile ( handle , FILE_MAP_ALL_ACCESS , 0 , 0 , 0 );

然后再向共享内存中写入数据:

m_Write . WaitOne ();
byte []  sendStr  =  Encoding . Default . GetBytes ( txtMsg . Text  +  '/0' );
//如果要是超长的话,应另外处理,最好是分配足够的内存
if ( sendStr . Length  <  mapLength )
       Copy ( sendStr , addr );
m_Read . Release ();


这是在一个单独的方法中实现的,可多次调用,但受信号量的控制。其中txtMsg是一个文本框控件,实际中可用任意字符串,加最后的'/0'是为了让在共享内存中的字符串有一个结束符,否则在内存中取出时是以'/0'为准的,就会出现取多的情况。
Copy方法的实现如下:

static  unsafe  void  Copy ( byte []  byteSrc , IntPtr  dst )
   {
    fixed  ( byte *  pSrc  =  byteSrc )
    {
     byte *  pDst  =  ( byte *) dst ;
     byte *  psrc  =  pSrc ;
     for ( int  i = 0 ; i < byteSrc . Length ; i ++)
     {
      * pDst  =  * psrc ;
      pDst ++;
      psrc  ++;
     }
    }
   }
注意unsafe 关键字,在编译时一定要打开非安全代码开关。
最后不要忘了在A进程中关闭共享内存对象,以免内存泄露。
    UnmapViewOfFile ( addr );
    CloseHandle ( handle );

要在B进程中读取共享内存中的数据,首先要打开共享内存对象:
m_Write  =  Semaphore . OpenExisting ( "WriteMap" );
m_Read  =  Semaphore . OpenExisting ( "ReadMap" );
handle  =  OpenFileMapping ( 0x0002 , 0 , "shareMemory" );
读取共享内存中的数据:
    m_Read . WaitOne ();
    string  str  =  MapViewOfFile ( handle , FILE_MAP_ALL_ACCESS , 0 , 0 , 0 );
    txtMsg . Text  =  str ;
    m_Write . Release ();
这里获取了字符串,如果要获取byte数组,请参考上面的Copy函数实现。

 

使用微软消息队列实现C#进程间通信
 http://tech.ddvip.com/2007-11/119554606737754.html

在.NET中使用命名管道完成进程间通信

http://www.hackhome.com/InfoView/Article_121029_2.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值