windows服務程序啓動外部exe程序

       目的是service中檢查某個exe是否運行了,沒有則啓動程序。

       啓動的程序有界面。網上不少是使用ShellExecute來實現的,發現有問題,在win7下實現之後,service啓動該程序時會出現一個提示,點擊查看會切換到一個奇怪的界面來運行程序。這是因爲已經調到session0了,如下圖所示:

                    

        出現這個現象的原因是:

        在Windows XP、Windows Server 2003 或早期Windows 系統時代,當第一個用戶登錄系統後服務和應用程序是在同一個Session 中運行的。但是這種運行方式提高了系統安全風險,因爲服務是通過提升了用戶權限運行的,而應用程序往往是那些不具備管理員身份的普通用戶運行的,其中的危險顯而易見。 從Vista 開始Session 0 中只包含系統服務,其他應用程序則通過分離的Session 運行,將服務與應用程序隔離提高系統的安全性。如下圖所示:

                                  

        這樣使得Session 0 與其他Session 之間無法進行交互,不能通過服務向桌面用戶彈出信息窗口、UI 窗口等信息。所以會彈出那個窗口。

        所以ShellExecute適用於Vista之前的系統。Vista之後,則需要穿過session隔離來啓動一個程序。方法是取得當前Session Id,然後以此id創建令牌,再調用CreateProcessAsUser以當前用戶來啓動程序。

       代碼示例如下:

     

BOOL bSuccess = TRUE;
        DWORD dwSessionId = WTSGetActiveConsoleSessionId();
        if(dwSessionId == 0xFFFFFFFF)
        {
            bSuccess = FALSE;
            //logger->TraceInfo("no active console session");
            continue;
        }


        HANDLE hToken = NULL;
        if(!WTSQueryUserToken(dwSessionId,&hToken))
        {
            bSuccess = FALSE;
            //logger->TraceInfo("WTSQueryUserToken erong %d",GetLastError());
            continue;
        }


        STARTUPINFO si;
        PROCESS_INFORMATION pi;
        ZeroMemory(&si,sizeof(STARTUPINFO));
        ZeroMemory(&pi,sizeof(PROCESS_INFORMATION));
        si.cb = sizeof(STARTUPINFO);
        wchar_t desktopName[] = L"WinSta0\\Default";
        si.lpDesktop = desktopName;
        si.wShowWindow = TRUE;
        si.dwFlags     = STARTF_USESHOWWINDOW;


        std::wstring wstr;
        //這裏是想啓動的程序的路徑
        std::string str = (QCoreApplication::applicationDirPath()+"/"+PROGRAMNAME).toStdString();
        wstr = std::wstring(str.begin(),str.end());
        LPWSTR lp = (LPWSTR)wstr.c_str();


        LPVOID pEnv = NULL;
        if(FALSE == CreateEnvironmentBlock(&pEnv,hToken,FALSE))
        {
            bSuccess = FALSE;
            //logger->TraceInfo("CreateEnvironmentBlock failed");
            continue;
        }


        DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE|CREATE_UNICODE_ENVIRONMENT;
        if(!CreateProcessAsUser(hToken,NULL,lp,NULL,NULL,FALSE,dwCreationFlag,pEnv,NULL,&si,&pi))
        {
            bSuccess = FALSE;
            //logger->TraceInfo("CreateProcessAsUser erong %d",GetLastError());
            continue;
        }


        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);


        if (hToken != NULL)
            CloseHandle(hToken);
        if (pEnv != NULL)
            DestroyEnvironmentBlock(pEnv);

 注意:

1.如果是非管理員用戶,服務是無法調用管理員用戶的程序,只能啓動普通用戶的程序。

2.service只能啓動普通用戶權限的程序,不能啓動以管理員權限運行(UAC)的程序。

 

demo及使用說明

https://github.com/fengxieye/Qt-demo/tree/master/service

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章