C#进程间通讯技术-整理。

原文: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); 2  3         void Form1_Load(object sender, EventArgs e) 4         { 5             ThreadPool.QueueUserWorkItem(delegate 6                { 7                    pipeServer.BeginWaitForConnection((o) => 8                    { 9                        NamedPipeServerStream server = (NamedPipeServerStream)o.AsyncState;10                        server.EndWaitForConnection(o);11                        StreamReader sr = new StreamReader(server);12                        StreamWriter sw = new StreamWriter(server);13                        string result = null;14                        string clientName = server.GetImpersonationUserName();15                        while (true)16                        {17                            result = sr.ReadLine();18                            if (result == null || result == "bye")19                                break;20                            this.Invoke((MethodInvoker)delegate { lsbMsg.Items.Add(clientName+" : "+result); });21                        }22                    }, pipeServer);23                });24         }
1 NamedPipeServerStream pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut, 4, PipeTransmissionMode.Message, PipeOptions.Asynchronous); 2  3         void Form1_Load(object sender, EventArgs e) 4         { 5             ThreadPool.QueueUserWorkItem(delegate 6                { 7                    pipeServer.BeginWaitForConnection((o) => 8                    { 9                        NamedPipeServerStream server = (NamedPipeServerStream)o.AsyncState;10                        server.EndWaitForConnection(o);11                        StreamReader sr = new StreamReader(server);12                        StreamWriter sw = new StreamWriter(server);13                        string result = null;14                        string clientName = server.GetImpersonationUserName();15                        while (true)16                        {17                            result = sr.ReadLine();18                            if (result == null || result == "bye")19                                break;20                            this.Invoke((MethodInvoker)delegate { lsbMsg.Items.Add(clientName+" : "+result); });21                        }22                    }, pipeServer);23                });24         }

有关 NamedPipeClientStream 类 官方MSDN文档说明

客户端主要代码

 1   NamedPipeClientStream pipeClient = 2                     new NamedPipeClientStream("192.168.1.100", "testpipe", 3                         PipeDirection.InOut, PipeOptions.Asynchronous, 4                         TokenImpersonationLevel.None); 5         StreamWriter sw = null; 6         void Form2_Load(object sender, EventArgs e) 7         { 8             pipeClient.Connect(); 9             sw = new StreamWriter(pipeClient);10             sw.AutoFlush = true;11         }12 13         private void button1_Click_1(object sender, EventArgs e)14         {15             sw.WriteLine(textBox1.Text);16         }
1   NamedPipeClientStream pipeClient = 2                     new NamedPipeClientStream("192.168.1.100", "testpipe", 3                         PipeDirection.InOut, PipeOptions.Asynchronous, 4                         TokenImpersonationLevel.None); 5         StreamWriter sw = null; 6         void Form2_Load(object sender, EventArgs e) 7         { 8             pipeClient.Connect(); 9             sw = new StreamWriter(pipeClient);10             sw.AutoFlush = true;11         }12 13         private void button1_Click_1(object sender, EventArgs e)14         {15             sw.WriteLine(textBox1.Text);16         }

 

 

经发现,命名管道, 其实是基于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函数实现。
时间: 2024-12-22 22:49:10

C#进程间通讯技术-整理。的相关文章

PHP中实现进程间通讯

进程 PHP中实现进程间通讯 邱文宇   本文将讨论在PHP4环境下如何使用进程间通讯机制--IPC(Inter-Process-Communication).本文讨论的软件环境是linux+php4.0.4或更高版本.首先,我们假设你已经装好了PHP4和UNIX, 为了使得php4可以使用共享内存和信号量,必须在编译php4程序时激活shmop和sysvsem这两个扩展模块. 实现方法:在PHP设定(configure)时加入如下选项. --enable-shmop --enable-sysv

在PHP中实现进程间通讯

