|
1 const EVENT_OBJECT_SHOW = 0x8002; |
|
2 const EVENT_OBJECT_HIDE = 0x8003; |
|
3 const WINEVENT_OUTOFCONTEXT = 0; |
|
4 const WINEVENT_SKIPOWNPROCESS = 2; |
|
5 const QS_ALLINPUT = 0x04FF; |
|
6 const INFINITE = 0xFFFFFFFF; |
|
7 const WAIT_OBJECT_0 = 0; |
|
8 const WAIT_TIMEOUT = 258; |
|
9 const PM_NOREMOVE = 0; |
|
10 |
|
11 function DialogWatcher(titleText, onDialogStart, onDialogEnd) { |
|
12 this.titleText = titleText; |
|
13 this.onDialogStart = onDialogStart; |
|
14 this.onDialogEnd = onDialogEnd; |
|
15 } |
|
16 |
|
17 DialogWatcher.prototype.init = function() { |
|
18 this.hwnd = undefined; |
|
19 if (!this.user32) { |
|
20 this.user32 = ctypes.open("user32.dll"); |
|
21 } |
|
22 if (!this.findWindow) { |
|
23 this.findWindow = user32.declare("FindWindowW", |
|
24 ctypes.winapi_abi, |
|
25 ctypes.uintptr_t, |
|
26 ctypes.jschar.ptr, |
|
27 ctypes.jschar.ptr); |
|
28 } |
|
29 if (!this.winEventProcType) { |
|
30 this.winEventProcType = ctypes.FunctionType(ctypes.stdcall_abi, |
|
31 ctypes.void_t, |
|
32 [ctypes.uintptr_t, |
|
33 ctypes.uint32_t, |
|
34 ctypes.uintptr_t, |
|
35 ctypes.long, |
|
36 ctypes.long, |
|
37 ctypes.uint32_t, |
|
38 ctypes.uint32_t]).ptr; |
|
39 } |
|
40 if (!this.setWinEventHook) { |
|
41 this.setWinEventHook = user32.declare("SetWinEventHook", |
|
42 ctypes.winapi_abi, |
|
43 ctypes.uintptr_t, |
|
44 ctypes.uint32_t, |
|
45 ctypes.uint32_t, |
|
46 ctypes.uintptr_t, |
|
47 this.winEventProcType, |
|
48 ctypes.uint32_t, |
|
49 ctypes.uint32_t, |
|
50 ctypes.uint32_t); |
|
51 } |
|
52 if (!this.unhookWinEvent) { |
|
53 this.unhookWinEvent = user32.declare("UnhookWinEvent", |
|
54 ctypes.winapi_abi, |
|
55 ctypes.int, |
|
56 ctypes.uintptr_t); |
|
57 } |
|
58 if (!this.pointType) { |
|
59 this.pointType = ctypes.StructType("tagPOINT", |
|
60 [ { "x": ctypes.long }, |
|
61 { "y": ctypes.long } ] ); |
|
62 } |
|
63 if (!this.msgType) { |
|
64 this.msgType = ctypes.StructType("tagMSG", |
|
65 [ { "hwnd": ctypes.uintptr_t }, |
|
66 { "message": ctypes.uint32_t }, |
|
67 { "wParam": ctypes.uintptr_t }, |
|
68 { "lParam": ctypes.intptr_t }, |
|
69 { "time": ctypes.uint32_t }, |
|
70 { "pt": this.pointType } ] ); |
|
71 } |
|
72 if (!this.peekMessage) { |
|
73 this.peekMessage = user32.declare("PeekMessageW", |
|
74 ctypes.winapi_abi, |
|
75 ctypes.int, |
|
76 this.msgType.ptr, |
|
77 ctypes.uintptr_t, |
|
78 ctypes.uint32_t, |
|
79 ctypes.uint32_t, |
|
80 ctypes.uint32_t); |
|
81 } |
|
82 if (!this.msgWaitForMultipleObjects) { |
|
83 this.msgWaitForMultipleObjects = user32.declare("MsgWaitForMultipleObjects", |
|
84 ctypes.winapi_abi, |
|
85 ctypes.uint32_t, |
|
86 ctypes.uint32_t, |
|
87 ctypes.uintptr_t.ptr, |
|
88 ctypes.int, |
|
89 ctypes.uint32_t, |
|
90 ctypes.uint32_t); |
|
91 } |
|
92 if (!this.getWindowTextW) { |
|
93 this.getWindowTextW = user32.declare("GetWindowTextW", |
|
94 ctypes.winapi_abi, |
|
95 ctypes.int, |
|
96 ctypes.uintptr_t, |
|
97 ctypes.jschar.ptr, |
|
98 ctypes.int); |
|
99 } |
|
100 if (!this.messageBox) { |
|
101 // Handy for debugging this code |
|
102 this.messageBox = user32.declare("MessageBoxW", |
|
103 ctypes.winapi_abi, |
|
104 ctypes.int, |
|
105 ctypes.uintptr_t, |
|
106 ctypes.jschar.ptr, |
|
107 ctypes.jschar.ptr, |
|
108 ctypes.uint32_t); |
|
109 } |
|
110 }; |
|
111 |
|
112 DialogWatcher.prototype.getWindowText = function(hwnd) { |
|
113 var bufType = ctypes.ArrayType(ctypes.jschar); |
|
114 var buffer = new bufType(256); |
|
115 |
|
116 if (this.getWindowTextW(hwnd, buffer, buffer.length)) { |
|
117 return buffer.readString(); |
|
118 } |
|
119 }; |
|
120 |
|
121 DialogWatcher.prototype.processWindowEvents = function(timeout) { |
|
122 var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) { |
|
123 var nhwnd = Number(hwnd) |
|
124 if (event == EVENT_OBJECT_SHOW) { |
|
125 if (nhwnd == self.hwnd) { |
|
126 // We've already picked up this event via FindWindow |
|
127 return; |
|
128 } |
|
129 var windowText = self.getWindowText(hwnd); |
|
130 if (windowText == self.titleText && self.onDialogStart) { |
|
131 self.hwnd = nhwnd; |
|
132 self.onDialogStart(nhwnd); |
|
133 } |
|
134 } else if (event == EVENT_OBJECT_HIDE && nhwnd == self.hwnd && self.onDialogEnd) { |
|
135 self.onDialogEnd(); |
|
136 self.hwnd = null; |
|
137 } |
|
138 }; |
|
139 var self = this; |
|
140 var callback = this.winEventProcType(function(hook, event, hwnd, idObject, |
|
141 idChild, dwEventThread, |
|
142 dwmsEventTime) { |
|
143 onWinEvent(self, hook, event, hwnd, idObject, idChild, dwEventThread, |
|
144 dwmsEventTime); |
|
145 } ); |
|
146 var hook = this.setWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, |
|
147 0, callback, 0, 0, |
|
148 WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); |
|
149 if (!hook) { |
|
150 return; |
|
151 } |
|
152 // Check if the window is already showing |
|
153 var hwnd = this.findWindow(null, this.titleText); |
|
154 if (hwnd && hwnd > 0) { |
|
155 this.hwnd = Number(hwnd); |
|
156 if (this.onDialogStart) { |
|
157 this.onDialogStart(this.hwnd); |
|
158 } |
|
159 } |
|
160 |
|
161 if (!timeout) { |
|
162 timeout = INFINITE; |
|
163 } |
|
164 |
|
165 var waitStatus = WAIT_OBJECT_0; |
|
166 var expectingStart = this.onDialogStart && this.hwnd === undefined; |
|
167 var startWaitTime = Date.now(); |
|
168 while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) { |
|
169 waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ? |
|
170 INFINITE : timeout, 0); |
|
171 if (waitStatus == WAIT_OBJECT_0) { |
|
172 var msg = new this.msgType; |
|
173 this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE); |
|
174 } |
|
175 if (waitStatus == WAIT_TIMEOUT || (Date.now() - startWaitTime) >= timeout) { |
|
176 break; |
|
177 } |
|
178 } |
|
179 |
|
180 this.unhookWinEvent(hook); |
|
181 // Returns true if the hook was successful, something was found, and we never timed out |
|
182 return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0; |
|
183 }; |
|
184 |