概述
在程式中MessageBox彈出的對話框,用于向使用者展示消息,這是一個模式視窗,可阻止應用程式中的其他操作,直到使用者将其關閉。但是有時候在自動化程式中,如果彈出對話框,程式将會中斷,等待人工的幹預,這是一個非常不好的互動體驗,如果程式能夠自動幫我們點選其中一個按鈕,讓對話框消失,該有多好。
原理
通過對話框的标題查找對話框,擷取對話框的句柄,然後對話框發送指令。
涉及知識點
- MessageBox 顯示消息視窗(也稱為對話框)向使用者展示消息。這是一個模式視窗,可阻止應用程式中的其他操作,直到使用者将其關閉。System.Windows.Forms.MessageBox可包含通知并訓示使用者的文本、按鈕和符号。
- Thread 建立和控制線程,設定其優先級并擷取其狀态。本例子主要建立一個線程,查找彈出的視窗。
- WIN32 API 也就是Microsoft Windows 32位平台的應用程式程式設計接口。每一個服務,就是一個函數,用于和Windows進行互動。
- MessageBoxButtons 是一個Enum,表示對話框上顯示哪些按鈕。
- PostMessage 是Windows API(應用程式接口)中的一個常用函數,用于将一條消息放入到消息隊列中。消息隊列裡的消息通過調用GetMessage和PeekMessage取得。
程式運作效果
核心代碼

1 using System;
2 using System.Collections.Generic;
3 using System.Drawing;
4 using System.Linq;
5 using System.Runtime.InteropServices;
6 using System.Text;
7 using System.Threading;
8 using System.Threading.Tasks;
9
10 namespace MessageBoxTest
11 {
12 /// <summary>
13 /// 作者:Alan.hsiang
14 /// 日期:2018-04-18
15 /// 描述:通過WinAPI進行查找視窗,并對視窗進行操作
16 /// </summary>
17 public class MessageBoxHelper
18 {
19 /// <summary>
20 /// 查找視窗
21 /// </summary>
22 /// <param name="hwnd">視窗句柄</param>
23 /// <param name="title">視窗标題</param>
24 /// <returns></returns>
25 [DllImport("user32.dll", CharSet = CharSet.Auto)]
26 static extern IntPtr FindWindow(IntPtr hwnd, string title);
27
28 /// <summary>
29 /// 移動視窗
30 /// </summary>
31 /// <param name="hwnd">視窗句柄</param>
32 /// <param name="x">起始位置X</param>
33 /// <param name="y">起始位置Y</param>
34 /// <param name="nWidth">視窗寬度</param>
35 /// <param name="nHeight">視窗高度</param>
36 /// <param name="rePaint">是否重繪</param>
37 [DllImport("user32.dll", CharSet = CharSet.Auto)]
38 static extern void MoveWindow(IntPtr hwnd, int x, int y, int nWidth, int nHeight, bool rePaint);
39
40 /// <summary>
41 /// 擷取視窗矩形
42 /// </summary>
43 /// <param name="hwnd">視窗句柄</param>
44 /// <param name="rect"></param>
45 /// <returns></returns>
46 [DllImport("user32.dll", CharSet = CharSet.Auto)]
47 static extern bool GetWindowRect(IntPtr hwnd, out Rectangle rect);
48
49 /// <summary>
50 /// 向視窗發送資訊
51 /// </summary>
52 /// <param name="hwnd">視窗句柄</param>
53 /// <param name="msg">資訊</param>
54 /// <param name="wParam">高位元組</param>
55 /// <param name="lParam">低位元組</param>
56 /// <returns></returns>
57 [DllImport("user32.dll", CharSet = CharSet.Auto)]
58 static extern int PostMessage(IntPtr hwnd, int msg, uint wParam, uint lParam);
59
60 public const int WM_CLOSE = 0x10; //關閉指令
61
62 public const int WM_KEYDOWN = 0x0100;//按下鍵
63
64 public const int WM_KEYUP = 0x0101;//按鍵起來
65
66 public const int VK_RETURN = 0x0D;//Enter鍵
67
68 public static bool IsWorking = false;
69
70 /// <summary>
71 /// 對話框标題
72 /// </summary>
73 public static string[] titles = new string[4] { "請選擇", "提示", "錯誤", "警告" };
74
75 /// <summary>
76 /// 查找和移動視窗
77 /// </summary>
78 /// <param name="title">視窗标題</param>
79 /// <param name="x">起始位置X</param>
80 /// <param name="y">起始位置Y</param>
81 public static void FindAndMoveWindow(string title, int x, int y)
82 {
83 Thread t = new Thread(() =>
84 {
85 IntPtr msgBox = IntPtr.Zero;
86 while ((msgBox = FindWindow(IntPtr.Zero, title)) == IntPtr.Zero) ;
87 Rectangle r = new Rectangle();
88 GetWindowRect(msgBox, out r);
89 MoveWindow(msgBox, x, y, r.Width - r.X, r.Height - r.Y, true);
90 });
91 t.Start();
92 }
93
94 /// <summary>
95 /// 查找和關閉視窗
96 /// </summary>
97 /// <param name="title">标題</param>
98 private static void FindAndKillWindow(string title)
99 {
100 IntPtr ptr = FindWindow(IntPtr.Zero, title);
101 if (ptr != IntPtr.Zero)
102 {
103 int ret = PostMessage(ptr, WM_CLOSE, 0, 0);
104 Thread.Sleep(1000);
105 ptr = FindWindow(IntPtr.Zero, title);
106 if (ptr != IntPtr.Zero)
107 {
108 PostMessage(ptr, WM_KEYDOWN, VK_RETURN, 0);
109 PostMessage(ptr, WM_KEYUP, VK_RETURN, 0);
110 }
111 }
112 }
113
114 /// <summary>
115 /// 查找和關閉視窗
116 /// </summary>
117 public static void FindAndKillWindow()
118 {
119 Thread t = new Thread(() =>
120 {
121 while (IsWorking)
122 {
123 //按标題查找
124 foreach (string title in titles)
125 {
126 FindAndKillWindow(title);
127 }
128 Thread.Sleep(3000);
129 }
130 });
131
132 t.Start();
133 }
134 }
135 }
View Code
原部落格位址: