小李就职于一家国有企业的子公司,最近承担起了一项投屏演示汇报的任务,上级领导要来视察工作,小李需要干3件事:一是播放一段公司的宣传片;二是利用PPT汇报公司业绩情况;三是演示介绍公司最新研发的某软件系统。公司有会议室,会议室有投影仪,小李把自己要做的3件事工作都准备好了,也连接投影仪进行了测试,一切准备OK,正准备下班。这时候小李的直接领导王总对小李提出了工作要求,投屏切换过程不显示过程,也就是说,3项内容的播放和演示时,中间切换必须连贯,不能让领导看到电脑桌面和切换过程。
这可把小李给难住了,他从中学开始学计算机的时候,老师就教他用Alt+Tab快捷键实现活动窗口的切换显示,从没见谁还能提出这么变态的要求,但吐槽归吐槽,工作来之不易,王总的要求轻易拒绝不得。小李想来想去,决定向自己的好朋友“八两”求助。八两听完小李的哭诉,已基本了解到这个需求,决定为小李量身定做一款软件工具,帮助小李保住这份饭碗。
通过Windows自带的切换窗口热键(Alt+Tab或者Win+Tab),显然是达不到这样的要求,王总所谓的无缝切换,必然是“kua”的一下,投屏界面直接跳变到目标窗口了,也就是说,要提前把3个软件都打开,根据演示汇报的需要,一步到位直接切换到指定窗口。要实现这样的效果,靠人工点击是不行的,我们可以想到,如果能为任务栏上每个打开的窗口都赋予一个专属的系统热键,由热键来切换指定的窗口显示,比如小李的桌面上打开了3个软件,通过使用“Ctrl+Num1”“Ctrl+Num2”“Ctrl+Num3”这3中热键,实现3个软件的快速切换显示,岂不是就大功告成了?这个思路理论上非常行得通,既然行得通,技术上就可以实现。
另外,Windows强大的API库,可以实现对窗口的灵活操作,这也为我们开发这样一款用于切换窗口的工具软件提供了有力支撑。
需要用到的API函数包括GetDesktopWindow、GetWindow等。
GetDesktopWindow函数可以获取到桌面窗口句柄,不带参数,直接返回句柄值。
GetWindow函数可以获取与指定窗口有特定关系的窗口句柄,其中:hWnd参数是要指定的窗口句柄;uCmd参数用于说明要获得的窗口与指定窗口之间的关系,该参数取值为0-6,各值具体含义可以自行百度。
API函数的声明形式如下:
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
代码逻辑是:
主要代码如下:
List list = new List();
IntPtr desktopWindow = GetDesktopWindow();
IntPtr window = GetWindow(desktopWindow, 5);
while (window != IntPtr.Zero)
{
//这里根据需要,还需进一步判断window是否可见等
//符合要求的window,加入到结果清单中
list.Add(window);
//继续执行,获取同级下一个窗口
window = WinAPI.GetWindow(window, 2);
}
需要用的的API函数包括GetWindowPlacement、ShowWindow、SetFocus、SetForegroundWindow等。
GetWindowPlacement函数可以获取指定窗口的显示状态。
ShowWindow函数可以设置指定窗口的显示状态。
SetFocus函数可以设置指定窗口获得焦点。
SetForegroundWindow函数可以将指定窗口带到最前端显示。
API函数的声明形式如下:
[DllImport("user32.dll")]
public static extern bool GetWindowPlacement(IntPtr hWnd, ref Windowplacement lpwndpl);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
[DllImport("user32.dll")]
public static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32")]
public static extern int SetForegroundWindow(IntPtr hwnd);
public struct Windowplacement
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
}
代码逻辑是;
主要代码如下:
//handle是要切换至的指定窗口句柄
IntPtr handle = list[0];
Windowplacement placement = new Windowplacement();
GetWindowPlacement(handle, ref placement);
if (placement.showCmd == 2)
{
ShowWindow(handle, 9);
}
SetFocus(handle);
SetForegroundWindow(handle);
实现全局热键有多种方式,比如系统热键、键盘钩子等,为了避免热键冲突以及实现更灵活的代码,我们采用键盘钩子程序来实现热键效果。
需要用到的API函数包括GetModuleHandle、SetWindowsHookEx、CallNextHookEx、UnhookWindowsHookEx等。
GetModuleHandle用于获取一个应用程序或动态链接库的模块句柄。
SetWindowsHookEx实现将一个应用程序定义的挂钩处理过程安装到挂钩链中去。
CallNextHookEx用于将钩子信息传递到当前钩子链中的下一个子程
UnhookWindowsHookEx用于卸载钩子处理程序。
API函数的声明形式如下:
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll")]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(int idHook);
[StructLayout(LayoutKind.Sequential)]
public class KeyBoardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
代码逻辑是:
主要代码如下:
public int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
//这是键盘钩子的处理函数
if (nCode >= 0)
{
var kbh = (KeyBoardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyBoardHookStruct));
//这里根据需要进一步判断键盘的按下和弹起情况
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
//注册钩子
var handle = GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName);
var hHook = SetWindowsHookEx(13, new HookProc(HookCallback), handle , 0);
//卸载钩子
UnhookWindowsHookEx(hHook);
经过1天的开发和调试优化,八两将软件发送给小李,小李拿到软件后,立即兴奋地到公司会议室测试,结果真如他所希望的那样,实现了多个程序之间的无缝切换,小李高兴地直呼666!最终软件成品的界面如下:
软件的下载地址为https://download.csdn.net/download/yuechi01/88634645