Windows服務(wù)編寫(Windows Service,system權(quán)限)程序顯示界面與用戶交互(xp,win7通用)
1、VC2008中編寫“Windows服務(wù)”(Windows Service)程序
源碼資源下載:/201604/yuanma/TestService_jb51.rar
vc2008下新建一個 ATL 項目-》 選擇創(chuàng)建一個“服務(wù)”類型的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 : 調(diào)用CoInitializeSecurity 并為服務(wù)提供適當(dāng)?shù)?
// 安全設(shè)置
// 建議- PKT 級別的身份驗證、
// RPC_C_IMP_LEVEL_IDENTIFY 的模擬級別
// 以及適當(dāng)?shù)姆荖ULL 安全說明符。
return S_OK ;
}
//重寫這個函數(shù)來啟動任務(wù)啦
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才能啟動服務(wù)時走到這里
//可以在這里啟動線程,或者什么其他東西來做自己的工作的啦
//這里是什么都沒有做了,只輸出一條信息
LogEvent(_T ("widebright 的服務(wù)啟動咯,呵呵 "));
SetServiceStatus(SERVICE_RUNNING );
}
//進(jìn)入消息循環(huán),不停的處理消息,可能最后分發(fā)到 Handler去處理,調(diào)用了OnShutdown等函數(shù)的。
__super::RunMessageLoop ();
}
if (SUCCEEDED (hr))
{
hr = __super ::PostMessageLoop();
}
//可以在適當(dāng)?shù)臅r候調(diào)用Uninstall函數(shù)來卸載掉服務(wù)
//__super::Uninstall();
return hr ;
}
//重寫,服務(wù)退出處理
void OnShutdown () throw()
{
LogEvent(_T ("TestService 的服務(wù)退出咯,一點都不好玩呵呵 "));
}
};
CTestServiceModule _AtlModule;
//
extern "C" int WINAPI _tWinMain (HINSTANCE , HINSTANCE ,
LPTSTR , int nShowCmd)
{
return _AtlModule .WinMain( nShowCmd);
}
2、我只要根據(jù)需要重寫相應(yīng)的函數(shù)來實現(xiàn)自己想要的功能就行了
比如你想創(chuàng)建的“服務(wù)”隨系統(tǒng)啟動,可以重寫CAtlServiceModuleT 的Install函數(shù),把里面的CreateService函數(shù)的參數(shù)修改一下,例如添加與用戶交互可以使用 SERVICE_INTERACTIVE_PROCESS,具體可以去MSDN上查找CreateService這個API的說明。
如果想處理服務(wù) 停止和啟動的動作,可以參考CAtlServiceModuleT 的源代碼重寫OnStop ()等函數(shù)。我上面簡單到重寫了Run函數(shù),輸出一條“事件”其實具體 工作是可以放到這里來完成的吧。
編譯,生成程序之后就可以測試了,
執(zhí)行“TestService -/Service” 就可以把服務(wù)注冊到系統(tǒng)了,命令行參數(shù)其實是在CAtlServiceModuleT::ParseCommandLine 這個函數(shù)里面處理,可以去看一下,必要的話重寫也是可以的,加上調(diào)用 UnInstall來刪除服務(wù)的代碼也很不錯的吧。
注冊后,就看用“sc start” 或者“net start” 等命令來操縱服務(wù)了。在“服務(wù)”控制器里面控制與可以:如圖
3、這時候在Run函數(shù)中啟動一個Notepad.exe,此時沒有界面顯示,在xp下可以使用下面的方法實現(xiàn)notepad與用戶的交互:
//for xp system
DWORD _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
//打開winsta0
hwinsta = 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 ;
}
//打開desktop
hdesk = 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 ;
}
這種方法的關(guān)鍵是OpenWindowStation、SetProcessWindowStation、OpenDesktop和SetThreadDesktop這四個函數(shù)。這種方法的思路是:當(dāng)前進(jìn)程所處于的Session必須有界面交互能力,這樣才能顯示出對話框。由于第一個交互式用戶會登錄到擁有WinSta0的Session 0,所以,強(qiáng)制性地把服務(wù)所在的進(jìn)程與WinSta0關(guān)聯(lián)起來,并且打開當(dāng)前的桌面,把工作線程掛到該桌面上,就可以顯示出對話框。
4、這種方法在WinXP和Windows2003下工作得不錯,很遺憾,在Vista和Windows2008下,一旦執(zhí)行到OpenWindowStation,試圖代開WinSta0工作站時,程序就會出異常。
首先了解一下程序要具備怎樣的條件才能與界面交互。Windows提供了三類對象:用戶界面對象(User Interface)、GDI對象和內(nèi)核對象。內(nèi)核對象有安全性,而前兩者沒有。為了對前兩者提供安全性,通過工作站對象(Window station)和桌面對象(Desktop)來管理用戶界面對象,因為工作站對象和桌面對象有安全特性。簡單說來,工作站是一個帶有安全特性的對象,它與進(jìn)程相關(guān)聯(lián),包含了一個或多個桌面對象。當(dāng)工作站對象被創(chuàng)建時,它被關(guān)聯(lián)到調(diào)用進(jìn)程上,并且被賦給當(dāng)前Session。交互式工作站W(wǎng)inSta0,是唯一一個可以顯示用戶界面,接受用戶輸入的工作站。它被賦給交互式用戶的登錄Session,包含了鍵盤、鼠標(biāo)和顯示設(shè)備。所有其他工作站都是非交互式的,這就意味著它們不能顯示用戶界面,不能接受用戶的輸入。當(dāng)用戶登錄到一臺啟用了終端服務(wù)的計算機(jī)上時,每個用戶都會啟動一個Session。每個Session都會與自己的交互式工作站相聯(lián)系。桌面是一個帶有安全特性的對象,被包含在一個窗口工作站對象中。一個桌面對象有一個邏輯的顯示區(qū)域,包含了諸如窗口、菜單、鉤子等等這樣的用戶界面對象。
在Vista之前,之所以可以通過打開Winsta0和缺省桌面顯示對話框,是因為不管是服務(wù)還是第一個登錄的交互式用戶,都是登錄到Session 0中。因此,服務(wù)程序可以通過強(qiáng)制打開WinSta0和桌面來獲得交互能力。
然而,在Vista和Windows2008中,Session 0專用于服務(wù)和其他不與用戶交互的應(yīng)用程序。第一個登錄進(jìn)來,可以進(jìn)行交互式操作的用戶被連到Session 1上。第二個登錄進(jìn)行的用戶被分配給Session 2,以此類推。Session 0完全不支持要與用戶交互的進(jìn)程。如果采取在服務(wù)進(jìn)程中啟動子進(jìn)程來顯示對話框,子對話框?qū)o法顯示;如果采取用OpenWindowStation系統(tǒng)API打開WinSta0的方法,函數(shù)調(diào)用會失敗??傊?,Vista和Windows2008已經(jīng)堵上了在Session 0中產(chǎn)生界面交互的路。這就是原因所在。
那么,是否真的沒法在服務(wù)中彈出對話框了呢?對于服務(wù)進(jìn)程自身來說,確實如此,操作系統(tǒng)已經(jīng)把這條路堵上了。但是,我們想要的并不是“在服務(wù)進(jìn)程中彈出對話框”,我們想要的不過是“當(dāng)服務(wù)出現(xiàn)某些狀況的時候,在桌面上彈出對話框”。既然在Session 0中無法彈出對話框,而我們看到的桌面是Session X,并非Session 0,很自然的一個想法是:能不能讓Session 0通知其他的Session,讓當(dāng)前桌面正顯示著的Session彈一個對話框呢?
幸運的是,還真可以這樣做。
//for win7
DWORD _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 privilege
if( !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 token
NULL, // file to execute
lpCommand, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
dwCreationFlags,// creation flags
pEnv, // pointer to new environment block
NULL, // 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 task
if( NULL != hUserToken )
{
CloseHandle( hUserToken );
}
else;
if( NULL != hUserTokenDup)
{
CloseHandle( hUserTokenDup );
}
else;
if( NULL != hPToken )
{
CloseHandle( hPToken );
}
else;
return dwRet ;
}
5、啟動服務(wù)后顯示了system權(quán)限的Notepad.exe,并且可以與用戶進(jìn)行交互
當(dāng)然,在本例子啟動進(jìn)程的地方創(chuàng)建一個對話框也是可以顯示對話框的。
上一篇:C#文件目錄操作方法匯總
欄 目:C#教程
下一篇:C#模擬鏈表數(shù)據(jù)結(jié)構(gòu)的實例解析
本文標(biāo)題:Windows服務(wù)編寫(Windows Service,system權(quán)限)程序顯示界面與用戶交互(xp,win7通用)
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/6553.html
您可能感興趣的文章
- 01-10C#實現(xiàn)自定義windows系統(tǒng)日志的方法
- 01-10C#3.0使用EventLog類寫Windows事件日志的方法
- 01-10C#使用windows服務(wù)開啟應(yīng)用程序的方法
- 01-10C#微信開發(fā)(服務(wù)器配置)
- 01-10輕松學(xué)習(xí)C#的正則表達(dá)式
- 01-10分享一個C#編寫簡單的聊天程序(詳細(xì)介紹)
- 01-10C# 調(diào)用 JavaWebservice服務(wù)遇到的問題匯總
- 01-10使用C#編寫簡單的圖形化的可發(fā)送附件的郵件客戶端程序
- 01-10Windows系統(tǒng)中使用C#讀取文本文件內(nèi)容的小示例
- 01-10Windows中使用C#為文件夾和文件編寫密碼鎖的示例分享


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10C#中split用法實例總結(jié)
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改


