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