進程通信之一 使用WM_COPYDATA C++及C#實現 z


原文地址:http://blog.csdn.net/morewindows/article/details/6804157

進程間通信最簡單的方式就是發送WM_COPYDATA消息。本文提供C++及C#程序相互通信的二種實現方式。這樣消息的接收端可以用C++實現,發送端可以用C++或C#實現。

 

發送WM_COPYDATA消息:

SendMessage(接收窗口句柄, WM_COPYDATA, (WPARAM)發送窗口句柄, (LPARAM)&CopyData);

 

其中的CopyData為COPYDATASTRUCT結構類型,該結構定義如下:

typedef struct tagCOPYDATASTRUCT {

DWORD dwData;  // Specifies data to be passed to the receiving application.

DWORD cbData;  //Specifies the size, in bytes, of the data pointed to by the lpData member.

 PVOID lpData;    // Pointer to data to be passed to the receiving application. can be NULL.

} COPYDATASTRUCT, *PCOPYDATASTRUCT;

注 意:該消息只能由SendMessage()來發送,而不能使用PostMessage()。因為系統必須管理用以傳遞數據的緩沖區的生命期,如果使用了 PostMessage(),數據緩沖區會在接收方(線程)有機會處理該數據之前,就被系統清除和回收。此外如果lpData指向一個帶有指針或某一擁有 虛函數的對象時,也要小心處理。

 

如果傳入的句柄不是一個有效的窗口或當接收方進程意外終止時,SendMessage()會立即返回,因此發送方在這種情況下不會陷入一個無窮的等待狀態中。

 

返回值問題,MSDN上說如果接收方處理了,返回TRUE,否則返回FALSE,但是本人在實驗時,都是返回0(接收方已經處理)。

 

接收WM_COPYDATA消息:

只要用COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;就可以了。接收方應認為這些數據是只讀的。

 

由於發送方在接收方處理WM_COPYDATA消息完畢前都是處於等待中,所以接收方應當盡快處理WM_COPYDATA消息。

 

以一個簡單的例子來說明如何使用WM_COPYDATA消息,有二個程序,一個用來發送表示當前時間信息的字符串,另一個接收數據后顯示到編輯框中。例子中有幾點要注意:

1.如何得到當前控制台窗口句柄?VS2008下可以直接使用HWND GetConsoleWindow(void);函數。

2.使用char *ctime(const time_t *timer);將一個time_t類型轉化成一個字符串時,函數會在字符串末尾加下'\n',因為發送前要將這個'\n'去掉。

 

發送消息的程序代碼(VS2008下編譯通過):

 

#include <windows.h>
#include <time.h>
#include <conio.h>
#include <stdio.h>
int main()
{
    const char szDlgTitle[] = "RecvMessage";

    HWND hSendWindow = GetConsoleWindow ();
    if (hSendWindow == NULL)
        return -1;
    HWND hRecvWindow = FindWindow(NULL, szDlgTitle);
    if (hRecvWindow == NULL)
        return -1;

    char szSendBuf[100];
    time_t  timenow;
    COPYDATASTRUCT CopyData;

    for (int i = 0; i < 10; i++)
    {
        time(&timenow);
        sprintf(szSendBuf, "%s", ctime(&timenow));//注意,ctime()返回的字符串后面帶了'\n'
        CopyData.dwData = i;
        CopyData.cbData = strlen(szSendBuf);
        szSendBuf[CopyData.cbData - 1] = '\0';
        CopyData.lpData = szSendBuf;

        SendMessage(hRecvWindow, WM_COPYDATA, (WPARAM)hSendWindow, (LPARAM)&CopyData);
        printf("%s\n", szSendBuf);
        Sleep(1000);
    }
    return 0;
}

 

接收消息程序代碼(VC6.0下編譯通過):

程序中的IDC_EDIT_RECVMESSAGE為編輯框的ID。

 

