c# 进程间的通信实现之一简单字符串收发

   使用Windows API实现两个进程间(含窗体)的通信在Windows下的两个进程之间通信通常有多种实现方式,在.NET中,有如命名管道、消息队列、共享内存等实现方式,这篇文章要讲的是使用Windows的API来实现简单的进程间通信,这两个进程既可以都是基于C#开发,也可以都是基于C++开发,也可以是一个C#开发而另一个为C++开发,在C++开发方面,不需要额外调用Windows的API,而是可以直接使用相关方法即可。所以,这里重点要讲的就是在C#中是如何做的,而至于在C++中是如何做的将给出例子,并不做详述。

对于接收消息,只需要重写DefWndProc函数即可,对于发送消息,笔者编写了一个类MsgHandler来实现。要顺利实现消息的接收与发送,使用了Windows的API:FindWindow、SendMessage等。在C#环境中,通过DllImport来引入相应的API,代码示例如下:

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

// IsWindow method, using Windows API
[DllImport("User32.dll", EntryPoint = "IsWindow")]
private static extern bool IsWindow(int hWnd);

// SendMessage method, using Windows API
[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
);

// SendMessage method, using Windows API
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(
int hWnd, // handle to destination window
int Msg, // message
int wParam, // first message parameter
string lParam // second message parameter
);

笔者查阅了相关网络资源,发现很少有提及使用自定义消息来发送和接收消息的,几乎都是使用了系统消息WM_COPYDATA来实现。在本例中,笔者除了使用系统消息WM_COPYDATA来收发消息外,还将使用自定义消息来实现收发消息。不过,值得注意的是,笔者在测试过程中发现,使用自定义的消息来收发结构体时发生了一些异常,该异常提示说内存不能读,对于该问题,还有待进一步解决,当然,若是哪位前辈或朋友有遇到过该问题并已顺利解决的话,不妨告知,笔者将洗耳恭听。

消息发送类MsgHandler的代码示例如下:

