转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家拍砖!
参考英文文章: <<Virtual Desktop: A Simple Desktop Management Tool>>
源代码请到原文链接下载。
几个月前,我的一个大学同学正在用一个工具来管理混合桌面。我很好奇有这种的工具。我想它应该仅用来隐藏显示不同桌面的应用程序。作为好奇,我开始研究混合桌面,并得到了这篇文章。我发现之前认为这个工具仅是显示隐藏用的想法是错的。让我们看它如何工作的。
其实Windows用Window Station and Desktop 结构来提供可增加的安全。
暂时,我们提到的“Desktop“是指逻辑显示表面,它包含用户接口对象(GDI对象)和用户对象。
一个Window Station 是一个安全的内核对象,它包含剪切板,一个原子表,一个或更多的桌面对象。这些存在默认的窗口站“WinSta0“。
它是一个交互的窗口站,“WinSta0“是仅有的交互窗口站,它能显示用户接口或接收用户输入,而其它的窗口站是非交互的。
默认在交互的容器站中有三个桌面:默认的、WinLogon和ScreenSaver。
()1)通过按ATL+CTL+DEL弹出的窗口是WinLogon桌面。
(2)无论用户在什么时候登陆,默认窗口被创建并可见。这里的“默认”是指对所有登陆用户的默认的活动桌面。当你按了ATL+CTL+DEL,你将会切换回到WinLogon桌面。
(3当屏保被激活时,ScreenSave桌面会被可见并获得激活。
因为这些桌面名字是case-insensitive,这些仅能是一个在容器站指定名字的桌面,但是一个桌面名字能在不同的窗口站中反复出现。
一个桌面也是一个安全的内核对象,当一个新的桌面被创建,它和调用它的进程的当前的窗口站绑定在一起。那里将一直仅有一个桌面是可见并准备接收用户输入,
被提供的桌面和交互窗口站绑定在一起。激活的窗口被调用。
以下的桌面和窗口站的工程是为了提供在不同进程中的顶层解决。
虚拟桌面帮助我们创建和在不同的桌面中转换。它甚至给你一个从一个桌面转到另一个桌面的感觉,但它不是真的,因为任何运行的应用程序不能被从一个
桌面移到另一个桌面。如我所说,它只是一个假象,这个应用程序在当前的桌面会被关闭,且在特殊的桌面会被打开。这个可能导致应用程序未保存的数据的丢失,
且可能不能保存状态,因为它可能重启。
这个应用程序用hooks来显示应用程序控制面板中的“Move To"菜单选项。对于hooks更多细节,在文章中我会提到,我将让你大约了解一下工程中用到的APIs和类。
主要是获得当前桌面的名字、根据名字设置应用程序标题并生成右下角图标、遍历系统中的桌面数量和名字并写注册表、
载入Evnent Hooker.dll,调用接口安装窗口HOOK和消息HOOK。
//Checks whether the specified desktop is current active desktop(deskop of the current thread). bool CDesktopManager::GetCurrentDesktopName(TCHAR szDesktopName[ARRAY_SIZE]) { bool bReturn = false; try { DWORD iOutCount = 0; HDESK hCurrentDesktop = GetThreadDesktop(GetCurrentThreadId()); if(!GetUserObjectInformation(hCurrentDesktop, UOI_NAME, szDesktopName, ARRAY_SIZE - 1, &iOutCount)) throw _T("\nCDesktopManager::GetCurrentDesktopName:\t GetUserObjectInformation() failed."); bReturn = true; } catch(TCHAR *pszErrorString) { DebugPrintErrorMessage(pszErrorString); } catch(...) { DebugPrintErrorMessage(_T("\nCDesktopManager::IsCurrentDesktop\tException caught in CDesktopManager::IsCurrentDesktop.")); bReturn = false; } return bReturn; }
//Windows procedure hook (filter) function. LRESULT CALLBACK WinProcHookProcedure(int nCode, WPARAM wParam, LPARAM lParam) { if(0 > nCode) return CallNextHookEx(g_hPreviousWinProcHook ,nCode,wParam,lParam); CWPSTRUCT *pwsStruct = (CWPSTRUCT *) lParam; switch(pwsStruct->message) { //Handle the init menu message. case WM_INITMENU: { HMENU hMenu = (HMENU) pwsStruct->wParam; OutputDebugString(_T("\nWinProcHookProcedure:\tMenu WM_INITMENUPOPUP Event Fired.")); TCHAR szWindowText[ARRAY_SIZE] = {0}; GetWindowText(pwsStruct->hwnd, szWindowText, ARRAY_SIZE); //Hook "Move To" Menu Into System Menu. if(0 != _tcscmp(szWindowText, _T("Virtual Desktop !"))) InsertMenu(pwsStruct->hwnd); } } return CallNextHookEx(g_hPreviousWinProcHook ,nCode,wParam,lParam); } //Set the Windows procedure filter. bool InstallWinProcHook(void) { bool bReturn = false; try { //Set the Windows procedure filter. OutputDebugString(_T("\nInstallWinProcHook:\tWinProc Event Hooked.")); g_hPreviousWinProcHook = SetWindowsHookEx(WH_CALLWNDPROC,&WinProcHookProcedure,g_hInstance,0); if(NULL == g_hPreviousWinProcHook) { TCHAR szError[ARRAY_SIZE] = {0}; wsprintf(szError,_T("Last Error : %d"),GetLastError()); OutputDebugString(_T("\nInstallWinProcHook:\tFailed to Hook WinProc Event.\n")); OutputDebugString(szError); bReturn = false; } else { OutputDebugString(_T("\nInstallWinProcHook:\tWinProc Event Hooked.\n")); bReturn = true; } } catch(...) { OutputDebugString(_T("\nInstallWinProcHook:\tException caught in InstallWinProcHook.")); bReturn = false; } return bReturn; }
在安装窗口HOOK的API函数 SetWindowsHookEx 时360安全卫士报告有异常!(这种情况怎么避免呢?知道的读者能告知我解决方法吗?)
//Set the message hook (Filter). bool InstallMessageHook(void) { bool bReturn = false; try { //Set the message hook (Filter). g_hPreviousMsgHook = SetWindowsHookEx(WH_GETMESSAGE,&MessageHookProcedure,g_hInstance,0); if(NULL == g_hPreviousMsgHook) { OutputDebugString(_T("\nInstallMessageHook:\tFailed to Hook Messages.\n")); bReturn = false; } else { OutputDebugString(_T("\nInstallMessageHook:\tMessages Hooked.\n")); bReturn = true; } } catch(...) { OutputDebugString(_T("\nInstallMessageHook:\tException caught in InstallMessageHook.")); bReturn = false; } return bReturn; }
//Switch between desktops. bool CDesktopManager::SwitchDesktop(TCHAR *pszDesktopName) { bool bReturn = false; try { if (NULL == pszDesktopName) { OutputDebugString(_T("\nCDesktopManager::SwitchDesktop:\tNULL DesktopName in CDesktopManager::SwitchToDesktop")); throw false; } /*if( !_tcsicmp(_T("Winlogon"), szDesktopName) || !_tcsicmp(_T("Disconnect"), szDesktopName)) { TCHAR szErrorMsg[ARRAY_SIZE] = {_T("You can not switch to ")}; _tcscat_s(szErrorMsg, ARRAY_SIZE -1, szDesktopName); _tcscat_s(szErrorMsg, ARRAY_SIZE -1, _T(" Desktop.")); MessageBox(NULL, szErrorMsg, TXT_MESSAGEBOX_TITLE, MB_ICONINFORMATION); throw false; }*/ //Open desktop handle to switch to. HDESK hDesktopToSwitch = OpenDesktop(pszDesktopName, DF_ALLOWOTHERACCOUNTHOOK, TRUE, GENERIC_ALL); if(NULL == hDesktopToSwitch) { TCHAR *pszError = NULL; TCHAR szErrorMsg[ARRAY_SIZE] = {0}; int iErrorCode = GetLastError(); if(5 == iErrorCode) { FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, iErrorCode, 0, (LPWSTR) &pszError, 0, NULL); wsprintf(szErrorMsg, _T("Failed to switch to %s desktop.\n\t %s"), pszDesktopName, pszError); MessageBox(NULL, szErrorMsg, TXT_MESSAGEBOX_TITLE, MB_ICONINFORMATION | MB_TOPMOST | MB_TASKMODAL); OutputDebugString(pszError); } wsprintf(szErrorMsg, _T("\nCDesktopManager::SwitchDesktop:\tOpenDesktop Failed in CDesktopManager::SwitchToDesktop. Last Error : %s"), pszError); OutputDebugString(szErrorMsg); throw false; } //Switch the desktop. if(FALSE == ::SwitchDesktop(hDesktopToSwitch)) { OutputDebugString(_T("\nCDesktopManager::SwitchDesktop:\tSwitchDesktop Failed in CDesktopManager::SwitchToDesktop")); throw false; } //Close the desktop handle. CloseDesktop(hDesktopToSwitch); bReturn = true; } catch(bool bThrownVal) { bReturn = bThrownVal; } catch(...) { bReturn = false; DebugPrintErrorMessage(_T("\nCDesktopManager::SwitchDesktop:\tException caught in CDesktopManager::SwitchToDesktop")); } return bReturn; }
//Creates the new desktop (Adds new desktop) void CVirtualDesktopDlg::OnBnClickedAddNewDesktop() { // TODO: Add your control notification handler code here TCHAR szCaption[ARRAY_SIZE] = {0}; m_AddNewDesktop.GetWindowText(szCaption, ARRAY_SIZE - 1); if(_tcsicmp(szCaption, _T("&New"))) { m_DesktopNameControl.GetWindowText(szCaption, ARRAY_SIZE -1); //Left trimming the string. while(' ' == szCaption[0]) _tcscpy_s(szCaption, ARRAY_SIZE - 1, szCaption + 1); int iLen = (int) _tcslen(szCaption); //Right trimming the string. while(' ' == szCaption[--iLen]) szCaption[iLen] = '\0'; if(_tcslen(szCaption)) { //Check the desktop name in the list. if(-1 != m_DesktopListControl.SelectString(0, szCaption)) { MessageBox(_T("Desktop already created !"), TXT_MESSAGEBOX_TITLE, MB_ICONEXCLAMATION | MB_TOPMOST | MB_TASKMODAL); } //Create the desktop if(CDesktopManager::CreateDesktop(szCaption)) { if(IDYES == MessageBox(_T("New Desktop is been created.\nWould you like to switch to new desktop ?"), TXT_MESSAGEBOX_TITLE, MB_YESNO | MB_ICONINFORMATION | MB_TOPMOST | MB_TASKMODAL)) SwitchDesktopTo(szCaption); m_DesktopListControl.AddString(szCaption); m_DesktopListControl.SelectString(0, szCaption); OnLbnSelchangeDesktopList(); UpdateHotKeys(); } } else { MessageBox(_T("Please enter Desktop Name"), TXT_MESSAGEBOX_TITLE, MB_ICONEXCLAMATION | MB_TOPMOST | MB_TASKMODAL); m_DesktopNameControl.SetWindowText(_T("")); m_DesktopNameControl.SetFocus(); } } else { m_AddNewDesktop.SetWindowText(_T("&Add")); m_DesktopNameControl.SetWindowText(_T("")); m_DesktopNameControl.EnableWindow(TRUE); m_SwitchToDesktop.EnableWindow(FALSE); m_DesktopNameControl.SetFocus(); } }