2008년 12월 20일
WM_COPYDATA (1)
IPC(Inter-Process Communication)의 방법 중 메시지는 두 프로세스가 서로 윈도우 핸들만 알고 있다면 약속된 메시지의 wParam, lParam을 통해 정보를 쉽고 빠르게 교환 할 수 있습니다.메시지는 메모리를 거치지 않고 운영체제에 의해 직접 전달 되므로 프로세스 공간이 격리되어 있더라도 데이터를 전송하는데 전혀 문제가 없습니다. 하지만 일반적으로 사용자 정의 메시지를 이용해서 전송하는 데이터의 경우에는 대량의 문자열이나 구조체(또는 클래스) 같은 큰 데이터를 전달할 수 없습니다. 이는 메시지와 함께 전달되는 wParam, lParam은 둘 다 더해봐야 불과 8바이트에 불과하기 때문입니다. 이 파라미터로 포인터를 전달하는 방법은 이전에 포스팅 했던 IPC에 대해서 .. 에서도 언급했지만 각 프로세스는 메모리 공간을 서로 침범하지 못하도록 경리해두었기 때문에 전달 된 포인터로는 포인터를 전달한 프로세스의 힙 메모리 공간에 접근할 수 없습니다.

그렇다면 대량의 문자열이나 구조체(또는 클래스)를 다른 프로세스에 전달하기 위해서는 어떤 방법을 사용할까요? 바로 WM_COPYDATA 메시지와 COPYDATASTRUCT를 이용하면 가능합니다. 자세한 내용은 아래 MSDN의 내용을 참고하시고 간단한 예제를 만들어 보겠습니다.

WM_COPYDATA Message


An application sends the WM_COPYDATA message to pass data to another application.

Syntax

To send this message, call the SendMessage function as follows.
lResult = SendMessage(     // returns LRESULT in lResult
   (HWND) hWndControl,     // handle to destination control
   (UINT) WM_COPYDATA,     // message ID
   (WPARAM) wParam,     // = (WPARAM) () wParam;
   (LPARAM) lParam     // = (LPARAM) () lParam;
);

Parameters

wParam
Handle to the window passing the data.
lParam
Pointer to a COPYDATASTRUCT structure that contains the data to be passed.

Return Value

If the receiving application processes this message, it should return TRUE; otherwise, it should return FALSE.



Remarks

The data being passed must not contain pointers or other references to objects not accessible to the application receiving the data.

While this message is being sent, the referenced data must not be changed by another thread of the sending process.

The receiving application should consider the data read-only. The lParam parameter is valid only during the processing of the message. The receiving application should not free the memory referenced by lParam. If the receiving application must access the data after SendMessage returns, it must copy the data into a local buffer.



COPYDATASTRUCT Structure


The COPYDATASTRUCT structure contains data to be passed to another application by the WM_COPYDATA message.

Syntax

typedef struct tagCOPYDATASTRUCT {
ULONG_PTR dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;

Members

dwData
Specifies data to be passed to the receiving application.
cbData
Specifies the size, in bytes, of the data pointed to by the lpData member.
lpData
Pointer to data to be passed to the receiving application. This member can be NULL.

예제의 시나리오는 ProcessA라는 대화상자가 SendMessage 버튼을 클릭하면 ProcessB라는 대화상자에게 WM_COPYDATA를 이용해서 데이터를 전송하고 ProcessB 대화상자는 이 데이터를 송신한 후 데이터를 메시지 박스로 보여주고 다시 ProcessA에게 데이터를 에코 한 후 ProcessA는 에코 된 데이터를 메시지 박스로 보여주도록 하겠습니다.

이를 위해 우선 ProcessA와 ProcessB가 통신을 하기 위해서 프로토콜을 정의하고 해당 프로토콜에 대해 전달 될 데이터 형을 정의하도록 하겠습니다.


//
//    프로세스 간 통신을 하기 전에 전송할 데이터 종류에 대한 프로토콜 정의 ..
//
struct Protocol
{
    enum { PROTOCOL_BOOKINFO, PROTOCOL_COOKINFO };
};

//
// 해당 프로토콜에 대해 전달 될 데이터 형
//
struct BookInfo
{
    int m_nID;
    TCHAR m_szName[100];
};

//
// 해당 프로토콜에 대해 전달 될 데이터 형
//
struct CookInfo
{
   ...
};


ProcessA code ..

void CProcessADlg::OnBnClickedButton1()
{
    // TODO: Add your control notification handler code here

    //
    //    데이터를 전송 할 프로세스의 핸들을 이용하기 위해 윈도우 객체를 얻어 옵니다.
    //
    CWnd* pWnd = FindWindow(NULL, "ProcessB");
    ASSERT(pWnd);
    //
    //    전송할 데이터를 셋팅합니다.
    //
    BookInfo bookInfo = {1000, {0, }};
   
    TCHAR* szName = _T("Keep going ..");

    bookInfo.m_nID = 1000;
    _tcsncpy(bookInfo.m_szName, szName, _tcslen(szName));
    //
    //    전송할 데이터를 COPYDATASTURCT에 셋팅하고 전송합니다.
    //
    COPYDATASTRUCT cds;

    cds.dwData = Protocol::PROTOCOL_BOOKINFO;
    cds.cbData = sizeof(bookInfo);
    cds.lpData = (LPVOID)&bookInfo;

    ::SendMessage(pWnd->m_hWnd, WM_COPYDATA, (WPARAM)GetSafeHwnd(), (LPARAM)&cds);
}

LRESULT CProcessADlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: Add your specialized code here and/or call the base class

    switch(message)
    {
    case WM_COPYDATA:
        {
            PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT) lParam;
            //
            //    프로토콜에 따른 처리를 위해서 ..
            //
            switch(pcds->dwData)
            {
            case Protocol::PROTOCOL_BOOKINFO:
                {
                    //
                    //    전송받은 데이터(COPYDATASTRUCT 구조체)를 풀어서 BookInfo에 셋팅합니다.
                    //
                    BookInfo bookInfo;

                    bookInfo.m_nID = ((BookInfo*)pcds->lpData)->m_nID;
                    _tcsncpy(bookInfo.m_szName, ((BookInfo*)pcds->lpData)->m_szName, pcds->cbData - sizeof(int));
                    //
                    //    전송받은 데이터를 메시지 박스를 출력합니다.
                    //
                    CString strReceiveMsg;
                    strReceiveMsg.Format(_T("ID : %d\tName : %s"), bookInfo.m_nID, bookInfo.m_szName);

                    AfxMessageBox(strReceiveMsg);           

                    break;
                }
            case Protocol::PROTOCOL_COOKINFO:
                {
                    // Do something ..
                    break;
                }
            }
        }
    }
    return CDialog::WindowProc(message, wParam, lParam);
}


