在Windows下的两个进程之间通信通常有命名管道、消息队列、共享内存等实现方式,这篇文章要讲解的是使用Windows的API来实现简单的进程间通信。对于接收消息,只需要重写DefWndProc函数即可,对于发送消息,写一个发送消息MsgHandler来实现。
首先是MsgHandler类
using System;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace CommunicationTest
{
/// <summary>
/// 发送消息处理程序类
/// 调用该方法“SendMessageToTargetWindow”发送
/// </summary>
class MsgHandler
{
/// <summary>
/// 系统定义的消息
/// </summary>
private const int WM_COPYDATA = 0x004A;
/// <summary>
/// 用户己定义消息
/// </summary>
private const int WM_DATA_TRANSFER = 0x0437;
// 使用Windows API的FindWindow方法
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern int FindWindow(string lpClassName, string lpWindowName);
//使用Windows API的IsWindow方法
[DllImport("User32.dll", EntryPoint = "IsWindow")]
private static extern bool IsWindow(int hWnd);
// 使用Windows API的SendMessage方法
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(
int hWnd,
int Msg,
int wParam,
ref COPYDATASTRUCT lParam
);
// 使用Windows API的SendMessage方法
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(
int hWnd,
int Msg,
int wParam,
string lParam
);
/// <summary>
/// CopyDataStruct
/// </summary>
private struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
/// <summary>
/// 向目标窗口发送消息
/// </summary>
/// <param name="wndName">查找目标窗体名称</param>
/// <param name="msg">要发送的消息,字符串</param>
/// <returns>返回的数据是否成功或则失败</returns>
public static bool SendMessageToTargetWindow(string wndName, string msg)
{
Debug.WriteLine(string.Format("SendMessageToTargetWindow: 向目标窗口发送消息 {0}: {1}", wndName, msg));
int iHWnd = FindWindow(null, wndName);
if (iHWnd == 0)
{
string strError = string.Format("SendMessageToTargetWindow: 没有发现{0}窗体!", 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);
// 使用系统定义的消息wmcopydata来发送消息。
int iReturn = SendMessage(iHWnd, WM_COPYDATA, 0, ref cdsBuffer);
if (iReturn < 0)
{
string strError = string.Format("SendMessageToTargetWindow: 没有发现{0}窗体!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
return false;
}
return true;
}
}
/// <summary>
/// 向目标窗口发送消息
/// </summary>
/// <param name="wndName">查找目标窗体名称</param>
/// <param name="wParam">first parameter, integer</param>
/// <param name="lParam">second parameter, string</param>
/// <returns>数据发送成或者失败</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
{
//使用定义的消息wmdatatransfer来发送消息。
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;
}
}
}
}
然后是数据接收窗体
using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace CommunicationTest
{
public partial class First : Form
{
/// <summary>
///系统定义的消息
/// </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 First()
{
InitializeComponent();
}
/// <summary>
///覆盖DefWndProc函数,以便通过它接收消息。
/// </summary>
/// <param name="m">message</param>
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
// 这里,我们使用wmcopydata消息来接收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;
// 在这里,我们使用我们定义的消息wmdatatransfer来接收
// 普通数据,例如整数,字符串。
// 我们尝试使用我们定义的消息来接收COPYDATASTRUCT,
// 但它没工作! !它告诉我们,我们试图进入受保护的
//,通常意味着其他记录被打破了。
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>
/// 处理传入的数据
/// </summary>
/// <param name="data">输入数据</param>
private void ProcessIncomingData(byte[] bytesData)
{
string strRevMsg = "Receive message: " + Encoding.Default.GetString(bytesData);
textBox7.Text = Encoding.Default.GetString(bytesData);
//lstReceivedMsg.Items.Add(strRevMsg);
}
/// <summary>
/// 处理传入的数据
/// </summary>
/// <param name="iWParam">一个整数</param>
/// <param name="sLParam">一个字符串参数</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>
/// 使用系统定义的消息wmcopydata向目标窗口发送消息
/// </summary>
private void btnSend1_Click_1(object sender, EventArgs e)
{
string strWndName = this.txtTargetWndName.Text;
//检查目标窗口的名称为空/空
if (string.IsNullOrEmpty(strWndName))
{
MessageBox.Show("目标窗口名不能为空或空", "Information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
string strMsg = this.txtSendingMsg.Text;
// 检查发送消息是空的还是空的
if (string.IsNullOrEmpty(strMsg))
{
MessageBox.Show("发送消息必须不是空的或空的!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
//向目标窗口发送消息
bool bReturn = MsgHandler.SendMessageToTargetWindow(strWndName, strMsg);
}
/// <summary>
/// 初始化页面
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_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_1(object sender, EventArgs e)
{
this.Text = txtCurrentWndName.Text;
}
}
}
接收数据是只要重写DefWndProc函数