在Windows中服务与桌面分别位于不同的会话中,服务与桌面交互主要有以下方法:
1. 在用户会话中创建进程,服务与该进程交互,实现服务与桌面交互。这种方法可以实现复杂的交互,但首先需要解决的是获取当前用户的会话,利用CreateProcessAsUser函数来创建进程。
2. 通过函数WTSSendMessage实现弹窗提醒。
这里将展示第一种方法,在创建服务函数CreateService中有三个参数需要注意:
1) dwServiceType必须含SERVICE_INTERACTIVE_PROCESS选项
2) lpServiceStartName必须是LocalSystem,默认NULL就可以。
3) lpPassword为NULL。
获取当前会话主要为通过函数WTSGetActiveConsoleSessionId来获取当前活动会话,然后获取用户Token作为CreateProcessAsUser函数的参数。主要函数代码如下:
dwSessionId = WTSGetActiveConsoleSessionId();
WTSQueryUserToken_t WTSQueryUserTokenProc = (WTSQueryUserToken_t)(::GetProcAddress(hmod,"WTSQueryUserToken"));
// Get token of the logged in user by the active session ID
BOOL bRet = (*WTSQueryUserTokenProc)(dwSessionId, ¤tToken);
if (!bRet)
{
::FreeLibrary(hmod);
return 0;
}
bRet = DuplicateTokenEx(currentToken, //TOKEN_ASSIGN_PRIMARY | SecurityImpersonation
MAXIMUM_ALLOWED,
0,SecurityImpersonation , TokenPrimary, &primaryToken);
if (!bRet)
{
::FreeLibrary(hmod);
return 0;
}
if(!::SetTokenInformation(primaryToken,TokenSessionId,&dwSessionId,sizeof(DWORD)))
{
::FreeLibrary(hmod);
return 0;
}
if(!::SetPrivilege(primaryToken,SE_TCB_NAME,TRUE)) {
::FreeLibrary(hmod);
return 0;
}
::FreeLibrary(hmod);
return primaryToken;
Session解决了,CreateProcessAsUser就可以顺利调用了:
HANDLE primaryToken = ::GetCurrentUserToken();
if (primaryToken == 0)
{
return INVALID_HANDLE_VALUE;
}
void* lpEnvironment = NULL;
HMODULE hmod = ::LoadLibrary(TEXT("Userenv.dll"));
CreateEnvironmentBlock_t CreateEnvironmentBlockProc = (CreateEnvironmentBlock_t)(::GetProcAddress(hmod,"CreateEnvironmentBlock"));
// Get all necessary environment variables of logged in user
// to pass them to the process
BOOL resultEnv = (*CreateEnvironmentBlockProc)(&lpEnvironment,primaryToken, TRUE);
if (!resultEnv)
{
::FreeLibrary(hmod);
::CloseHandle(primaryToken);
::free(primaryToken);
return INVALID_HANDLE_VALUE;
}
DWORD dwSize = ::_tclen(exe) + ::_tclen(argument) + 10;
dwSize = dwSize * sizeof(TCHAR);
PWSTR cmd = new TCHAR[dwSize];
if(argument != NULL && ::_tclen(argument) != 0)
::StringCchPrintfW(cmd,dwSize,TEXT("\"%s\" %s"),exe,argument);
else
::StringCchPrintfW(cmd,dwSize,TEXT("%s"),exe);
PROCESS_INFORMATION processInfo;
STARTUPINFO StartupInfo = {sizeof(StartupInfo)};
SECURITY_ATTRIBUTES Security1;
SECURITY_ATTRIBUTES Security2;
ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.lpDesktop = L"winsta0\\default";
// Start the process on behalf of the current user
// error in here,maybe should elevate the service permission
BOOL result = CreateProcessAsUser(primaryToken,
cmd,NULL,NULL,
NULL,/*&Security1,&Security2*/ FALSE, /*CREATE_NO_WINDOW |*/ NORMAL_PRIORITY_CLASS |
CREATE_UNICODE_ENVIRONMENT, lpEnvironment, path,
&StartupInfo, &processInfo);
if(!result) {
::FreeLibrary(hmod);
::CloseHandle(primaryToken);
delete cmd;
return INVALID_HANDLE_VALUE;
}
DestroyEnvironmentBlock_t DestroyEnvironmentBlockProc = (DestroyEnvironmentBlock_t)(::GetProcAddress(hmod,"DestroyEnvironmentBlock"));
(*DestroyEnvironmentBlockProc)(lpEnvironment);
::FreeLibrary(hmod);
::FreeLibrary(hmod);
::CloseHandle(primaryToken);
delete cmd;
return processInfo.hProcess;
完整源码见这里
MSDN交互服务: https://msdn.microsoft.com/en-us/library/ms683502(VS.85).aspx