ProcessB code ..

LRESULT CProcessBDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: Add your specialized code here and/or call the base class

    switch(message)
    {
    case WM_COPYDATA:
        {
            PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT) lParam;
            //
            //    프로토콜에 따른 처리를 위해서 ..
            //
            switch(pcds->dwData)
            {
            case Protocol::PROTOCOL_BOOKINFO:
                {
                    //
                    //    전송받은 데이터(COPYDATASTRUCT 구조체)를 풀어서 BookInfo에 셋팅합니다.
                    //
                    BookInfo bookInfo;

                    bookInfo.m_nID = ((BookInfo*)pcds->lpData)->m_nID;
                    _tcsncpy(bookInfo.m_szName, ((BookInfo*)pcds->lpData)->m_szName, pcds->cbData - sizeof(int));
                    //
                    //    전송받은 데이터를 메시지 박스를 출력합니다.
                    //
                    CString strReceiveMsg;
                    strReceiveMsg.Format(_T("ID : %d\tName : %s"), bookInfo.m_nID, bookInfo.m_szName);

                    AfxMessageBox(strReceiveMsg);           
                    //
                    //    wParam으로 들어 온 윈도우 핸들을 이용해서 데이터를 전송한 프로세스에게 데이터를 에코합니다.
                    //   
                    HWND hWnd = (HWND) wParam;
                    ::SendMessage(hWnd, WM_COPYDATA, 0, (LPARAM) pcds);

                    break;
                }
            case Protocol::PROTOCOL_COOKINFO:
                {
                    // Do something ..
                    break;
                }
            }
        }

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


실행 결과 ..


* 참고 문헌

Windows API 정복 (가남사, 김상형 저) - 제33장 IPC 라. WM_COPYDATA

* 예제 프로젝트

WM_COPYDATA.alz, WM_COPYDATA.a01, WM_COPYDATA.a00
by greenfrog | 2008/12/20 20:45 | C++ / WIN32 / MFC | 트랙백 | 핑백(1) | 덧글(1)
트랙백 주소 : http://greenfrog7.egloos.com/tb/1257629
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Linked at Programmer green.. at 2008/12/21 21:47

... WM_COPYDATA(1)</a> 포스팅 된 글 중에서 다음 struct BookInfo 구조체의 m_szName 멤버 변수의 경우 사이즈가 정해져 있습니다. 그런데 만약 m_szName을 가변적으로 즉, 동적으로 메모리를 할당해서 WM_COPYDATA 메시지를 이용해서 다른 프로세스에게 데이터를 전송하기 위해서는 어떻게 해야할까요? 일감으로는 그냥 동적 할당한 후 <a style="font-weight: bold;" href="http://greenf ... more

Commented by 오곡 at 2013/10/11 16:04
잘배우고 갑니다~

:         :

:

비공개 덧글



<< 이전 페이지 | 다음 페이지 >>