이 블로그 검색

2014년 5월 28일 수요일

MFC- AfxGetMainWnd()함수 와 스레드

다른 함수와는 달리 AfxGetMainWnd()함수는 스레드와 관련이 있어 주의하여야 한다.
이함수의 내부를 보면 다음과 같이 되어 있다.
_AFXWIN_INLINE CWnd* AFXAPI AfxGetMainWnd()
{
  CWinThread* pThread = AfxGetThread();
  return pThread != NULL ? pThread->GetMainWnd() : NULL;
}
내부에서 AfxGetThread()함수를 사용하는것을 볼수가 있다.
이것은 현재 스레드의 GetMainWnd()함수를 호출한다.
이것은 다른 스레드에서 메인 윈도우의 핸들이 필요할때는 문제가 된다.
즉 다른 스레드에서 다음과 같이 AfxGetMainWnd()함수를 호출하면 메인 윈도우의 핸들을 얻을수가 없게 된다.
이럴때는
CWinApp *p = AfxGetApp();
CWnd *MainWnd = p->GetMainWnd();
형식으로 하면 해결할수 있다.

여기서 주의 할점이 있다.
어떤 다이얼로그(윈도우)를 다른 스레드로 생성을 시켰다고 하자.
이 윈도우에서 메인 윈도우로 접근할려면 어떻게 해야 할까?
위에서 사용한 방법대로 하돼..타입 캐스팅에 주의를 해야한다.
CMessengerX_ClientApp *pApp 는 CWinApp를 상속받은 클래이고,
CMainFrame *pMainFrm 은 CFrameWnd를 상속받은 클래스일때,

pApp = (CMessengerX_ClientApp *)AfxGetApp();
pMainFrm = (CMainFrame *) pApp->GetMainWnd();

다음과 같이 하면 메인 윈도우의 메인 프레임 클래스 핸들을 얻을수 있다.
이렇게 얻게 되면 다음 부터는,
CView *p = pMainFrm->GetActiveView();
CDocument *pDoc = p->GetDocument();
등으로 확장해 나갈수 있을것이다.

2014년 5월 23일 금요일

WTSRegisterSessionNotification

서비스 프로그램을 작성하다 보면..
간혹 데스크탑과 연동해야 하는 일이 발생한다.

입력없는 콘솔 프로그램 같은 경우는 그냥 서비스에서 바로 수행하면 되지만
입력을 받는 콘솔이나, GUI를 가진 프로그램을 띄워야할 경우는
프로세스 목록에만 나오고.. 깜깜 무소식이다.

일반 응용프로그램에서야 WTSRegisterSessionNotification 함수를 이용하여 아주 쉽게
로그온된 시점을 구할 수 있지만 불행하게도 서비스에서는 사용할 수 없다.


또한 서비스 시작시에 응용 프로그램을 수행시키면, 아직 로그온 되기 전이기때문에
저런 설정을 다 해놓는다 하여도 로그온된 화면에는 아무것도 보이지 않을 수 있다.

1. 꼭 서비스로 관리해야하는가?
2. 입력을 가지는 콘솔이나 GUI를 가졌는가?
3. 서비스 구동시에 시작해야 하는가?
4. 로그온 된 데스크탑에 보여주어야 하는가?

위 사항들을 잘 판단하고 그래도 꼭 필요하다면 다음과 같은 정보를 구해야한다.
1. 서비스로 개발하기
2. 데스크탑과 연동하는 옵션
3. 로그온된 시점 구하기

1. 서비스로 개발하기
위 사항은 이미 자료가 많이 나와있고, 대부분 알려진 자료이므로 그냥 좋은 자료를 찾는다 ^^;

2. 데스크탑과 연동하는 옵션       첫번째로 가장 쉬운 방법은 서비스 관리자를 이용하는 방법이다.
사용자 삽입 이미지
     그림에서 보이는것 처럼 로그온 설정시에 서비스와 데스크탑 상호 작용 허용에 첵크를
     해줌으로써 아주 간단하게 해결할 수 있다.

     두번째는 서비스를 인스톨할 때, 즉 CreateService 함수를 이용하여 서비스를 설치할 때
     다섯번째 인자에 SERVICE_INTERACTIVE_PROCESS 를 함께 넣어주는 것이다.
     이것은 첫번째 방법을 프로그램적으로 해결하는 것이다.

3. 로그온된 시점 구하기로그온된 시점을 구하는 방법도 그리 어렵지 않다. 하지만 제약사항이 많아서.. 
특히나 지금 처리하는 방법은 Windows XP 이상, Windows Server 2003 이상에서만 
동작하는 방법이므로, 하위 버전 윈도우를 처리하는 경우에는 해당사항이 없다. -_-;;;
(예전에는 이벤트 로그를 뒤적거려서 처리했었다...)

서비스를 핸들하기 위하여 RegisterServiceCtrlHandler 함수 대신에 좀더 구체적인 정보를
전달해주는 RegisterServiceCtrlHandlerEx 를 이용하는 방법이다.

RegisterServiceCtrlHandlerEx API는 시스템의 세션정보 변화를 감지하는 이벤트인
WM_WTSSESSION_CHANGE 를 처리할 수 있도록 해준다.

SetServiceStatus 에서 SERVICE_STATUS 값의 dwControlsAccepted 값에 꼭
SERVICE_ACCEPT_SESSIONCHANGE 를 함께 넣어주도록 하자.

이렇게 RegisterServiceCtrlHandlerEx 에 전달된 콜백함수에는 다음과 같은 인자가 있다.
DWORD WINAPI HandlerEx(
  [in]                 DWORD dwControl,
  [in]                 DWORD dwEventType,
  [in]                 LPVOID lpEventData,
  [in]                 LPVOID lpContext
);

저기서 첫번째 인자에 SERVICE_CONTROL_SESSIONCHANGE 가 넘어오면 로그온 세션에
무언가 변화가 생긴것이다.

두번째 인자로 어떤 변화가 발생하였는지를 감지할 수 있다. MSDN의 설명을 참고해보면...
ValueMeaning
WTS_CONSOLE_CONNECT
0x1
A session was connected to the console terminal.
WTS_CONSOLE_DISCONNECT
0x2
A session was disconnected from the console terminal.
WTS_REMOTE_CONNECT
0x3
A session was connected to the remote terminal.
WTS_REMOTE_DISCONNECT
0x4
A session was disconnected from the remote terminal.
WTS_SESSION_LOGON0x5
A user has logged on to the session.
WTS_SESSION_LOGOFF
0x6
A user has logged off the session.
WTS_SESSION_LOCK
0x7
A session has been locked.
WTS_SESSION_UNLOCK
0x8
A session has been unlocked.
WTS_SESSION_REMOTE_CONTROL
0x9
A session has changed its remote controlled status. To determine the status, call GetSystemMetrics and check the SM_REMOTECONTROL metric.

그러므로 위시점에서 WTS_SESSION_LOGON 일 경우에 프로그램을 기동시키면 정확하게로그온된 시점에 프로그램이 구동하게 된다.