Windows服務編寫(Windows Service,system許可權)程式顯示介面與使用者互動(xp,win7通用)_C#教程

來源:互聯網
上載者:User

1、VC2008中編寫“Windows服務”(Windows Service)程式

源碼資源下載:/201604/yuanma/TestService_jb51.rar

vc2008下建立一個 ATL 項目-》 選擇建立一個“服務”類型的ATL 項目TestService,將產生如下代碼,

class CTestServiceModule : public CAtlServiceModuleT< CTestServiceModule, IDS_SERVICENAME >{public :DECLARE_LIBID(LIBID_TestServiceLib )DECLARE_REGISTRY_APPID_RESOURCEID (IDR_TESTSERVICE, "{1FF78006-B225-4CC0-A7DE-E0C9D31C9937}" )HRESULT InitializeSecurity () throw(){// TODO : 調用CoInitializeSecurity 並為服務提供適當的// 安全設定// 建議- PKT 層級的身分識別驗證、// RPC_C_IMP_LEVEL_IDENTIFY 的類比層級// 以及適當的非NULL 安全記事符。return S_OK ;}//重寫這個函數來啟動任務啦HRESULT Run (int nShowCmd = SW_HIDE ) throw(){HRESULT hr = S_OK;hr = __super ::PreMessageLoop( nShowCmd);if (hr == S_OK){if (m_bService ){//需要定義#define _ATL_NO_COM_SUPPORT才能啟動服務時走到這裡//可以在這裡啟動線程,或者什麼其他東西來做自己的工作的啦//這裡是什麼都沒有做了,只輸出一條資訊LogEvent(_T ("widebright 的服務啟動咯,呵呵 "));SetServiceStatus(SERVICE_RUNNING );}//進入訊息迴圈,不停的處理訊息,可能最後分發到 Handler去處理,調用了OnShutdown等函數的。__super::RunMessageLoop ();}if (SUCCEEDED (hr)){hr = __super ::PostMessageLoop();}//可以在適當的時候調用Uninstall函數來卸載掉服務//__super::Uninstall();return hr ;}//重寫,服務退出處理void OnShutdown () throw(){LogEvent(_T ("TestService 的服務退出咯,一點都不好玩呵呵 "));}};CTestServiceModule _AtlModule;//extern "C" int WINAPI _tWinMain (HINSTANCE , HINSTANCE ,LPTSTR , int nShowCmd){return _AtlModule .WinMain( nShowCmd);}

2、我只要根據需要重寫相應的函數來實現自己想要的功能就行了

比如你想建立的“服務”隨系統啟動,可以重寫CAtlServiceModuleT 的Install函數,把裡面的CreateService函數的參數修改一下,例如添加與使用者互動可以使用 SERVICE_INTERACTIVE_PROCESS,具體可以去MSDN上尋找CreateService這個API的說明。

如果想處理服務 停止和啟動的動作,可以參考CAtlServiceModuleT 的原始碼重寫OnStop ()等函數。我上面簡單到重寫了Run函數,輸出一條“事件”其實具體 工作是可以放到這裡來完成的吧。

編譯,產生程式之後就可以測試了,

執行“TestService -/Service” 就可以把服務註冊到系統了,命令列參數其實是在CAtlServiceModuleT::ParseCommandLine 這個函數裡面處理,可以去看一下,必要的話重寫也是可以的,加上調用 UnInstall來刪除服務的代碼也很不錯的吧。

註冊後,就看用“sc start” 或者“net start” 等命令來操縱服務了。在“服務”控制器裡面控制與可以:如圖

3、這時候在Run函數中啟動一個Notepad.exe,此時沒有介面顯示,在xp下可以使用下面的方法實現notepad與使用者的互動:

//for xp systemDWORD _stdcall LaunchAppIntoSession0( LPTSTR lpCommand ){////////////////////////////////////////////system show dlg////////////////////HDESK hdeskCurrent ;HDESK hdesk ;HWINSTA hwinstaCurrent ;HWINSTA hwinsta ;hwinstaCurrent = GetProcessWindowStation ();if (hwinstaCurrent == NULL){return FALSE ;}hdeskCurrent = GetThreadDesktop (GetCurrentThreadId());if (hdeskCurrent == NULL){return FALSE ;}//開啟winsta0//開啟winsta0hwinsta = OpenWindowStation (L"Winsta0" , FALSE, WINSTA_ALL_ACCESS);// WINSTA_ACCESSCLIPBOARD|// WINSTA_ACCESSGLOBALATOMS |// WINSTA_ENUMDESKTOPS |// WINSTA_CREATEDESKTOP |// WINSTA_CREATEDESKTOP |// WINSTA_ENUMERATE |// WINSTA_EXITWINDOWS |// WINSTA_READATTRIBUTES |// WINSTA_READSCREEN |// WINSTA_WRITEATTRIBUTES);if (hwinsta == NULL){return FALSE ;}if (!SetProcessWindowStation (hwinsta)){return FALSE ;}//開啟desktophdesk = OpenDesktop (L"default" , 0, FALSE,DESKTOP_CREATEMENU |DESKTOP_CREATEWINDOW |DESKTOP_ENUMERATE|DESKTOP_HOOKCONTROL|DESKTOP_JOURNALPLAYBACK |DESKTOP_JOURNALRECORD |DESKTOP_READOBJECTS |DESKTOP_SWITCHDESKTOP |DESKTOP_WRITEOBJECTS);if (hdesk == NULL){return FALSE ;}SetThreadDesktop(hdesk );////////////////////////////////////////////end of system show dlg////////////////////STARTUPINFO si = { sizeof( si) }; SECURITY_ATTRIBUTES saProcess , saThread; PROCESS_INFORMATION piProcessB , piProcessC; // Prepare to spawn Process B from Process A. // The handle identifying the new process // object should be inheritable. saProcess.nLength = sizeof( saProcess); saProcess.lpSecurityDescriptor = NULL; saProcess.bInheritHandle = TRUE; // The handle identifying the new thread // object should NOT be inheritable. saThread.nLength = sizeof( saThread);saThread.lpSecurityDescriptor = NULL;saThread.bInheritHandle = FALSE; CreateProcess(NULL , lpCommand, & saProcess, &saThread , FALSE, 0, NULL , NULL, & si, &piProcessB ); if (!SetProcessWindowStation (hwinstaCurrent))return FALSE ;if (!SetThreadDesktop (hdeskCurrent))return FALSE ;if (!CloseWindowStation (hwinsta))return FALSE ;if (!CloseDesktop (hdesk))return FALSE ;return TRUE ;}


這種方法的關鍵是OpenWindowStation、SetProcessWindowStation、OpenDesktop和SetThreadDesktop這四個函數。這種方法的思路是:當前進程所處於的Session必須有介面互動能力,這樣才能顯示出對話方塊。由於第一個互動式使用者會登入到擁有WinSta0的Session 0,所以,強制性地把服務所在的進程與WinSta0關聯起來,並且開啟當前的案頭,把背景工作執行緒掛到該案頭上,就可以顯示出對話方塊。

4、這種方法在WinXP和Windows2003下工作得不錯,很遺憾,在Vista和Windows2008下,一旦執行到OpenWindowStation,試圖代開WinSta0工作站時,程式就會出異常。


首先瞭解一下程式要具備怎樣的條件才能與介面互動。Windows提供了三類對象:使用介面物件(User Interface)、GDI對象和核心對象。核心對象有安全性,而前兩者沒有。為了對前兩者提供安全性,通過工作站對象(Window station)和案頭對象(Desktop)來系統管理使用者介面對象,因為工作站對象和案頭對象有安全特性。簡單說來,工作站是一個帶有安全特性的對象,它與進程相關聯,包含了一個或多個案頭對象。當工作站對象被建立時,它被關聯到調用進程上,並且被賦給當前Session。互動式工作站WinSta0,是唯一一個可以顯示使用者介面,接受使用者輸入的工作站。它被賦給互動式使用者的登入Session,包含了鍵盤、滑鼠和顯示裝置。所有其他工作站都是非互動,這就意味著它們不能顯示使用者介面,不能接受使用者的輸入。當使用者登入到一台啟用了終端服務的電腦上時,每個使用者都會啟動一個Session。每個Session都會與自己的互動式工作站相聯絡。案頭是一個帶有安全特性的對象,被包含在一個視窗工作站對象中。一個案頭對象有一個邏輯的顯示地區,包含了諸如視窗、菜單、鉤子等等這樣的使用介面物件。

在Vista之前,之所以可以通過開啟Winsta0和預設案頭顯示對話方塊,是因為不管是服務還是第一個登入的互動式使用者,都是登入到Session 0中。因此,服務程式可以通過強制開啟WinSta0和案頭來獲得互動能力。

然而,在Vista和Windows2008中,Session 0專用於服務和其他不與使用者互動的應用程式。第一個登入進來,可以進行互動式操作的使用者被連到Session 1上。第二個登入進行的使用者被分配給Session 2,以此類推。Session 0完全不支援要與使用者互動的進程。如果採取在服務進程中啟動子進程來顯示對話方塊,子對話方塊將無法顯示;如果採取用OpenWindowStation系統API開啟WinSta0的方法,函數調用會失敗。總之,Vista和Windows2008已經堵上了在Session 0中產生介面互動的路。這就是原因所在。

那麼,是否真的沒法在服務中彈出對話方塊了呢?對於服務進程自身來說,確實如此,作業系統已經把這條路堵上了。但是,我們想要的並不是“在服務進程中彈出對話方塊”,我們想要的不過是“當服務出現某些狀況的時候,在案頭上彈出對話方塊”。既然在Session 0中無法彈出對話方塊,而我們看到的案頭是Session X,並非Session 0,很自然的一個想法是:能不能讓Session 0通知其他的Session,讓當前案頭正顯示著的Session彈一個對話方塊呢?

幸運的是,還真可以這樣做。

//for win7DWORD _stdcall LaunchAppIntoDifferentSession( LPTSTR lpCommand ){DWORD dwRet = 0;PROCESS_INFORMATION pi ;STARTUPINFO si ;DWORD dwSessionId ;HANDLE hUserToken = NULL;HANDLE hUserTokenDup = NULL;HANDLE hPToken = NULL;HANDLE hProcess = NULL;DWORD dwCreationFlags ;HMODULE hInstKernel32 = NULL;typedef DWORD (WINAPI * WTSGetActiveConsoleSessionIdPROC)();WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL;hInstKernel32 = LoadLibrary (L"Kernel32.dll" );if (!hInstKernel32 ) {return FALSE ;}OutputDebugString(L "LaunchAppIntoDifferentSession 1\n" );WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC )GetProcAddress( hInstKernel32,"WTSGetActiveConsoleSessionId" );// Log the client on to the local computer.dwSessionId = WTSGetActiveConsoleSessionId ();do{WTSQueryUserToken( dwSessionId ,&hUserToken );dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;ZeroMemory( &si , sizeof( STARTUPINFO ) );si.cb = sizeof( STARTUPINFO );si.lpDesktop = L"winsta0\\default" ;ZeroMemory( &pi , sizeof( pi) );TOKEN_PRIVILEGES tp ;LUID luid ;if( !::OpenProcessToken ( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY| TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID| TOKEN_READ | TOKEN_WRITE , &hPToken ) ){dwRet = GetLastError ();break;}else;if ( !LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &luid ) ){dwRet = GetLastError ();break;}else;tp.PrivilegeCount =1;tp.Privileges [0].Luid = luid;tp.Privileges [0].Attributes = SE_PRIVILEGE_ENABLED;if( !DuplicateTokenEx ( hPToken, MAXIMUM_ALLOWED, NULL , SecurityIdentification , TokenPrimary, & hUserTokenDup ) ){dwRet = GetLastError ();break;}else;//Adjust Token privilegeif( !SetTokenInformation ( hUserTokenDup,TokenSessionId ,(void*)& dwSessionId,sizeof (DWORD) ) ){dwRet = GetLastError ();break;}else;if( !AdjustTokenPrivileges ( hUserTokenDup, FALSE, &tp , sizeof(TOKEN_PRIVILEGES ), (PTOKEN_PRIVILEGES) NULL, NULL ) ){dwRet = GetLastError ();break;}else;LPVOID pEnv =NULL;if( CreateEnvironmentBlock ( &pEnv, hUserTokenDup, TRUE ) ){dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT ;}else pEnv =NULL;// Launch the process in the client's logon session.if( CreateProcessAsUser ( hUserTokenDup, // client's access tokenNULL, // file to executelpCommand, // command lineNULL, // pointer to process SECURITY_ATTRIBUTESNULL, // pointer to thread SECURITY_ATTRIBUTESFALSE, // handles are not inheritabledwCreationFlags,// creation flagspEnv, // pointer to new environment blockNULL, // name of current directory& si, // pointer to STARTUPINFO structure& pi // receives information about new process) ){}else{dwRet = GetLastError ();break;}}while( 0 );//Perform All the Close Handles taskif( NULL != hUserToken ){CloseHandle( hUserToken );}else;if( NULL != hUserTokenDup){CloseHandle( hUserTokenDup );}else;if( NULL != hPToken ){CloseHandle( hPToken );}else;return dwRet ;}

5、啟動服務後顯示了system許可權的Notepad.exe,並且可以與使用者進行互動


當然,在本例子啟動進程的地方建立一個對話方塊也是可以顯示對話方塊的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.