背景:
以前写过类似TurboLaunch的WPF版快捷方式管理软件,加入了3D动画,还集成了虚拟桌面,但是因为比较忙,自出了第一个版本后就不了了之了,Bug多多,也懒得改了,后来就渐渐忘记了。不巧的是,今天有位朋友向我要拖入可执行文件或者快捷方式生成缩略图的代码,才把它翻了出来,想想又好久没写Blog了,所以就单独拎出虚拟桌面这块做了个小Demo,和大家分享下。说到这个虚拟桌面,还是当时刚学C#那会在Winform下实现的,Oh,扯远了,进入正题。
设计思路:
虚拟桌面说白了就是将窗体分组进行显示,操作窗体的显隐藏,每个虚拟桌面可以包含多个窗体,并且只显示该虚拟桌面所包含的窗体。当然,桌面和任务栏是共享的,即所有的虚拟桌面都共用一个桌面和任务栏。
下面列出用到的API
private static class API


{

Import API#region Import API


/**//// <summary>
/// 注册热键
/// </summary>
/// <param name="hWnd"></param>
/// <param name="id"></param>
/// <param name="control"></param>
/// <param name="vk"></param>
/// <returns></returns>
[DllImport("user32")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint control, Keys vk);


/**//// <summary>
/// 取消热键
/// </summary>
/// <param name="hWnd"></param>
/// <param name="id"></param>
/// <returns></returns>
[DllImport("user32")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);


/**//// <summary>
/// 该函数枚举所有屏幕上的顶层窗口
/// </summary>
/// <param name="lpEnumFunc"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("user32")]
public static extern bool EnumWindows(CallBack lpEnumFunc, int lParam);

//[DllImport("user32")]
//public static extern string GetWindowText(IntPtr hWnd, IntPtr lpString, int nMaxCount);//取得一个窗体的标题(caption)文字,或者一个控件的内容


/**//// <summary>
/// 在指定的设备场景中描绘桌面墙纸图案
/// </summary>
/// <param name="hdc"></param>
/// <returns></returns>
[DllImport("user32", EntryPoint = "PaintDesktop")]
public static extern int PaintDesktop(
int hdc
);


/**//// <summary>
/// 该函数返回桌面窗口的句柄
/// </summary>
/// <returns></returns>
[DllImport("user32")]
public static extern IntPtr GetDesktopWindow();


/**//// <summary>
/// 判断窗口是否可见
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
[DllImport("user32")]
public static extern int IsWindowVisible(IntPtr hWnd);


/**//// <summary>
/// 获取指定窗体句柄
/// </summary>
/// <param name="LpClassName"></param>
/// <param name="LpWindowName"></param>
/// <returns></returns>
[DllImport("user32")]
public static extern IntPtr FindWindow(string LpClassName, string LpWindowName);


/**//// <summary>
/// 该函数获得一个窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配。这个函数查找子窗口,从排在给定的子窗口后面的下一个子窗口开始。在查找时不区分大小写。
/// </summary>
/// <param name="hwndParent"></param>
/// <param name="hwndChildAfter"></param>
/// <param name="lpszClass"></param>
/// <param name="lpszWindow"></param>
/// <returns></returns>
[DllImport("user32")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);


/**//// <summary>
/// 该函数设置指定窗口的显示状态
/// </summary>
/// <param name="hWnd">目标窗口句柄</param>
/// <param name="nCmdShow">状态参数</param>
/// <returns></returns>
[DllImport("user32")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

//[DllImport("user32")]
//public static extern int GetClassName(IntPtr hWnd, string sClassName, int nMaxCount);
#endregion
}
主要使用ShowWindow函数来设置窗体的显隐藏,其状态参数定义(API说明里是有10个的,不过我只定义了4个比较常用的)如下

WindowAction
private enum WindowAction
{
/// <summary>
/// 隐藏窗口并激活其他窗口
/// </summary>
Hide = 0x00,
/// <summary>
/// 在窗口原来的位置以原来的尺寸激活和显示窗口
/// </summary>
Show = 0x04,
/// <summary>
/// 激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。
/// </summary>
Restore = 0x03,
/// <summary>
/// 以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态。
/// </summary>
ShowNoActivate = 0x08
}
在虚拟桌面间切换时,先清空当前窗体组(一个虚拟桌面对应一个窗体组),接着枚举所有窗体,保存状态为非隐藏的窗体句柄至当前窗体组,然后置该组所有窗体(除了桌面、任务栏和本虚拟软件程序)状态为WindowAction.Hide,然后置目标窗体组内所有窗体状态为WindowAction.ShowNoActivate。
当然,不要忘记了在程序退出事件中加入显示所有窗体组中的窗体的代码,否则非当前组的窗体可就看不到了。
源代码在VS2008下编译通过
源代码下载
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!