[Win32] 服务程序开发(2)Session 0隔离(上)

 本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/47614907

在上一篇博文中,我说了如何编写一个系统服务,SERVICE_STATUS结构中有一个SdwServiceType成员,ERVICE_INTERACTIVE_PROCESS表示服务是一个交互式服务,可以与用户交互,MSDN上虽然是这么说的,其实经过测试,在XP上这一个完全不影响服务显示UI,只要安装服务时允许服务与桌面交互即可(见下图)。而在WinVista和Win7以及更高的平台,天生就有Session 0隔离,这一个加不加就显得更鸡肋了。。

[Win32] 服务程序开发(2)Session 0隔离(上)_第1张图片

MSDN关于Session 0隔离的兼容性白皮书:(话说MSDN上有篇中文的文档可真不容易,,)https://msdn.microsoft.com/zh-cn/library/ee663077.aspx

简单说,Win32子系统中有会话这一概念,Vista以后,服务被隔离到了Session 0会话,用户会话从1开始,因此他们之间不能直接共享UI。

我们知道,会话之中是窗口站,窗口站里面有桌面,其中用户会话的交互式窗口站是固定的,也就是winsta0,其中的默认桌面也是固定的default,也就是说我们只需要在这个桌面窗口一个进程即可。

当然,直接使用CreateProcess肯定是不行的,因为进程的令牌会继承,进程令牌里有会话ID。那么我们就使用CreateProcessAsUser即可。

如何在编写服务,如何在服务使用下面的代码请参见:[Win32] 服务程序开发(1)基本概念和服务程序的框架

void ShowMessage(LPWSTR lpszMessage, LPWSTR lpszTitle)
{
	// 获得当前Session ID
	DWORD dwSession = WTSGetActiveConsoleSessionId();
	DWORD dwResponse = 0;

	// 显示消息对话框
	WTSSendMessageW(WTS_CURRENT_SERVER_HANDLE, dwSession,
		lpszTitle,
		static_cast<DWORD>((wcslen(lpszTitle) + 1) * sizeof(wchar_t)),
		lpszMessage,
		static_cast<DWORD>((wcslen(lpszMessage) + 1) * sizeof(wchar_t)),
		0, 0, &dwResponse, FALSE);
}
void CreateUserProcess(LPCTSTR Filename)
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	RtlZeroMemory(&si, sizeof(STARTUPINFO));
	RtlZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
	si.cb = sizeof(STARTUPINFO);
	//si.lpDesktop = TEXT("Winsta0\\default");

	HANDLE hToken, hDuplicatedToken = NULL;
	WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hToken);//获取用户令牌
	DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hDuplicatedToken);//复制令牌
	LPVOID lpEnvironment = NULL;
	CreateEnvironmentBlock(&lpEnvironment, hDuplicatedToken, FALSE);//创建当前用户环境

	//在当前用户创建进程(CREATE_UNICODE_ENVIRONMENT表示用户环境是Unicode字符串)
	if (CreateProcessAsUser(hDuplicatedToken, Filename, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, NULL, &si, &pi) == 0){
		ShowMessage(L"在用户界面创建进程失败", L"Error");
	}

	//释放资源
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	CloseHandle(hToken);
	CloseHandle(hDuplicatedToken);
	DestroyEnvironmentBlock(lpEnvironment);
}


CreateUserProcess这个函数就是创建一个当前用户的进程,不过如果开启了UAC这个进程是没有管理员权限的,因为开启UAC时WTSQueryUserToken获取的是低权限令牌。

在Win8.1测试:运行正常。

在Win10测试:新创建的进程窗口不能直接创建在前面,即使新进程调用SetWindowPos

在Win7测试:和win10效果一样,新创建的进程窗口不能直接创建在前面,即使新进程调用SetWindowPos

在XP测试:和win8.1效果一样,运行正常。

进一步测试:运行的程序创建一个有总在最前风格的程序窗口

Win8.1正常、Win7正常、Win10正常、XP正常

下一篇中,将介绍如何在当前用户桌面上创建一个SYSTEM权限的进程




你可能感兴趣的:(Win32,windows,api)