using System;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace CommunicationTest
{
/// <summary>
/// Send message handler class.
/// Call the method "SendMessageToTargetWindow" to send
/// the message what we want.
/// </summary>
class MsgHandler
{
/// <summary>
/// System defined message
/// </summary>
private const int WM_COPYDATA = 0x004A;

/// <summary>
/// User defined message
/// </summary>
private const int WM_DATA_TRANSFER = 0x0437;

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

// IsWindow method, using Windows API
[DllImport("User32.dll", EntryPoint = "IsWindow")]
private static extern bool IsWindow(int hWnd);

// SendMessage method, using Windows API
[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
);

// SendMessage method, using Windows API
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(
int hWnd, // handle to destination window
int Msg, // message
int wParam, // first message parameter
string lParam // second message parameter
);

/// <summary>
/// CopyDataStruct
/// </summary>
private struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}

/// <summary>
/// Send message to target window
/// </summary>
/// <param name="wndName">The window name which we want to found</param>
/// <param name="msg">The message to be sent, string</param>
/// <returns>success or not</returns>
public static bool SendMessageToTargetWindow(string wndName, string msg)
{
Debug.WriteLine(string.Format("SendMessageToTargetWindow: Send message to target window {0}: {1}", wndName, msg));

int iHWnd = FindWindow(null, wndName);
if (iHWnd == 0)
{
string strError = string.Format("SendMessageToTargetWindow: The target window [{0}] was not found!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
return false;
}
else
{
byte[] bytData = null;
bytData = Encoding.Default.GetBytes(msg);

COPYDATASTRUCT cdsBuffer;
cdsBuffer.dwData = (IntPtr)100;
cdsBuffer.cbData = bytData.Length;
cdsBuffer.lpData = Marshal.AllocHGlobal(bytData.Length);
Marshal.Copy(bytData, 0, cdsBuffer.lpData, bytData.Length);

// Use system defined message WM_COPYDATA to send message.
int iReturn = SendMessage(iHWnd, WM_COPYDATA, 0, ref cdsBuffer);
if (iReturn < 0)
{
string strError = string.Format("SendMessageToTargetWindow: Send message to the target window [{0}] failed!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
return false;
}

return true;
}
}

/// <summary>
/// Send message to target window
/// </summary>
/// <param name="wndName">The window name which we want to found</param>
/// <param name="wParam">first parameter, integer</param>
/// <param name="lParam">second parameter, string</param>
/// <returns>success or not</returns>
public static bool SendMessageToTargetWindow(string wndName, int wParam, string lParam)
{
Debug.WriteLine(string.Format("SendMessageToTargetWindow: Send message to target window {0}: wParam:{1}, lParam:{2}", wndName, wParam, lParam));

int iHWnd = FindWindow(null, wndName);
if (iHWnd == 0)
{
string strError = string.Format("SendMessageToTargetWindow: The target window [{0}] was not found!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
return false;
}
else
{
// Use our defined message WM_DATA_TRANSFER to send message.
int iReturn = SendMessage(iHWnd, WM_DATA_TRANSFER, wParam, lParam);
if (iReturn < 0)
{
string strError = string.Format("SendMessageToTargetWindow: Send message to the target window [{0}] failed!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
return false;
}

return true;
}
}
}
}

消息接收重写了DefWndProc方法,其代码示例如下:

/// <summary>
/// Override the DefWndProc function, in order to receive the message through it.
/// </summary>
/// <param name="m">message</param>
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
// Here, we use WM_COPYDATA message to receive the COPYDATASTRUCT
case WM_COPYDATA:
COPYDATASTRUCT cds = new COPYDATASTRUCT();
cds = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
byte[] bytData = new byte[cds.cbData];
Marshal.Copy(cds.lpData, bytData, 0, bytData.Length);
this.ProcessIncomingData(bytData);
break;

// Here, we use our defined message WM_DATA_TRANSFER to receive the
// normal data, such as integer, string.
// We had try to use our defined message to receive the COPYDATASTRUCT,
// but it didn‘t work!! It told us that we try to access the protected
// memory, it usually means that other memory has been broken.
case WM_DATA_TRANSFER:
int iWParam = (int)m.WParam;
string sLParam = m.LParam.ToString();
this.ProcessIncomingData(iWParam, sLParam);
break;

default:
base.DefWndProc(ref m);
break;
}
}

消息的接收与发送最终通过一个WinForm展现出来,其代码实现如下:

using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace CommunicationTest
{
public partial class FrmTest : Form
{
/// <summary>
/// System defined message
/// </summary>
private const int WM_COPYDATA = 0x004A;

/// <summary>
/// User defined message
/// </summary>
private const int WM_DATA_TRANSFER = 0x0437;

/// <summary>
/// CopyDataStruct
/// </summary>
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}

public FrmTest()
{
InitializeComponent();
}

/// <summary>
/// Override the DefWndProc function, in order to receive the message through it.
/// </summary>
/// <param name="m">message</param>
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
// Here, we use WM_COPYDATA message to receive the COPYDATASTRUCT
case WM_COPYDATA:
COPYDATASTRUCT cds = new COPYDATASTRUCT();
cds = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
byte[] bytData = new byte[cds.cbData];
Marshal.Copy(cds.lpData, bytData, 0, bytData.Length);
this.ProcessIncomingData(bytData);
break;

// Here, we use our defined message WM_DATA_TRANSFER to receive the
// normal data, such as integer, string.
// We had try to use our defined message to receive the COPYDATASTRUCT,
// but it didn‘t work!! It told us that we try to access the protected
// memory, it usually means that other memory has been broken.
case WM_DATA_TRANSFER:
int iWParam = (int)m.WParam;
string sLParam = m.LParam.ToString();
this.ProcessIncomingData(iWParam, sLParam);
break;

default:
base.DefWndProc(ref m);
break;
}
}

/// <summary>
/// Process the incoming data
/// </summary>
/// <param name="data">incoming data</param>
private void ProcessIncomingData(byte[] bytesData)
{
string strRevMsg = "Receive message: " + Encoding.Default.GetString(bytesData);

lstReceivedMsg.Items.Add(strRevMsg);
}

/// <summary>
/// Process the incoming data
/// </summary>
/// <param name="iWParam">a integer parameter</param>
/// <param name="sLParam">a string parameter</param>
private void ProcessIncomingData(int iWParam, string sLParam)
{
StringBuilder strBuilder = new StringBuilder();
strBuilder.Append("wParam: ");
strBuilder.Append(iWParam.ToString());
strBuilder.Append(", lParam: ");
strBuilder.Append(sLParam);

lstReceivedMsg.Items.Add(strBuilder.ToString());
}

