扩展阅读:http://www.cnblogs.com/joye-shen/archive/2012/06/16/2551864.htmlphp
1、进程间通信的方式html
1)共享内存数据库
包括:内存映射文件,共享内存DLL,剪切板。编程
2)命名管道及匿名管道windows
3)消息通信api
4)利用代理方法。例如SOCKET,配置文件,注册表方式。数组
等方式。安全
方法一:通信。多线程
进程间通信的方式有不少,经常使用的有共享内存(内存映射文件、共享内存DLL、剪切板等)、命名管道和匿名管道、发送消息等几种方法来直接完成,另外还能够经过socket口、配置文件和注册表等来间接实现进程间数据通信任务。以上这几种方法各有优缺点,具体到在进程间进行大数据量数据的快速交换问题上,则能够排除使用配置文件和注册表的方法;另外,因为管道和socket套接字的使用须要有网卡的支持,所以也能够不予考虑。这样,可供选择的通信方式只剩下共享内存和发送消息两种。app
2、发送消息实现进程间通信前准备
下面的例子用到一个windows api 32函数
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr wnd,int msg,IntPtr wP,IntPtr lP);
要有此函数,须要添加using System.Runtime.InteropServices;命名空间
此方法各个参数表示的意义
wnd:接收消息的窗口的句柄。若是此参数为HWND_BROADCAST,则消息将被发送到系统中全部顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。
msg:指定被发送的消息类型。
wP:消息内容。
lP:指定附加的消息指定信息。
用api参考手册查看SendMessage用法时,参考手册则提示
SendMessage与PostMessage之间的区别:SendMessage和PostMessage,这两个函数虽然功能很是类似,都是负责向指定的窗口发送消息,可是SendMessage() 函数发出消息后一直等到接收方的消息响应函数处理完以后才能返回,并可以获得返回值,在此期间发送方程序将被阻塞,SendMessage() 后面的语句不能被继续执行,便是说此方法是同步的。而PostMessage() 函数在发出消息后立刻返回,其后语句可以被当即执行,可是没法获取接收方的消息处理返回值,便是说此方法是异步的。
3、发送消息实现进程间通信具体步骤
1.新建windows应用程序
(1)打开VS2008,新建一个“windows 应用程序”,主窗口为Form1,项目名称:ProcessCommunication
(2)在Form1上添加一个标签为textBox1的文本框,并为Form1添加KeyDown事件,当Form1接收到KewDown消息时,将接收到的数据显示在label1上。
public Form1()
{
InitializeComponent();
this.KeyDown+=new KeyEventHandler(Form1_KeyDown);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
this.textBox1.Text = Convert.ToString(e.KeyValue);
}
(3)编译运行,生成ProcessCommunication.exe
2.新建windows应用程序
(1)打开VS2008,新建一个“windows 应用程序”,主窗口为Form1,项目名称:ProcessCommunication1,
并在Form1上添加一个按钮和一个文本框
namespace ProcessCommunication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Win32 API函数:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr wnd,int msg,IntPtr wP,IntPtr lP);
private void button1_Click(object sender, EventArgs e)
{
Process[] pros = Process.GetProcesses(); //获取本机全部进程
for (int i = 0; i < pros.Length; i++)
{
if (pros[i].ProcessName == "ProcessCommunication") //名称为ProcessCommunication的进程
{
IntPtr hWnd = pros[i].MainWindowHandle; //获取ProcessCommunication.exe主窗口句柄
int data = Convert.ToInt32(this.textBox1.Text); //获取文本框数据
SendMessage(hWnd, 0x0100, (IntPtr)data, (IntPtr)0); //点击该按钮,以文本框数据为参数,向Form1发送WM_KEYDOWN消息
}
}
}
}
}
3.启动ProcessCommunication.exe可执行文件,弹出Form1窗体称为接受消息窗体。
启动ProcessCommunication1.exe可执行文件,在弹出的窗体中的文本框中输入任意数字,点击button1按钮,接受消息窗体textBox1即显示该数字。
到此结束。
方法二:IPC通信机制Remoting
=======
最近一直纠结与使用多进程仍是多线程来构建程序。多线程的方法彷佛不错,可是一个进程可承受的线程数有有限的,而且因为每一个线程都与UI有着些许关系,线程的工做大多数时间浪费在阻塞上了,效率实在不是很高。
笔者遂在google上搜索进程间通信的方案。发现有不少种,其中IPC通道彷佛是个不错的选择,支持本机的进程间通信,能够做为备选方案之一,下面介绍如下基本的编程方法,以做备忘。
首先创建一个IPC通信中使用的对象,其中MarshalByRefObject 是必须的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
using
System;
namespace
Ipctest
{
public
class
test:MarshalByRefObject
{
private
int
iCount = 0;
public
int
count()
{
iCount++;
return
iCount;
}
public
int
Add(
int
x)
{
iCount += x;
return
iCount;
}
}
}
|
接着建一个服务端控制台程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
using
System;
using
System.Runtime.Remoting;
using
System.Runtime.Remoting.Channels;
using
System.Runtime.Remoting.Channels.Ipc;
namespace
Ipctest
{
class
Program
{
static
void
Main(
string
[] args)
{
IpcChannel serverchannel =
new
IpcChannel(
"testchannel"
);
ChannelServices.RegisterChannel(serverchannel,
false
);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof
(test),
"test"
, WellKnownObjectMode.Singleton);
Console.WriteLine(
"press Enter to exit"
);
Console.ReadLine();
Console.WriteLine(
"server stopped"
);
}
}
}
|
最后是客户端控制台程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using
System;
using
System.Runtime.Remoting;
using
System.Runtime.Remoting.Channels;
using
System.Runtime.Remoting.Channels.Ipc;
namespace
Ipctest
{
class
Program
{
static
void
Main(
string
[] args)
{
IpcChannel tcc =
new
IpcChannel();
ChannelServices.RegisterChannel(tcc,
false
);
WellKnownClientTypeEntry remotEntry =
new
WellKnownClientTypeEntry(
typeof
(test),
"ipc://testchannel/test"
);
RemotingConfiguration.RegisterWellKnownClientType(remotEntry);
test st =
new
test();
Console.WriteLine(
"{0},{1}"
,st.count(),st.Add(1));
Console.ReadLine();
}
}
}
|
在测试的过程当中会发现第一次调用客户端输出结果:
1,2
第二次输出结果
3,4
……
结果是比较符合要求的。
方法三:管道
最近在作一个数据库同步软件.!! 程序 服务端为 一个winform + windows Service 二大模块.! 因为程序功能的需求. 须要winform 与windows Service进程通信. 所以使用了 命名管道 来实现功能需求.!
以此记下笔记 , 并付上一Demo 有关 NamedPipeServerStream 类 官方MSDN文档说明 ![]() 1 NamedPipeServerStream pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut, 4, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
![]() 1 NamedPipeClientStream pipeClient =
经发现,命名管道, 实际上是基于TCP/IP 来链接. 且端口为 445
固然, 我这里只是 传输一个字符串作为信息而已.! 其实仍然 能够传输本身所定义的 对象 等.(记得序列化哟..) 源码 |
方法四:共享内存
次发了利用发消息实现的C#进程间的通信,此次又使用共享内存了,他们应用范围是不一样的,共享内存适用于共享大量数据的状况。
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函数实现。