[Win32] IPC - #2 Message(메시지)
저장소/VC++[출처] 윈도우즈 API 정복, 김상형 지음
메시지는 정보를 교환할 수 있는 가장 간단한 방법이며 또한 가장 빠른 방법이기도 하다.
두 프로세스가 서로 윈도우 핸들만 알고 있다면 약속된 메시지의 wParam, lParam을 통해 정보를 교환할 수 있다.
메시지는 메모리를 거치지 않고 운영체제에 의해 직접 전달되므로 주소 공간이 격리되어 있더라도 잘 전달된다.
비교적 크기가 작은 정보를 전달할 때만 사용할 수 있다.
정보라기보다는 단순한 어떤 사실을 통보하기 위한 목적으로도 사용할 수 있다.
그러나 문자열이나 구조체같은 큰 데이터는 이 메시지로 전달할 수 없다. 메시지와 함께 전달되는 wParam, lParam은 둘 다 더해봐야 불과 8바이트에 불과하기 때문이다.
Win32 환경은 프로세스간 메모리 공간이 격리됨으로 해서 훨씬 더 안정적이기는 하지만 간단한 문자열을 교환하는데도 아주 복잡한 방법이 동원되어야 하는 불편함이 있지만 이러한 불편함을 해소하기 위해 IPC를 위한 전용 메시지가 추가되었고, 이 메시지가 바로 WM_COPYDATA 메시지이다.
wParam에 정보를 보내는 윈도우의 핸들을 넣고 lParam에는 다음 구조체의 포인터를 넣는다.
dwData는 교환하고자 하는 정수값이며 lpData는 교환하고자 하는 문자열(또는 이진 데이터)이며 cbData는 lpData의 크기이다. 정수 하나와 포인터 하나를 보낼 수 있다. 이 구조체에 값을 채운 후 WM_COPYDATA 메시지를 보내면 운영체제가 구조체에 보관된 값을 해당 윈도우로 전달한다.
COPYDATASTRUCT 구조체의lpData벰버는 포인터이므로 구조체나 배열도 보낼 수 있다. 이 포인턴를 전달받는 프로세스가 참조할 수 있게 변환하는 것은 운영체제가 대신 하는 데 내부적으로 복잡한 처리를 하고 있음은 쉽게 상상이 갈 것이다. 전다랗ㄹ 데이터의 크기만큼 파일 맵핑을 생성하고 통신 대상 프로세스들의 주소 공간에 동시에 맵해 놓고 번지를 lParam으로 전달한다. 이 메시지는 무척 편맇게 사용할 수 있는 반면 그다지 효율이 좋지는 못하다.
WM_COPYDATA 메시지를 사용할 때는 다음 사항에 주의해야 한다.
1. lpData에는 받는 쪽에서 읽을 수 있는 값만 전달해야 한다. 단순한 문자열이라면 간단하겠지만 복잡한 이진 포맷의 데이터라면 통신하는 양쪽이 포맷을 알고 있어야만 한다.
2. 받은 쪽에서 데이터를 다 사용하기 전에 전달된 값이 바뀌지 않도록 해야 한다. SendMessage는 메시지가 완전히 처리되기 전에 리턴하지 않으므로 자신이 직접 변경할 수는 없지만 멀티 스레드를 쓸 경우 다른 스레드에서 값을 변경할 여지를 차단해야 한다.
3. 받는 데이터는 일기 전용이므로 변경해서는 안 된다. 만약 꼭 변경하고자 한다면 로컬 메모리에 복사한 후 사본을 변경해야 한다.
4. WM_COPYDATA 메시지는 SendMessage로 보내야 하며 PostMessage로 부쳐서는 안 된다. 임시적인 처리를 통해 데이터를 전달하는 것이므로 메시지를 큐에 붙이는 것은 의미가 없다. (SendMessage로만 보낼 수 있다. PostMessage로 큐에 붙이면 이 메시지는 무시된다.)
메시지는 정보를 교환할 수 있는 가장 간단한 방법이며 또한 가장 빠른 방법이기도 하다.
두 프로세스가 서로 윈도우 핸들만 알고 있다면 약속된 메시지의 wParam, lParam을 통해 정보를 교환할 수 있다.
메시지는 메모리를 거치지 않고 운영체제에 의해 직접 전달되므로 주소 공간이 격리되어 있더라도 잘 전달된다.
[ICPMessage1]
#define WM_IPC WM_USER + 1
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
#define WM_IPC WM_USER + 1
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TCHAR* pszMsg = _T("마우스 왼쪽 버튼을 누르면 메시지 전송됨.");
HWND hWnd2;
switch(iMessage)
{
case WM_LBUTTONDOWN:
PAINTSTRUCT ps;
TCHAR* pszMsg = _T("마우스 왼쪽 버튼을 누르면 메시지 전송됨.");
HWND hWnd2;
switch(iMessage)
{
case WM_LBUTTONDOWN:
hWnd2 = ::FindWindow(NULL, _T("IPCMessage2"));
if(NULL != hWnd2)
::SendMessage(hWnd2, WM_IPC, (WPARAM)1234, (LPARAM)0);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTORY:
::PostQuitMessage(0);
return 0;
if(NULL != hWnd2)
::SendMessage(hWnd2, WM_IPC, (WPARAM)1234, (LPARAM)0);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTORY:
::PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}
[ICPMessage2]
#define WM_IPC WM_USER + 1
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
[ICPMessage2]
#define WM_IPC WM_USER + 1
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TCHAR* pszMsg = _T("메시지를 전달받는 녀석.");
TCHAR szStr[128] = {0,};
switch(iMessage)
{
case WM_IPC:
hdc = ::GetDC(hWnd);
_stprintf(szStr, _T("%d를 받았습니다."), wParam);
::TextOut(hdc, 10, 100, szStr, _tcslen(szStr));
::ReleaseDC(hWnd, hdc);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTORY:
::PostQuitMessage(0);
return 0;
PAINTSTRUCT ps;
TCHAR* pszMsg = _T("메시지를 전달받는 녀석.");
TCHAR szStr[128] = {0,};
switch(iMessage)
{
case WM_IPC:
hdc = ::GetDC(hWnd);
_stprintf(szStr, _T("%d를 받았습니다."), wParam);
::TextOut(hdc, 10, 100, szStr, _tcslen(szStr));
::ReleaseDC(hWnd, hdc);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTORY:
::PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}return (DefWindowProc(hWnd, iMessage, wParam, lParam));
비교적 크기가 작은 정보를 전달할 때만 사용할 수 있다.
정보라기보다는 단순한 어떤 사실을 통보하기 위한 목적으로도 사용할 수 있다.
그러나 문자열이나 구조체같은 큰 데이터는 이 메시지로 전달할 수 없다. 메시지와 함께 전달되는 wParam, lParam은 둘 다 더해봐야 불과 8바이트에 불과하기 때문이다.
Win32 환경은 프로세스간 메모리 공간이 격리됨으로 해서 훨씬 더 안정적이기는 하지만 간단한 문자열을 교환하는데도 아주 복잡한 방법이 동원되어야 하는 불편함이 있지만 이러한 불편함을 해소하기 위해 IPC를 위한 전용 메시지가 추가되었고, 이 메시지가 바로 WM_COPYDATA 메시지이다.
wParam에 정보를 보내는 윈도우의 핸들을 넣고 lParam에는 다음 구조체의 포인터를 넣는다.
typedef struct tagCOPYDATASTRUCT{ // cds
DWORD dwData;
DWORD cbData;
PVOID lpData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT;
dwData는 교환하고자 하는 정수값이며 lpData는 교환하고자 하는 문자열(또는 이진 데이터)이며 cbData는 lpData의 크기이다. 정수 하나와 포인터 하나를 보낼 수 있다. 이 구조체에 값을 채운 후 WM_COPYDATA 메시지를 보내면 운영체제가 구조체에 보관된 값을 해당 윈도우로 전달한다.
[CopyData1]
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
[CopyData2]
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
COPYDATASTRUCT* pcds;
TCHAR* pszMsg = _T("CopyData1 프로그램으로부터 메시지를 전달받는다.");
switch(iMessage)
{
case WM_COPYDATA:
pcds = (PCOPYDATASTRUCT)lParam;
hdc = ::GetDC(hWnd);
::TextOut(hdc, 10, 100, (LPCTSTR)pcds->lpData, pcds->cbData);
::ReleaseDC(hWnd, hdc);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
COPYDATASTRUCT cds;
TCHAR* pszStr = _T("WM_COPYDATA 메시지를 위한 Tesst String");
TCHAR* pszMsg = _T("마우스 왼쪽 버튼을 누르면 메시지 전송됨.");
HWND hWnd2;
switch(iMessage)
{
case WM_LBUTTONDOWN:
PAINTSTRUCT ps;
COPYDATASTRUCT cds;
TCHAR* pszStr = _T("WM_COPYDATA 메시지를 위한 Tesst String");
TCHAR* pszMsg = _T("마우스 왼쪽 버튼을 누르면 메시지 전송됨.");
HWND hWnd2;
switch(iMessage)
{
case WM_LBUTTONDOWN:
cds.dwData = 0;
cds.cbData = _tcslen(pszStr);
cds.lpData = pszStr;
hWnd2 = ::FindWindow(NULL, _T("CopyData2"));
if(NULL != hWnd2)
::SendMessage(hWnd2, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTORY:
::PostQuitMessage(0);
return 0;
cds.cbData = _tcslen(pszStr);
cds.lpData = pszStr;
hWnd2 = ::FindWindow(NULL, _T("CopyData2"));
if(NULL != hWnd2)
::SendMessage(hWnd2, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTORY:
::PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}return (DefWindowProc(hWnd, iMessage, wParam, lParam));
[CopyData2]
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
COPYDATASTRUCT* pcds;
TCHAR* pszMsg = _T("CopyData1 프로그램으로부터 메시지를 전달받는다.");
switch(iMessage)
{
case WM_COPYDATA:
pcds = (PCOPYDATASTRUCT)lParam;
hdc = ::GetDC(hWnd);
::TextOut(hdc, 10, 100, (LPCTSTR)pcds->lpData, pcds->cbData);
::ReleaseDC(hWnd, hdc);
return 0;
case WM_PAINT:
hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, 10, 50, pszMsg, _tcslen(pszMsg));
::EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
}
}
}
COPYDATASTRUCT 구조체의lpData벰버는 포인터이므로 구조체나 배열도 보낼 수 있다. 이 포인턴를 전달받는 프로세스가 참조할 수 있게 변환하는 것은 운영체제가 대신 하는 데 내부적으로 복잡한 처리를 하고 있음은 쉽게 상상이 갈 것이다. 전다랗ㄹ 데이터의 크기만큼 파일 맵핑을 생성하고 통신 대상 프로세스들의 주소 공간에 동시에 맵해 놓고 번지를 lParam으로 전달한다. 이 메시지는 무척 편맇게 사용할 수 있는 반면 그다지 효율이 좋지는 못하다.
WM_COPYDATA 메시지를 사용할 때는 다음 사항에 주의해야 한다.
1. lpData에는 받는 쪽에서 읽을 수 있는 값만 전달해야 한다. 단순한 문자열이라면 간단하겠지만 복잡한 이진 포맷의 데이터라면 통신하는 양쪽이 포맷을 알고 있어야만 한다.
2. 받은 쪽에서 데이터를 다 사용하기 전에 전달된 값이 바뀌지 않도록 해야 한다. SendMessage는 메시지가 완전히 처리되기 전에 리턴하지 않으므로 자신이 직접 변경할 수는 없지만 멀티 스레드를 쓸 경우 다른 스레드에서 값을 변경할 여지를 차단해야 한다.
3. 받는 데이터는 일기 전용이므로 변경해서는 안 된다. 만약 꼭 변경하고자 한다면 로컬 메모리에 복사한 후 사본을 변경해야 한다.
4. WM_COPYDATA 메시지는 SendMessage로 보내야 하며 PostMessage로 부쳐서는 안 된다. 임시적인 처리를 통해 데이터를 전달하는 것이므로 메시지를 큐에 붙이는 것은 의미가 없다. (SendMessage로만 보낼 수 있다. PostMessage로 큐에 붙이면 이 메시지는 무시된다.)
'저장소 > VC++' 카테고리의 다른 글
[Win32] Service - #4 서비스 제어 (0) | 2009.05.13 |
---|---|
[Win32] Service - #3 서비스 설치 (0) | 2009.05.13 |
[Win32] Service - #2 서비스 생성 (0) | 2009.05.13 |
[Win32] Service - #1 정의 (0) | 2009.05.13 |
[Win32] IPC - #1 정의 (0) | 2009.05.12 |