C#进程间通讯技术-整理。
扩展阅读:http://www.cnblogs.com/joye-shen/archive/2012/06/16/2551864.html
一、进程间通讯的方式
1)共享内存
包括:内存映射文件,共享内存DLL,剪切板。
2)命名管道及匿名管道
3)消息通讯
4)利用代理方法。例如SOCKET,配置文件,注册表方式。
等方式。
方法一:通讯。
进程间通讯的方式有很多,常用的有共享内存(内存映射文件、共享内存DLL、剪切板等)、命名管道和匿名管道、发送消息等几种方法来直接完成,另外还可以通过socket口、配置文件和注册表等来间接实现进程间数据通讯任务。以上这几种方法各有优缺点,具体到在进程间进行大数据量数据的快速交换问题上,则可以排除使用配置文件和注册表的方法;另外,由于管道和socket套接字的使用需要有网卡的支持,因此也可以不予考虑。这样,可供选择的通讯方式只剩下共享内存和发送消息两种。
二、发送消息实现进程间通讯前准备
下面的例子用到一个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() 函数在发出消息后马上返回,其后语句能够被立即执行,但是无法获取接收方的消息处理返回值,即是说此方法是异步的。
三、发送消息实现进程间通讯具体步骤
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 NamedPipeServerStream pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut, 4, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
有关 NamedPipeClientStream 类 官方MSDN文档说明 客户端主要代码
1 NamedPipeClientStream pipeClient = 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函数实现。
C#进程间通讯技术-整理。的更多相关文章
- UNIX环境高级编程——进程间通讯方法整理
一.无名管道pipe #include <unistd.h> int pipe(int fd [2]) 二.fifo #include <sys/stat.h> int mkf ...
- Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8479282.html 在Android中进程间通信是比较难的一部分,同时又非常 ...
- 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制【转】
本文转载自:https://blog.csdn.net/freekiteyu/article/details/70082302 Android-Binder进程间通讯机制 概述 最近在学习Binder ...
- Android 进程间通讯方式
Android 进程间通讯方式 1.通过单向数据管道传递数据 管道(使用PipedWriter/ 创建PipedReader)是java.io包的一部分.也就是说,它们是一般的Java功能,而不是An ...
- C#进程间通讯或同步的框架引荐
这篇文章主要介绍了一个进程间通讯同步的C#框架,代码具有相当的稳定性和可维护性,随着.NET的开源也会被注入更多活力,推荐!需要的朋友可以参考下 0.背景简介 微软在 .NET 框架中提供了多种实用 ...
- High Performance Networking in Google Chrome 进程间通讯(IPC) 多进程资源加载
小结: 1. 小文件存储于一个文件中: 在内部,磁盘缓存(disk cache)实现了它自己的一组数据结构, 它们被存储在一个单独的缓存目录里.其中有索引文件(在浏览器启动时加载到内存中),数据文件( ...
- Android Binder 进程间通讯机制梳理
什么是 Binder ? Binder是Android系统中进程间通讯(IPC)的一种方式,也是Android系统中最重要的特性之一.Binder的设计采用了面向对象的思想,在Binder通信模型的四 ...
- 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 百篇博客分析OpenHarmony源码 | v28.03
百篇博客系列篇.本篇为: v28.xx 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当 ...
- Android进程间通讯
最近研究了一下Android进程间通讯,原来只是会用,但是只是会用是不行滴,就来研究一下. 刚开始看的时候,我的头是这么大,看了一夜的时候,头就变成这样了,,吓得宝宝赶紧上床休息了,. 先喝喝茶讲个故 ...
随机推荐
- JAVA面向对象总结
面向对象概述 面向对象是当前计算机界关心的重点,它是90年代软件开发方法的主流.面向对象的概念和应用已超越程序设计和软件开发,如数据库系统.交互式界面.应用结构.应用平台.分布式系统.网络 ...
- Tradesy | IT桔子
Tradesy | IT桔子 Tradesy www.tradesy.com 认领 关注 分享
- 日期函数(SqlServer)
1.常用日期方法(下面的GetDate() = '2006-11-08 13:37:56.233') (1)DATENAME ( datepart ,date ) 返回表示指定日期的指定日期部分的字符 ...
- double精度的坑与BigDecimal
近期经常接触支付相关的功能,在开发及测试过程中,开始金额都使用的是double类型,而近期新进的需求存在支付时打折的情况,也就是会出现如 1.23元的情况,那么这时候问题来了,如果是直接使用1.23进 ...
- 执行npm安装模块的命令 Cannot find module
npm 安装了 appium 和 appium-doctor 运行命令,appium-doctor 提示找不到模块: C:\Users\autotest>appiummodule.js:471 ...
- Nginx各个配置块功能详解
Nginx学习笔记-入门篇 nginx初探 ginx服务器是轻量级web服务器中广受好评的一款产品,常用功能有HTTP代理与反向代理(目前已支持七层与四层代理),负载均衡,web缓存. nginx配置 ...
- nyist oj 79 拦截导弹 (动态规划基础题)
拦截导弹 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描写叙述 某国为了防御敌国的导弹突击.发展中一种导弹拦截系统.可是这样的导弹拦截系统有一个缺陷:尽管它的第一发炮弹可以 ...
- USB挂起与唤醒.
USB可见设备状态分为连接(Attached),上电(Powered),默认(Default),地址(Address),配置(Configured)和挂起(Suspended)6个状态.所谓可见,即U ...
- SQL2008安装重启失败
我今天安装SQL2008的一些问题经历SQL2008安装重启失败大致出错信息如下:RebootRequiredCheck 检查是否需要挂起计算机重新启动.挂起重新启动会导致安装程序失败. 失败 需要重 ...
- taglib的使用
使用自定义的taglib可以是我们对页面数据的处理放在后台,不仅使用方便,而且影藏了处理逻辑,也更加的安全. 需要使用到servlet.jar 1.在web-inf下建立taglib.tld文件 &l ...



