虚拟桌面:一个简单的桌面管理工具

 

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家拍砖!

参考英文文章: <<Virtual Desktop: A Simple Desktop Management Tool>>

源代码请到原文链接下载。


一、引言

几个月前,我的一个大学同学正在用一个工具来管理混合桌面。我很好奇有这种的工具。我想它应该仅用来隐藏显示不同桌面的应用程序。作为好奇,我开始研究混合桌面,并得到了这篇文章。我发现之前认为这个工具仅是显示隐藏用的想法是错的。让我们看它如何工作的。

二、Window Station and Window Desktop

其实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和类。

Different APIs Used and Classes Built

1、初始化工作

主要是获得当前桌面的名字、根据名字设置应用程序标题并生成右下角图标、遍历系统中的桌面数量和名字并写注册表、

载入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;
}
2、响应窗口切换、启动应用程序、新建窗口动作

 

//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();
	}
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Python 中创建一个虚拟环境可以帮助您隔离不同项目所需的依赖项,从而避免版本冲突。 首先,您需要安装 virtualenv 库,您可以使用以下命令: ``` pip install virtualenv ``` 然后,您可以使用以下命令在您的项目目录中创建虚拟环境: ``` virtualenv env ``` 接下来,您可以激活虚拟环境: ``` source env/bin/activate ``` 现在,您可以安装您的项目所需的所有依赖项。任何安装的库将与其他项目隔离开来,并且只对该虚拟环境可用。 要退出虚拟环境,请使用以下命令: ``` deactivate ``` 希望这对您有所帮助! ### 回答2: 在Python中,可以使用venv模块创建一个虚拟环境,用于桌面软件开发。虚拟环境可以隔离不同项目所需的库和依赖,防止不同项目之间的冲突。 首先,需要确保Python的版本在3.3以上。然后,可以通过命令行进入要创建虚拟环境的文件夹,并执行以下步骤: 1. 创建虚拟环境:使用命令`python3 -m venv my_env`创建一个名为my_env的虚拟环境。这将在当前文件夹下创建一个包含虚拟环境的文件夹。 2. 激活虚拟环境:进入虚拟环境的目录,执行以下命令: - 对于Windows系统:`my_env\Scripts\activate` - 对于Mac/Linux系统:`source my_env/bin/activate` 3. 安装所需包:在虚拟环境中,可以使用pip命令安装所需的包,例如:`pip install package_name`。 4. 开始桌面软件开发:在虚拟环境中,可以使用Python来编写桌面软件,使用所需的库和工具。 5. 退出虚拟环境:当开发完成或者不再需要虚拟环境时,可以使用以下命令退出虚拟环境:`deactivate`。 通过以上步骤,我们可以创建一个独立的、隔离的虚拟环境,用于桌面软件开发。这可确保项目所需的依赖不会与其他项目发生冲突,并且可以方便地管理和维护项目。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值