『片段』Win32 模式窗体 消息路由
需求背景
近来,有个需求: 和一个外部程序对接。
具体是,我这边 主程序用 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?
答:
闪动5次,Spy++ 拦截到的消息有 60多个,用代码模拟消息 —— 至少就是 100多行代码,还不一定正确。
Form2 确实可以删除,也确实可以用 代码来模拟消息 —— 这个设想是可行的,就是代码量大,麻烦而已。
问题3:
问: 多出来的 Form2 影响用户体验
答:
你可以把 Form2 调整为 1x1 像素 —— 然后把这个 Form2 藏起来。
或者,你可以把 Form2 做成一个 半透明的 提示窗体,其实也挺美观的。

『片段』Win32 模式窗体 消息路由的更多相关文章
- 『片段』OracleHelper (支持 多条SQL语句)
C# 调用 Oracle 是如此尴尬 >System.Data.OracleClient.dll —— .Net 自带的 已经 过时作废. >要链接 Oracle 服务器,必须在 本机安装 ...
- 『片段』C# DateTime 时间相减 和 时区的关系
本文只是基础代码片段,直接先写 结论: C# DateTime 时间相减 —— 和 时区无关,只和时间值有关. 运行结果: 测试代码: using System; using System.Colle ...
- 『片段』ShellHelper 控制台程序 的 程序调用(支持输入命令得到返回字符串输出)
背景: > 之前做 OGG 时,被 OGG的配置 恶心到了.(OGG是啥,这里就不解释了) > 总之就是一个 控制台程序,总是得手动执行一堆命令,每次都得输入 —— 实在是打字打累了. & ...
- 『TensorFlow』分布式训练_其二_单机多GPU并行&GPU模式设定
建议比对『MXNet』第七弹_多GPU并行程序设计 一.tensorflow GPU设置 GPU指定占用 gpu_options = tf.GPUOptions(per_process_gpu_mem ...
- 『设计』Laura.Compute 设计思路
前言: 前一篇文章 <『开源』也顺手写一个 科学计算器:重磅开源> ,继 Laura.Compute 算法开源之后,有 博客园 园友 希望公开一下 Laura.Compute算法 的 设计 ...
- iOS 多线程:『RunLoop』详尽总结
1. RunLoop 简介 1.1 什么是 RunLoop? 可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停 ...
- 『AngularJS』$location 服务
项目中关于 $location的用法 简介 $location服务解析在浏览器地址栏中的URL(基于window.location)并且让URL在你的应用中可用.改变在地址栏中的URL会作用到$loc ...
- 『开源』Slithice 2013 服务器集群 设计和源码
相关介绍文章: <『设计』Slithice 分布式架构设计-支持一体式开发,分布式发布> <『集群』001 Slithice 服务器集群 概述> <『集群』002 Sli ...
- 『设计』Slithice 分布式架构设计-支持一体式开发,分布式发布
项目原因: 参与过各种 分布式项目,有 Socket,Remoting,WCF,当然还有最常用的可以跨平台的 WebService. 分布式编码的时间浪费: 但是,无一例外的,开发分布式程序的开发遵循 ...
随机推荐
- 微信小程序-框架详解(1)
配置 -app.json文件对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.tab等 { "pages": [ //决定页面文件的路径 "pag ...
- 微信小程序入门三实战
微信小应用借鉴了很多web的理念,但是其与传统的webApp.微信公共号这些BS架构不同,他是CS架构,是客户端的程序 小程序开发实战--豆瓣电影 项目配置 -在app.jsop中进行简单配置 --n ...
- Key-Value Store Indexer(Lily HBase Indexer) 小型采坑
环境: Cloudera Express 5.12.1 JDK 1.8.0_92 CentOS 7 步骤1:数据导入到Hbase中(非正题,跳过) hbase中表为allDoc,两个Family:fu ...
- 适合Python的5大练手项目, 你练了么?
在练手项目的选择上,还存在疑问?不知道要从哪种项目先下手? 首先有两点建议: 最好不要写太应用的程序练手,要思考什么更像是知识,老只会写写爬虫是无用的,但是完全不写也不行. 对于练手的程序,要注意简化 ...
- ML.NET指南
ML.NET是一个免费的.开源和跨平台的机器学习框架,使您能够构建定制的机器学习解决方案,并将它们集成到您的. net应用程序.本指南提供了许多关于与ML.NET合作资源. 关于ML.NET的更多信息 ...
- 设计一个卖不同种类车的4s店
# 定义奔驰车类 class BenchiCar(object): # 定义车的方法 def move(self): print('---奔驰车在移动---') def stop(self): pri ...
- Cookie、Session、jsp、EL、JSTL
会话技术 Cookie Session 从访问一个站点,到关闭不继续访问 称为一次会话过程.会话技术就是记录本次会话中客户端的状态与数据的. 会话技术分为cookie.session. cooki ...
- Oracle数据库表分区
一.Oracle数据库表分区概念和理解 1.1.已经存在的表没有方法可以直接转化为分区表. 1.2.不在分区字段上建立分区索引,在别的字段上建立索引相当于全局索引.效率 ...
- 进阶-Redis 知识梳理
redis介绍 1.什么是NoSQL NoSQL(统称),泛指非关系型的数据库,NoSQL即Not-Only SQL,它可以作为关系型数据库的良好补充. 2.NoSQL数据库的四大分类如下: 键值(K ...
- git - 简明指南
助你入门 git 的简明指南,木有高深内容 ;) 作者:罗杰·杜德勒 感谢:@tfnico, @fhd 和 Namics如有纰漏,请在 github 提报问题 安装 下载 git OSX 版 下载 g ...