private void FrmTest_Load(object sender, EventArgs e)
{
this.Text = "First Test Form";
this.txtCurrentWndName.Text = "First Test Form";
this.txtTargetWndName.Text = "Second Test Form";
}

private void txtCurrentWndName_TextChanged(object sender, EventArgs e)
{
this.Text = txtCurrentWndName.Text;
}

/// <summary>
/// Send message to the target window with system defined message WM_COPYDATA
/// </summary>
private void btnSend1_Click(object sender, EventArgs e)
{
string strWndName = this.txtTargetWndName.Text;
// Check the target window name is null/empty or not
if (string.IsNullOrEmpty(strWndName))
{
MessageBox.Show("The target window name must not be null or empty!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}

string strMsg = this.txtSendingMsg.Text;
// Check the sending message is null/empty or not
if (string.IsNullOrEmpty(strMsg))
{
MessageBox.Show("The sending message must not be null or empty!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}

// Send message to the target window
bool bReturn = MsgHandler.SendMessageToTargetWindow(strWndName, strMsg);
}

/// <summary>
/// Send message to the target window with user defined message WM_DATA_TRANSFER
/// </summary>
private void btnSend2_Click(object sender, EventArgs e)
{
string strWndName = this.txtTargetWndName.Text;
// Check the target window name is null/empty or not
if (string.IsNullOrEmpty(strWndName))
{
MessageBox.Show("The target window name must not be null or empty!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}

string strWParam = this.txtWParam.Text;
string strLParam = this.txtLParam.Text;
// Check the sending message is null/empty or not
if (string.IsNullOrEmpty(strWParam) || string.IsNullOrEmpty(strLParam))
{
MessageBox.Show("The sending message must not be null or empty!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}

int iWParam = 0;
// Convert string to integer
try
{
iWParam = Int32.Parse(strWParam);
}
catch (Exception ex)
{
MessageBox.Show("Convert string to integer exception: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

// Send message to the target window
bool bReturn = MsgHandler.SendMessageToTargetWindow(strWndName, iWParam, strLParam);
}
}
}

通过上述的C#部分的代码,已经可以实现两个C#窗体间的通信,其界面截图如下图所示:

那么,在C++中又是如何实现的呢?这个其实也是很简单的。要实现消息的收发,同理也要重写WindowProc以便于接收消息,而通过调用方法亦可实现消息的发送。
    对于消息接收,如果是系统消息,可以通过OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)事件完成;如果是自定义消息,可以通过重写WindowProc完成。代码示例如下:

BOOL CTestDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值

DWORD dwSize = pCopyDataStruct->cbData;

CString szData;// = (LPCTSTR)(pCopyDataStruct->lpData);
TCHAR* ptchar = new TCHAR[dwSize+1];

memcpy( ptchar, pCopyDataStruct->lpData, dwSize );
ptchar[dwSize] = TEXT(‘/0‘);
szData = ptchar;
// TODO: Add some code here to handler the message

delete[] ptchar;

return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}

LRESULT CTestDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: 在此添加专用代码和/或调用基类
COPYDATASTRUCT szCpyData;

switch ( message )
{
case WM_DATA_TRANSFER:
szCpyData = *((PCOPYDATASTRUCT)lParam);
// TODO: Add some code here to handler the message
break;

default:
break;
}

return CDialog::WindowProc(message, wParam, lParam);
}

对于消息发送,只需要调用形如SendMessage(m_hwndMsg, WM_DATA_TRANSFER, wParam, lParam)方法即可实现,lParam参数可以是PCOPYDATASTRUCT等。

通过上面的介绍,相信已经可以轻松实现两个进程间(含窗体)的通信的,使用这样的方法,既简单又能够满足大部分的应用需求,不失为一种简便的方法。

时间: 2024-10-10 20:08:38

c# 进程间的通信实现之一简单字符串收发的相关文章

进程间的通信简单总结

运行在不同的端系统的之间的通信: 进程指运行在端系统上的一个程序. 若要进行进程之间的通信,那么进程肯定就是成对出现的. 进程间的通信你可以想象是两个人用电话进行交流,然后它必须需要进行通信的一些基础设施,这个基础设施就是套接字,如果你在在两个进程之间进行数据交流,你可以把套接字想象成一个门,你打开门把你要传递的数据丢出去,然后另一个进程打开门,然后就收数据. 但是你把数据丢出门,它是如何找到目的地,另一个进程呢?数据报从一个进程找到另一个进程这就是进程寻址.你在打电话你需要提供电话号码,因此你

Linux进程间的通信

一.管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: A. 管道是半双工的,数据只能向一个方向流动: B. 需要双工通信时,需要建立起两个管道: C. 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程): D. 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中. 匿名管道的创建:该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义;因此,一

【转】使用AIDL实现进程间的通信之复杂类型传递

使用AIDL实现进程间的通信之复杂类型传递 首先要了解一下AIDL对Java类型的支持. 1.AIDL支持Java原始数据类型. 2.AIDL支持String和CharSequence. 3.AIDL支持传递其他AIDL接口,但你引用的每个AIDL接口都需要一个import语句,即使位于同一个包中. 4.AIDL支持传递实现了android.os.Parcelable接口的复杂类型,同样在引用这些类型时也需要import语句.(Parcelable接口告诉Android运行时在封送(marsha

进程间的通信:管道

进程间的通信:管道 Linux中将命令联系到一起使用实际上就是把一个进程的输出通过管道传递给另一个进程的输入,这些都是shell封装好的,对标准输入和输出流进行了重新连接,使数据流从键盘输入经过两个程序最终输出到屏幕上.如下: cmd1|cmd2 进程管道 在两个程序之间传递数据最简单的方法就是使用popen()和pclose()了.原型如下: #include <stdio.h> FILE *popen(const char *command, const char *open_mode);

从AIDL开始谈Android进程间Binder通信机制

本文首先概述了Android的进程间通信的Binder机制,然后结合一个AIDL的例子,对Binder机制进行了解析. 概述 我们知道,在Android app中的众多activity,service等组件可以运行在同一进程中,也可以运行在不同进程中.当组件运行在同一进程中进行通信就显得比较简单,在之前的Android线程间通信机制中已经讲过了:而当它们运行在不同的进程中时,就需要使用我们本文中所要介绍的Binder机制了. Binder作为一种进程间通信机制,负责提供远程调用的功能(RPC),

Android进程间的通信之AIDL

Android服务被设计用来执行很多操作,比如说,可以执行运行时间长的耗时操作,比较耗时的网络操作,甚至是在一个单独进程中的永不会结束的操作.实现这些操作之一是通过Android接口定义语言(AIDL)来完成的.AIDL被设计用来执行进程间通信,另一种实现方式见博文Android进程间的通信之Messenger.本文我们将学习如何创建AIDL文件实现Android进程间通信.在正式学习之前,我们先澄清一些"事实". 关于Android Service 1.Android服务不是后台任务

Nginx学习——Nginx进程间的通信

nginx进程间的通信 进程间消息传递 共享内存 共享内存还是Linux下提供的最主要的进程间通信方式,它通过mmap和shmget系统调用在内存中创建了一块连续的线性地址空间,而通过munmap或者shmdt系统调用可以释放这块内存.使用共享内存的优点是当多个进程使用同一块共享内存时,在不论什么一个进程改动了共享内存中的内容后,其它进程通过訪问这段共享内存都可以得到改动后的内容. Nginx定义了ngx_shm_t结构体.用于描写叙述一块共享内存, typedef struct{ //指向共享

Android进程间的通信之Messenger

Android进程间的通信方式可以通过以下两种方式完成: 1 Android接口定义语言(AIDL) 2 使用Messenger绑定服务 本文我们将学习使用Messenger绑定服务的方式进行进程间的通信. Android AIDL和Messenger区别 使用Messenger是执行进程间通信最简单的方法,因为Messenger会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计.而纯粹的AIDL接口会同时向服务发送多个请求,服务随后必须应对多线程处理.AIDL通常应用在服

Linux/UNIX之进程间的通信(2)

进程间的通信(2) 有三种IPC我们称为XSI IPC,即消息队列.信号量以及共享存储器,它们之间有很多相似之处. 标识符和键 每个内核的IPC结构(消息队列.信号量或共享存储段)都用一个非负整数的标识符加以引用.例如,为了对一个消息队列发送或取消息,只需要知道其队列标识符.与文件描述符不同,IPC标识符不是小的整数.当一个IPC结构被创建,以后被删除时,与这种结果相关的标识符连续加1,知道达到一个整型数的最大值,然后又回到0. 标识符是IPC对象的内部名.为使多个合作进程能够在同一IPC对象上