#include "stdafx.h"
#include "resource.h"
#include <stdio.h>
BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     // TODO: Place code here.
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
    return 0;
}
BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    const char szDlgTitle[] = "RecvMessage";
    static HWND s_hEditShowRecv;

    switch (message)
    {
    case WM_INITDIALOG:
        SetWindowText(hDlg, szDlgTitle);
        s_hEditShowRecv = GetDlgItem(hDlg, IDC_EDIT_RECVMESSAGE);
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
        case IDCANCEL:
            EndDialog(hDlg, LOWORD(wParam));
            return TRUE;
        }
        break;

    case WM_COPYDATA:
        {
            COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;
            char szBuffer[300];

            memset(szBuffer, 0, sizeof(szBuffer));
            sprintf(szBuffer, "dwData:%d cbData:%d\r\nlpData:0x%08x = %s\r\n\r\n", 
                pCopyData->dwData, pCopyData->cbData, 
                (PVOID)pCopyData->lpData, (char*)pCopyData->lpData);
            //在編輯框中追加數據
            SendMessage(s_hEditShowRecv, EM_SETSEL, (WPARAM)-1, (LPARAM)-1); // (0, -1)表示全選, (-1,任意)表示全不選
            SendMessage(s_hEditShowRecv, EM_REPLACESEL, FALSE, (LPARAM)szBuffer);
            SendMessage(s_hEditShowRecv, EM_SCROLLCARET, 0, 0);
        }
        return TRUE;
    }
    return FALSE;
}

 

運行結果如下 (先啟動接收消息程序再運行發送消息程序):

 

 

 

有的時候,發送消息程序用C#實現起來更加方便,因此在這也提供了用C#實現的例子發送消息程序供大家參考:

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;  //[DllImport("user32.dll")]中DllImport的命名空間

namespace UseWMCOPYDATA
{
    class Program
    {
        static void Main(string[] args)
        {
            string strDlgTitle = "RecvMessage"; 

            //接收端的窗口句柄
            IntPtr hwndRecvWindow = ImportFromDLL.FindWindow(null, strDlgTitle);
            if (hwndRecvWindow == IntPtr.Zero)
            {
                Console.WriteLine("請先啟動接收消息程序");
                return;
            }

            //自己的窗口句柄
            IntPtr hwndSendWindow = ImportFromDLL.GetConsoleWindow();
            if (hwndSendWindow == IntPtr.Zero)
            {
                Console.WriteLine("獲取自己的窗口句柄失敗,請重試");
                return;
            }

            for (int i = 0; i < 10; i++)
            {
                string strText = DateTime.Now.ToString();
                //填充COPYDATA結構
                ImportFromDLL.COPYDATASTRUCT copydata = new ImportFromDLL.COPYDATASTRUCT();
                copydata.cbData = Encoding.Default.GetBytes(strText).Length; //長度 注意不要用strText.Length;
                copydata.lpData = strText;                                   //內容

                ImportFromDLL.SendMessage(hwndRecvWindow, ImportFromDLL.WM_COPYDATA, hwndSendWindow, ref copydata);

                Console.WriteLine(strText);
                Thread.Sleep(1000);
            }

        }
    }

    public class ImportFromDLL
    {
        public const int WM_COPYDATA = 0x004A;

        //啟用非托管代碼
        [StructLayout(LayoutKind.Sequential)] 
        public struct COPYDATASTRUCT 
        {
            public int dwData;    //not used
            public int cbData;    //長度
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpData; 
        }

        [DllImport("User32.dll")]
        public static extern int SendMessage(
            IntPtr hWnd,     // handle to destination window 
            int Msg,         // message
            IntPtr wParam,    // first message parameter 
            ref COPYDATASTRUCT pcd // second message parameter 
        );

        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("Kernel32.dll", EntryPoint = "GetConsoleWindow")]
        public static extern IntPtr GetConsoleWindow();

    }
}

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM