• 『片段』Win32 模式窗体 消息路由


    需求背景

    近来,有个需求: 和一个外部程序对接。

    具体是,我这边 主程序用 Process 启动外部程序。外部程序启动后,我这边调用的窗体不允许再进行任何操作。

    当外部程序关闭时,外部程序会向我这边的主程序 返回结果。

     

    传统做法

    1 Process process = Process.Start("外部程序.exe", "-外部程序的参数");
    2 process.Exited += (sender, e)=> { MessageBox.Show("外部程序关闭"); }
    3 process.WaitForExit();  //主程序等待 外部程序执行结束

    以往,三行代码 就能搞定。 但是有个问题: process.WaitForExit(); 这段代码,将会导致 UI线程 假死。

    这时,你在UI线程上,多点几次鼠标,系统就会弹窗提示: 有个程序未响应,是否将其关闭。 (优化过的Ghost系统 甚至会直接将你的 主程序关闭)

    —— 这种用户体验 自然是 非常不好的。

     

    问题来了

    点击主窗体,如何让外部程序弹出模式窗体(非常难表达)

    —— 就是说:启动外部程序后,如果我点击 主程序,这时候 外部程序 主动弹出,盖过 主程序,就像 模式窗体 一样。

     

    传统的模式窗体

    如下图,Form1 以 模式窗体 打开 Form2, 这时候再点击 Form1 —— Form2 会自动弹5次。

     

    直接给出实现后的效果

    我们假设,这个外部程序是 Visual Studio —— 我们点击 Form1,结果 Visual Studio 弹出来 闪了5下 (就像模式窗体一样)。

     

    代码实现解释

    效果图 已经在上面了 —— 一种很小众的需求。

    实现原理:Form1 以模式窗体 打开 Form2

    当 点击 Form1 时,理论上 Form2 应该 闪动5下。

    但是,我们改写了 Form2 的消息机制,我们将 闪动5下 的 消息,路由给了 Visual Studio。

    于是,最终的效果就是,我点击 Form1,结果 外部程序 Visual Studio 却很像模式窗体 一样的 闪了5下。

    关键代码就在 Form2

     1     private DateTime m_DialogTime = DateTime.Now;
     2     protected override void WndProc(ref Message m)
     3     {
     4         //base.WndProc(ref m);
     5         //return; 
     6 
     7 
     8         if (m.Msg == Win32Msg.WM_WINDOWPOSCHANGING)
     9         {
    10             //WM_WINDOWPOSCHANGING 消息之后, 1秒以内的 WM_NCACTIVATE 的消息, 才会进行消息路由
    11             //为什么要控制在 1秒 内: 
    12             //你可以尝试一下, 去掉 m_DialogTime 相关代码, 然后将 消息路由的窗体 最小化 
    13             //—— 这时候, m_DialogTime 参数导致的区别就显现了
    14             m_DialogTime = DateTime.Now; 
    15             base.WndProc(ref m); 
    16             return; 
    17         }
    18 
    19 
    20         if (m.Msg == Win32Msg.WM_NCACTIVATE)
    21         {
    22             if ((DateTime.Now - m_DialogTime).TotalMilliseconds > 1000) return;
    23             //IntPtr intPtr = FindForm3Handle();
    24             IntPtr intPtr = FindOuterFormHandle();  //查找外部程序的 句柄
    25             if (intPtr != IntPtr.Zero)
    26             {
    27                 if (!Win32API.IsZoomed(intPtr)) Win32API.ShowWindow(intPtr, 1);
    28                 Win32API.SendMessage(intPtr, (uint)m.Msg, (int)m.WParam, (int)m.LParam);
    29                 Win32API.SetWindowPos(intPtr, IntPtr.Zero, 0, 0, 0, 0, (uint)(SWPFlags.SWP_NOMOVE | SWPFlags.SWP_NOSIZE));
    30                 return;
    31             }
    32         }
    33         base.WndProc(ref m);
    34     }

    有人质疑了

    以上效果,想必有人质疑了:

    问题1:

    问: 主程序是 Form1, 用户点击的也是 Form1 —— 请问:Form2 有什么用?

    答: Form2 就是 用来路由消息的,把 原本 Form2 的消息 路由给 外部程序。

    问题2:

    问: Form2 只是提供消息的?那为什么不直接模拟 闪动5次的 消息? 为什么不删掉 Form2?

    答:

    1. 闪动5次,Spy++ 拦截到的消息有 60多个,用代码模拟消息 —— 至少就是 100多行代码,还不一定正确。

    2. Form2 确实可以删除,也确实可以用 代码来模拟消息 —— 这个设想是可行的,就是代码量大,麻烦而已。

    问题3:

    问: 多出来的 Form2 影响用户体验

    答:

    1. 你可以把 Form2 调整为 1x1 像素 —— 然后把这个 Form2 藏起来。

    2. 或者,你可以把 Form2 做成一个 半透明的 提示窗体,其实也挺美观的。

  • 相关阅读:
    Java线程:新特征-阻塞栈
    Java线程:新特征-阻塞队列
    Java线程:新特征-信号量
    Java线程:新特征-锁(下)
    Java线程:新特征-锁(上)
    Java线程:新特征-有返回值的线程
    Java线程:新特征-线程池
    Java线程:volatile关键字
    Java线程:并发协作-死锁
    通过Roslyn动态生成程序集
  • 原文地址:https://www.cnblogs.com/shuxiaolong/p/Win32_Dialog_Route.html
Copyright © 2020-2023  润新知