c# 進程間的通訊實現之一簡單字串收發

來源:互聯網
上載者:User

標籤:

   使用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 messagedelete[] 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 messagebreak;default:break;}return CDialog::WindowProc(message, wParam, lParam);}
    對於訊息發送,只需要調用形如SendMessage(m_hwndMsg, WM_DATA_TRANSFER, wParam, lParam)方法即可實現,lParam參數可以是PCOPYDATASTRUCT等。

 

    通過上面的介紹,相信已經可以輕鬆實現兩個進程間(含表單)的通訊的,使用這樣的方法,既簡單又能夠滿足大部分的應用需求,不失為一種簡便的方法。

 

c# 進程間的通訊實現之一簡單字串收發

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.