xref: /freebsd/crypto/krb5/src/windows/installer/wix/custom/custom.cpp (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 #ifdef __NMAKE__
2 
3 # NMAKE portion.
4 # Build with : nmake /f custom.cpp
5 # Clean with : nmake /f custom.cpp clean
6 
7 # Builds custom.dll
8 
9 OUTPATH = .
10 
11 # program name macros
12 CC = cl /nologo
13 
14 LINK = link /nologo
15 
16 RM = del
17 
18 DLLFILE = $(OUTPATH)\custom.dll
19 
20 DLLEXPORTS =\
21     -EXPORT:EnableAllowTgtSessionKey \
22     -EXPORT:RevertAllowTgtSessionKey \
23     -EXPORT:AbortMsiImmediate \
24     -EXPORT:UninstallNsisInstallation \
25     -EXPORT:KillRunningProcesses \
26     -EXPORT:ListRunningProcesses \
27     -EXPORT:InstallNetProvider \
28     -EXPORT:UninstallNetProvider
29 
30 $(DLLFILE): $(OUTPATH)\custom.obj
31     $(LINK) /OUT:$@ /DLL $** $(DLLEXPORTS)
32 
33 $(OUTPATH)\custom.obj: custom.cpp custom.h
34     $(CC) /c /Fo$@ custom.cpp
35 
36 all: $(DLLFILE)
37 
38 clean:
39     $(RM) $(DLLFILE)
40     $(RM) $(OUTPATH)\custom.obj
41     $(RM) $(OUTPATH)\custom.exp
42 
43 !IFDEF __C_TEXT__
44 #else
45 /*
46 
47 Copyright 2004,2005 by the Massachusetts Institute of Technology
48 
49 All rights reserved.
50 
51 Permission to use, copy, modify, and distribute this software and its
52 documentation for any purpose and without fee is hereby granted,
53 provided that the above copyright notice appear in all copies and that
54 both that copyright notice and this permission notice appear in
55 supporting documentation, and that the name of the Massachusetts
56 Institute of Technology (M.I.T.) not be used in advertising or publicity
57 pertaining to distribution of the software without specific, written
58 prior permission.
59 
60 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
61 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
62 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
63 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
64 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
65 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
66 SOFTWARE.
67 
68 */
69 
70 /**************************************************************
71 * custom.cpp : Dll implementing custom action to install Kerberos for Windows
72 *
73 *         The functions in this file are for use as entry points
74 *         for calls from MSI only. The specific MSI parameters
75 *         are noted in the comments section of each of the
76 *         functions.
77 *
78 * rcsid: $Id$
79 **************************************************************/
80 
81 #pragma unmanaged
82 
83 // Only works for Win2k and above
84 #define _WIN32_WINNT 0x500
85 #include "custom.h"
86 #include <shellapi.h>
87 
88 // linker stuff
89 #pragma comment(lib, "msi")
90 #pragma comment(lib, "advapi32")
91 #pragma comment(lib, "shell32")
92 #pragma comment(lib, "user32")
93 
94 void ShowMsiError( MSIHANDLE hInstall, DWORD errcode, DWORD param ){
95 	MSIHANDLE hRecord;
96 
97 	hRecord = MsiCreateRecord(3);
98 	MsiRecordClearData(hRecord);
99 	MsiRecordSetInteger(hRecord, 1, errcode);
100 	MsiRecordSetInteger(hRecord, 2, param);
101 
102 	MsiProcessMessage( hInstall, INSTALLMESSAGE_ERROR, hRecord );
103 
104 	MsiCloseHandle( hRecord );
105 }
106 
107 static void ShowMsiErrorEx(MSIHANDLE hInstall, DWORD errcode, LPTSTR str,
108                            DWORD param )
109 {
110     MSIHANDLE hRecord;
111 
112     hRecord = MsiCreateRecord(3);
113     MsiRecordClearData(hRecord);
114     MsiRecordSetInteger(hRecord, 1, errcode);
115     MsiRecordSetString(hRecord, 2, str);
116     MsiRecordSetInteger(hRecord, 3, param);
117 
118     MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecord);
119 
120     MsiCloseHandle(hRecord);
121 }
122 
123 #define LSA_KERBEROS_KEY "SYSTEM\\CurrentControlSet\\Control\\Lsa\\Kerberos"
124 #define LSA_KERBEROS_PARM_KEY "SYSTEM\\CurrentControlSet\\Control\\Lsa\\Kerberos\\Parameters"
125 #define KFW_CLIENT_KEY "SOFTWARE\\MIT\\Kerberos\\Client\\"
126 #define SESSKEY_VALUE_NAME "AllowTGTSessionKey"
127 
128 #define SESSBACKUP_VALUE_NAME "AllowTGTSessionKeyBackup"
129 #define SESSXPBACKUP_VALUE_NAME "AllowTGTSessionKeyBackupXP"
130 
131 
132 /* Set the AllowTGTSessionKey registry keys on install.  Called as a deferred custom action. */
133 MSIDLLEXPORT EnableAllowTgtSessionKey( MSIHANDLE hInstall ) {
134     return SetAllowTgtSessionKey( hInstall, TRUE );
135 }
136 
137 /* Unset the AllowTGTSessionKey registry keys on uninstall.  Called as a deferred custom action. */
138 MSIDLLEXPORT RevertAllowTgtSessionKey( MSIHANDLE hInstall ) {
139     return SetAllowTgtSessionKey( hInstall, FALSE );
140 }
141 
142 UINT SetAllowTgtSessionKey( MSIHANDLE hInstall, BOOL pInstall ) {
143     TCHAR tchVersionString[1024];
144     TCHAR tchVersionKey[2048];
145     DWORD size;
146     DWORD type;
147     DWORD value;
148     HKEY hkKfwClient = NULL;
149     HKEY hkLsaKerberos = NULL;
150     HKEY hkLsaKerberosParm = NULL;
151     UINT rv;
152     DWORD phase = 0;
153 
154     // construct the backup key path
155     size = sizeof(tchVersionString) / sizeof(TCHAR);
156     rv = MsiGetProperty( hInstall, _T("CustomActionData"), tchVersionString, &size );
157     if(rv != ERROR_SUCCESS) {
158         if(pInstall) {
159             ShowMsiError( hInstall, ERR_CUSTACTDATA, rv );
160             return rv;
161         } else {
162             return ERROR_SUCCESS;
163         }
164     }
165 
166     _tcscpy( tchVersionKey, _T( KFW_CLIENT_KEY ) );
167     _tcscat( tchVersionKey, tchVersionString );
168 
169     phase = 1;
170 
171     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, tchVersionKey, 0, ((pInstall)?KEY_WRITE:KEY_READ), &hkKfwClient );
172     if(rv != ERROR_SUCCESS)
173         goto cleanup;
174 
175     phase = 2;
176 
177     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( LSA_KERBEROS_KEY ), 0, KEY_READ | KEY_WRITE, &hkLsaKerberos );
178     if(rv != ERROR_SUCCESS)
179         goto cleanup;
180 
181     phase = 3;
182 
183     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( LSA_KERBEROS_PARM_KEY ), 0, KEY_READ | KEY_WRITE, &hkLsaKerberosParm );
184     if(rv != ERROR_SUCCESS) {
185         hkLsaKerberosParm = NULL;
186     }
187 
188     if(pInstall) {
189         // backup the existing values
190         if(hkLsaKerberosParm) {
191             phase = 4;
192 
193             size = sizeof(value);
194             rv = RegQueryValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
195             if(rv != ERROR_SUCCESS)
196                 value = 0;
197 
198             phase = 5;
199             rv = RegSetValueEx( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
200             if(rv != ERROR_SUCCESS)
201                 goto cleanup;
202         }
203 
204         phase = 6;
205         size = sizeof(value);
206         rv = RegQueryValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
207         if(rv != ERROR_SUCCESS)
208             value = 0;
209 
210         phase = 7;
211         rv = RegSetValueEx( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
212         if(rv != ERROR_SUCCESS)
213             goto cleanup;
214 
215         // and now write the actual values
216         phase = 8;
217         value = 1;
218         rv = RegSetValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
219         if(rv != ERROR_SUCCESS)
220             goto cleanup;
221 
222         if(hkLsaKerberosParm) {
223             phase = 9;
224             value = 1;
225             rv = RegSetValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
226             if(rv != ERROR_SUCCESS)
227                 goto cleanup;
228         }
229 
230     } else { // uninstalling
231         // Don't fail no matter what goes wrong.  This is also a rollback action.
232         if(hkLsaKerberosParm) {
233             size = sizeof(value);
234             rv = RegQueryValueEx( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
235             if(rv != ERROR_SUCCESS)
236                 value = 0;
237 
238             RegSetValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
239         }
240 
241         size = sizeof(value);
242         rv = RegQueryValueEx( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
243         if(rv != ERROR_SUCCESS)
244             value = 0;
245 
246         RegSetValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
247 
248         RegDeleteValue( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ) );
249         RegDeleteValue( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ) );
250     }
251 
252     // all done
253     rv = ERROR_SUCCESS;
254 
255 cleanup:
256     if(rv != ERROR_SUCCESS && pInstall) {
257         ShowMsiError(hInstall, 4005, phase);
258     }
259     if(hkKfwClient) RegCloseKey( hkKfwClient );
260     if(hkLsaKerberos) RegCloseKey( hkLsaKerberos );
261     if(hkLsaKerberosParm) RegCloseKey( hkLsaKerberosParm );
262 
263     return rv;
264 }
265 
266 /* Abort the installation (called as an immediate custom action) */
267 MSIDLLEXPORT AbortMsiImmediate( MSIHANDLE hInstall ) {
268     DWORD rv;
269 	DWORD dwSize = 0;
270 	LPTSTR sReason = NULL;
271 	LPTSTR sFormatted = NULL;
272 	MSIHANDLE hRecord = NULL;
273 	LPTSTR cAbortReason = _T("ABORTREASON");
274 
275 	rv = MsiGetProperty( hInstall, cAbortReason, _T(""), &dwSize );
276 	if(rv != ERROR_MORE_DATA) goto _cleanup;
277 
278 	sReason = new TCHAR[ ++dwSize ];
279 
280 	rv = MsiGetProperty( hInstall, cAbortReason, sReason, &dwSize );
281 
282 	if(rv != ERROR_SUCCESS) goto _cleanup;
283 
284     hRecord = MsiCreateRecord(3);
285 	MsiRecordClearData(hRecord);
286 	MsiRecordSetString(hRecord, 0, sReason);
287 
288 	dwSize = 0;
289 
290 	rv = MsiFormatRecord(hInstall, hRecord, "", &dwSize);
291 	if(rv != ERROR_MORE_DATA) goto _cleanup;
292 
293 	sFormatted = new TCHAR[ ++dwSize ];
294 
295 	rv = MsiFormatRecord(hInstall, hRecord, sFormatted, &dwSize);
296 
297 	if(rv != ERROR_SUCCESS) goto _cleanup;
298 
299 	MsiCloseHandle(hRecord);
300 
301 	hRecord = MsiCreateRecord(3);
302 	MsiRecordClearData(hRecord);
303 	MsiRecordSetInteger(hRecord, 1, ERR_ABORT);
304 	MsiRecordSetString(hRecord,2, sFormatted);
305 	MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecord);
306 
307 _cleanup:
308 	if(sFormatted) delete sFormatted;
309 	if(hRecord) MsiCloseHandle( hRecord );
310 	if(sReason) delete sReason;
311 
312 	return ~ERROR_SUCCESS;
313 }
314 
315 /* Kill specified processes that are running on the system */
316 /* Uses the custom table KillProcess.  Called as an immediate action. */
317 
318 #define MAX_KILL_PROCESSES 255
319 #define FIELD_SIZE 256
320 
321 struct _KillProc {
322     TCHAR * image;
323     TCHAR * desc;
324     BOOL    found;
325     DWORD   pid;
326 };
327 
328 #define RV_BAIL if(rv != ERROR_SUCCESS) goto _cleanup
329 
330 MSIDLLEXPORT KillRunningProcesses( MSIHANDLE hInstall ) {
331     return KillRunningProcessesWorker( hInstall, TRUE );
332 }
333 
334 /* When listing running processes, we populate the ListBox table with
335    values associated with the property 'KillableProcesses'.  If we
336    actually find any processes worth killing, then we also set the
337    'FoundProcceses' property to '1'.  Otherwise we set it to ''.
338 */
339 
340 MSIDLLEXPORT ListRunningProcesses( MSIHANDLE hInstall ) {
341     return KillRunningProcessesWorker( hInstall, FALSE );
342 }
343 
344 UINT KillRunningProcessesWorker( MSIHANDLE hInstall, BOOL bKill )
345 {
346     UINT rv = ERROR_SUCCESS;
347     _KillProc * kpList;
348     int nKpList = 0;
349     int i;
350     int rowNum = 1;
351     DWORD size;
352     BOOL found = FALSE;
353 
354     MSIHANDLE hDatabase = NULL;
355     MSIHANDLE hView = NULL;
356     MSIHANDLE hViewInsert = NULL;
357     MSIHANDLE hRecord = NULL;
358     MSIHANDLE hRecordInsert = NULL;
359 
360     HANDLE hSnapshot = NULL;
361 
362     PROCESSENTRY32 pe;
363 
364     kpList = new _KillProc[MAX_KILL_PROCESSES];
365     memset(kpList, 0, sizeof(*kpList) * MAX_KILL_PROCESSES);
366 
367     hDatabase = MsiGetActiveDatabase( hInstall );
368     if( hDatabase == NULL ) {
369         rv = GetLastError();
370         goto _cleanup;
371     }
372 
373     // If we are only going to list out the processes, delete all the existing
374     // entries first.
375 
376     if(!bKill) {
377 
378         rv = MsiDatabaseOpenView( hDatabase,
379             _T( "DELETE FROM `ListBox` WHERE `ListBox`.`Property` = 'KillableProcesses'" ),
380             &hView); RV_BAIL;
381 
382         rv = MsiViewExecute( hView, NULL ); RV_BAIL;
383 
384         MsiCloseHandle( hView );
385 
386         hView = NULL;
387 
388         rv = MsiDatabaseOpenView( hDatabase,
389               _T( "SELECT * FROM `ListBox` WHERE `Property` = 'KillableProcesses'" ),
390             &hViewInsert); RV_BAIL;
391 
392         MsiViewExecute(hViewInsert, NULL);
393 
394         hRecordInsert = MsiCreateRecord(4);
395 
396         if(hRecordInsert == NULL) {
397             rv = GetLastError();
398             goto _cleanup;
399         }
400     }
401 
402     // Open a view
403     rv = MsiDatabaseOpenView( hDatabase,
404         _T( "SELECT `Image`,`Desc` FROM `KillProcess`" ),
405         &hView); RV_BAIL;
406 
407     rv = MsiViewExecute( hView, NULL ); RV_BAIL;
408 
409     do {
410         rv = MsiViewFetch( hView, &hRecord );
411         if(rv != ERROR_SUCCESS) {
412             if(hRecord)
413                 MsiCloseHandle(hRecord);
414             hRecord = NULL;
415             break;
416         }
417 
418         kpList[nKpList].image = new TCHAR[ FIELD_SIZE ]; kpList[nKpList].image[0] = _T('\0');
419         kpList[nKpList].desc = new TCHAR[ FIELD_SIZE ];  kpList[nKpList].desc[0] = _T('\0');
420         nKpList++;
421 
422         size = FIELD_SIZE;
423         rv = MsiRecordGetString(hRecord, 1, kpList[nKpList-1].image, &size); RV_BAIL;
424 
425         size = FIELD_SIZE;
426         rv = MsiRecordGetString(hRecord, 2, kpList[nKpList-1].desc, &size); RV_BAIL;
427 
428         MsiCloseHandle(hRecord);
429     } while(nKpList < MAX_KILL_PROCESSES);
430 
431     hRecord = NULL;
432 
433     // now we have all the processes in the array.  Check if they are running.
434 
435     hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
436     if(hSnapshot == INVALID_HANDLE_VALUE) {
437         rv = GetLastError();
438         goto _cleanup;
439     }
440 
441     pe.dwSize = sizeof( PROCESSENTRY32 );
442 
443     if(!Process32First( hSnapshot, &pe )) {
444         // technically we should at least find the MSI process, but we let this pass
445         rv = ERROR_SUCCESS;
446         goto _cleanup;
447     }
448 
449     do {
450         for(i=0; i<nKpList; i++) {
451             if(!_tcsicmp( kpList[i].image, pe.szExeFile )) {
452                 // got one
453                 if(bKill) {
454                     // try to kill the process
455                     HANDLE hProcess = NULL;
456 
457                     // If we encounter an error, instead of bailing
458                     // out, we continue on to the next process.  We
459                     // may not have permission to kill all the
460                     // processes we want to kill anyway.  If there are
461                     // any files that we want to replace that is in
462                     // use, Windows Installer will schedule a reboot.
463                     // Also, it's not like we have an exhaustive list
464                     // of all the programs that use Kerberos anyway.
465 
466                     hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID);
467                     if(hProcess == NULL) {
468                         rv = GetLastError();
469                         break;
470                     }
471 
472                     if(!TerminateProcess(hProcess, 0)) {
473                         rv = GetLastError();
474                         CloseHandle(hProcess);
475                         break;
476                     }
477 
478                     CloseHandle(hProcess);
479 
480                 } else {
481                     TCHAR buf[256];
482 
483                     // we are supposed to just list out the processes
484                     rv = MsiRecordClearData( hRecordInsert ); RV_BAIL;
485                     rv = MsiRecordSetString( hRecordInsert, 1, _T("KillableProcesses"));
486                     rv = MsiRecordSetInteger( hRecordInsert, 2, rowNum++ ); RV_BAIL;
487                     _itot( rowNum, buf, 10 );
488                     rv = MsiRecordSetString( hRecordInsert, 3, buf ); RV_BAIL;
489                     if(_tcslen(kpList[i].desc)) {
490                         rv = MsiRecordSetString( hRecordInsert, 4, kpList[i].desc ); RV_BAIL;
491                     } else {
492                         rv = MsiRecordSetString( hRecordInsert, 4, kpList[i].image ); RV_BAIL;
493                     }
494                     MsiViewModify(hViewInsert, MSIMODIFY_INSERT_TEMPORARY, hRecordInsert); RV_BAIL;
495 
496                     found = TRUE;
497                 }
498                 break;
499             }
500         }
501    } while( Process32Next( hSnapshot, &pe ) );
502 
503     if(!bKill) {
504         // set the 'FoundProcceses' property
505         if(found) {
506             MsiSetProperty( hInstall, _T("FoundProcesses"), _T("1"));
507         } else {
508             MsiSetProperty( hInstall, _T("FoundProcesses"), _T(""));
509         }
510     }
511 
512     // Finally:
513     rv = ERROR_SUCCESS;
514 
515 _cleanup:
516 
517     if(hRecordInsert) MsiCloseHandle(hRecordInsert);
518     if(hViewInsert) MsiCloseHandle(hView);
519 
520     if(hSnapshot && hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot);
521 
522     while(nKpList) {
523         nKpList--;
524         delete kpList[nKpList].image;
525         delete kpList[nKpList].desc;
526     }
527     delete kpList;
528 
529     if(hRecord) MsiCloseHandle(hRecord);
530     if(hView) MsiCloseHandle(hView);
531 
532     if(hDatabase) MsiCloseHandle(hDatabase);
533 
534     if(rv != ERROR_SUCCESS) {
535         ShowMsiError(hInstall, ERR_PROC_LIST, rv);
536     }
537 
538     return rv;
539 }
540 
541 static bool IsNSISInstalled()
542 {
543     HKEY nsisKfwKey = NULL;
544     HRESULT res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
545                                "SOFTWARE\\Microsoft\\Windows\\CurrentVersion"
546                                "\\Uninstall\\Kerberos for Windows",
547                                0,
548                                KEY_READ | KEY_WOW64_32KEY,
549                                &nsisKfwKey);
550     if (res != ERROR_SUCCESS)
551         return FALSE;
552 
553     RegCloseKey(nsisKfwKey);
554     return TRUE;
555 }
556 
557 static HANDLE NSISUninstallShellExecute(LPTSTR pathUninstall)
558 {
559     SHELLEXECUTEINFO   sei;
560     ZeroMemory ( &sei, sizeof(sei) );
561 
562     sei.cbSize          = sizeof(sei);
563     sei.hwnd            = GetForegroundWindow();
564     sei.fMask           = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI |
565                           SEE_MASK_NOCLOSEPROCESS;
566     sei.lpVerb          = _T("runas"); // run as administrator
567     sei.lpFile          = pathUninstall;
568     sei.lpParameters    = _T("");
569     sei.nShow           = SW_SHOWNORMAL;
570 
571     if (!ShellExecuteEx(&sei)) {
572         // FAILED! TODO: report details?
573     }
574     return sei.hProcess;
575 }
576 
577 static HANDLE NSISUninstallCreateProcess(LPTSTR pathUninstall)
578 {
579     STARTUPINFO sInfo;
580     PROCESS_INFORMATION pInfo;
581     pInfo.hProcess = NULL;
582     pInfo.hThread = NULL;
583 
584     // Create a process for the uninstaller
585     sInfo.cb = sizeof(sInfo);
586     sInfo.lpReserved = NULL;
587     sInfo.lpDesktop = _T("");
588     sInfo.lpTitle = _T("NSIS Uninstaller for Kerberos for Windows");
589     sInfo.dwX = 0;
590     sInfo.dwY = 0;
591     sInfo.dwXSize = 0;
592     sInfo.dwYSize = 0;
593     sInfo.dwXCountChars = 0;
594     sInfo.dwYCountChars = 0;
595     sInfo.dwFillAttribute = 0;
596     sInfo.dwFlags = 0;
597     sInfo.wShowWindow = 0;
598     sInfo.cbReserved2 = 0;
599     sInfo.lpReserved2 = 0;
600     sInfo.hStdInput = 0;
601     sInfo.hStdOutput = 0;
602     sInfo.hStdError = 0;
603 
604     if (!CreateProcess(pathUninstall,
605                        _T("Uninstall /S"),
606                        NULL,
607                        NULL,
608                        FALSE,
609                        CREATE_SUSPENDED,
610                        NULL,
611                        NULL,
612                        &sInfo,
613                        &pInfo)) {
614         // failure; could grab info, but we should be able to recover by
615         // using NSISUninstallShellExecute...
616     } else {
617         // success
618         // start up the thread
619         ResumeThread(pInfo.hThread);
620         // done with thread handle
621         CloseHandle(pInfo.hThread);
622     }
623     return pInfo.hProcess;
624 }
625 
626 
627 /* Uninstall NSIS */
628 MSIDLLEXPORT UninstallNsisInstallation( MSIHANDLE hInstall )
629 {
630     DWORD rv = ERROR_SUCCESS;
631     DWORD lastError;
632     // lookup the NSISUNINSTALL property value
633     LPTSTR cNsisUninstall = _T("UPGRADENSIS");
634     LPTSTR strPathUninst = NULL;
635     DWORD dwSize = 0;
636     HANDLE hProcess = NULL;
637     HANDLE hIo = NULL;
638     HANDLE hJob = NULL;
639 
640     rv = MsiGetProperty( hInstall, cNsisUninstall, _T(""), &dwSize );
641     if(rv != ERROR_MORE_DATA) goto _cleanup;
642 
643     strPathUninst = new TCHAR[ ++dwSize ];
644 
645     rv = MsiGetProperty(hInstall, cNsisUninstall, strPathUninst, &dwSize);
646     if(rv != ERROR_SUCCESS) goto _cleanup;
647 
648     hProcess = NSISUninstallCreateProcess(strPathUninst);
649     if (hProcess == NULL) // expected when run on UAC-limited account
650         hProcess = NSISUninstallShellExecute(strPathUninst);
651 
652     if (hProcess == NULL) {
653         // still no uninstall process? ick...
654         lastError = GetLastError();
655         rv = 40;
656         goto _cleanup;
657     }
658     // note that it is not suffiecient to wait for the initial process to
659     // finish; there is a whole process tree that we need to wait for.  sigh.
660     JOBOBJECT_ASSOCIATE_COMPLETION_PORT acp;
661     acp.CompletionKey = 0;
662     hJob = CreateJobObject(NULL, _T("NSISUninstallObject"));
663     if(!hJob) {
664         rv = 41;
665         goto _cleanup;
666     }
667 
668     hIo = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
669     if(!hIo) {
670         rv = 42;
671         goto _cleanup;
672     }
673 
674     acp.CompletionPort = hIo;
675 
676     SetInformationJobObject(hJob,
677                             JobObjectAssociateCompletionPortInformation,
678                             &acp,
679                             sizeof(acp));
680 
681     AssignProcessToJobObject(hJob, hProcess);
682 
683     DWORD msgId;
684     ULONG_PTR unusedCompletionKey;
685     LPOVERLAPPED unusedOverlapped;
686     for (;;) {
687         if (!GetQueuedCompletionStatus(hIo,
688                                        &msgId,
689                                        &unusedCompletionKey,
690                                        &unusedOverlapped,
691                                        INFINITE)) {
692             Sleep(1000);
693         } else if (msgId == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
694             break;
695         }
696     }
697 
698 _cleanup:
699     if (hProcess) CloseHandle(hProcess);
700     if (hIo) CloseHandle(hIo);
701     if (hJob) CloseHandle(hJob);
702 
703     if (IsNSISInstalled()) {
704         // uninstall failed: maybe user cancelled uninstall, or something else
705         // went wrong...
706         if (rv == ERROR_SUCCESS)
707             rv = 43;
708     } else {
709         // Maybe something went wrong, but it doesn't matter as long as nsis
710         // is gone now...
711         rv = ERROR_SUCCESS;
712     }
713 
714     if (rv == 40) {
715         // CreateProcess() / ShellExecute() errors get extra data
716         ShowMsiErrorEx(hInstall, ERR_NSS_FAILED_CP, strPathUninst, lastError);
717     } else if (rv != ERROR_SUCCESS) {
718         ShowMsiError(hInstall, ERR_NSS_FAILED, rv);
719     }
720 
721     if (strPathUninst) delete strPathUninst;
722     return rv;
723 }
724 
725 /* Check and add or remove networkprovider key value
726         str : target string
727         str2: string to add/remove
728         bInst: == 1 if string should be added to target if not already there,
729 	otherwise remove string from target if present.
730 */
731 int npi_CheckAndAddRemove( LPTSTR str, LPTSTR str2, int bInst ) {
732 
733     LPTSTR target, charset, match;
734     int ret=0;
735 
736     target = new TCHAR[lstrlen(str)+3];
737     lstrcpy(target,_T(","));
738     lstrcat(target,str);
739     lstrcat(target,_T(","));
740     charset = new TCHAR[lstrlen(str2)+3];
741     lstrcpy(charset,_T(","));
742     lstrcat(charset,str2);
743     lstrcat(charset,_T(","));
744 
745     match = _tcsstr(target, charset);
746 
747     if ((match) && (bInst)) {
748         ret = INP_ERR_PRESENT;
749         goto cleanup;
750     }
751 
752     if ((!match) && (!bInst)) {
753         ret = INP_ERR_ABSENT;
754         goto cleanup;
755     }
756 
757     if (bInst) // && !match
758     {
759        lstrcat(str, _T(","));
760        lstrcat(str, str2);
761        ret = INP_ERR_ADDED;
762        goto cleanup;
763     }
764 
765     // if (!bInst) && (match)
766     {
767        lstrcpy(str+(match-target),match+lstrlen(str2)+2);
768        str[lstrlen(str)-1]=_T('\0');
769        ret = INP_ERR_REMOVED;
770        goto cleanup;
771     }
772 
773 cleanup:
774 
775     delete target;
776     delete charset;
777     return ret;
778 }
779 
780 /* Sets the registry keys required for the functioning of the network provider */
781 
782 DWORD InstNetProvider(MSIHANDLE hInstall, int bInst) {
783     LPTSTR strOrder;
784     HKEY hkOrder;
785     LONG rv;
786     DWORD dwSize;
787     HANDLE hProcHeap;
788 
789     strOrder = (LPTSTR) 0;
790 
791     CHECK(rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, STR_KEY_ORDER, 0, KEY_READ | KEY_WRITE, &hkOrder ));
792 
793     dwSize = 0;
794     CHECK(rv = RegQueryValueEx( hkOrder, STR_VAL_ORDER, NULL, NULL, NULL, &dwSize ) );
795 
796     strOrder = new TCHAR[ (dwSize + STR_SERVICE_LEN + 4) * sizeof(TCHAR) ];
797 
798     CHECK(rv = RegQueryValueEx( hkOrder, STR_VAL_ORDER, NULL, NULL, (LPBYTE) strOrder, &dwSize));
799 
800     strOrder[dwSize] = '\0';	/* reg strings are not always nul terminated */
801 
802     npi_CheckAndAddRemove( strOrder, STR_SERVICE , bInst);
803 
804     dwSize = (lstrlen( strOrder ) + 1) * sizeof(TCHAR);
805 
806     CHECK(rv = RegSetValueEx( hkOrder, STR_VAL_ORDER, NULL, REG_SZ, (LPBYTE) strOrder, dwSize ));
807 
808     /* everything else should be set by the MSI tables */
809     rv = ERROR_SUCCESS;
810 _cleanup:
811 
812     if( rv != ERROR_SUCCESS ) {
813         ShowMsiError( hInstall, ERR_NPI_FAILED, rv );
814     }
815 
816     if(strOrder) delete strOrder;
817 
818     return rv;
819 }
820 
821 MSIDLLEXPORT InstallNetProvider( MSIHANDLE hInstall ) {
822     return InstNetProvider( hInstall, 1 );
823 }
824 
825 MSIDLLEXPORT UninstallNetProvider( MSIHANDLE hInstall) {
826     return InstNetProvider( hInstall, 0 );
827 }
828 
829 #endif
830 #ifdef __NMAKE__
831 !ENDIF
832 #endif
833