本文将讨论在PHP4环境下如何使用进程间通讯机制--IPC(Inter-Process-Communication).本文讨论的软件环境是linux+php4.0.4或更高版本.首先,我们假设你已经装好了PHP4和UNIX, 为了使得php4可以使用共享内存和信号量,必须在编译php4程序时激活shmop和sysvsem这两个扩展模块. 实现方法:在PHP设定(configure)时加入如下选项. --enable-shmop --enable-sysvsem  这样就使得你的PHP系统可以处理

PHP中实现进程间通讯_php基础

PHP中实现进程间通讯 邱文宇   本文将讨论在PHP4环境下如何使用进程间通讯机制--IPC(Inter-Process-Communication).本文讨论的软件环境是linux+php4.0.4或更高版本.首先,我们假设你已经装好了PHP4和UNIX, 为了使得php4可以使用共享内存和信号量,必须在编译php4程序时激活shmop和sysvsem这两个扩展模块. 实现方法:在PHP设定(configure)时加入如下选项. --enable-shmop --enable-sysvsem

NetBSD进程间通讯系统分析

简单的进程间通讯: 管道 管道是 UNIX 最传统, 最简单, 也是最有效的进程间通讯方法. NetBSD 处理管道的代码在 kern/sys_pipe.c, 它的读写函数作为 file 结构的 fileops 挂载, 并在 read(2), write(2) 时被调用. 管道创建 pipe(2) 的响应函数实 sys_pipe(). 它首先两次调用 pipe_create(), 第一次申请读端口将调用 pipespace() 申请一个用作缓冲区的内核地址空间 (回忆 BsdSrcUvm, su

Linux 进程间通讯共享内存方式

共享内存方式:从物理内存里面拿出来一部分作为多个进程共享. 共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入数据,共享这个内存的所有进程都可以立即看到其中内容. 共享内存实现步骤: 一.创建共享内存,使用shmget函数. 二.映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数. 创建共享内存shmget: intshmget(key_t key, size_t size, int shmflg) 功能:得到一个共享内存标识符或创建一个共享内存对象并

c++进程间通讯(共享内存)时

问题描述 c++进程间通讯(共享内存)时 我的需求:一个进程批量的数据不间断的存入差不多每秒有400k的数据这样子(不一定是一次存入的,可能是分几次),而另一个内存要从共享内存中读取这些数据,读取完就释放那块内存. 如何使共享的内存具有一定的数据结构,如同stl中的vector那样. 解决方案 需要自己做序列化,反序列化,把数据转回vector.自己定义格式等,知道多大一块内存数据表示一个vector的元素.然后一个个获取,存入vector 解决方案二: 如何使共享的内存具有一定的数据结构?共享

关于linux使用动态库进行进程间通讯

问题描述 关于linux使用动态库进行进程间通讯 各位: 两个进程间通过动态库的方式如何进行参数的传递? 我首先在一个库中做了如下的内容: #include ""caculate.h""#include ""stdio.h""int iShare; #pragma data_seg (""shareddate"")int iShareInSeg = 1;#pragma data_seg#

进程间通讯-Android开发 AIDL接口文件里使用内部类报错unknown type

问题描述 Android开发 AIDL接口文件里使用内部类报错unknown type 在Android开发时涉及到一个进程间通讯,要传递一个内部类对象到远程服务端.已经把内部类对象用Parcelable序列化了,但是编译总是报错unknown type 外部类.内部类. 1.AIDL能否传递内部类对象到远程端? 2.此问题该怎样解决?

进程间通讯问题,内存共享的实现

问题描述 需要在多个进程传输数据,其实一个检测进程一直获取检测的数据,供其他进程及时读取使用,数据库不是很大,但是数据更新非常的频繁,考虑到效率问题,不知道用什么方法实现?暂时决定采用内存共享的方式实现.希望各位大虾多加指点.具体怎么实现比较好?需要注意哪些地方?如果有类似的源代码参考就最好了. 解决方案 解决方案二:方案1.使用WM_COPYDATA消息方案2.使用WriteProcessMemory(),ReadProcessMemory()访问其他进程的内存方案3.使用内存镜像文件解决方案