티스토리 뷰

프로그래밍/API

API IPC - WM_COPYDATA

에어버스 2016. 8. 6. 13:13
API IPC - WM_COPYDATA

Win32 환경은 프로세스간 메모리 공간이 격리되어 사용자 정의 메시지 전달로는 안되고
WM_COPYDATA 메시지를 이용한다.

 wParam 

 정보를 보내는 윈도우의 핸들 지정

 lParam 

 COPYDATA 구조체의 포인터 지정

1
2
3
4
5
typedef struct tagCOPYDATASTRUCT {
    ULONG_PTR dwData;
    DWORD cbData;
    _Field_size_bytes_(cbData) PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
cs

dwData : 교환하고자 하는 정수값, WM_COPYDATA 메시지가 다른 여러곳에서 오는 경우 구분자로 이용 가능할듯
lpData : 교환하고자 하는 문자열(또는 이진 데이터)
cdData : lpData의 크기

정수 하나와 포인터 하나를 보낼 수 있다.
이 구조체에 값을 채운 후 WM_COPYDATA 메시지를 보내면 OS가 tagCopyDataSTRUCT 구조체에 보관된 값을 해당 윈도우로 전달한다.

구조체나 배열도 보낼 수 있는데 이 포인터를 전달 받는 프로세스가 참조할 수 있게 변환하는 것은 OS가 대신 하는데 내부적으로 복잡한 처리를 하고 있음을 쉽게 상상이 갈 것이다. 전달할 데이터의 크기만큼 파일 맵핑을 생성하고 통신 대상 프로세스들의 주소 공간에 동시에 맵해 놓고 맵된 번지를 lParam으로 전달한다. 이 메시지는 무척 편리하게 사용할 수 있는 반면 그다지 효율이 좋지는 못하며 다음 사항에 주의해서 사용해야 한다.

1. lpData에는 받는 쪽에서 읽을 수 있는 값만 전달해야 한다. 단순한 문자열이라면 간단하겠지만 복잡한 이진 포맷의 데이터라면 통신하는 양쪽이 포맷을 알고 있어야만 한다.
2. 받은 쪽에서 데이터를 다 사용하기 전에 전달된 값이 바뀌지 않도록 해야 하낟. SendMessage는 메시지가 완전히 처리되기 전에 리턴하지 않으므로 자신이 직접 변경할 수는 없지만 멀티 스레드를 쓸 경우 다른 스레드에서 값을 변경할 여지를 차단해야 한다.
3. 받는 데이터는 읽기 전용이므로 변경해서는 안된다. 만약, 꼭 변경하고자 한다면 로컬 메모리에 복사한 후 사본을 변경해야 한다.
4. WM_COPYDATA 메시지는 SendMessage로 보내야 하며 PostMessage로 부쳐서는 안된다. 임시적인 처리를 통해 데이터를 전달하는 것이므로 메시지를 큐에 붙이는 것은 의미가 없다.

 

cbData 에 직접 문자열을 보내는 경우 오래된 책을 갖고 연습하니 유니코드랑 충돌 나는건지 결과 화면이 다르다. 프로젝트를 유니코드로 하고 실행하면 위 그림처럼 문자열이 잘린다. 그래서, 보내는 쪽에서는 문자열 길이를 2배로 하고, 받는 쪽에서 반으로 줄인다. 그러나 cbData에 사용자 정의 구조체를 넣고 전송하는 경우 사용자 정의 구조체 안에 있는 문자열은 잘리지 않는다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    COPYDATASTRUCT cds;
    TCHAR* str = L"WM_DATACOPY 메시지를 위한 테스트 스트링";
    TCHAR* pMsg = L"마우스 왼쪽 버튼을 누르면 다른 프로세스로 메시지를 보냅니다.";
    HWND hWnd1 = NULL;
    switch (message)
    {
    case WM_LBUTTONDOWN:
        cds.dwData = 0;
        cds.cbData = lstrlen(str)*2; // 유니코드로 할때 문자열 잘림으로 인해 2배로 함
        cds.lpData = str;
        hWnd1 = FindWindow(NULL, L"Win32Project1");
        if (hWnd1 != NULL)
            SendMessage(hWnd1, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다.
        TextOut(hdc, 1050, pMsg, lstrlen(pMsg));
        EndPaint(hWnd, &ps);
    }
    break;
...
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    COPYDATASTRUCT* pcds;
    HDC hdc;
    TCHAR* pMsg = L"다른 프로그램으로 부터 메시지를 전달 받는다.";
    static int n = 90;
    switch (message)
    {
    case WM_COPYDATA:
        pcds = (PCOPYDATASTRUCT)lParam;
         hdc = GetDC(hWnd);
        TextOut(hdc, 10, n=(n+12+5), (LPCTSTR)pcds->lpData, pcds->cbData/2);
        ReleaseDC(hWnd, hdc);
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다.
        TextOut(hdc, 1050, pMsg, lstrlen(pMsg));
        EndPaint(hWnd, &ps);
    }
    break;
...
}
cs

 

아래는 구조체를 보내는 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    struct _Nation {
        TCHAR name[60];
        int Population;
        TCHAR Code[3];
    } Nation[]{
        L"WM_COPYDATA 메시지를 위한 테스트 스트링"123456789, L"1",
        L"대한민국"50000000, L"82",
        L"중국"1000000000, L"86"
    };
    COPYDATASTRUCT cds;
    TCHAR* pMsg = L"마우스 왼쪽 버튼을 누르면 다른 프로세스로 메시지를 보냅니다.";
    HWND hWnd1 = NULL;
    switch (message)
    {
    case WM_LBUTTONDOWN:
        cds.dwData = 0;
        cds.cbData = sizeof(Nation);
        cds.lpData = Nation;
        hWnd1 = FindWindow(NULL, L"Win32Project1");
        if (hWnd1 != NULL)
            SendMessage(hWnd1, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다.
        TextOut(hdc, 1050, pMsg, lstrlen(pMsg));
        EndPaint(hWnd, &ps);
    }
    break;
...
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    struct _Nation{
        TCHAR name[60];
        int Population;
        TCHAR Code[3];
    } *pNation;
    TCHAR str[60];
    COPYDATASTRUCT* pcds;
    HDC hdc;
    int i;
    TCHAR* pMsg = L"다른 프로그램으로 부터 메시지를 전달 받는다.";
    static int n = 90;
    switch (message)
    {
    case WM_COPYDATA:
        pcds = (PCOPYDATASTRUCT)lParam;
         hdc = GetDC(hWnd);
         pNation = (_Nation*)pcds->lpData;
         for (i = 0; i < 3; i++)
         {
             lstrcpy(str, pNation[i].Code);
             TextOut(hdc, 10, n = (n + 12 + 5), str, lstrlen(str));
             lstrcpy(str, pNation[i].name);
             TextOut(hdc, 10, n = (n + 12 + 5), str, lstrlen(str));
             wsprintf(str, L"%d", pNation[i].Population);
             TextOut(hdc, 10, n = (n + 12 + 5), str, lstrlen(str));
         }
        ReleaseDC(hWnd, hdc);
        break;
...
}
cs


 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28