进程间通讯的方式:
1、Windows消息通讯
2、共享内存(内存映射文件,共享内存DLL,剪切板)
3、命名管道及匿名管道
4、IPC
5、利用代理方法(如SOCKET,配置文件,注册表方式)
Windows消息通讯实例:C#进程间通信Windows消息通讯,SendMessage-C#文档类资源-CSDN下载
共享内存实例:C#进程间通信共享内存-C#文档类资源-CSDN下载
管道通信实例:C#使用管道Pipe在进程间通信-C#文档类资源-CSDN下载
IPC通信实例:C#进程间通信,IPC通信-C#文档类资源-CSDN下载
进程间通讯的方式有很多,以上这几种方法各有优缺点,在进程间进行大数据量数据的快速交换问题上,使用配置文件和注册表的方法不是很合适,而管道和socket套接字的使用则需要有网卡的支持。
Windows消息通讯:
SendMessage函数功能:该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。而函数PostMessage不同,将一个消息寄送到一个线程的消息队列后立即返回。
函数原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
参数:
hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。
Msg:指定被发送的消息。
wParam:指定附加的消息指定信息。
IParam:指定附加的消息指定信息。
返回值:返回值指定消息处理的结果,依赖于所发送的消息。
SendMessage与PostMessage之间的区别:SendMessage和PostMessage,这两个函数虽然功能非常相似,都是负责向指定的窗口发送消息,但是SendMessage() 函数发出消息后一直等到接收方的消息响应函数处理完之后才能返回,并能够得到返回值,在此期间发送方程序将被阻塞,SendMessage() 后面的语句不能被继续执行,即是说此方法是同步的。而PostMessage() 函数在发出消息后马上返回,其后语句能够被立即执行,但是无法获取接收方的消息处理返回值,即是说此方法是异步的。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public struct CopyDataStruct
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
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 // 第二个消息参数
);
private void button1_Click(object sender, EventArgs e)
{
string strURL = textBox1.Text;
CopyDataStruct cds;
cds.dwData = (IntPtr)1; //这里可以传入一些自定义的数据,但只能是4字节整数
cds.lpData = strURL; //消息字符串
//注意,这里的长度是按字节来算的
cds.cbData = System.Text.Encoding.Default.GetBytes(strURL).Length + 1;
SendMessage(FindWindow(null, "Form2"), WM_COPYDATA, 0, ref cds); //窗口标题
}
//接收消息方法
protected override void WndProc(ref System.Windows.Forms.Message e)
{
if (e.Msg == WM_COPYDATA)
{
CopyDataStruct cds = (CopyDataStruct)e.GetLParam(typeof(CopyDataStruct));
textBox2.Text = cds.lpData.ToString();
}
base.WndProc(ref e);
}
}
共享内存:顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。 特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
服务端代码:
namespace IMServer
{
/// <summary>
/// 用于共享内存方式通信的 值类型 结构体
/// </summary>
public struct ServiceMsg
{
public int Id;
public long NowTime;
}
class Program
{
static void Main(string[] args)
{
Console.Write("IMServer_State 请输入共享内存公用名(默认:testmap):");
string shareName = Console.ReadLine();
if (string.IsNullOrEmpty(shareName))
shareName = "testmap";
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(shareName, 1024000,MemoryMappedFileAccess.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
//注意这里的偏移量 4 byte * 5 write by IMServer_Message
using (MemoryMappedViewStream stream = mmf.CreateViewStream(20, 0))
{
var writer = new BinaryWriter(stream);
for (int i = 5; i < 10; i++)
{
writer.Write(i);
Console.WriteLine("{0}位置写入流:{0}", i);
}
}
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(1024, 10240))
{
int colorSize = Marshal.SizeOf(typeof (ServiceMsg));
var color = new ServiceMsg();
for (int i = 0; i < colorSize*5; i += colorSize)
{
color.Id = i;
color.NowTime = DateTime.Now.Ticks;
//accessor.Read(i, out color);
accessor.Write(i, ref color);
Console.WriteLine("{1}\tNowTime:{0}", new DateTime(color.NowTime), color.Id);
Thread.Sleep(1000);
}
}
Thread.Sleep(5000);
mutex.ReleaseMutex();
}
Console.WriteLine("测试: 我是 即时通讯 - 状态服务 我启动啦!!!");
Console.ReadKey();
}
}
}
客户端代码:
namespace IMClent
{
/// <summary>
/// 用于共享内存方式通信的 值类型 结构体
/// 非持久内存映射文件:未映射到磁盘上的现有文件的内存映射文件
/// </summary>
public struct ServiceMsg
{
public int Id;
public long NowTime;
}
class Program
{
static void Main(string[] args)
{
Console.Write("IMServer_Message 请输入共享内存公用名(默认:testmap):");
string shareName = Console.ReadLine();
if (string.IsNullOrEmpty(shareName))
shareName = "testmap";
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(shareName, 1024000,MemoryMappedFileAccess.ReadWrite))
{
bool mutexCreated;
//进程间同步
var mutex = new Mutex(true, "testmapmutex", out mutexCreated);
using (MemoryMappedViewStream stream = mmf.CreateViewStream()) //创建文件内存视图流
{
var writer = new BinaryWriter(stream);
for (int i = 0; i < 5; i++)
{
writer.Write(i);
Console.WriteLine("{0}位置写入流:{0}", i);
}
}
mutex.ReleaseMutex();
Console.WriteLine("启动状态服务,按【回车】读取共享内存数据");
Console.ReadLine();
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
var reader = new BinaryReader(stream);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("{1}位置:{0}", reader.ReadInt32(), i);
}
}
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(1024, 10240))
{
int colorSize = Marshal.SizeOf(typeof (ServiceMsg));
ServiceMsg color;
for (int i = 0; i < 50; i += colorSize)
{
accessor.Read(i, out color);
Console.WriteLine("{1}\tNowTime:{0}", new DateTime(color.NowTime), color.Id);
}
}
mutex.ReleaseMutex();
}
Console.WriteLine("测试: 我是 即时通讯 - 消息服务 我启动啦!!!");
Console.ReadKey();
}
}
}
管道通信:进程间通信的一种方式,Pipes:管道,分为无名管道:在父子进程间交换数据;有名管道:可在不同主机间交换数据,分为服务器方和客户方,在Win9X下只支持有名管道客户。命名管道是一个有名字的,单向或双向的通信管道。管道的名称有两部分组成:计算机名和管道名,例如\\[host_name]\pipe\[pipe_name]\(括号内为参数)。对于同一主机来讲允许有多个同一命名管道的实例并且可以由不同的进程打开,但是不同的管道都有属于自己的管道缓冲区而且有自己的通讯环境互不影响,并且命名管道可以支持多个客户端连接一个服务器端。命名管道客户端不但可以与本机上的服务器通讯也可以同其他主机上的服务器通讯。
服务端代码:
namespace PipeServerDemo
{
class Program
{
static void Main(string[] args)
{
Thread newThread = new Thread(new ThreadStart(ServerThread));
newThread.IsBackground = true;
newThread.Start();
Console.WriteLine("Server Start.");
Console.ReadLine();
}
static string strWrite;
private static void ServerThread()
{
try
{
//这里第一个参数是我的计算机名
NamedPipeClientStream pipeClientA =
new NamedPipeClientStream("\\.", "testpipe",
PipeDirection.InOut, PipeOptions.None,
TokenImpersonationLevel.Impersonation);
StreamWriter sw = new StreamWriter(pipeClientA);
StreamReader sr = new StreamReader(pipeClientA);
pipeClientA.Connect();
sw.AutoFlush = true;
// 确认服务器连接
if (sr.ReadLine() == "MyServer")
{
Console.WriteLine("Client Connected.");
//向管道中写入数据(先转化为字符串)
strWrite = "It's one Client.";
sw.Write(strWrite);
}
//关闭客户端
pipeClientA.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
客户端代码:
namespace PipeClientDemo
{
class Program
{
static int numThreads = 3;
static void Main(string[] args)
{
Thread newThread = new Thread(ServerThread);
newThread.Start();
Console.WriteLine("Client Start.");
Console.ReadLine();
}
private static void ServerThread(object data)
{
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);
Console.WriteLine("NamedPipeServerStream thread created.");
//等待客户端连接
pipeServer.WaitForConnection();
Console.WriteLine("Client connected.");
try
{
StreamReader sr = new StreamReader(pipeServer);
StreamWriter sw = new StreamWriter(pipeServer);
sw.AutoFlush = true;
//客户端通过此消息进行确认
sw.WriteLine("MyServer");
// Obtain the filename from the connected client.
string content = sr.ReadLine();
Console.WriteLine("Reading {0}", content);
pipeServer.Disconnect();
}
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
pipeServer.Close();
}
}
}
参考: