프로그래밍 관련/MFC

C/C++ MFC 스레드(Thread) SendMessage PostMessage 관련

AlrepondTech 2020. 9. 10. 04:33
반응형

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://readme79.tistory.com/6

 

 

[알아야 할 것]
1. 크기(size) 및 효율성(performance)의 이유로 MFC 객체(Object)는 스레드에 안전한 구조가 아니다.

2. MFC 객체에 접근(Access)하기 위해서는 CWinThread 객체를 사용한 스레드여야 한다. (User-Interface Thread, Worker Thread)
  - User-Interface Thread와 Worker Thread의 차이 : User-Interface Thread는 스레드 자체의 메시지 큐를 가지며 일반적으로 UI를 포함한다.

3. 일반적인 규칙으로 MFC 객체는 그것을 생성한 스레드에서만 접근할 수 있다. MFC는 윈도우 핸들과 윈도우 객체를 연결하는 
    맵(Windows Handle Maps)을 가지는데 이것은 스레드의 로컬 영역에 저장되기 때문이다.
  - Worker Thread에서 CDocument의 UpdateAllViews 나 CView의 UpdateWindow 등을 호출해도 뷰는 업데이트 되지 않는다.
  - Worker Thread에서 AfxGetMainWnd 등을 호출하면 NULL이 반환된다.
  - Worker Thread에서 CDialog의 UpdateData 등을 호출하면 ASSERT 에러가 난다.
  - Worker Thread에서 UI를 직접 접근할 경우 Dead lock에 빠질 수 있다.

4. SendMessage와 PostMessage의 차이를 알자.
  - SendMessage를 호출하면 윈도우의 메시지 프로시저를 바로 호출해서 결과를 리턴 받는다.
  - PostMessage를 호출하면 메시지가 시스템 메시지 큐에 보내지고 호출한 함수는 바로 리턴이 된다.
    시스템은 해당 윈도우 스레드의 메시지 큐에 메시지를 보내고 메시지가 처리된다.
 - 따라서 Worker Thread에서 SendMessage를 호출하면 Worker Thread에서 메시지 핸들러를 호출하게 되는것이고
    PostMessage를 호출하면 메인 Thread에서 메시지 핸들러를 호출하게 되는것이다.

[MFC 어플리케이션에서 Worker Thread를 사용할 경우 지켜야 할 규칙]
1. UI에 대한 접근은 UI를 생성한 클래스에서만 한다. (Worker Thread에서 UI 접근 함수를 직접 호출하지 않는다.)

2. 메인 스레드와 서브 스레드(Worker Thread)에서 동시에 접근하는 Data는 최대한 줄인다.

3. 서브 스레드(Worker Thread)에서 처리한 작업은 사용자 메시지를 만든 후 PostMessage로 보내서 메인 스레드에서
   처리할 수 있도록 한다.

[참조 URL]
http://msdn2.microsoft.com/en-us/library/ms810439.aspx

http://msdn2.microsoft.com/en-us/library/h14y172e(vs.71).aspx

http://www.microsoft.com/msj/0798/c0798.aspx

http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c3791/

http://www.codeproject.com/threads/usingworkerthreads.asp



출처: http://readme79.tistory.com/6 ["Hello, world" 의 열정으로~]

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://daru87.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0-Message-%EC%82%AC%EC%9A%A9-%EB%B0%8F-PostMessage%EC%99%80-SendMessage%EC%9D%98-%EC%B0%A8%EC%9D%B4

 

 

모든 윈도우 프로그램은 메시지 처리를 기본으로 하고 있습니다.
특정 프로세스가 처리시간이 길어지는경우 다시말해 응답시간이 길어지는경우 
해당 프로세스이후에 생기는 메시지들은 대기 상태가 되죠 
이것이 지속되면 프로그램이 다운이 되는 무응답상태가 되기도 하고요 
해서 이러한 프로세스에는 대기하는 시간동안 다른 처리를 하기위해 Application.Processmessages를 사용합니다. 
그리고 postmessage는 Win32Api인데요 sendmessage와 유사하지만 
해당 어플리케이션의 메시지 스택에 쌓아두고 완료되는것만 다릅니다.

 질문1) 두 함수의 기능

=> 메시지는 주로 사용자에 의해 발생되지만 프로그램 내부에서 윈도우간의 통신을

위해 의도적으로 다른 윈도우에게 메시지를 보낼 수도 있다. 이때는 다음 두 함수를

사용한다.

BOOL PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );

두 함수의 인수가 완전히 동일하다. 여기서 Post 라는 말은 우리말로 "붙인다"라고

번역하며 Send 라는 말은 "보낸다"라고 번역한다.


★ PostMessage 함수

PostMessage 함수는 Msg 인수로 지정된 메시지를 hWnd 윈도우의 메시지 큐에 집어넣어

윈도우 프로시저에서 이 메시지를 처리하도록 한다. 메시지를 메시지 큐에 넣어 놓기만

하고 곧바로 리턴하므로 메시지는 곧바로 처리되지 않는다. 큐에 붙여진 메시지는

GetMessage 에 의해 읽혀지고 DispatchMessage 에 의해 윈도우 프로시저로 보내져

처리될 것이다.

급하게 처리될 필요가 없거나 또는 지금 하고 있는 작업을 완전히 끝내야만 처리할

수 있는 메시지는 PostMessage 함수로 큐에 붙인다. 이 함수로 붙여진 메시지는

언제 처리될지 정확하게 예측하기 힘들다. 그래서 붙여지는 메시지의 wParam 과

lParam 에는 지역 포인터를 사용하지 않아야 한다. 메시지를 붙일 시점에는

포인터가 존재했더라도 메시지가 처리될 시점에는 포인터가 무효해질 수 있기

때문이다.


PostMessage 는 메시지를 큐에 붙인 후 성공하면 TRUE 를 리턴하며 실패하면

FALSE 를 리턴하는데 이 리턴값은 가급적이면 점검해보는 것이 좋다. 왜냐하면

메시지 큐는 크기가 한정되어 있기 때문에 고속으로 전송되는 모든 메시지를

다 수용하지 못할 수도 있기 때문이다.

 

PostMessage 의 첫번째 인수인 hWnd 는 이 메시지를 받을 윈도우의 핸들인데 이값은

아주 특수하게 NULL 일 수도 있다. 즉, 메시지를 받는 대상 윈도우가 없는 메시지를

큐에 붙일 수도 있는데 이런 메시지는 특정 윈도우에게 신호를 보내기 위한 것이

아니라 응용 프로그램 전반에 걸친 작업 지시를 보낼때 사용된다. 대상 윈도우가

없기 때문에 이 메시지는 윈도우 프로시저가 처리할 수 없으며 반드시 메시지 루프에서

직접 처리해주어야 한다.

 

while(GetMessage(&Message, 0, 0, 0) {
   if (Message.message == WM_SOME ) {
        // 직접 처리
   } else {
       TranslateMessage( &Message );
       DispatchMessage( &Message );
   }
}

 

GetMessage 로 메시지를 꺼낸 후 곧바로 메시지 ID 를 비교해보고 스레드를 위한

메시지인지 검사해본다. 만약 스레드를 위한 메시지라면 메시지 루프에서

직접 처리해야 하며 DispatchMessage 함수로 보내서는 안된다. 대상 윈도우가

지정되지 않은 메시지이기 때문에 이 메시지를 받아줄 윈도우 프로시저가 없기

때문이다. PostMessage 함수가 메시지를 붙여넣는 큐가 윈도우를 위한 큐가 아니라

스레드를 위한 큐이기 때문에 이런 기법이 가능하다. 다른 스레드의 메시지 큐에

메시지를 붙일 때는 다음 함수가 사용된다.

 

BOOL PostThreadMessage ( DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam );

윈도우 핸들 대신 스레드의 ID를 첫 번째 인수로 지정해준다. 이때 이 메시지를 받을

스레드는 반드시 스레드 큐를 가지고 있어야 하는데 큐를 가지지 않는

작업 스레드(Worker Thread)는 메시지를 받지 못한다.

 

★ SendMessage 함수

메시지를 큐에 넣는 것이 아니라 곧바로 윈도우 프로시저로 보내 즉각 처리하도록

하며 메시지가 완전히 처리되기 전에는 리턴하지 않는다. 즉 블록시킨다. 그래서

SendMessage는 윈도우간, 특히 부모 윈도우와 차일드 컨트롤간의 통신에 자주 사용된다.

 


         PostMessage     SendMessage
        ↙            ↙
┏━┓    ┏━┓ 메시지루프    ↙
┃ ┃------→  ┃ ┃-------------→ WndProc
┃ ┃    ┃ ┃
┃ ┃    ┃ ┃
┗━┛    ┗━┛
시스템    스레드
메시지큐   메시지큐


질문2) 함수명앞의 LRESULT , BOOL 의 선언은 function 의 리턴예약어 Result 와 Boolean 명칭하는건가요?

=> BOOL 은 델파이에서의 Boolean 과 같습니다.


그러나, LRESULT 는 Result 와는 다릅니다.

LRESULT 는 Win32 API 에서 쓰이는 리턴값의 한 종류입니다.

Result 는 델파이에서 function 의 마지막에 사용되는 C언어로 따지자면 return 예약어와 같은 것입니다.

 

PostMessage(Self.Handle, WM_CLOSE, 0, 0);
현재 폼에 종료 메세지를 보낸다.

※ self.Close; 명령과 같은 역할을 합니다.

 

 

 

출처 - http://www.zetswing.com/bbs/board.php?bo_table=DELPHI_TIP&wr_id=52 (제트스윙)

 

 

 

 

=======================

=======================

=======================

 

 

출처: http://ssmhz.tistory.com/18

 

 

 

BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

 

두 함수의 인수는 완전히 동일합니다. 여기서 Post라는 말은 우리말로 "붙인다"라고 번역되며 "Send"라는 말은 "보낸다"라고 번역됩니다.

PostMessage 함수는 Msg인수로 지정된 메시지를 hWnd 윈도우의 메시지 큐에 집어넣어 윈도우 프로시저에서 이메시지를 처리하도록합니다. 메시지를 큐에 넣기만 하고 바로 리턴하므로 메시지를 붙인 후 즉시 다른 작업을 할 수 있지만 큐에 대기하고 있는 다른 메시지가 있으면 뒤에 붙인 메시지는 곧바로 처리되지 않구요. 큐에 붙여진 메시지는 GetMessage에 의해 읽혀지고 DispatchMessage에 의해 윈도우 프로시저로 보내져 처리될 겁니다.

 

급하게 처리할 필요가 없거나 또는 지금 하고 있는 작업을 완전히 끝내야만 처리할 수 있는 메시지는 PostMessage함수로 큐에 붙이구요. 이함수로 붙여진 메시지는 언제 처리될지 정확하게 예측하기 힘들구요. 그래서 붙여지는 메시지늬 wParam과 lparam에는 지역포인터를 사용지 말아야 해요. 메시지를 붙일 시점에는 포인터가 존재했더라도 메시지가 처리될 시점에는 포인터가 무효해질 수 있기 때문이죠.PostMessage는 메시지를 큐에 붙인 후 성공하면 TRUE를 리턴하며 실패하면 FALSE를 리턴하는데 메시지 큐는 크기가 한정되어 있기 때문에 고속으로 전송되는 모든 메시지를 다 수용하지 못 할 수도 있어요. 다행히 Win32환경에서는 큐 크기가 대폭 늘어나서 웬만해서는 큐가 부족한 상황이 잘 발생하지 않아요.

 

 SendMessage는 메시지를 큐에 넣는 것이 아니라 곧바로 윈도우 프로시저로 보내 즉각 처리하도록 하며 메시지가 완전히 처리되기 전에는 리턴하지 않죠. 즉 블록시켜서 SendMessage는 윈도우간 특히 부모 윈도우와 차일드 컨트롤간의 통신에 자주 사용되구요. 예를 들어 리스트 박으세어 LB_ADDSTRING 이라는 메시즈를 보내면 이는 리스트 박스에게 문자열 항목을 추가하라는 명령이 되며 항목이 완전히 추가되고 난 후에 SendMessage가 리턴되죠.

 

 윈도우간에 메시지를 교환할 때 어떤 함수를 사용할 것인가는 신중하게 결정해야 하는데 대부분의 경우는 SendMessage로 보내는 것이 정석이며 또 효율적이죠. 또 WM_COPYDATA같은 메시지는 그 특성상 반드시 SendMessage로만 보내야 하며 PostMessage로 붙여서는 안되요.

 

즉 정리하자면 SendMEssage는 당장 어떤 일을 하라는 명령이며, PostMessage는 한가해질 때 어떤 일을 하라는 신호죠.

부가하자면 PostMessage는 큐에 넣고, SendMessage는 WndProc의 case 하나를 호출하는 것과 같아요.

극히 책을 참조하여 객관적으로 적었기 때문에 제 주관적인 느낌이나 내용은 극히 없으므로,

틀린 내용이 있다면 지적하여도 무방합니다.

 



출처: http://ssmhz.tistory.com/18 [최초가 되거나 최고가 되어라]

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://egloos.zum.com/greenfrog7/v/863907

왜 PreTranslateMessage()에서 SendMessage를 받지 못 할까요??

http://greenfrog7.egloos.com/863907

서버로 부터 핫픽(에코) 메시지를 받는 부모 프로세스(클라이언트)의 쓰레드가 있습니다. 이 쓰레드는 서버가 비정상 종료 했을 경우 이에 대한 메시지를 부모 프로세스에게 전달해 주도록 프로그램을 설계하였습니다.( 쓰레드를 돌려 놓고 종료 될 때까지 기다리자는 UI가 무한대기 상태에 빠지는 현상이 생길게 뻔해서 이런 방법을 사용했습니다 ^^) 그리고 사용자 정의 메시지를 만들고 이 메시지를 SendMessage로 날렸습니다. 이 글 제목처럼 PreTranslateMessage()가 받아주길 바란면서요 ;;

결과는 .. 이 글을 읽고 계시는 분들 중 Win32 API를 공부하신 분들은 이미 입가에 미소를 짓고 계시겠죠? ㅎㅎ 결과적으로 PreTranslateMessage()는 SendMessage로 날린 메시지를 받지 못합니다. 왜 그런지 지금부터 알아보겠습니다 :)

Win32 API로 윈도우 프로그래밍을 해보신 분들은 아래의 코드를 보신적 있으실 겁니다.


while(GetMessage(&Message000))
{
    TranslateMessage(&Message);
    DispatchMessage(&Message);
}


윈도우의 경우 메시지가 발생하게 되면 시스템 큐에 메시지가 채워지게 되고 곧 사용되어질 쓰레드 메시지 큐에 메시지가 전달되게 됩니다. 그러면 해당 쓰레드(즉 메시지를 받는 프로그램)는 GetMessage를 통해 쓰레드 큐로부터 메시지를 전달 받습니다. 그리고 이 메시지는 PreTranslateMessage를 거친 후 TranslateMessage나 DispatchMessage에 전달되게 되죠. PreTranslateMessage는 메시지는 TranslateMessage나 DispatchMessage에 전해지기 전에 처리할 필요가 있을때 사용됩니다.  
위 PreTranslateMessage의 설명에서도 언급했드시 메시지 큐로부터 전달 받은 메시지를 처리하는 역할을 하는 함수이지요. 그런데 SendMessage는 메시지를 큐에 넣는 것이 아니라 곧바로 윈도우 프로시저로 보내 즉각 처리하도록 하며 메시지가 완전히 처리되기 전에는 리턴하지 않습니다. 결국 함수를 잘못 고른거죠 ^^ 그렇다면 메시지 큐에 메시지를 전달할 수 있는 함수는 누구일까요? 바로 PostMessage입니다. 이 함수는 Msg인수로 지정된 메시지를 윈도우의 메시지 큐에 집어넣어 윈도우 프로시저에서 이 메시지를 처리하도록 합니다. 즉, PostMessage가 전달한 메시지는 메시지 큐에 전달되고 GetMessage가 이 메시지는 받은 후 PreTranslateMessage에 메시지를 전달하여 처리할 수 있도록 하는 것이지요 ^^

이건 샘플 프로그램의 코드입니다.

 


아래는 샘플 프로그램 실행 화면인데요 SendMessage버튼을 누르면 SendMessge를 발생 시키고 PostMessage버튼을 누르면 메시지를 메시지 큐에 전달하고 PretranslateMessage에서 이를 처리하여 "Receive PostMessage ..^^" 라는 메시지를 뿌려줍니다.

 

?? 아무런 반응이 없네요 ^^ SendMessage는 메시지 큐로 메시지를 보내지 않으니까요 ~~

 

 

       

 

 

오호 ~ 잘 되네요 ^^



※ 위 글에 대한 잘못된 부분이나 궁금하신 부분에 대한 피드백은 언제나 환영입니다 ~ 물론, 퍼가신다면 영광이구요. 우리나라의 모든 개발자 분들께서 바쁘시지만 서로 자신의 귀중한 경험과 지식을 공유해서 함께 발전해 나갈 수 있었으면 합니다 ^^


샘플 프로젝트 다운로드 : 

 Message.zip

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

지나가다가 한마디 씁니다 결론만 말하면 
send message 로 해도 pretranslatemessage함수에 갑니다
지금 테스트를 잘못하신게 다이어로그 하나로 자신에게 sendmessage하면 응답을 기다리느라 지금 멈춰있는거같이 보이는거지 안가는게 아닙니다

다이어로그 두개로 서로 테스트 해보시면 이해가 되실겁니다

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

 

안녕하세요? resercher 님 sendmessage 로 보낸 메시지가 pretranslatemessage 로 가는게 맞나요? 
저는 다이얼로그 두개를 띄워놓고 wm_user+1 을 sendmessage 로 보내고 있습니다만.. 도무지 들어오질 않는데요... 
postmessage 로 보내는건 다 들어옵니다. 덧글 남겨주신게 맞다면 원인이 무엇일지 짐작도 안가네요..

 

 

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://m.blog.naver.com/itrainl4/90091242684

 

BOOL CSubClass001Dlg::PreTranslateMessage(MSG* pMsg)
{
 // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.

 if(pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)  //엔터키 막기 
 {

     ::MessageBox(((CEdit*)GetDlgItem(IDC_EDIT1))->m_hWnd,_T("에디트메시지박스"),_T("알림"),MB_OK);
     ::SetFocus( ((CEdit*)GetDlgItem(IDC_EDIT1))->m_hWnd );


  return TRUE;
 }
 else if(pMsg->hwnd == ((CEdit*)GetDlgItem(IDC_EDIT1))->m_hWnd)       //에디트컨트롤에 대한 서브클래싱
 {
  if(pMsg->message == WM_LBUTTONDOWN)
  {
   ::SendMessage(pMsg->hwnd,EM_SETSEL,0,3);
  }
 }

 return CDialog::PreTranslateMessage(pMsg);
}

 

 

Win32방식으로는 SetWindowLong으로

해당 윈도우에 대한 프로시저를 새로운 프로시저로 변경하고

프로그램 종료시 돌려놓는 방식으로 사용가능하다.

하지만 굳이 SetWindowLong방식을 사용하지 않더라도

MFC에서는 모든 컨트롤에 대한 서브클래싱이

PreTranslateMessage재정의 하나로 끝난다.

 

 

2.Win32방식의 서브클래싱 예제

 

#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;
LPCTSTR lpszClass=TEXT("SubEdit");

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
   ,LPSTR lpszCmdParam,int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;

 WndClass.cbClsExtra=0;
 WndClass.cbWndExtra=0;
 WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
 WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
 WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance=hInstance;
 WndClass.lpfnWndProc=WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style=CS_HREDRAW | CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
  NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);

 while (GetMessage(&Message,NULL,0,0)) {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }
 return (int)Message.wParam;
}

#define ID_EDIT1 100
#define ID_EDIT2 101
HWND hEdit1, hEdit2;
WNDPROC OldEditProc;
LRESULT CALLBACK EditSubProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 switch (iMessage) {
 case WM_KEYDOWN:
  if (wParam==VK_RETURN) {
   MessageBox(hWnd,TEXT("Enter is Pressed"),TEXT("Edit"),MB_OK);
   SetFocus(hWnd);
  }
  if (wParam==VK_TAB) {
   SetFocus(hEdit2);
  }
  if (wParam==VK_LEFT) wParam=VK_RIGHT;
  else if (wParam==VK_RIGHT) wParam=VK_LEFT;
  break;
 case WM_LBUTTONDOWN:
  SendMessage(hWnd,EM_SETSEL,0,-1);
  return 0;
 }
 return CallWindowProc(OldEditProc,hWnd,iMessage,wParam,lParam);
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 PAINTSTRUCT ps;
 HDC hdc;
 TCHAR *Mes=TEXT("에디트의 Enter키 입력을 검출합니다");
 switch (iMessage) {
 case WM_CREATE:
  hEdit1=CreateWindow(TEXT("edit"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
   ES_AUTOHSCROLL,
   10,10,200,25,hWnd,(HMENU)ID_EDIT1,g_hInst,NULL);
  hEdit2=CreateWindow(TEXT("edit"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
   ES_AUTOHSCROLL,
   10,50,200,25,hWnd,(HMENU)ID_EDIT2,g_hInst,NULL);
  SetFocus(hEdit1);
  OldEditProc=(WNDPROC)SetWindowLongPtr

(hEdit1,GWLP_WNDPROC,(LONG_PTR)EditSubProc);
  return 0;
 case WM_KEYDOWN:
  if (wParam == VK_RETURN) {
   MessageBox(hWnd,TEXT("엔터키를 눌렀습니다."),TEXT("알림"),MB_OK);
  }
  return 0;
 case WM_PAINT:
  hdc=BeginPaint(hWnd, &ps);
  TextOut(hdc,10,100,Mes,lstrlen(Mes));
  EndPaint(hWnd, &ps);
  return 0;
 case WM_DESTROY:
  SetWindowLongPtr(hEdit1,GWLP_WNDPROC,

(LONG_PTR)OldEditProc);
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

3.Win32의 SetWindowLong과

PreTranslateMessage의 적절한 조화

 

-PreTranslateMessage에서는 오직 WM_KEYDOWN,WM_KEYUP,WM_CHAR 메시지만 관리하고

나머지는 새로운 프로시저에게 위임하는 방식

이 방식이 필요한 이유는 프로시저 재활용이 편리하기 때문이다.

 

LRESULT CALLBACK EditSubProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 switch (iMessage) {
 case WM_LBUTTONDOWN:
  SendMessage(hWnd,EM_SETSEL,0,-1);
  return 0;
 }
 return CallWindowProc(OldEditProc,hWnd,iMessage,wParam,lParam);
}

 

BOOL CSubClass001Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // 시스템 메뉴에 "정보..." 메뉴 항목을 추가합니다.

 // IDM_ABOUTBOX는 시스템 명령 범위에 있어야 합니다.
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // 이 대화 상자의 아이콘을 설정합니다. 응용 프로그램의 주 창이 대화 상자가 아닐 경우에는
 // 프레임워크가 이 작업을 자동으로 수행합니다.
 SetIcon(m_hIcon, TRUE);   // 큰 아이콘을 설정합니다.
 SetIcon(m_hIcon, FALSE);  // 작은 아이콘을 설정합니다.

 // TODO: 여기에 추가 초기화 작업을 추가합니다.
 SetTimer(1,100,NULL);
 
 return TRUE;  // 컨트롤에 대한 포커스를 설정하지 않을 경우 TRUE를 반환합니다.
}

 

void CSubClass001Dlg::OnTimer(UINT nIDEvent)
{
 // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

 if(nIDEvent == 1)
 {
  KillTimer(1);
  ::SetFocus( ((CEdit*)GetDlgItem(IDC_EDIT1))->m_hWnd );
  OldEditProc = (WNDPROC)SetWindowLongPtr(((CEdit*)GetDlgItem(IDC_EDIT1))->m_hWnd,GWLP_WNDPROC,(LONG_PTR)EditSubProc);
 }

 CDialog::OnTimer(nIDEvent);
}

 

void CSubClass001Dlg::OnClose()
{
 // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
 SetWindowLongPtr( ((CEdit*)GetDlgItem(IDC_EDIT1))->m_hWnd,GWLP_WNDPROC,(LONG_PTR)OldEditProc);

 CDialog::OnClose();
}

BOOL CSubClass001Dlg::PreTranslateMessage(MSG* pMsg)
{
 // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.

 if(pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
 {
  return TRUE;
 }

 return CDialog::PreTranslateMessage(pMsg);
}

[출처] [MFC -메시지-서브클래싱]PreTranslateMessage를 이용한 서브클래싱|작성자 장미빛바다

 

 

 

 

반응형

 

 

728x90

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://www.gpgstudy.com/forum/viewtopic.php?t=9072

 

 

스레드에서 PostMessage보내는데..

전체글 글쓴이: 비회원 » 2006-01-14 20:48

안녕하세요

스레드에서 PostMessage를 이용해서 어떠한 동작을 하게끔 하는데요...
근데, 이녀석이 가끔 발생을 안하는거 같습니다.
즉, 스레드에서 어떤 동작이 빠르게 일어나면서, 그때 마다 PostMessage를 보내는데
가끔 한번씩 빼먹는거지요...

근데, 이 PostMesaage를 보내기 전에 Sleep(0)을 해줬더니, 현재는 문제가 없어보입니다.
원래 이렇게 해줘야 하는건가요??
꼭 Sleep이 아니더라도, 위에서 파일을 쓰는동작을 했는데도 잘 동작하더군요...
그래서 생각하기에 PostMessage를 보내기 전에 약간의 지연이 필요한거 아닌가 생각이 드네요~

상위

perpet전체글: 94가입일: 2004-06-07 10:59

 

전체글 글쓴이: perpet » 2006-01-15 01:41

정확한 프로그램 구조를 모르겠지만..질문의 정보로만 대답해줄수있는것이..
한번 SendMessage 함수로 바꿔보시라는것 밖에 없습니다.
제가 알기론 PostMessge 는 SendMessge 와달리 바로 처리되지 않고 내부 메시지큐에 던져지는 걸로 알고있습니다..그게 문제일까 하는생각이..^^

상위

비회원

메시지 큐에

전체글 글쓴이: 비회원 » 2006-01-15 01:47

메시지큐에 메시지가 너무 많이 쌓여있는 경우도 있을것 같군요.
윈2000,xp에서 최대 메시지수가 10000개랍니다. 설마 이렇게 많이 쌓아둘까 하지만 ㅋ
위에분처럼 센드메시지는 포스트메시지와는 달리 큐를 거치지 않기때문에 메시지 개수 초과라면 센드메시지로는 아무 문제 없겠군요.

상위

ProgC전체글: 190가입일: 2005-08-07 08:37사는 곳: 게임회사

명령어 처리할 시간

전체글 글쓴이: ProgC » 2006-01-15 03:49

답은 SendMessage입니다만... 
이상합니다. 순서대로라면 PostMessage를 보내고 난 후에 Sleep(0 or 1)을 해야 돌아가는게 
정상적일듯 한데요. 그 이전에 해야 한다면 우연치 않게 task가 돌아가는 순서가 딱 맞아서 
돌아갈듯 싶네요. PostMessage를 하기 전에 sleep(0)하고 난 후에도 한번씩 빼먹는지 다시한번
검사할 필요성이 있다고 생각됩니다. 안전한 방법은 SendMessage이고 굳이 PostMessage를 써야 한다면
쓰고 난 후에 Sleep(1)이 적당할 듯 싶네요.

just coding!

상위

비회원

 

전체글 글쓴이: 비회원 » 2006-01-15 11:01

UI 에 관한 내용이 처리중일 경우에(창을 드래그 한다거나..등) post 로 전달받은 메시지가 유실되는 경우가 
나타났었습니다.

상위

비회원

좀더 정확한 상황을 설명해보면..

전체글 글쓴이: 비회원 » 2006-01-15 11:39

질문의 내용이 조금 부족했었나 보네요

우선 스레드 부분에서는 어떤 하드웨어의 동작이 발생을 하게 되면
그 처리를 해주고요...
평소때는 Render를 계속 해주고 있습니다.

근데, 하드웨어 동작이 몇초가 아니라 몇 ms 단위로 발생하는 경우가 많습니다.(계속적으로 그런건 아니구요)
하지만, 바로바로 처리가 이루어 지기 때문에 큐가 넘치는 경우는 없을꺼라 생각합니다.

처음에는 SendMessage를 사용했었습니다. SendMessage를 썼을경우에는 즉시 처리 되기 때문에
해당 동작을 빠트리는 경우는 없더군요...
하지만, 하드웨어가 죽어버리는 현상이 나타납니다.
그래서 PostMessage를 쓴건데... 빠트리는 경우가 대략 100에 5번 이하 정도...

상위

ducklmg전체글: 155가입일: 2004-11-08 16:46

 

전체글 글쓴이: ducklmg » 2006-01-15 12:32

제 생각에는 메시지 루프에서 PeekMessage() 하면서 메시지를 하나씩 빼 먹는거 같은데요...

일반적으로 렌더링하면서 메시지 처리 할려고 GetMessage()와 PeekMessage()를 섞어서 사용하는데

아마 이부분에서 메시지를 처리하지 않는게 아닌가 생각됩니다.

난 너를 만나기 위해 이 세상에 태어났어
그러니 내 생활비는 네가 대 주어야만 해

상위

비회원

 

전체글 글쓴이: 비회원 » 2006-01-15 12:46

그렇다면, PostMessage 윗부분에서 Sleep 한것도 보장이 안되겠네요??
언젠간 또 빼먹고 그럴가능성이 있다는 말씀??

그럼 해결책은 따로 또 관리를 해줘야 하는건가요??
즉, PostMessage만큼 카운트 값등을 가지고 있다가, 그것만큼 처리가 안되면
수동으로 처리를 해줘야하는걸까요?? 흐음....

상위

비회원

 

전체글 글쓴이: 비회원 » 2006-01-15 22:36

PostMessage() 반환값을 한번 체크해보시죠. 성공하면 큐에는 들어가는거니, 처리쪽에서 빼먹는거고

SendMessageTimeout() 이용해서, 타임아웃 걸리는 경우 다시 시도해보는것도 한 방법이겠죠.

윈도 메세지 큐이용하지 말고, 자체 메세지 큐를 이용하는것도 한 방법입니다.

상위

비회원

 

전체글 글쓴이: 비회원 » 2006-01-15 22:44

윈도 메세지큐는 메세지 들어가는 순서대로 무조건 처리되는건 아니고

내부적으로, 우선순위가 높은것, 영역 무효화 같은것은 중복된걸 하나로 합치기도 하고

WM_PAINT 같은 경우는 처리 할수 없는 경우는 버리기도 합는것으로 알고있습니다.

정규 메세지외에 WM_USER 쪽에는 이와는 좀 틀릴듯한데 관련 도움말을 한번 찾아보세요.

그런데 쓰레드 문제는 시스템쪽이 아닌 거의 100% 다 자기코드에서 발생하는겁니다. 

설령 아니라도 해도, 자기가 맞춰야죠. 시스템을 엎을순 없으니 

상위

비회원

 

전체글 글쓴이: 비회원 » 2006-01-16 17:38

흐음... 어쩔수 없이 따로 관리하는편이 편하겠네요~~~ 에겅~

상위

비회원

PostThreadMessage를 이용해 보심이

전체글 글쓴이: 비회원 » 2006-01-17 13:16

::PostThreadMessage( 받는쓰레드아이디, 메세지, 0, 0 );

이런식으로 해보심이 어떠신지... ^^

상위

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://debugjung.tistory.com/entry/SendMessage-PostMessage

 

 

## SendMessage & PostMessage ##
1. 기본적인 동작 방식


 - 기본적으로 시스템에는 하나의 메시지큐가 있고, 또한 각 스레드 마다 메시지큐가 하나식 생긴다.

 - 스레드가 처음 생성될 시에는 메시지큐가 생성되지 않고, 메시지큐가 필요한 시점에서 생성된다.
  (메시지큐가 필요한 시점이란 메시지를 필요로 하는 관련 함수가 한번이라도 호출 되는 시점을 말한다..gdi함수 등..)

 

 - SendMessage의 경우 메시지큐를 거치지 않고 직접 윈도우 프로시저를 호출한다.

 - PostMessage의 경우 메시지큐에서 메시지를 받아와 순서대로 처리한다.
   (모든 메시지가 메시지큐에 차례로 들어가는 것은 아니다. 예를 들어 WM_PAINT의 경우 이미 메지시큐에
    WM_PAINT가 있다면 뒤에 들어온 WM_PAINT는 무시된다. 단 무효화 영역은 결합된다.)

 

  - PostMessage의 경우 메시지큐가 풀이 되면 메시지가 들어가지 않는다. 메시지가 올바로 삽입되었는지는  
    PostMessage의 반환값으로 검사할 수 있다.




2. SendMessage의 동작

 (PostMessage 경우 동작이 명확하기 때문에 설명은 필요없을 듯 함)

 

- SendMessage의 경우 윈도우 프로시저를 직접 콜하기 때문에 동작이 간단해 보이지만,
  다른 스레드와 연관될 경우 내용이 복잡해 진다.

 

   일단 기본적으로 컨텍스트 스위칭이 발생해야 하고 또 어느 시점에서 호출이 이루어져야 하는지 등의 내용을 알아야 한다.

  

   결론만 말하자만 thread a 에서 thread b에서 생성된 x라는 윈도우로 sendmessag 보낸다면 
   그 호출은 thread b에서 GetMessage, PeekMessage, WateMessage 함수를 호출한 시점에서 동작한다.
   

   이는 thread b에서 메시지 처리중에 thread a에서 호출한 sendmessage가 호출됨으써 발생하는 문제를 
   제거하기 위함이다. 즉 하나의 메시지를 완전히 처리하고 다음 메시지를 처리할 준비가 되었을 때 sendMessage에 대한 처리를 하는 것이다. 

   그렇다고 SendMessage에 의 해 GetMessage, PeekMessage에서 메시지를 가지고 올 수 있는 것은 아니다.
   호출 시점만 그렇다는 것이다.(sendMessag가 메시지큐에 메지지를 넣는 것이 아니기 때문에 당연하다.)


   또한 thread a에서 sendmessage를 호출 하였을 때 thread b에서 처리 완료하기 전 까지 thread a는 블럭상태가 된다.
   (이 블럭 상태에서도 thread a로 들어오는 sendmessage가 처리가 되었다..
    sendmessage reentrancy에 대해서는 아래에서 알아보기로 하자...)


   그럼 sendmessage동작중에 조심해야 할 것들은??
   일단 데드락을 조심하라고 하는데 아직 이쪽은 확실한 이해가 안된다.
   (아마 보내는 스레드랑 받는 스레드랑 서로 기다리게 되는 경우에 발생할 듯 한데... 어째보면 당연하다..-_-)

   이 쪽은 InSendMessage, ReplyMessag쪽 함수 참고

   일단 조심해야할 것들~
    sendmessag로 스레드간 메시지를 보내고, 이것이 만약 GetMessage가 호출 된 시점에서 
    처리가 될 때 그 안에서 다시 GetMeessage가 호출 될 경우 문제가 발생한다.(블락되어 버리는 듯)
   (처리 함수에서 MessageBox등을 띄울 때도 그렇다. 아마 MessageBox내에서 GetMessage를 호출하는 듯) 
   아직 다른 경우는 모르겠음...





3. SendMessage reentrant


SendMessage 중에 reentrant가 가능하다.

 

예를 들어 간단히 하나의 경우만 살펴보자.

thread a 에서 OnButton1을 클릭 했을 때 thread b로 SendMessage 를 보내고 블럭되어 있을 때
thread c에서 thread a로 SendMessage를 보내서 OnMessage1이 실행된다고 하면

 

그 때의 콜 스택은 아래와 같다.



OnMessage1 <=======================
CWnd::OnWndMsg
CWnd::WindowProc
AfxCallWndProc
AfxWndProc
AfxWndProcBase
USER32! 77cf8734()
USER32! 77cf8816() 
USER32! 77cfb4c0()
USER32! 77cfb50c()
NTDLL! 7c93eae3()
USER32! 77d0f3e3()
OnButton1() <=======================



즉, Onbutton1의 SendMessage 내부에서 다시 윈도우 프로시저 내로 직접 콜이 되는 형태이다.

 

재진입으로 인해 원치 않는 순서로 전역번수가 변할 수 있으니 주의가 필요하다!!

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://tansanc.tistory.com/entry/PostMessage-SendMessage-%EC%93%B0%EB%A0%88%EB%93%9C%EA%B0%84-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%A3%BC%EA%B3%A0%EB%B0%9B%EC%9D%84%EB%95%8C-%EB%8F%99%EA%B8%B0%ED%99%94

 

 

What is the difference between Send Message and Post Message and how these relate to C# ,WPF and Pure windows programming?

PostMessage (in "pure windows programming", aka win32 API) is asynchronous, i.e., to quote the docs:

Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.

To post a message in the message queue associated with a thread, use the PostThreadMessage function.

SendMessage is synchronous, that is, again quoting:

Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.

To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.

A good tutorial on these two functions and their use is here.

The connection to WPF is discussed in this SO question.

 

 

PostMessage 는 메세지 큐에만 넣어주고 메세지 처리 까지는 기다리지 않기 때문에 비동기 처리, 보낸 순서와 메세지 처리 끝나는 시점이 순서가 맞지 않을 수 있다.

 

 

SnedMessage 는 메세지 큐에만 넣어주고 메세지 처리 까지 기다리기 때문에 동기 처리, 하나의 메세지가 다 처리되고나서 다음 메세지가 처리된다.



출처: http://tansanc.tistory.com/entry/PostMessage-SendMessage-쓰레드간-메시지-주고받을때-동기화 [TanSanC]

 

 

 

=======================

=======================

=======================

 

 

 

[출처] winapi.co.kr

 

 요약 하자면 SendMessage는 바로 메시지 처리하고 결과 받을 때, PostMessage는 메시지 큐에 넣어줄 때 사용한다.

 

SendMessage

원형

 LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

인수

 hWnd : 메시지를 받을 윈도우 핸들. HWND_BROADCAST일 경우 모든 최상위 윈도우에게 보내진다. 숨겨진 윈도우나 사용금지된 윈도우도 포함되며 오버랩드, 팝업 윈도우도 포함된다. 단 차일드 윈도우에게는 보내지지 않는다.

 Msg : 전달할 메시지

 wParam : 메시지 추가 정보. 메시지에 따라 의미가 달라진다.

 lParam : 메시지 추가 정보.

리턴

 메시지를 처리한 결과가 리턴된다. 리턴값은 전달되는 메시지에 따라 달라진다.

설명

 메시지를 윈도우에게 보낸다. 해당 윈도우의 윈도우 프로시저를 호출하여 이 메시지가 완전히 처리되기 전에는 리턴하지 않는다. 같은 스레드에 속한 윈도우에게 메시지를 보낼 때는 마치 서브루틴을 호출하는 것과 동일하다. 예를 들어 메인 윈도우가 차일드 윈도우인 리스트 박스에게 LB_ADDSTRING이나 LB_GETCOUNT 등의 메시지를 보내면 리스트 박스는 해당 동작을 수행하는 서브루틴을 호출하고 이 동작이 완료될 때까지 SendMessage는 리턴하지 않는다.

다른 스레드에 속한 윈도우에게 메시지를 보낼 때는 스레드 스위칭이 발생하며 메시지를 받는 스레드가 메시지를 읽는 코드를 실행중이어야 한다. 만약 메시지를 받는 스레드가 메시지 처리에 오랜 시간을 소모한다면 SendMessage를 호출한 스레드는 이 함수가 리턴할 때까지 블록 상태로 남아있게 된다.

리스트 박스를 생성하고 문자열을 하나 추가한다.

HWND hList; LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { switch(iMessage) { case WM_CREATE: hList=CreateWindow("listbox",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER, 10,10,100,200,hWnd,(HMENU)0,g_hInst,NULL); SendMessage(hList,LB_ADDSTRING,0,(LPARAM)"문자열"); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return(DefWindowProc(hWnd,iMessage,wParam,lParam)); }

리스트 박스로 문자열을 추가할 때는 LB_ADDSTRING 메시지를 보내주면 된다. 이와 같이 부모 윈도우가 차일드에게 명령을 내리거나 상태를 조사하는 가장 기본적인 방법은 SendMessage로 메시지를 보내는 것이다. 각 차일드별로 보낼 수 있는 메시지의 종류가 다양하다.

 

SendMessage 함수는 보낸 메시지가 완전히 처리되기 전에는 리턴하지 않는 블록 특성을 가지고 있다. 특히 이런 특성은 다른 스레드간에 메시지를 주고 받을 때 자주 목격되는데 이 문제를 해결하는 방법에 대해서는 InSendMessage 함수를 참고하기 바란다.

WM_COPYDATA 등의 특정 메시지는 반드시 SendMessage 함수로만 보내야 하며 PostMessage를 쓸 수 없는 것도 있다.

PostMessage : 메시지를 큐에 붙이기만 하고 즉시 리턴한다.

InSendMessage, ReplyMessage, SendDlgItemMessage

 

PostMessage

원형

 LRESULT PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

인수 

 hWnd : 메시지를 받을 윈도우 핸들. HWND_BROADCAST일 경우 모든 최상위 윈도우에게 보내진다. 숨겨진 윈도우나 사용금지된 윈도우도 포함되며 오버랩드, 팝업 윈도우도 포함된다. 단 차일드 윈도우에게는 보내지지 않는다. NULL일 경우 PostThreadMessage 함수와 같아지며 특정한 대상 윈도우없이 스레드에게 메시지를 보낸다.

리턴

 Msg : 붙일 메시지

 wParam : 메시지 추가 정보. 메시지에 따라 의미가 달라진다.

 lParam : 메시지 추가 정보.

 메시지 붙이기에 성공하면 0이 아닌 값을 리턴한다.

설명

 메시지를 해당 윈도우의 메시지 큐에 붙이며 즉시 리턴한다. 이렇게 붙여진 메시지는 메시지 루프의 GetMessage 함수에 의해 꺼내져서  DispatchMessage 함수에 의해 윈도우 프로시저로 보내지며 최종적으로 윈도우 프로시저에 의해 처리된다.

 SendMessage와는 달리 메시지를 큐에 붙인 후 곧바로 리턴하므로 이 메시지는 곧바로 처리되지 않으며 메시지를 붙인 스레드는 곧바로 다른 작업을 할 수 있다.

 

 메시지를 붙이는 시점과 메시지를 처리하는 시점이 비동기적이기 때문에 PostMessage의 wParam, lParam으로 포인터를 전달하는 것은 의미가 없다. 메시지를 붙일 때 존재하던 값이 메시지를 처리하는 시점에서는 사라질 수 있기 때문이다. 특히 WM_COPYDATA 메시지는 임시적인 데이터를 전역 공유 메모리에 생성한 후 전달하는데 이 메시지는 절대로 PostMessage로 붙일 수 없으며 SendMessage로만 보내야 한다. 

 PostMessage 호출의 아주 특수한 경우로 첫번째 인수가 NULL일 수도 있는데 이 경우는 특정 윈도우에게 메시지를 붙이는 것이 아니라 응용 프로그램 전반에 걸친 작업 지시를 보내는 경우이다. 대상 윈도우가 없기 때문에 이렇게 붙여진 메시지는 윈도우 프로시저까지 전달되지 않으며 메시지 루프에서 직접 처리해야 한다. 이때의 메시지 루프는 다음과 같이 작성한다.

while(GetMessage(&Message,0,0,0)) { if (Message.message==WM_SOME) { // 직접 처리 } else { TranslateMessage(&Message); DispatchMessage(&Message); } }

 GetMessage로 읽은 메시지가 대상 윈도우가 없는 스레드 전반에 걸친 메시지이므로 DispatchMessage를 호출하기 전에 while루프 내부에서 직접 처리해 주어야 한다.

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://m.blog.naver.com/huvluv/220438053553

 

 

다른 Thread의 SendMessage의 Deadlock 현상 해결(WaitForSingleObject 관련)

 

 

 차일드 윈도우에서 시리얼 통신으로 데이터를 받는 중에 갑자기 종료를 하면 데드락에 걸리는 버그가 생겼다. 

 원인을 분석해보니 ReadFile이 있는 스레드에서 데이터 수신 완료 메시지와 차일드 윈도우(해당 스레드를 만든 차일드)의 WM_DESTROY에 있는 WaitForSingleObject 사이에서 나는 에러 였다. 에러를 찾고나니 의외로 쉽게 해결책을 찾을 수 있었다.

 

 

 

- 서적 : Windows API 정복 / 한빛 미디어 / 김상형 지음 - p.473 (13-2-나. 메시지 데드락)

- 구글에서 찾은 내용 : https://social.msdn.microsoft.com/Forums/en-US/ae039b55-1155-452a-804b-4e30fb61358b/sendmessage-deadlock-problem?forum=vcmfcatl

SendMessage deadlock problem

social.msdn.microsoft.com

 

 

 

위에서 찾은 내용을 요약하자면

1. 다른 스레드에서 SendMessage는 해당 윈도우에서 메시지를 다 처리하기 전까지 리턴하지 못한다.

2. 다른 스레드에서 SendMessage로 메시지를 보냈는데, 해당 윈도우에서 WaitForSingleObject에 걸리면 데드락에 빠진다.

3. 그래서 SendMessage 대신 PostMessage 나 SendNotifyMessage, SendMessageCallback, SendMessageTimeout 을 사용한다.

 

 위의 방법 이외에 다른 방법은 서적에 더 자세히 나와있다.

 

 

나의 경우에는 종료 시에만 문제가 됐으므로 SendMessageTimeout을 사용하여 해결하였다. 각각의 함수에 대한 사용법은 MSDN에 나왔있다. 

 

 

 

SendMessageTimeout에 대한 사용법(MSDN)- https://msdn.microsoft.com/ko-r/library/windows/desktop/ms644952(v=vs.85).aspx

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://www.sosori.com/2016/04/mfc-thread-dialog-updatedata.html

 

[MFC] Thread(쓰레드)에서 Dialog의 UpdateData()사용하는 방법

 

다이얼로그 사용시특정동작을 실행하면 특정동작이 돌아가는 동안 다이얼로그가 먹통이 됩니다. 이를 해결하기 위해 쓰레드를 사용하게되는데 이때 쓰레드 안에서 바로 UpdateData함수를 사용하면 에러가 발생합니다. 이를 해결하기 위해 윈도우에 메세지로 UpdateData를보내서 다이얼로그의 변경값을 실시간으로 변경하게 할 수 있습니다.

 

-- 소스 -- 

header 부분에 추가
#define THREAD_UPDATE ( WM_USER +1 )  //메세지 값으로 WM_USER +1 이값이 아니어도 상관은 없습니다.

 

*Thread가 돌아가며 사용하는 UpdateData함수의 모든 Dlg 클래스에 추가해야 한다.

해당 Dlg의 h부분에 추가
class ThreadDlg 
{
...

afx_msg LRESULT OnThreadUpdate(WPARAM w, LPARAM l); //USER_MSG_TEST 메시지를 받아 실행하게되는 함수 정의이다. 함수형과 이름은 바꿔도 상관없지만 변수부분은 그대로 유지하도록 한다.

...

}


ThreadDlg 의 cpp 부분에 추가

BEGIN_MESSAGE_MAP(CCMDLLDlg , CDialog)
...

 ON_MESSAGE (THREAD_UPDATE , OnThreadUpdate)  //여기에 메시지 ID 값과 함수 이름을 넣는다. 해당 ID로 메시지가 오면 해당 함수을 실행하게 된다.
...

END_MESSAGE_MAP()
 

메시지로 실행될 함수부분 추가

//헤더파일에 정의된 함수를 작성한다.
LRESULT ThreadDlg::OnThreadUpdate(WPARAM w, LPARAM l)
{
  UpdateData(TRUE);  
//이 부분에서 다이얼로그를 업데이트한다.
  return 0;
}

 

실재 쓰레드 안에서는 해당 다이얼로그의 포인터를 이용하여

m_dlg->UpdateData(TRUE);

하던 것을

m_dlg->PostMessage(THREAD_UPDATE ,0,0); //뒤의 두값이 메시지로 넘어가는 변수 w와 l이다. 

또는

m_dlg->SendMessage(THREAD_UPDATE ,0,0);

위의 PostMessage와 SendMessage는 실행시간에 약간의 차이가 있어 PostMessage를 사용할 경우 정상동작이 이루어 지지 않을 수 있다. PostMessage() 의 경우 Message 처리 결과를 기다리지 않고 복귀하고, SendMessage() 의 경우 처리를 완료한 후에 다음 진행이 이루어 진다.

 

위의 함수를 멤버함수로 만들어 간편하게 사용이 가능하다.

void ThreadDlg::myUpdateData(BOOL enable)
{
  SendMessage(THREAD_UPDATE ,enable,0);

}

LRESULT ThreadDlg::OnThreadUpdate(WPARAM w, LPARAM l)
{
  UpdateData(w);  
//이 부분에서 다이얼로그를 업데이트한다.
  return 0;
}

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://hatti.tistory.com/entry/MFC-SendMessage

 

클래스간 데이터 전달을 하는 방법이 무엇이 있을까.

 

1. 전역변수

2. 헤더 include 후 변수 접근

3. SendMessage

 

SendMessage를 이용하면 클래스간 데이터 전달이 용이함을 알고, 사용해보았다(몇번의 삽질과 함께...)

 

LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ) throw(); static LRESULT SendMessage( HWND hWnd, UINT message, // 메시지 이름 WPARAM wParam, // 정수형 포인터 전달인자 LPARAM lParam // 실수형 포인터 전달인자 ) throw();

 

주로 static을 쓰게 될텐데...

 

선언해주어야 하는 파트는 3파트.

 

1. 메시지 등록

- '이런 메시지를 쓰겠습니다.' 하는 부분 

        - 메시지 등록은 이 메시지를 받을 곳 메시지 맵에 등록을 한다. 던져줬는데 받는놈이 뭔지 모르면 그건 스팸일테니... 매시지 맵의 역할에 대해서는 따로 공부..
 

        ex) BEGIN_MESSAGE_MAP(CsendMessageDlg, CDialog)

ON_MESSAGE(메시지 구별자, 처리함수)

      END_MESSAGE_MAP()
 

2. 처리함수 정의

- 위의 처리함수를 정의하는 부분.
 

3. 메시지 전달

- 메시지를 전달하는 부분

ex) ::SendMessage(전달 받을 곳의 핸들(받는사람) , 메시지 구별자, 전달인자1, 전달인자2);
 


흐름

 

SendMessage - 받는 핸들 찾음 - 처리 함수 호출

 


#sendMessageDlg.cpp // 부모 DialogBEGIN_MESSAGE_MAP(CsendMessageDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_BN_CLICKED(IDOK, &CsendMessageDlg::OnBnClickedOk) ON_BN_CLICKED(IDC_BUTTON1, &CsendMessageDlg::OnBnClickedButton1) ON_MESSAGE(UM_TEST_SENDMESSAGE, &CsendMessageDlg::sets) // 1. 메시지 등록 END_MESSAGE_MAP() LRESULT CsendMessageDlg::sets(WPARAM wParam, LPARAM lParam) // 2. 처리함수 정의 { CString *data = (CString*)lParam; // 이렇게 받아옴. 왜냐하면 lParam 은 포인터니까!! CString을 넘긴 주소를 가리키고 있다. ::MessageBox(NULL, _T("asdf"), (LPCWSTR)data, MB_OK); // 이건 그냥 확인용 }



#Modaless.h
#define UM_TEST_SENDMESSAGE WM_USER +1 // 메시지 구별자 등록



#Modaless.cpp
// 자식 DialogBOOL CModaless::OnInitDialog() { CDialog::OnInitDialog(); CString *str = new CString("asdf"); ::SendMessage(((CSendMessageDlg*)GetParent())->GetSafeHwnd(), UM_TEST_SENDMESSAGE, 0, (LPARAM)str); // ((CSendMessageDlg*)GetParent())->GetSafeHwnd() : 상위 Dialog의 핸들을 가져옴 // 만약 하위 Dialog에게 보내려면 하위 Dialog의 객체에 sendMessage를 던져주면된다. // 그땐 주소(hWnd가 필요없다. :D) // 3. 메시지 전달 return TRUE; }

 

 

여기에서 보면 가장 중요한 부분이 SendMessage 부분인것 같다.

 

첫번째 전달인자는 받을 클래스의 핸들러가 들어간다. 즉, 받는사람 주소인 셈.

나는 부모 다이얼로그에 전달하기위해서 getparent를 이용해서 핸들러를 받아왔다.

 

전달인자는 WPARAM 과 LPARAM.

둘다 dword 형으로

WPARAM은 UINT, LPARAM은 LONG 형으로 각각 정수형, 실수형 포인터이다.

 

끗.

 

PS. 위 내용은 단순히 라이브러리를 어떻게 쓰는지에 대한 방법을 적은 것이다.

후에 꼭, SendMessage와 PostMessage에 대한 이해를 하는것이 중요하다.

 

라이브러리를 쓰는 개발자보다는 라이브러리를 만들 수 있는 개발자가 되자.



출처: http://hatti.tistory.com/entry/MFC-SendMessage [네 보기 좋다하여 꺾지 말아라]

 

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://softwareji.tistory.com/56

 

 

1. 이벤트 처리 방식

윈도우에서 이벤트가 발생했을 때 취합되는 메시지 내용
- 이벤트가 발생한 윈도우 핸들
- 이벤트의 종류
- 이벤트가 접수된 시간
- 이벤트가 발생한 위치(좌표)

 

2. 메시지 루프

① 키보드 입력 (이벤트 발생)
② 시스템 큐 ( 운영체제 )
③ 애플리케이션 큐
④ WinMain() (메시지 루프)
⑤ WndProc() (윈도우 프로시저)

 

cf. 시스템 큐 vs. 어플리케이션 큐

시스템 큐 : 운영체제가 가지고 있는 메시지 저장소, 저장된 이벤트 메시지를 애플리케이션 큐로 넘겨 줌
                키보드, 마우스 관련 (H/W)시스템 메시지는 시스템 큐를 거침.
애플리케이션 큐 : 실행 중인 응용 프로그램마다 하나씩 가지고 있는 메시지 저장소
* 즉 응용 프로그램이 소유한 메모리 버퍼
가끔씩 애플리케이션큐에 직접 메시지를 보내야 한는 경우 사용되는 메시지 전달 API 함수로 PostMessage 함수.

 SendMessage()   메시지 큐(Sys/App.Queue)에 추가하지 않고 바로 윈도우 프로시저 함수로 바로 전달되는 메시지 경우 이 함수 역할.
 PreTranslateMessage() 함수로 필터링 할 수 없음.
 이 함수는 메시지가 완전히 처리되기 전까지 반환하지 않음.(동기화 관련)
 그러므로 메시지 처리 후 반드시 처리 확인이 필요한 경우에 사용.
 PostMessage()  시스템 큐를 거치지 않고 직접 애플리케이션 큐에 보내짐.(H/W Msg. 는 고려하지 않음)
 메시지 큐에 추가하고 곧바로 반환.
 해당 메시지를 바로 처리하지 않고도 해당 메시지를 붙인 스레드는 다른 작업이 가능.
 즉, 메시지가 비동기적으로 처리 되어도 상관없는 경우.

 

e.g. PostMessage() 와 Multi-Thread
멀티스레드 프로그래밍의 경우, Worker Thread 에서 UserInterface Thread(윈도우)에 표시하려면 정기적으로 사용자 정의 메시지를 해당 윈도우에 전달.
진행 상황을 PostMessage() 함수를 이용해 Progress Ctrl 현재 위치를 갱신할 수 있음.
또한 AfxGetMainWnd() 함수를 이용하면 어디서든 MainFrame 윈도우의 주소를 알아낼 수 있으므로 사용자 정의 메시지를 보낼 때 주로 활용

 

while() 문으로 이루어진 메시지 루프는 GetMessage() 함수의 반환 값이 거짓일 때까지 계속 루프를 반복.
GetMessage() 함수는 애플리케이션 큐에 저장된 메시지를 인수 Message 에 담음.
인수 Message 는 메시지를 저장할 수 있도록 구조체로 선언됨.

 

 

 

/* * Message structure */ typedef struct tagMSG { HWND hwnd; // 이벤트가 발생한 윈도우 핸들 UINT message; // 이벤트 메시지 종류 WPARAM wParam; // 부가 정보 LPARAM lParam; // 부가 정보 DWORD time; // 이벤트가 접수된 시각 POINT pt; // 이벤트가 발생한 위치(좌표) #ifdef _MAC DWORD lPrivate; // 보안 설정 #endif } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

 

메시지 종류를 구분할 수 있는 값에서 WM_QUIT 일 경우 GetMessage 함수는 거짓 값을 반환. 그 외의 값이면 참.
참일 경우 TranslateMessage 함수와 DispatchMessage 함수를 수행하며 계속 루프

 

※ 메세지 루프는 기본적으로 메세지 큐에서 메세지를 읽어오는 GetMessage() 또는 PeekMessage()함수와, 
이 중 키보드 메세지를 문자 메세지로 변환하는 TranslateMessage() 함수, 그리고 메세지를 윈도우 프로시져로 보내는 DispatchMessage() 함수로 구성 !

 

3. TranslateMessage 함수

키보드 입력 이벤트 중 문자 입력을 처리하는 함수.
즉, 메세지를 문자 메세지로 변환하는 역할. 문자 메세지로의 필터링 !
'A' 라는 키를 눌렀다 떼면 WM_KEYDOWN, WM_CHAR, WM_KEYUP 세 가지 메시지가 발생.
WM_CHAR 사용자 의해서 아닌 메시지 루프에서 인위적으로 생긴 메시지.
GetMessage 함수로 읽은 메시지는 TranslateMessage 함수로 넘겨지고, 
메시지가 WM_KEYDOWN(H/W 또는 System Msg.) 키가 눌려졌는지 문자 키가 눌러졌는지 검사한 후에 문자 키가 눌려졌다면 WM_CHAR 메시지를 발생.
물론 문자 입력이 아닐 경우 DispatchMessage 함수로 바로 넘어감.

cf. MFC 에서는 비슷한 이름의 함수로 PreTranslateMessage 있음.
윈도우 프로시저 함수나 메시지 핸들러 함수를 호출하기 전에 호출.
주된 역할로 메시지 필터링 ! 즉, 불필요한 메시지를 처리할 때.
WindowProc() 함수처럼 이 함수를 재정의하여 모든 윈도우 메시지를 처리하는 것도 가능.(Dlg 클래스 속성의 재정의 버튼을 통해 생성)
호출 시점은 메시지 큐(어플리케이션 큐)에서 메시지를 꺼낸 직후 ! TranslateMSG 함수 앞에서 실행.
따라서 필터링이 가능한 메시지는 메시지 큐를 경유하는 메시지로 한정.

반환 값으로는, 더 이상 메시지가 처리되기를 원하지 않다면 1 (TRUE)을 리턴 함.
반면에 메시지가 계속 처리되기 원한다면 0 (FALSE)를 리턴. 

 

4. DispatchMessage 함수

GetMessage 함수로부터 전달된 메시지를 윈도우 프로시저로 보냄.
이 함수는 윈도우 프로시저가 메시지를 완전히 처리하기 전까지는 반환하지 않음.

 

5. DefWindowProc 함수

MFC 에서 주로 사용하는 함수로써,
이 함수는 윈도우 프로시저가 처리하지 않은 메시지의 디폴트 처리를 한다. 
WndProc은 원하는 메시지를 처리하고 자신이 처리하지 않은 메시지는 이 함수에게 전달하여 디폴트 처리를 하도록 해 주어야 한다.
애플리케이션에는 영향은 없지만 어떤 윈도우 메시지도 처리해주는 정리 필터 역할.

 

 

 

 

=======================

=======================

=======================

 

 

출처: http://www.tipssoft.com/bulletin/board.php?bo_table=QnA&wr_id=13375

 

 

질문 1 : 쓰레드 내에서는 SendMessage, PostMessage 중 어떤걸 써야 하는건가요?

지금 테스트 중인데 쓰레드내에서 SendMessage가 안돼어서 질문 드립니다..

 

 

질문 2 : 메인에서 어떤작업을 계속적으로 처리 하는 도중 다른 쓰레드에서 PostMessage를 날렸을 경우

처리 못하는 경우가 있는지 알고 십습니다.

 

질문 3 : 메시지 처리시 PreTranslateMessage() 함수 에서 다 처리 하면 되는건가요?

아니면 다른 방식으로 처리게 있는건가요?  

 

아직 초짜라서 모른는게 많네요..

답변 부탁드립니다.

 

------------------------------------------------------------------------------------------------------------------------------

 

SendMessage는 동기식 구조를 만들때 사용하고 PostMessage는 비동기식 구조를 만들때 사용합니다. 
따라서 두 메시지는 어떤것이 좋다고 이야기할수 있는 비교대상이 아닙니다. ^^;; 스레드를 
어떻게 사용했는지에 따라서 다르긴 하겠지만 두메시지 모두 사용가능해야합니다. 

스레드를 사용하는 방법에 따라서 다르긴하지만 스레드에서는 이야기하신 두 함수를 사용하는것보다 
이벤트 객체와 WaitForSingle 와 같은 함수를 사용하여 동기화시키는 방법을 더 많이 사용합니다. 

그리고 메시지는 메시지맵에서 직접 ON_MESSAGE 매크로를 사용해서 등록하셔도 되고 DefWindowProc 
에서 정의하여 사용하셔도 됩니다.

 

 

=======================

=======================

=======================

 

 

반응형