지난 내용
[강좌로 보는 서비스 프로그래밍] 서비스란 무엇인가? [1/?]
[강좌로 보는 서비스 프로그래밍] 외적으로 보여지는 서비스 [2/?]
[강좌로 보는 서비스 프로그래밍] 설치와 제거 [3/?]
[강좌로 보는 서비스 프로그래밍] 가끔 쓸모있는 관련 함수들 [4/?]
[강좌로 보는 서비스 프로그래밍] 시작/중지/일시정지 [5/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 [6/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 본체 코딩 Cont. [6/?]
[강좌로 보는 서비스 프로그래밍] 뼈다귀 서비스 코드 [7/?]
[강좌로 보는 서비스 프로그래밍] 서비스의 디버깅 [8/?]
[강좌로 보는 서비스 프로그래밍] 내컴에 누가 왔는지 다알아 [9/?]

지난장에서는 SERVICE_CONTROL_SESSIONCHANGE 를 받아서, 어떠한 이벤트가 떨어지는지 그리고
그러한 정보를 추출해서 필요한 로그를 남기는 작업을 진행하여 보았다.

그렇다면, 이번에는 누군가 컴퓨터에 로그인을 하면, 해당 정보를 출력해주는 환영인사를 남겨보자.
아래 그림은 MessageBox.exe 라는 유틸성 다이알로그를 하나 만들어 놓고, 사용자가 로그인을 하면 해당 정보를
추출하여, 메시지 박스를 띄워 주도록 구성되었다.


동작을 그려보면 다음과 같다.


대충 보면, 윈도우 시작프로그램에 MessageBox.exe를 등록해 놓고 뿌려주는것과 기능은 같다고 할 수 있겠지만
실제로 구현하는 레벨이 좀 다르다. 아무튼 여기서는 CreateProcessToDesktop 라고 하는 유틸성 함수를 제공해
주기 때문에 아래처럼 등록만 해주면, 서비스에서도  GUI 를 가진 프로그램을 원하는 세션에 띄워줄 수 있다.
DWORD WINAPI service_handler(DWORD fdwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
   CServiceManager manager;

#ifdef _DEBUG
    if(fdwControl != SERVICE_CONTROL_INTERROGATE)
    {
        fprintf(FileLog, "EVENT> %s\n", stname[fdwControl].text);
        FileLog.flush();
    }
#endif

    switch (fdwControl)
   {
   case SERVICE_CONTROL_PAUSE:
       manager.SetServiceRunStatus(SERVICE_PAUSE_PENDING);
        // 서비스를 일시 중지 시킨다.
       manager.SetServiceRunStatus(SERVICE_PAUSED);
       break;

   case SERVICE_CONTROL_CONTINUE:
       manager.SetServiceRunStatus(SERVICE_CONTINUE_PENDING);
       // 일시 중지 시킨 서비스를 재개한다.
       manager.SetServiceRunStatus(SERVICE_RUNNING);
       break;

   case SERVICE_CONTROL_STOP:
       manager.SetServiceRunStatus(SERVICE_STOP_PENDING);
       // 서비스를 멈춘다 (즉, 종료와 같은 의미)
       // 서비스를 종료하면, service_main 는 절대로 리턴하지 않는다.
       // 그러므로 해제하려면 작업이 있으면 모든것을 이곳에서 처리한다.
       manager.SetServiceRunStatus(SERVICE_STOPPED);
       break;

        // 윈도우에서 세션에 변경 사항이 발생하였을 경우, 발생하는 이벤트.
    case SERVICE_CONTROL_SESSIONCHANGE:
        {
            // 세션에 관련된 이벤트 정보는 아래의 구조체에 복사하여 사용한다.
            WTSSESSION_NOTIFICATION wtsnoti = {0};
            memcpy(&wtsnoti, lpEventData, sizeof(WTSSESSION_NOTIFICATION));

            CServiceUtility::CWTSSession ws(WTS_CURRENT_SERVER_HANDLE, wtsnoti.dwSessionId);
#ifdef _DEBUG
            fprintf(FileLog, " WTS[%s <%d>] => IP(%s), Client(%s), Domain(%s), User(%s), WinStation(%s)\n",
                wtsname[dwEventType].text,
                wtsnoti.dwSessionId,
                ws.IPAddress,
                ws.ClientName,
                ws.DomainName,
                ws.UserName,
                ws.WinStation);
            FileLog.flush();
#endif

            if(dwEventType == WTS_SESSION_LOGON)
            {
                char Path[MAX_PATH] = {0}, Message[MAX_PATH] = {0};
                strcpy(Path, manager.m_InstallParam.lpBinaryPathName);
                GetClearModulePath(Path);
                strcat(Path, "MessageBox.exe");
                sprintf(Message, "%s 에서 오신 %s님\n\n반갑습니다.", ws.IPAddress, ws.UserName);

                CServiceUtility su;
                STARTUPINFO si = {0};
                si.cb = sizeof(STARTUPINFO);
                PROCESS_INFORMATION pi = {0};
                Sleep(1000);
                su.CreateProcessToDesktop(Path, Message, si, pi, wtsnoti.dwSessionId);
            }
        }

   default:
       break;
   }

    return NO_ERROR;
}

추가로, CreateProcessToDesktop 함수가 좀더 깔끔하고 단순하게 변경되었다. 기존에 만들 때는 이것 저것 끼워 맞추어서 모양도
동작상태도 깔끔하지 못했는데.... 이번에는 좀 제대로 나왔다.
BOOL CServiceUtility::CreateProcessToDesktop(char* pszExecute, char* args, STARTUPINFO& si, PROCESS_INFORMATION& pi, UINT SessionID)
{
    BOOL bRet = FALSE;
    HANDLE hTokenNew = NULL, hTokenDup = NULL;
    DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
    void* EnvBlock = NULL;

       // 해당 세션의 유저 토큰을 아래 함수로 바로 가져올 수 있엇다.
    if(WTSQueryUserToken(SessionID, &hTokenNew) == FALSE)
        return FALSE;

   if(DuplicateTokenEx(hTokenNew,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hTokenDup) == NULL)
    {
        CloseHandle(hTokenNew);
        return FALSE;
    }

    CloseHandle(hTokenNew);
    if(CreateEnvironmentBlock(&EnvBlock, hTokenDup, FALSE))
        dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT;

    bRet = ::CreateProcessAsUser(hTokenDup,
        pszExecute,
        args,
        NULL,
        NULL,
        FALSE,
        dwCreationFlag,
        EnvBlock,
        NULL,
        &si,
        &pi);

    if(dwCreationFlag | CREATE_UNICODE_ENVIRONMENT)
        DestroyEnvironmentBlock(EnvBlock);
    CloseHandle(hTokenDup);
    return bRet;
}

오늘이 벌써 금요일 이네요.. 평안한 주말 보내세요.. ^^;;;

+ Recent posts