Avoid quoting environment variable values.
[msysgit.git] / share / WinGit / modules.inc.iss
blobf641a214a34a6e110da43b847a772a33ee8121b9
1 // This file contains code paths for Windows 95, Windows 2000 and Windows Vista
2 // to get a list of processes that use a given module (DLL). For the processes
3 // that lock the file, the process ID, the full path to the executable, and file
4 // description is returned. This information can then used to present the user a
5 // list of processes / applications that she needs to close before a module can
6 // be replaced / a replacement will take effect.
7 //
8 // Since Windows Vista, processes that register with the Restart Manager can be
9 // asked to be restarted without any user interaction. The "Restartable" flag in
10 // the "ProcessEntry" indicates whether this is supported or not.
12 // Please note that this code only works for modules, not for files that are
13 // locked by processes in other ways, e.g. by opening them for exclusive read /
14 // write access.
16 // In contrast to existing solutions like [1] or [2], this one has the advantages
17 // of not requiring an external DLLs, being Open Source and having support for
18 // the Windows Vista Restart Manager API.
20 // [1] http://www.vincenzo.net/isxkb/index.php?title=PSVince
21 // [2] http://raz-soft.com/display-english-posts-only/files-in-use-extension-for-inno-setup/
24 Common code
27 const
28 // General constants.
29 MAX_PATH = 260;
30 MAX_MODULE_NAME32 = 255;
32 // Return codes.
33 ERROR_SUCCESS = 0;
34 ERROR_MORE_DATA = 234;
35 INVALID_HANDLE_VALUE = -1;
37 // For OpenProcess().
38 PROCESS_VM_READ = $0010;
39 PROCESS_QUERY_INFORMATION = $0400;
41 type
42 HMODULE = DWORD;
43 LONG = Longint;
44 ULONG = Cardinal;
45 BYTE_PTR = DWORD;
46 ULONG_PTR = DWORD;
48 IdList=array of DWORD;
50 ProcessEntry=record
51 ID:DWORD;
52 Path,Name:String;
53 Restartable:Boolean;
54 end;
55 ProcessList=array of ProcessEntry;
57 function CloseHandle(hObject:THandle):Boolean;
58 external 'CloseHandle@Kernel32.dll';
60 // We need to always use ANSI version of this function, see the bottom note in GetFileDescription().
61 function GetFileVersionInfoSize(lptstrFilename:AnsiString;var lpdwHandle:DWORD):DWORD;
62 external 'GetFileVersionInfoSizeA@Version.dll';
64 // We need to always use ANSI version of this function, see the bottom note in GetFileDescription().
65 function GetFileVersionInfo(lptstrFilename:AnsiString;dwHandle,dwLen:DWORD;lpData:array of Byte):Boolean;
66 external 'GetFileVersionInfoA@Version.dll';
68 // We need to always use ANSI version of this function, see the bottom note in GetFileDescription().
69 function VerQueryValue(pBlock:array of Byte;lpSubBlock:AnsiString;var lplpBuffer:PAnsiChar;var puLen:UINT):Boolean;
70 external 'VerQueryValueA@Version.dll';
72 // Returns the file description as stored in the VS_VERSION_INFO resource. This
73 // is used as the process name rather than using the window title, as e.g. editors
74 // might display the current file rather than the application name in the title bar.
75 function GetFileDescription(FileName:String):String;
76 var
77 Dummy,Size:DWORD;
78 Info:array of Byte;
79 Buffer:PAnsiChar;
80 BufLen:UINT;
81 BufStr:String;
82 Offset:Integer;
83 Language,Codepage,LanguageFB,CodepageFB:WORD;
84 begin
85 Size:=GetFileVersionInfoSize(Filename,Dummy);
86 if Size=0 then begin
87 Exit;
88 end;
90 SetArrayLength(Info,Size);
91 if not GetFileVersionInfo(FileName,0,Size,Info) then begin
92 Exit;
93 end;
95 // Query the language and codepage in order to query locale specific strings.
96 if not VerQueryValue(Info,'\VarFileInfo\Translation',Buffer,BufLen) then begin
97 Exit;
98 end;
100 // This will fail if "Buffer" contains inner #0 characters, in which case
101 // the "else" branch below is taken, and we are guessing some values.
102 BufStr:=Buffer;
104 if Length(BufStr)>=BufLen then begin
105 Offset:=1;
106 repeat
107 // Decode the WORDs from the string.
108 Language:=Ord(BufStr[Offset+1]);
109 Language:=(Language shl 8)+Ord(BufStr[Offset]);
111 Codepage:=Ord(BufStr[Offset+3]);
112 Codepage:=(Codepage shl 8)+Ord(BufStr[Offset+2]);
114 // Use the first entry or English as a fallback.
115 if (Offset=1) or (Language=$0409) then begin
116 LanguageFB:=Language;
117 CodepageFB:=Codepage;
118 end;
120 Offset:=Offset+4;
121 until (Language=GetUILanguage) or (Offset>BufLen);
123 // If we did not find the UI language, use the fallback.
124 if Language<>GetUILanguage then begin
125 Language:=LanguageFB;
126 Codepage:=CodepageFB;
127 end;
128 end else begin
129 Language:=$0000; // Process Default Language
130 Codepage:=$04b0; // 1200 (UTF-16, Little-Endian)
131 LanguageFB:=$0000; // Process Default Language
132 CodepageFB:=$04e4; // 1252 (West European, Latin)
133 end;
135 // Query the file description.
136 BufStr:=Format('\StringFileInfo\%.4x%.4x\FileDescription',[Language,Codepage]);
137 if not VerQueryValue(Info,BufStr,Buffer,BufLen) then begin
138 // Try the fallback if the first choice failed.
139 BufStr:=Format('\StringFileInfo\%.4x%.4x\FileDescription',[LanguageFB,CodepageFB]);
140 if not VerQueryValue(Info,BufStr,Buffer,BufLen) then begin
141 Exit;
142 end;
143 end;
145 // As we cannot cast PAnsiChar to a Unicode string here, we always
146 // need to use the ANSI functions for VerQueryValue etc.
147 Result:=Buffer;
148 end;
151 Code for Windows 95 and above
154 const
155 TH32CS_SNAPPROCESS = $0002;
156 TH32CS_SNAPMODULE = $0008;
157 TH32CS_SNAPMODULE32 = $0010;
159 type
160 PROCESSENTRY32=record
161 dwSize,cntUsage,th32ProcessID:DWORD;
162 th32DefaultHeapID:ULONG_PTR;
163 th32ModuleID,cntThreads,th32ParentProcessID:DWORD;
164 pcPriClassBase:LONG;
165 dwFlags:DWORD;
166 szExeFile:array[1..MAX_PATH] of Char;
167 end;
168 MODULEENTRY32=record
169 dwSize,th32ModuleID,th32ProcessID,GlblcntUsage,ProccntUsage:DWORD;
170 modBaseAddr:BYTE_PTR;
171 modBaseSize:DWORD;
172 hModule:HMODULE;
173 szModule:array[1..MAX_MODULE_NAME32+1] of Char;
174 szExePath:array[1..MAX_PATH] of Char;
175 end;
177 function CreateToolhelp32Snapshot(dwFlags,th32ProcessID:DWORD):THandle;
178 external 'CreateToolhelp32Snapshot@Kernel32.dll stdcall delayload';
180 function Process32First(hSnapshot:THandle;var lppe:PROCESSENTRY32):Boolean;
181 #ifdef UNICODE
182 external 'Process32FirstW@Kernel32.dll stdcall delayload';
183 #else
184 external 'Process32FirstA@Kernel32.dll stdcall delayload';
185 #endif
187 function Process32Next(hSnapshot:THandle;var lppe:PROCESSENTRY32):Boolean;
188 #ifdef UNICODE
189 external 'Process32NextW@Kernel32.dll stdcall delayload';
190 #else
191 external 'Process32NextA@Kernel32.dll stdcall delayload';
192 #endif
194 function Module32First(hSnapshot:THandle;var lpme:MODULEENTRY32):Boolean;
195 #ifdef UNICODE
196 external 'Module32FirstW@Kernel32.dll stdcall delayload';
197 #else
198 external 'Module32FirstA@Kernel32.dll stdcall delayload';
199 #endif
201 function Module32Next(hSnapshot:THandle;var lpme:MODULEENTRY32):Boolean;
202 #ifdef UNICODE
203 external 'Module32NextW@Kernel32.dll stdcall delayload';
204 #else
205 external 'Module32NextA@Kernel32.dll stdcall delayload';
206 #endif
208 // Returns a list of running processes that currectly use the specified module.
209 // The module may be a filename to a DLL with or without path.
210 function FindProcessesUsingModules_Win95(Modules:TArrayOfString;var Processes:ProcessList):Boolean;
212 Success:Boolean;
213 ProcSnap:THandle;
214 ProcEntry:PROCESSENTRY32;
215 ModSnap:THandle;
216 ModEntry:MODULEENTRY32;
217 ModPath,ProcPath:String;
218 i:Longint;
219 begin
220 SetArrayLength(Processes,0);
221 Result:=False;
223 ProcSnap:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
224 if ProcSnap=INVALID_HANDLE_VALUE then begin
225 Exit;
226 end;
228 // Compare strings case-insensitively.
229 for i:=0 to GetArraylength(Modules)-1 do begin
230 Modules[i]:=Lowercase(Modules[i]);
231 end;
233 // Loop over the processes in the system.
234 ProcEntry.dwSize:=SizeOf(ProcEntry);
235 Success:=Process32First(ProcSnap,ProcEntry);
237 while Success do begin
238 if ProcEntry.th32ProcessID>0 then begin
239 ModSnap:=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE or TH32CS_SNAPMODULE32,ProcEntry.th32ProcessID);
240 if ModSnap<>INVALID_HANDLE_VALUE then begin
241 // Loop over the modules in the process.
242 ModEntry.dwSize:=SizeOf(ModEntry);
243 Success:=Module32First(ModSnap,ModEntry);
245 // Assume the first module always is the executable itself.
246 ProcPath:=ArrayToString(ModEntry.szExePath);
247 Success:=Module32Next(ModSnap,ModEntry);
249 while Success do begin
250 ModPath:=ArrayToString(ModEntry.szExePath);
252 for i:=0 to GetArraylength(Modules)-1 do begin
253 if Pos(Modules[i],Lowercase(ModPath))>0 then begin
254 i:=GetArrayLength(Processes);
255 SetArrayLength(Processes,i+1);
256 Processes[i].ID:=ProcEntry.th32ProcessID;
257 Processes[i].Path:=ProcPath;
258 Processes[i].Name:=GetFileDescription(ProcPath);
259 if Length(Processes[i].Name)=0 then begin
260 Processes[i].Name:=ExtractFileName(ProcPath);
261 end;
262 Processes[i].Restartable:=False;
263 end;
264 end;
266 Success:=Module32Next(ModSnap,ModEntry);
267 end;
269 CloseHandle(ModSnap);
270 end;
271 end;
273 Success:=Process32Next(ProcSnap,ProcEntry);
274 end;
276 CloseHandle(ProcSnap);
278 Result:=True;
279 end;
281 // Returns a list of running processes that currectly use the specified module.
282 // The module may be a filename to a DLL with or without path.
283 function FindProcessesUsingModule_Win95(Module:String;var Processes:ProcessList):Boolean;
285 Modules:TArrayOfString;
286 begin
287 SetArrayLength(Modules,1);
288 Modules[0]:=Module;
289 Result:=FindProcessesUsingModules_Win95(Modules,Processes);
290 end;
293 Code for Windows 2000 and above
296 function EnumProcesses(pProcessIds:IdList;cb:DWORD;var pBytesReturned:DWORD):Boolean;
297 external 'EnumProcesses@Psapi.dll stdcall delayload';
299 function EnumProcessModules(hProcess:THandle;lphModule:IdList;cb:DWORD;var lpcbNeeded:DWORD):Boolean;
300 external 'EnumProcessModules@Psapi.dll stdcall delayload';
302 // Wrapper for EnumProcesses() that returns process IDs as a list.
303 function GetProcessList(var List:IdList):Boolean;
305 Size:Longint;
306 Bytes:DWORD;
307 begin
308 // Start with space for 64 processes.
309 Bytes:=32*SizeOf(Bytes);
311 repeat
312 Size:=Bytes*2;
313 SetArrayLength(List,Size/SizeOf(Bytes));
314 Result:=EnumProcesses(List,Size,Bytes);
315 until (Bytes<Size) or (not Result);
317 if Result then begin
318 SetArrayLength(List,Bytes/SizeOf(Bytes));
319 end else begin
320 SetArrayLength(List,0);
321 end;
322 end;
324 // Wrapper for EnumProcessModules() that returns module IDs as a list.
325 function GetModuleList(Process:THandle;var List:IdList):Boolean;
327 Size:Longint;
328 Bytes:DWORD;
329 begin
330 // Start with space for 64 modules.
331 Bytes:=32*SizeOf(Bytes);
333 repeat
334 Size:=Bytes*2;
335 SetArrayLength(List,Size/SizeOf(Bytes));
336 Result:=EnumProcessModules(Process,List,Size,Bytes);
337 until (Bytes<Size) or (not Result);
339 if Result then begin
340 SetArrayLength(List,Bytes/SizeOf(Bytes));
341 end else begin
342 SetArrayLength(List,0);
343 end;
344 end;
346 function OpenProcess(dwDesiredAccess:DWORD;bInheritHandle:BOOL;dwProcessId:DWORD):THandle;
347 external 'OpenProcess@Kernel32.dll stdcall delayload';
349 function GetModuleFileNameEx(hProcess:THandle;hModule:HMODULE;lpFilename:String;nSize:DWORD):DWORD;
350 #ifdef UNICODE
351 external 'GetModuleFileNameExW@Psapi.dll stdcall delayload';
352 #else
353 external 'GetModuleFileNameExA@Psapi.dll stdcall delayload';
354 #endif
356 // Returns a list of running processes that currectly use one of the specified modules.
357 // Each module may be a filename to a DLL with or without path.
358 function FindProcessesUsingModules_Win2000(Modules:TArrayOfString;var Processes:ProcessList):Boolean;
360 ProcList,ModList:IdList;
361 p,m,i:Longint;
362 Process:THandle;
363 Path:String;
364 PathLength:DWORD;
365 begin
366 SetArrayLength(Processes,0);
367 Result:=False;
369 if not GetProcessList(ProcList) then begin
370 Exit;
371 end;
373 // Compare strings case-insensitively.
374 for i:=0 to GetArraylength(Modules)-1 do begin
375 Modules[i]:=Lowercase(Modules[i]);
376 end;
378 for p:=0 to GetArraylength(ProcList)-1 do begin
379 Process:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,ProcList[p]);
380 if Process<>0 then begin
381 if GetModuleList(Process,ModList) then begin
382 for m:=0 to GetArraylength(ModList)-1 do begin
383 SetLength(Path,MAX_PATH);
384 PathLength:=GetModuleFileNameEx(Process,ModList[m],Path,MAX_PATH);
385 SetLength(Path,PathLength);
387 for i:=0 to GetArraylength(Modules)-1 do begin
388 if Pos(Modules[i],Lowercase(Path))>0 then begin
389 SetLength(Path,MAX_PATH);
390 PathLength:=GetModuleFileNameEx(Process,0,Path,MAX_PATH);
391 SetLength(Path,PathLength);
393 i:=GetArrayLength(Processes);
394 SetArrayLength(Processes,i+1);
395 Processes[i].ID:=ProcList[p];
396 Processes[i].Path:=Path;
397 Processes[i].Name:=GetFileDescription(Path);
398 if Length(Processes[i].Name)=0 then begin
399 Processes[i].Name:=ExtractFileName(Path);
400 end;
401 Processes[i].Restartable:=False;
402 end;
403 end;
404 end;
405 end;
406 CloseHandle(Process);
407 end;
408 end;
410 Result:=True;
411 end;
413 // Returns a list of running processes that currectly use the specified module.
414 // The module may be a filename to a DLL with or without path.
415 function FindProcessesUsingModule_Win2000(Module:String;var Processes:ProcessList):Boolean;
417 Modules:TArrayOfString;
418 begin
419 SetArrayLength(Modules,1);
420 Modules[0]:=Module;
421 Result:=FindProcessesUsingModules_Win2000(Modules,Processes);
422 end;
425 Code for Windows Vista and above
428 const
429 CCH_RM_SESSION_KEY = 32;
430 CCH_RM_MAX_APP_NAME = 255;
431 CCH_RM_MAX_SVC_NAME = 63;
433 RmUnknownApp = 0;
434 RmMainWindow = 1;
435 RmOtherWindow = 2;
436 RmService = 3;
437 RmExplorer = 4;
438 RmConsole = 5;
439 RmCritical = 1000;
441 RmStatusUnknown = $0000;
442 RmStatusRunning = $0001;
443 RmStatusStopped = $0002;
444 RmStatusStoppedOther = $0004;
445 RmStatusRestarted = $0008;
446 RmStatusErrorOnStop = $0010;
447 RmStatusErrorOnRestart = $0020;
448 RmStatusShutdownMasked = $0040;
449 RmStatusRestartMasked = $0080;
451 type
452 SessionKey=array[1..CCH_RM_SESSION_KEY+1] of Char;
454 FILETIME=record
455 dwLowDateTime,dwHighDateTime:DWORD;
456 end;
457 RM_UNIQUE_PROCESS=record
458 dwProcessId:DWORD;
459 ProcessStartTime:FILETIME;
460 end;
461 RM_APP_TYPE=DWORD;
462 RM_PROCESS_INFO=record
463 Process:RM_UNIQUE_PROCESS;
464 strAppName:array[1..CCH_RM_MAX_APP_NAME+1] of Char;
465 strServiceShortName:array[1..CCH_RM_MAX_SVC_NAME+1] of Char;
466 ApplicationType:RM_APP_TYPE;
467 AppStatus:ULONG;
468 TSSessionId:DWORD;
469 bRestartable:BOOL;
470 end;
472 function RmStartSession(var pSessionHandle:DWORD;dwSessionFlags:DWORD;strSessionKey:SessionKey):DWORD;
473 external 'RmStartSession@Rstrtmgr.dll stdcall delayload';
475 function RmEndSession(dwSessionHandle:DWORD):DWORD;
476 external 'RmEndSession@Rstrtmgr.dll stdcall delayload';
478 function RmRegisterResources(dwSessionHandle:DWORD;hFiles:UINT;rgsFilenames:TArrayOfString;nApplications:UINT;rgApplications:array of RM_UNIQUE_PROCESS;nServices:UINT;rgsServiceNames:TArrayOfString):DWORD;
479 external 'RmRegisterResources@Rstrtmgr.dll stdcall delayload';
481 function RmGetList(dwSessionHandle:DWORD;var pnProcInfoNeeded:UINT;var pnProcInfo:UINT;rgAffectedApps:array of RM_PROCESS_INFO;lpdwRebootReasons:IdList):DWORD;
482 external 'RmGetList@Rstrtmgr.dll stdcall delayload';
484 // Returns a list of running processes that currectly use one of the specified modules.
485 // Each module has to be a full path and filename to a DLL.
486 function FindProcessesUsingModules_WinVista(Modules:TArrayOfString;var Processes:ProcessList):Boolean;
488 Handle:DWORD;
489 Name:SessionKey;
490 Apps:array of RM_UNIQUE_PROCESS;
491 Services:TArrayOfString;
492 Process:THandle;
493 Path:String;
494 PathLength:DWORD;
495 Needed,Have,i:UINT;
496 AppList:array of RM_PROCESS_INFO;
497 ReasonList:IdList;
498 Success:DWORD;
499 begin
500 SetArrayLength(Processes,0);
501 Result:=False;
503 // NULL-terminate the array of chars.
504 Name[CCH_RM_SESSION_KEY+1]:=#0;
505 if RmStartSession(Handle,0,Name)<>ERROR_SUCCESS then begin
506 Exit;
507 end;
509 if RmRegisterResources(Handle,GetArrayLength(Modules),Modules,0,Apps,0,Services)=ERROR_SUCCESS then begin
510 // Reallocate the arrays until they are large enough to hold the process information.
511 Needed:=1;
512 repeat
513 Have:=Needed;
514 SetArrayLength(AppList,Have);
515 SetArrayLength(ReasonList,Have);
516 Success:=RmGetList(Handle,Needed,Have,AppList,ReasonList);
517 until (Have>=Needed) and (Success<>ERROR_MORE_DATA);
519 if (Success=ERROR_SUCCESS) and (Needed>0) then begin
520 for i:=0 to Needed-1 do begin
521 // Optionally, only list non-critical stand-alone processes that do not require a forced shutdown.
522 //if (AppList[i].ApplicationType=RmMainWindow) or (AppList[i].ApplicationType=RmExplorer) or (AppList[i].ApplicationType=RmConsole) then begin
523 Process:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,AppList[i].Process.dwProcessId);
524 if Process<>0 then begin
525 SetLength(Path,MAX_PATH);
526 PathLength:=GetModuleFileNameEx(Process,0,Path,MAX_PATH);
527 SetLength(Path,PathLength);
529 Have:=GetArrayLength(Processes);
530 SetArrayLength(Processes,Have+1);
531 Processes[Have].ID:=AppList[i].Process.dwProcessId;
532 Processes[Have].Path:=Path;
533 Processes[Have].Name:=ArrayToString(AppList[i].strAppName);
534 Processes[Have].Restartable:=AppList[i].bRestartable;
536 CloseHandle(Process);
537 end;
538 //end;
539 end;
540 Result:=True;
541 end;
542 end;
544 RmEndSession(Handle);
545 end;
547 // Returns a list of running processes that currectly use the specified module.
548 // The module has to be a full path and filename to a DLL.
549 function FindProcessesUsingModule_WinVista(Module:String;var Processes:ProcessList):Boolean;
551 Modules:TArrayOfString;
552 begin
553 SetArrayLength(Modules,1);
554 Modules[0]:=Module;
555 Result:=FindProcessesUsingModules_WinVista(Modules,Processes);
556 end;
559 Wrapper code
562 // Returns a list of running processes that currectly use one of the specified modules.
563 // Automatically calls the best implementation for the running OS.
564 function FindProcessesUsingModules(Modules:TArrayOfString;var Processes:ProcessList):Boolean;
566 Version:TWindowsVersion;
567 begin
568 GetWindowsVersionEx(Version);
570 if (Version.Major<5) or (not Version.NTPlatform) then begin
571 Result:=FindProcessesUsingModules_Win95(Modules,Processes);
572 end else if Version.Major<6 then begin
573 Result:=FindProcessesUsingModules_Win2000(Modules,Processes);
574 end else begin
575 Result:=FindProcessesUsingModules_WinVista(Modules,Processes);
576 end;
577 end;
579 // Returns a list of running processes that currectly use the specified module.
580 // Automatically calls the best implementation for the running OS.
581 function FindProcessesUsingModule(Module:String;var Processes:ProcessList):Boolean;
583 Version:TWindowsVersion;
584 begin
585 GetWindowsVersionEx(Version);
587 if (Version.Major<5) or (not Version.NTPlatform) then begin
588 Result:=FindProcessesUsingModule_Win95(Module,Processes);
589 end else if Version.Major<6 then begin
590 Result:=FindProcessesUsingModule_Win2000(Module,Processes);
591 end else begin
592 Result:=FindProcessesUsingModule_WinVista(Module,Processes);
593 end;
594 end;
597 Helper code
600 // Tries to replace an in-use file, e.g. a registered shell extension, by
601 // renaming it and then renaming the new file to the original name. Optionally,
602 // performs (un-)registering via regsvr32.
603 function ReplaceInUseFile(CurFile,NewFile:String;Register:Boolean):Boolean;
605 CurFilePath,CurFileName,NewFileName:String;
606 CurFileStem,CurFileTemp:String;
607 UnregisterFailed,RenameFailed:Boolean;
608 Msg:String;
609 begin
610 Result:=False;
612 // Note that CurFile may not exist, in which case NewFile is just renamed.
613 if not FileExists(NewFile) then begin
614 Exit;
615 end;
617 CurFilePath:=ExtractFilePath(CurFile);
618 CurFileName:=ExtractFileName(CurFile);
619 NewFileName:=ExtractFileName(NewFile);
621 // Get the file name without extension or period and use that as a suffix
622 // for the temporary file.
623 CurFileStem:=ChangeFileExt(CurFileName,'');
624 CurFileTemp:=GenerateUniqueName(CurFilePath,'.'+CurFileStem);
626 // Clean-up by trying to delete any previously renamed temporary files.
627 DelTree(CurFilePath+'\*.'+CurFileStem,False,True,False);
629 UnregisterFailed:=False;
630 RenameFailed:=False;
632 if FileExists(CurFile) then begin
633 if Register and (not UnregisterServer(Is64BitInstallMode,CurFile,False)) then begin
634 UnregisterFailed:=True;
635 end;
637 if (not DeleteFile(CurFile)) and (not RenameFile(CurFile,CurFileTemp)) then begin
638 RenameFailed:=True;
639 end;
640 end;
642 if not RenameFile(NewFile,CurFile) then begin
643 Msg:='Unable to install a new version of "'+CurFileName+'". ' +
644 'Please finish the installation manually by following theses steps on the command line:' + #13 + #13;
645 if FileExists(CurFile) then begin
646 if UnregisterFailed then begin
647 Msg := Msg + '- run "regsvr32 /u ' + CurFileName + '",' + #13;
648 end;
649 if RenameFailed then begin
650 Msg := Msg + '- rename "' + CurFileName + '" to something else,' + #13;
651 end;
652 end;
653 Msg := Msg + '- rename "' + NewFileName + '" to "' + CurFileName + '",' + #13;
654 Msg := Msg + '- run "regsvr32 ' + CurFileName + '".';
656 MsgBox(Msg,mbError,MB_OK);
657 end else begin
658 if Register then begin
659 RegisterServer(Is64BitInstallMode,CurFile,False);
660 end;
661 Result:=True;
662 end;
663 end;