投屏演示时,无缝切换显示已经打开的多个窗口(C#编程实现)

1.需求诞生

小李就职于一家国有企业的子公司,最近承担起了一项投屏演示汇报的任务,上级领导要来视察工作,小李需要干3件事:一是播放一段公司的宣传片;二是利用PPT汇报公司业绩情况;三是演示介绍公司最新研发的某软件系统。公司有会议室,会议室有投影仪,小李把自己要做的3件事工作都准备好了,也连接投影仪进行了测试,一切准备OK,正准备下班。这时候小李的直接领导王总对小李提出了工作要求,投屏切换过程不显示过程,也就是说,3项内容的播放和演示时,中间切换必须连贯,不能让领导看到电脑桌面和切换过程。

这可把小李给难住了,他从中学开始学计算机的时候,老师就教他用Alt+Tab快捷键实现活动窗口的切换显示,从没见谁还能提出这么变态的要求,但吐槽归吐槽,工作来之不易,王总的要求轻易拒绝不得。小李想来想去,决定向自己的好朋友“八两”求助。八两听完小李的哭诉,已基本了解到这个需求,决定为小李量身定做一款软件工具,帮助小李保住这份饭碗。

2.实现思路

通过Windows自带的切换窗口热键(Alt+Tab或者Win+Tab),显然是达不到这样的要求,王总所谓的无缝切换,必然是“kua”的一下,投屏界面直接跳变到目标窗口了,也就是说,要提前把3个软件都打开,根据演示汇报的需要,一步到位直接切换到指定窗口。要实现这样的效果,靠人工点击是不行的,我们可以想到,如果能为任务栏上每个打开的窗口都赋予一个专属的系统热键,由热键来切换指定的窗口显示,比如小李的桌面上打开了3个软件,通过使用“Ctrl+Num1”“Ctrl+Num2”“Ctrl+Num3”这3中热键,实现3个软件的快速切换显示,岂不是就大功告成了?这个思路理论上非常行得通,既然行得通,技术上就可以实现。

另外,Windows强大的API库,可以实现对窗口的灵活操作,这也为我们开发这样一款用于切换窗口的工具软件提供了有力支撑。

3.核心代码

3.1 读取窗口列表

需要用到的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);

 代码逻辑是:

  1. 先使用GetDesktopWindows获取到桌面窗口句柄;
  2. 根据桌面句柄获取桌面内第一个窗口句柄;
  3. 根据第一个窗口句柄循环获取其他同级窗口句柄。

主要代码如下:

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

 3.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;
}

 代码逻辑是;

  1. 首先获取指定窗口的当前显示状态;
  2. 如果指定窗口处于最小化,则恢复最小化之前的状态;
  3. 赋予指定窗口输入焦点;
  4. 将指定窗口设置为显示最前端。

主要代码如下:

//handle是要切换至的指定窗口句柄
IntPtr handle = list[0];
Windowplacement placement = new Windowplacement();
GetWindowPlacement(handle, ref placement);
if (placement.showCmd == 2)
{
    ShowWindow(handle, 9);
}
SetFocus(handle);
SetForegroundWindow(handle);

3.3 实现全局热键

实现全局热键有多种方式,比如系统热键、键盘钩子等,为了避免热键冲突以及实现更灵活的代码,我们采用键盘钩子程序来实现热键效果。

需要用到的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;
}

 代码逻辑是:

  1. 定义一个钩子处理函数HookCallback;
  2. 先获取程序自身的模块句柄;
  3. 注册键盘钩子程序,实现系统全局的按键监视;
  4. 程序退出时,注意卸载键盘钩子程序。

主要代码如下:

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);

4.完整程序

经过1天的开发和调试优化,八两将软件发送给小李,小李拿到软件后,立即兴奋地到公司会议室测试,结果真如他所希望的那样,实现了多个程序之间的无缝切换,小李高兴地直呼666!最终软件成品的界面如下:

投屏演示时,无缝切换显示已经打开的多个窗口(C#编程实现)_第1张图片

软件的下载地址为https://download.csdn.net/download/yuechi01/88634645 

你可能感兴趣的:(技术文章,c#,大屏端)