2025年3月31日 星期一 乙巳(蛇)年 正月初一 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > .net

使用Windows API实现两个进程间(含窗体)的通信

时间:12-12来源:作者:点击数:63

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

这里笔者使用2010Express来做实现,在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等。

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

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