windows7 default桌面,winlogon桌面和screensaver桌面的截屏
这个项目已经结束快两年了,由于公司需要,所以又来做个总结。
当时需要做屏幕截图,但是在winlogon桌面(ctrl+alt+del 或 登录界面 或 UAC)和screensave桌面(屏保下)截图都是黑屏,所以就引来了这个问题。
解决这个问题涉及到的东西比较多,包括session,window station,service,desktop什么的。关于他们的前世今生的问题今天我们就不讨论了,有兴趣的同胞们可以去查一查,这里有一个简单的概括http://blog.sina.com.cn/s/blog_42a88fc10100o6ez.html
总之我们记住本机windows的所有用户交互操作都在seesion0下的winsta0下的几个桌面中(default,winlogon,screensaver)。
在这里我们需要记住三件事:
1.同一时刻只能有一个桌面与用户进行交互(能接受鼠标,键盘输入),我们称之为InputDesktop
2.我们的操作(即截图)只能在InputDesktop中才能生效。
3.想在InputDesktop中进行有效操作的process需要一定的用户权限,普通进程无法进行有效操作。
基于以上三点,我们基本就能解决问题了。
首先,我们需要获得inputDesktop,这个可以使用windowsApi OpenInputDesktop()获得。(具体使用方法请查MSDN)。
注:其实最先还应该将我们的进程附到winsta0,OpenWinstation(winsta0,SetProcessWinstation(winsta0),由于我们的进程基本只能在winsta0中运行,所以一般不需这一步。
其次,是将我们的操作附到InputDesktop中,当然这个也是使用windows的API SetThreadDesktop(),这个函数会将我们当前线程切换到InputDesktop。
注意:在切换前我们应该调用 GetThreadDesktop将当前桌面保存起来,以便在特定操作(本文指截图)完成后再将线程切换回原来的desktop。
最后,我们需要给我们的进程赋予足够的权限。本文中,我们从服务继承SYSTEM权限(其他方式还未研究),主要有以下几个步骤:
a.写一个windows 服务,在服务中调用OpenProcessToken,取得服务的token。
b.调用DuplicateTokenEx复制服务的token到tokenDup。
c.然后调用SetTokenInfomation()对tokenDup进行一些其他设置。
d.最后调用CreateProcessAsUser(),以tokenDup为参数启动我们的程序,这样我们的程序就具有了SYSTEM权限了。
注:这里使用的是两个程序,我们的逻辑处理程序单独写,设为A,然后通过写一个服务B来启动程序A以便使A具有system权限。
虽然写起来没几个字,但是不得不说当时也是痛苦的调查了好几天。在此希望能给大家带来帮助,让后来者少走弯路。
为了方便理解,这个地方贴两段代码:
1.windows服务启动功能进程的代码
BOOL StartProcess(const TCHAR *appNameAndPath, const TCHAR* param, PROCESS_INFORMATION *procInfo) { HANDLE hTokenThis = NULL; HANDLE hTokenDup = NULL; HANDLE hThisProcess = GetCurrentProcess(); BOOL bResult = FALSE; bResult = OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis); if(!bResult) { return FALSE; } bResult = DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup); if(!bResult) { return FALSE; } DWORD dwSessionId = WTSGetActiveConsoleSessionId(); bResult = SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD)); DWORD UIAccess = 1; SetTokenInformation(hTokenDup, TokenUIAccess, &UIAccess, sizeof(DWORD)); if(!bResult) { return FALSE; } STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.lpDesktop = TEXT("WinSta0\\default"); LPVOID pEnv = NULL; DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; bResult = CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE); if(!bResult) { return FALSE; } bResult = CreateProcessAsUser(hTokenDup, appNameAndPath, (LPWSTR)param, NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, procInfo); if(!bResult) { return FALSE; } if(hTokenDup) { CloseHandle(hTokenDup); } return TRUE; }
二,功能进程切换desktop并截图的代码(其中GetDesktopBitmap(picNum)是截图函数,picNum没有意义,这是当初调研的时候写的,因为懒直接截图就存硬盘了,picnum是为了防止存的文件重名):
BOOL GetInputDesktop() { HDESK currentDesk = NULL; HDESK inputDesk = NULL; currentDesk = GetThreadDesktop(GetCurrentThreadId()); if(!currentDesk) { return FALSE; } inputDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); if (inputDesk == NULL) { DWORD tempError = GetLastError(); return FALSE; } if(!SetThreadDesktop(inputDesk)) { DWORD tempError = GetLastError(); CloseDesktop(inputDesk); return FALSE; } GetDesktopBitmap(picNum); if(!SetThreadDesktop(currentDesk)) { DWORD tempError = GetLastError(); return FALSE; } if(!CloseDesktop(inputDesk)) { return FALSE; } return TRUE; }
由于项目代码不能泄露,所以以上代码是当初调研的时候写的测试代码,所以可能有很多神奇的地方和随意的写法,望勿见怪。但是这两个代码肯定能用,刚写文章之前我还测试了一遍。如果大家要试验的话,拷过去修改一下应该就可以了。
转载来源:https://blog.csdn.net/baojianhuangbo/article/details/44852725
原创文章,转载请注明出处:http://124.221.219.47/article/pyrdm/