Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: src/installer-ca/close_application.cpp

Issue 11521026: initial custom action library, "hello, world" quality (Closed)
Patch Set: Created Sept. 3, 2013, 12:48 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 //------------------------------------------------------------------------------ -------------------
2 // <copyright file="CloseApps.cpp" company="Outercurve Foundation">
3 // Copyright (c) 2004, Outercurve Foundation.
4 // This software is released under Microsoft Reciprocal License (MS-RL).
5 // The license and further copyright text can be found in the file
6 // LICENSE.TXT at the root directory of the distribution.
7 // </copyright>
8 //
9 // <summary>
10 // Code to close applications via custom actions when the installer cannot.
11 // </summary>
12 //------------------------------------------------------------------------------ -------------------
13
14 #include "precomp.h"
15
16 #define PROCESS_CLOSE_WAIT_TIME 5000
17
18 // WixCloseApplication Target Description Condition Attribute s Sequence
19
20 // structs
21 LPCWSTR wzQUERY_CLOSEAPPS = L"SELECT `WixCloseApplication`, `Target`, `Descripti on`, `Condition`, `Attributes`, `Property` FROM `WixCloseApplication` ORDER BY ` Sequence`";
22 enum eQUERY_CLOSEAPPS { QCA_ID = 1, QCA_TARGET, QCA_DESCRIPTION, QCA_CONDITION, QCA_ATTRIBUTES, QCA_PROPERTY };
23
24 // CloseApplication.Attributes
25 enum CLOSEAPP_ATTRIBUTES
26 {
27 CLOSEAPP_ATTRIBUTE_NONE = 0,
28 CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE = 1,
29 CLOSEAPP_ATTRIBUTE_REBOOTPROMPT = 2,
30 CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE = 4,
31 };
32
33
34 /******************************************************************
35 EnumCloseWindowsProc - callback function which sends WM_CLOSE if the
36 current window matches the passed in process ID
37
38 ******************************************************************/
39 BOOL CALLBACK EnumCloseWindowsProc(HWND hwnd, LPARAM lParam)
40 {
41 DWORD dwProcessId = 0;
42
43 ::GetWindowThreadProcessId(hwnd, &dwProcessId);
44
45 // check if the process Id is the one we're looking for
46 if (dwProcessId != static_cast<DWORD>(lParam))
47 {
48 return TRUE;
49 }
50
51 WcaLog(LOGMSG_VERBOSE, "Sending close message to process id 0x%x", dwProcess Id);
52
53 ::PostMessageW(hwnd, WM_CLOSE, 0, 0);
54
55 WcaLog(LOGMSG_VERBOSE, "Result 0x%x", ::GetLastError());
56
57 // so we know we succeeded
58 ::SetLastError(ERROR_SUCCESS);
59
60 // A process may have more than one top-level window, continue searching thr ough all windows
61 return TRUE;
62 }
63
64 /******************************************************************
65 SendProcessCloseMessage - helper function to enumerate the top-level
66 windows and send WM_CLOSE to all matching a process ID
67
68 ******************************************************************/
69 void SendProcessCloseMessage(DWORD dwProcessId)
70 {
71 DWORD dwLastError;
72
73 WcaLog(LOGMSG_VERBOSE, "Attempting to send close message to process id 0x%x" , dwProcessId);
74
75 if (::EnumWindows(EnumCloseWindowsProc, dwProcessId))
76 return;
77
78 dwLastError = GetLastError();
79 if (dwLastError != ERROR_SUCCESS)
80 {
81 WcaLog(LOGMSG_VERBOSE, "CloseApp enumeration error: 0x%x", dwLastError);
82 }
83 }
84
85 /******************************************************************
86 SendApplicationCloseMessage - helper function to iterate through the
87 processes for the specified application and send all
88 applicable process Ids a WM_CLOSE message
89
90 ******************************************************************/
91 void SendApplicationCloseMessage(__in LPCWSTR wzApplication)
92 {
93 DWORD *prgProcessIds = NULL;
94 DWORD cProcessIds = 0, iProcessId;
95 HRESULT hr = S_OK;
96
97 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
98
99 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
100
101 if (SUCCEEDED(hr) && 0 < cProcessIds)
102 {
103 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, attempting to send close message.", wzApplication, cProcessIds);
104
105 for (iProcessId = 0; iProcessId < cProcessIds; ++iProcessId)
106 {
107 SendProcessCloseMessage(prgProcessIds[iProcessId]);
108 }
109
110 ProcWaitForIds(prgProcessIds, cProcessIds, PROCESS_CLOSE_WAIT_TIME);
111 }
112
113 ReleaseMem(prgProcessIds);
114 }
115
116 /******************************************************************
117 SetRunningProcessProperty - helper function that sets the specified
118 property if there are any instances of the specified executable
119 running. Useful to show custom UI to ask for shutdown.
120 ******************************************************************/
121 void SetRunningProcessProperty(
122 __in LPCWSTR wzApplication,
123 __in LPCWSTR wzProperty
124 )
125 {
126 DWORD *prgProcessIds = NULL;
127 DWORD cProcessIds = 0;
128 HRESULT hr = S_OK;
129
130 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
131
132 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
133
134 if (SUCCEEDED(hr) && 0 < cProcessIds)
135 {
136 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, setting '% ls' property.", wzApplication, cProcessIds, wzProperty);
137 WcaSetIntProperty(wzProperty, cProcessIds);
138 }
139
140 ReleaseMem(prgProcessIds);
141 }
142
143 /******************************************************************
144 WixCloseApplications - entry point for WixCloseApplications Custom Action
145
146 called as Type 1 CustomAction (binary DLL) from Windows Installer
147 in InstallExecuteSequence before InstallFiles
148 ******************************************************************/
149 extern "C" UINT __stdcall abp_close_applications(
150 __in MSIHANDLE hInstall
151 )
152 {
153 //AssertSz(FALSE, "debug WixCloseApplications");
154 HRESULT hr = S_OK;
155 UINT er = ERROR_SUCCESS;
156
157 LPWSTR pwzData = NULL;
158 LPWSTR pwzId = NULL;
159 LPWSTR pwzTarget = NULL;
160 LPWSTR pwzDescription = NULL;
161 LPWSTR pwzCondition = NULL;
162 LPWSTR pwzProperty = NULL;
163 DWORD dwAttributes = 0;
164 MSICONDITION condition = MSICONDITION_NONE;
165
166 DWORD cCloseApps = 0;
167
168 PMSIHANDLE hView = NULL;
169 PMSIHANDLE hRec = NULL;
170 MSIHANDLE hListboxTable = NULL;
171 MSIHANDLE hListboxColumns = NULL;
172
173 LPWSTR pwzCustomActionData = NULL;
174 //DWORD cchCustomActionData = 0;
175
176 //
177 // initialize
178 //
179 hr = WcaInitialize(hInstall, "abp_close_applications");
180 ExitOnFailure(hr, "failed to initialize");
181
182 //
183 // loop through all the objects to be secured
184 //
185 hr = WcaOpenExecuteView(wzQUERY_CLOSEAPPS, &hView);
186 ExitOnFailure(hr, "failed to open view on abp_close_applications table");
187 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
188 {
189 hr = WcaGetRecordString(hRec, QCA_ID, &pwzId);
190 ExitOnFailure(hr, "failed to get id from abp_close_applications table");
191
192 hr = WcaGetRecordString(hRec, QCA_CONDITION, &pwzCondition);
193 ExitOnFailure(hr, "failed to get condition from abp_close_applications t able");
194
195 if (pwzCondition && *pwzCondition)
196 {
197 condition = ::MsiEvaluateConditionW(hInstall, pwzCondition);
198 if (MSICONDITION_ERROR == condition)
199 {
200 hr = E_INVALIDARG;
201 ExitOnFailure1(hr, "failed to process condition for abp_close_ap plications '%ls'", pwzId);
202 }
203 else if (MSICONDITION_FALSE == condition)
204 {
205 continue; // skip processing this target
206 }
207 }
208
209 hr = WcaGetRecordFormattedString(hRec, QCA_TARGET, &pwzTarget);
210 ExitOnFailure(hr, "failed to get target from abp_close_applications tabl e");
211
212 hr = WcaGetRecordFormattedString(hRec, QCA_DESCRIPTION, &pwzDescription) ;
213 ExitOnFailure(hr, "failed to get description from abp_close_applications table");
214
215 hr = WcaGetRecordInteger(hRec, QCA_ATTRIBUTES, reinterpret_cast<int*>(&d wAttributes));
216 ExitOnFailure(hr, "failed to get attributes from abp_close_applications table");
217
218 hr = WcaGetRecordFormattedString(hRec, QCA_PROPERTY, &pwzProperty);
219 ExitOnFailure(hr, "failed to get property from abp_close_applications ta ble");
220
221 //
222 // send WM_CLOSE to currently running applications
223 //
224 if (dwAttributes & CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE)
225 {
226 SendApplicationCloseMessage(pwzTarget);
227 }
228
229 //
230 // Pass the targets to the deferred action in case the app comes back
231 // even if we close it now.
232 //
233 if ((dwAttributes & CLOSEAPP_ATTRIBUTE_REBOOTPROMPT) || (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE))
234 {
235 hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData);
236 ExitOnFailure(hr, "failed to add target data to CustomActionData");
237
238 hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData);
239 ExitOnFailure(hr, "failed to add attribute data to CustomActionData" );
240 }
241
242 if (pwzProperty && *pwzProperty)
243 {
244 SetRunningProcessProperty(pwzTarget, pwzProperty);
245 }
246
247 ++cCloseApps;
248 }
249
250 // if we looped through all records all is well
251 if (E_NOMOREITEMS == hr)
252 {
253 hr = S_OK;
254 }
255 ExitOnFailure(hr, "failed while looping through all apps to close");
256
257 //
258 // Do the UI dance now.
259 //
260 /*
261
262 TODO: Do this eventually
263
264 if (cCloseApps)
265 {
266 while (TRUE)
267 {
268 for (DWORD i = 0; i < cCloseApps; ++i)
269 {
270 hr = WcaAddTempRecord(&hListboxTable, &hListboxColumns, L"ListBo x", NULL, 0, 4, L"FileInUseProcess", i, target, description);
271 if (FAILED(hr))
272 {
273 }
274 }
275 }
276 }
277 */
278
279 //
280 // schedule the custom action and add to progress bar
281 //
282 if (pwzCustomActionData && *pwzCustomActionData)
283 {
284 Assert(0 < cCloseApps);
285
286 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"WixCloseApplicationsDefer red"), pwzCustomActionData, cCloseApps * COST_CLOSEAPP);
287 ExitOnFailure(hr, "failed to schedule WixCloseApplicationsDeferred actio n");
288 }
289
290 LExit:
291 if (hListboxColumns)
292 {
293 ::MsiCloseHandle(hListboxColumns);
294 }
295 if (hListboxTable)
296 {
297 ::MsiCloseHandle(hListboxTable);
298 }
299
300 ReleaseStr(pwzCustomActionData);
301 ReleaseStr(pwzData);
302 ReleaseStr(pwzProperty);
303 ReleaseStr(pwzCondition);
304 ReleaseStr(pwzDescription);
305 ReleaseStr(pwzTarget);
306 ReleaseStr(pwzId);
307
308 if (FAILED(hr))
309 er = ERROR_INSTALL_FAILURE;
310
311 // TEST BEGIN
312 // For testing, just fail.
313 er = ERROR_INSTALL_FAILURE;
314 // TEST END
315
316 return WcaFinalize(er);
317 }
318
319
320 /******************************************************************
321 WixCloseApplicationsDeferred - entry point for
322 WixCloseApplicationsDeferred Custom Action
323 called as Type 1025 CustomAction
324 (deferred binary DLL)
325
326 NOTE: deferred CustomAction since it modifies the machine
327 NOTE: CustomActionData == wzTarget\tdwAttributes\t...
328 ******************************************************************/
329 extern "C" UINT __stdcall WixCloseApplicationsDeferred(
330 __in MSIHANDLE hInstall
331 )
332 {
333 // AssertSz(FALSE, "debug WixCloseApplicationsDeferred");
334 HRESULT hr = S_OK;
335 DWORD er = ERROR_SUCCESS;
336
337 LPWSTR pwz = NULL;
338 LPWSTR pwzData = NULL;
339 LPWSTR pwzTarget = NULL;
340 DWORD dwAttributes = 0;
341
342 DWORD *prgProcessIds = NULL;
343 DWORD cProcessIds = 0;
344
345 //
346 // initialize
347 //
348 hr = WcaInitialize(hInstall, "WixCloseApplicationsDeferred");
349 ExitOnFailure(hr, "failed to initialize");
350
351 hr = WcaGetProperty(L"CustomActionData", &pwzData);
352 ExitOnFailure(hr, "failed to get CustomActionData");
353
354 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
355
356 pwz = pwzData;
357
358 //
359 // loop through all the passed in data
360 //
361 while (pwz && *pwz)
362 {
363 hr = WcaReadStringFromCaData(&pwz, &pwzTarget);
364 ExitOnFailure(hr, "failed to process CustomActionData");
365 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwAttributes ));
366 ExitOnFailure(hr, "failed to processCustomActionData");
367
368 WcaLog(LOGMSG_VERBOSE, "Checking for App: %ls Attributes: %d", pwzTarget , dwAttributes);
369
370 //
371 // send WM_CLOSE to currently running applications
372 //
373 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE)
374 {
375 SendApplicationCloseMessage(pwzTarget);
376 }
377
378 //
379 // If we find that an app that we need closed is still runing, require a
380 // reboot and bail. Keep iterating through the list in case other apps set
381 // CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE.
382 // Since the close here happens async the process may still be running,
383 // resulting in a false positive reboot message.
384 //
385 ProcFindAllIdsFromExeName(pwzTarget, &prgProcessIds, &cProcessIds);
386 if ((0 < cProcessIds) && (dwAttributes & CLOSEAPP_ATTRIBUTE_REBOOTPROMPT ))
387 {
388 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, requiring a reboot." , pwzTarget);
389
390 WcaDeferredActionRequiresReboot();
391 }
392
393 hr = WcaProgressMessage(COST_CLOSEAPP, FALSE);
394 ExitOnFailure(hr, "failed to send progress message");
395 }
396
397 LExit:
398 ReleaseMem(prgProcessIds);
399
400 ReleaseStr(pwzTarget);
401 ReleaseStr(pwzData);
402
403 if (FAILED(hr))
404 {
405 er = ERROR_INSTALL_FAILURE;
406 }
407 return WcaFinalize(er);
408 }
OLDNEW

Powered by Google App Engine
This is Rietveld