需求背景

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

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

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

传统做法

 Process process = Process.Start("外部程序.exe", "-外部程序的参数");
process.Exited += (sender, e)=> { MessageBox.Show("外部程序关闭"); }
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

     private DateTime m_DialogTime = DateTime.Now;
protected override void WndProc(ref Message m)
{
//base.WndProc(ref m);
//return; if (m.Msg == Win32Msg.WM_WINDOWPOSCHANGING)
{
//WM_WINDOWPOSCHANGING 消息之后, 1秒以内的 WM_NCACTIVATE 的消息, 才会进行消息路由
//为什么要控制在 1秒 内:
//你可以尝试一下, 去掉 m_DialogTime 相关代码, 然后将 消息路由的窗体 最小化
//—— 这时候, m_DialogTime 参数导致的区别就显现了
m_DialogTime = DateTime.Now;
base.WndProc(ref m);
return;
} if (m.Msg == Win32Msg.WM_NCACTIVATE)
{
if ((DateTime.Now - m_DialogTime).TotalMilliseconds > ) return;
//IntPtr intPtr = FindForm3Handle();
IntPtr intPtr = FindOuterFormHandle(); //查找外部程序的 句柄
if (intPtr != IntPtr.Zero)
{
if (!Win32API.IsZoomed(intPtr)) Win32API.ShowWindow(intPtr, );
Win32API.SendMessage(intPtr, (uint)m.Msg, (int)m.WParam, (int)m.LParam);
Win32API.SetWindowPos(intPtr, IntPtr.Zero, , , , , (uint)(SWPFlags.SWP_NOMOVE | SWPFlags.SWP_NOSIZE));
return;
}
}
base.WndProc(ref m);
}

有人质疑了

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

问题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 做成一个 半透明的 提示窗体,其实也挺美观的。

『片段』Win32 模式窗体 消息路由的更多相关文章

  1. 『片段』OracleHelper (支持 多条SQL语句)

    C# 调用 Oracle 是如此尴尬 >System.Data.OracleClient.dll —— .Net 自带的 已经 过时作废. >要链接 Oracle 服务器,必须在 本机安装 ...

  2. 『片段』C# DateTime 时间相减 和 时区的关系

    本文只是基础代码片段,直接先写 结论: C# DateTime 时间相减 —— 和 时区无关,只和时间值有关. 运行结果: 测试代码: using System; using System.Colle ...

  3. 『片段』ShellHelper 控制台程序 的 程序调用(支持输入命令得到返回字符串输出)

    背景: > 之前做 OGG 时,被 OGG的配置 恶心到了.(OGG是啥,这里就不解释了) > 总之就是一个 控制台程序,总是得手动执行一堆命令,每次都得输入 —— 实在是打字打累了. & ...

  4. 『TensorFlow』分布式训练_其二_单机多GPU并行&GPU模式设定

    建议比对『MXNet』第七弹_多GPU并行程序设计 一.tensorflow GPU设置 GPU指定占用 gpu_options = tf.GPUOptions(per_process_gpu_mem ...

  5. 『设计』Laura.Compute 设计思路

    前言: 前一篇文章 <『开源』也顺手写一个 科学计算器:重磅开源> ,继 Laura.Compute 算法开源之后,有 博客园 园友 希望公开一下 Laura.Compute算法 的 设计 ...

  6. iOS 多线程:『RunLoop』详尽总结

    1. RunLoop 简介 1.1 什么是 RunLoop? 可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停 ...

  7. 『AngularJS』$location 服务

    项目中关于 $location的用法 简介 $location服务解析在浏览器地址栏中的URL(基于window.location)并且让URL在你的应用中可用.改变在地址栏中的URL会作用到$loc ...

  8. 『开源』Slithice 2013 服务器集群 设计和源码

    相关介绍文章: <『设计』Slithice 分布式架构设计-支持一体式开发,分布式发布> <『集群』001 Slithice 服务器集群 概述> <『集群』002 Sli ...

  9. 『设计』Slithice 分布式架构设计-支持一体式开发,分布式发布

    项目原因: 参与过各种 分布式项目,有 Socket,Remoting,WCF,当然还有最常用的可以跨平台的 WebService. 分布式编码的时间浪费: 但是,无一例外的,开发分布式程序的开发遵循 ...

随机推荐

  1. remove the nth node from the end of the list

    problem description: remove the nth node from the end of the list for example: given: 1->2->3 ...

  2. mongodb的安装使用,window和centos环境

    官网:https://www.mongodb.org/downloads 版本:最终稳定版 (mongodb-win32-x86_64-2008plus-ssl-3.2.6-signed.msi 绿色 ...

  3. hadoop 2.x 简单实现wordCount

    简单实现hadoop程序,包括:hadoop2.x的实现写法 import org.apache.hadoop.conf.Configured; import org.apache.hadoop.fs ...

  4. python爬虫入门(九)Scrapy框架之数据库保存

    豆瓣电影TOP 250爬取-->>>数据保存到MongoDB 豆瓣电影TOP 250网址 要求: 1.爬取豆瓣top 250电影名字.演员列表.评分和简介 2.设置随机UserAge ...

  5. Flask入门之SQLAlchemy数据库连接操作(第15讲)

    一.库安装 Flask-SQLAlchemy 2 SQLAlchemy 1.0.8 二.进入venv 三.切换到项目Sample\ 文件夹,进入manager.py 的shell python man ...

  6. Ubuntu16+pinpoint环境搭建

    最近研究了pinpoint,稍后放上环境搭建教程,建议想学习搭建的同学记得参考pinpointGitHub

  7. H5之画布canvas小记,以及通过画布实现原子无规则运动

    我们知道html在h5出之前就仅仅只是一个标签,一个标记,语义化并不强,后来新增的标签如video,audio都是语义化更强(让人一看就懂是什么东西,反正我是这么理解的,一个div不代表着什么),本身 ...

  8. NIO之FileChannel类的理解和使用

    文章链接:http://blog.csdn.net/qq_16628781/article/details/70532307 知识点: FileChannel类及方法理解: 普通输入输出流复制文件: ...

  9. Linux用户登录日志查询

    # 1 utmp.wtmp.btmp文件 Linux用户登录信息放在三个文件中: 1 /var/run/utmp:记录当前正在登录系统的用户信息,默认由who和w记录当前登录用户的信息,uptime记 ...

  10. 深入JVM分析spring-boot应用hibernate-validator

    问题 可重现的Demo代码:demo.zip 最近排查一个spring boot应用抛出hibernate.validator NoClassDefFoundError的问题,异常信息如下: Caus ...