教你使用GetThreadDesktop、CreateDesktop和SetThreadDesktop等函数创建虚拟桌面的代码并重启桌面进程
我们来看一下如何使用GetThreadDesktop、CreateDesktop和SetThreadDesktop等函数来创建自己的虚拟桌面“
如果你是一位电脑爱好者或者是一名程序员,那么你可能会对虚拟桌面这个功能非常感兴趣。虚拟桌面可以让我们在一个屏幕上同时打开多个应用程序,并且不同的应用程序之间互相独立。这个功能在Windows 10中被称为“任务视图”,但是它并不像Linux或Mac OS X中的虚拟桌面那样强大。
如果你想要更好的体验虚拟桌面,那么就需要使用一些第三方工具或者自己编写代码来创建自己的虚拟桌面。本文将介绍如何使用GetThreadDesktop、CreateDesktop和SetThreadDesktop等函数来创建自己的虚拟桌面,并且重启Windows系统中与桌面相关的进程。
首先,我们需要了解几个相关概念。Windows系统中有一个叫做“窗口站”的概念,它类似于一个容器,可以包含多个“桌面”。每个窗口站都有一个默认的“输入台”(input desktop),也就是当前用户正在使用的显示器上显示出来的内容。当我们切换到另外一个输入台时,Windows会切换到另外一个桌面。
在Windows系统中,每个进程都有自己的“桌面”。当我们启动一个应用程序时,Windows会为该应用程序创建一个新的进程,并且为该进程创建一个新的“桌面”。这个“桌面”就是指应用程序所在的窗口站中的一个虚拟屏幕。
现在,我们来看一下如何使用GetThreadDesktop、CreateDesktop和SetThreadDesktop等函数来创建自己的虚拟桌面。首先,我们需要调用CreateDesktop函数来创建一个新的“桌面”。
“`cpp
HDESK hDesk = CreateDesktop(L”MyVirtualDesktop”, NULL, NULL, 0,
GENERIC_ALL, NULL);
“`
上述代码将会创建一个名字为“MyVirtualDesktop”的新“桌面”,并且返回它对应的句柄。接着,我们需要将当前线程切换到这个新创建的“桌面”上。可以使用SetThreadDesktop函数来实现:
BOOL bResult = SetThreadDesktop(hDesk);
注意:只有当前用户正在使用输入台(也就是默认显示器)上显示出来内容对应窗口站中某个特定虚拟屏幕时才能成功调用SetThreadDesktop。
如果你想要查看当前线程所处于哪个输入台上,则可以调用GetThreadDesktop函数:
HDESK hDesk = GetThreadDesktop(GetCurrentThreadId());
现在,我们已经切换到了新创建的虚拟桌面中。接下来,我们需要启动一些应用程序,并将它们移动到这个新的虚拟桌面中。
要将一个应用程序移动到指定的“桌面”上,可以使用以下代码:
HWND hWnd = FindWindow(NULL, L”Application Title”);
DWORD dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
SetThreadDesktop(hDesk);
AttachThreadInput(GetCurrentThreadId(), dwThreadId, TRUE);
SetForegroundWindow(hWnd);
上述代码中,我们首先使用FindWindow函数查找指定标题为“Application Title”的窗口句柄。接着,我们获取该窗口所在进程的线程ID,并且打开输入台对应窗口站中的一个虚拟屏幕(也就是使用OpenInputDesktop函数)。然后,我们切换当前线程到这个新打开的虚拟屏幕上,并且使用AttachThreadInput函数让当前线程与该进程所在线程同步。最后,我们再调用SetForegroundWindow函数将该窗口置顶。
现在,你已经成功地创建了自己的虚拟桌面并且将一些应用程序移动到了其中。但是,在Windows系统中重启与桌面相关进程时会出现问题:如果你尝试关闭Windows资源管理器(explorer.exe)或者注销/重新登录用户,那么你所创建的虚拟桌面会被销毁。
为了解决这个问题,我们需要编写一个小工具来重启与桌面相关的进程。下面是一段简单的C++代码:
BOOL RestartDesktopProcesses()
{
BOOL bResult = FALSE;
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return FALSE;
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
HWND hwndShellDefView = GetWindow(GetDesktopWindow(), GW_CHILD);
DWORD dwShellDefViewThreadId = GetWindowThreadProcessId(hwndShellDefView, NULL);
// Kill explorer.exe process
HANDLE hExplorerProcHandle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
FALSE,
dwShellDefViewThreadId);
if (hExplorerProcHandle != NULL)
{
TerminateProcess(hExplorerProcHandle, 0);
CloseHandle(hExplorerProcHandle);
// Wait for explorer.exe to restart
Sleep(5000);
// Restart other desktop processes
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.cb=sizeof(si);
PROCESS_INFORMATION pi;
CreateProcess(NULL,L”taskkill /f /im dwm.exe”,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
CreateProcess(NULL,L”explorer.exe”,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
bResult = TRUE;
}
return bResult;
}
上述代码中,我们首先打开了当前进程的访问令牌,并且启用了SE_DEBUG_NAME特权。接着,我们使用GetWindow函数获取输入台对应窗口站中的默认子窗口句柄(也就是Windows资源管理器),并且获取该进程所在线程ID。然后,我们使用OpenProcess函数打开该进程句柄,并且调用TerminateProcess函数终止它。接着,我们等待5秒钟以便Windows能够重启Windows资源管理器和Desktop Window Manager(dwm.exe)等桌面相关的进程。
最后,我们再次创建这些桌面相关的进程来确保它们已经被重启:
STARTUPINFO si