fef0b5043dfec47919644d48a4fe88fb36caba82
[msysgit.git] / share / WinGit / modules.inc.iss
blobfef0b5043dfec47919644d48a4fe88fb36caba82
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):DWORD;
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:=0;
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:=1;
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):DWORD;
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):DWORD;
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:=0;
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:=1;
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):DWORD;
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; // The application cannot be classified as any other type. An application of this type can only be shut down by a forced shutdown.
434 RmMainWindow = 1; // A Windows application run as a stand-alone process that displays a top-level window.
435 RmOtherWindow = 2; // A Windows application that does not run as a stand-alone process and does not display a top-level window.
436 RmService = 3; // The application is a Windows service.
437 RmExplorer = 4; // The application is Windows Explorer.
438 RmConsole = 5; // The application is a stand-alone console application.
439 RmCritical = 1000; // A system restart is required to complete the installation because a process cannot be shut down.
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 RmForceShutdown = $0001;
452 RmShutdownOnlyRegistered = $0010;
454 type
455 SessionKey=array[1..CCH_RM_SESSION_KEY+1] of Char;
457 FILETIME=record
458 dwLowDateTime,dwHighDateTime:DWORD;
459 end;
460 RM_UNIQUE_PROCESS=record
461 dwProcessId:DWORD;
462 ProcessStartTime:FILETIME;
463 end;
464 RM_APP_TYPE=DWORD;
465 RM_PROCESS_INFO=record
466 Process:RM_UNIQUE_PROCESS;
467 strAppName:array[1..CCH_RM_MAX_APP_NAME+1] of Char;
468 strServiceShortName:array[1..CCH_RM_MAX_SVC_NAME+1] of Char;
469 ApplicationType:RM_APP_TYPE;
470 AppStatus:ULONG;
471 TSSessionId:DWORD;
472 bRestartable:BOOL;
473 end;
474 RM_WRITE_STATUS_CALLBACK=DWORD;
476 function RmStartSession(var pSessionHandle:DWORD;dwSessionFlags:DWORD;strSessionKey:SessionKey):DWORD;
477 external 'RmStartSession@Rstrtmgr.dll stdcall delayload';
479 function RmEndSession(dwSessionHandle:DWORD):DWORD;
480 external 'RmEndSession@Rstrtmgr.dll stdcall delayload';
482 function RmRegisterResources(dwSessionHandle:DWORD;hFiles:UINT;rgsFilenames:TArrayOfString;nApplications:UINT;rgApplications:array of RM_UNIQUE_PROCESS;nServices:UINT;rgsServiceNames:TArrayOfString):DWORD;
483 external 'RmRegisterResources@Rstrtmgr.dll stdcall delayload';
485 function RmGetList(dwSessionHandle:DWORD;var pnProcInfoNeeded,pnProcInfo:UINT;rgAffectedApps:array of RM_PROCESS_INFO;lpdwRebootReasons:IdList):DWORD;
486 external 'RmGetList@Rstrtmgr.dll stdcall delayload';
488 function RmShutdown(dwSessionHandle:DWORD;lActionFlags:ULONG;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD;
489 external 'RmShutdown@Rstrtmgr.dll stdcall delayload';
491 function RmRestart(dwSessionHandle:DWORD;dwRestartFlags:DWORD;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD;
492 external 'RmRestart@Rstrtmgr.dll stdcall delayload';
494 // Returns a list of running processes that currectly use one of the specified modules.
495 // Each module has to be a full path and filename to a DLL.
496 function FindProcessesUsingModules_WinVista(Modules:TArrayOfString;var Processes:ProcessList):DWORD;
498 Handle:DWORD;
499 Name:SessionKey;
500 Apps:array of RM_UNIQUE_PROCESS;
501 Services:TArrayOfString;
502 Process:THandle;
503 Path:String;
504 PathLength:DWORD;
505 Needed,Have,i:UINT;
506 AppList:array of RM_PROCESS_INFO;
507 ReasonList:IdList;
508 Success:DWORD;
509 begin
510 SetArrayLength(Processes,0);
511 Result:=0;
513 // NULL-terminate the array of chars.
514 Name[CCH_RM_SESSION_KEY+1]:=#0;
515 if RmStartSession(Handle,0,Name)<>ERROR_SUCCESS then begin
516 Exit;
517 end;
519 if RmRegisterResources(Handle,GetArrayLength(Modules),Modules,0,Apps,0,Services)=ERROR_SUCCESS then begin
520 // Reallocate the arrays until they are large enough to hold the process information.
521 Needed:=1;
522 repeat
523 Have:=Needed;
524 SetArrayLength(AppList,Have);
525 SetArrayLength(ReasonList,Have);
526 Success:=RmGetList(Handle,Needed,Have,AppList,ReasonList);
527 until (Have>=Needed) and (Success<>ERROR_MORE_DATA);
529 if (Success=ERROR_SUCCESS) and (Needed>0) then begin
530 for i:=0 to Needed-1 do begin
531 Process:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,AppList[i].Process.dwProcessId);
532 if Process<>0 then begin
533 SetLength(Path,MAX_PATH);
534 PathLength:=GetModuleFileNameEx(Process,0,Path,MAX_PATH);
535 SetLength(Path,PathLength);
537 Have:=GetArrayLength(Processes);
538 SetArrayLength(Processes,Have+1);
539 Processes[Have].ID:=AppList[i].Process.dwProcessId;
540 Processes[Have].Path:=Path;
541 Processes[Have].Name:=ArrayToString(AppList[i].strAppName);
542 Processes[Have].Restartable:=AppList[i].bRestartable;
544 CloseHandle(Process);
545 end;
546 end;
547 Result:=Handle;
548 end;
549 end;
550 end;
552 // Returns a list of running processes that currectly use the specified module.
553 // The module has to be a full path and filename to a DLL.
554 function FindProcessesUsingModule_WinVista(Module:String;var Processes:ProcessList):DWORD;
556 Modules:TArrayOfString;
557 begin
558 SetArrayLength(Modules,1);
559 Modules[0]:=Module;
560 Result:=FindProcessesUsingModules_WinVista(Modules,Processes);
561 end;
564 Wrapper code
567 // Returns a list of running processes that currectly use one of the specified modules.
568 // Automatically calls the best implementation for the running OS. The return value is
569 // non-zero on success, and equals the Restart Manager session handle on Vista and above.
570 function FindProcessesUsingModules(Modules:TArrayOfString;var Processes:ProcessList):DWORD;
572 Version:TWindowsVersion;
573 begin
574 GetWindowsVersionEx(Version);
576 if (Version.Major<5) or (not Version.NTPlatform) then begin
577 Result:=FindProcessesUsingModules_Win95(Modules,Processes);
578 end else if Version.Major<6 then begin
579 Result:=FindProcessesUsingModules_Win2000(Modules,Processes);
580 end else begin
581 Result:=FindProcessesUsingModules_WinVista(Modules,Processes);
582 end;
583 end;
585 // Returns a list of running processes that currectly use the specified module.
586 // Automatically calls the best implementation for the running OS. The return value is
587 // non-zero on success, and equals the Restart Manager session handle on Vista and above.
588 function FindProcessesUsingModule(Module:String;var Processes:ProcessList):DWORD;
590 Version:TWindowsVersion;
591 begin
592 GetWindowsVersionEx(Version);
594 if (Version.Major<5) or (not Version.NTPlatform) then begin
595 Result:=FindProcessesUsingModule_Win95(Module,Processes);
596 end else if Version.Major<6 then begin
597 Result:=FindProcessesUsingModule_Win2000(Module,Processes);
598 end else begin
599 Result:=FindProcessesUsingModule_WinVista(Module,Processes);
600 end;
601 end;
604 Helper code
607 // Tries to replace an in-use file, e.g. a registered shell extension, by
608 // renaming it and then renaming the new file to the original name. Optionally,
609 // performs (un-)registering via regsvr32.
610 function ReplaceInUseFile(CurFile,NewFile:String;Register:Boolean):Boolean;
612 CurFilePath,CurFileName,NewFileName:String;
613 CurFileStem,CurFileTemp:String;
614 UnregisterFailed,RenameFailed:Boolean;
615 Msg:String;
616 begin
617 Result:=False;
619 // Note that CurFile may not exist, in which case NewFile is just renamed.
620 if not FileExists(NewFile) then begin
621 Exit;
622 end;
624 CurFilePath:=ExtractFilePath(CurFile);
625 CurFileName:=ExtractFileName(CurFile);
626 NewFileName:=ExtractFileName(NewFile);
628 // Get the file name without extension or period and use that as a suffix
629 // for the temporary file.
630 CurFileStem:=ChangeFileExt(CurFileName,'');
631 CurFileTemp:=GenerateUniqueName(CurFilePath,'.'+CurFileStem);
633 // Clean-up by trying to delete any previously renamed temporary files.
634 DelTree(CurFilePath+'\*.'+CurFileStem,False,True,False);
636 UnregisterFailed:=False;
637 RenameFailed:=False;
639 if FileExists(CurFile) then begin
640 if Register and (not UnregisterServer(Is64BitInstallMode,CurFile,False)) then begin
641 UnregisterFailed:=True;
642 end;
644 if (not DeleteFile(CurFile)) and (not RenameFile(CurFile,CurFileTemp)) then begin
645 RenameFailed:=True;
646 end;
647 end;
649 if not RenameFile(NewFile,CurFile) then begin
650 Msg:='Unable to install a new version of "'+CurFileName+'". ' +
651 'Please finish the installation manually by following theses steps on the command line:' + #13 + #13;
652 if FileExists(CurFile) then begin
653 if UnregisterFailed then begin
654 Msg := Msg + '- run "regsvr32 /u ' + CurFileName + '",' + #13;
655 end;
656 if RenameFailed then begin
657 Msg := Msg + '- rename "' + CurFileName + '" to something else,' + #13;
658 end;
659 end;
660 Msg := Msg + '- rename "' + NewFileName + '" to "' + CurFileName + '",' + #13;
661 Msg := Msg + '- run "regsvr32 ' + CurFileName + '".';
663 MsgBox(Msg,mbError,MB_OK);
664 end else begin
665 if Register then begin
666 RegisterServer(Is64BitInstallMode,CurFile,False);
667 end;
668 Result:=True;
669 end;
670 end;