Merge branch 'garden-shears'
[msysgit.git] / share / WinGit / modules.inc.iss
blob0cda6db4df4d5dc91cfde72d47497c5b94639074
1 [Code]
3 // This file contains code paths for Windows 95, Windows 2000 and Windows Vista
4 // to get a list of processes that use a given module (DLL). For the processes
5 // that lock the file, the process ID, the full path to the executable, and file
6 // description is returned. This information can then used to present the user a
7 // list of processes / applications that she needs to close before a module can
8 // be replaced / a replacement will take effect.
9 //
10 // Since Windows Vista, processes that register with the Restart Manager can be
11 // asked to be restarted without any user interaction. The "Restartable" flag in
12 // the "ProcessEntry" indicates whether this is supported or not.
14 // Please note that this code only works for modules, not for files that are
15 // locked by processes in other ways, e.g. by opening them for exclusive read /
16 // write access.
18 // In contrast to existing solutions like [1] or [2], this one has the advantages
19 // of not requiring an external DLLs, being Open Source and having support for
20 // the Windows Vista Restart Manager API.
22 // [1] http://www.vincenzo.net/isxkb/index.php?title=PSVince
23 // [2] http://raz-soft.com/display-english-posts-only/files-in-use-extension-for-inno-setup/
26 Common code
29 const
30 // General constants.
31 MAX_PATH = 260;
32 MAX_MODULE_NAME32 = 255;
34 // Return codes.
35 ERROR_SUCCESS = 0;
36 ERROR_MORE_DATA = 234;
37 INVALID_HANDLE_VALUE = -1;
39 // For OpenProcess().
40 PROCESS_VM_READ = $0010;
41 PROCESS_QUERY_INFORMATION = $0400;
43 type
44 HMODULE = DWORD;
45 LONG = Longint;
46 ULONG = Cardinal;
47 BYTE_PTR = DWORD;
48 ULONG_PTR = DWORD;
50 IdList=array of DWORD;
52 ProcessEntry=record
53 ID:DWORD;
54 Path,Name:String;
55 Restartable:Boolean;
56 end;
57 ProcessList=array of ProcessEntry;
59 function CloseHandle(hObject:THandle):Boolean;
60 external 'CloseHandle@Kernel32.dll';
62 // We need to always use ANSI version of this function, see the bottom note in GetFileDescription().
63 function GetFileVersionInfoSize(lptstrFilename:AnsiString;var lpdwHandle:DWORD):DWORD;
64 external 'GetFileVersionInfoSizeA@Version.dll';
66 // We need to always use ANSI version of this function, see the bottom note in GetFileDescription().
67 function GetFileVersionInfo(lptstrFilename:AnsiString;dwHandle,dwLen:DWORD;lpData:array of Byte):Boolean;
68 external 'GetFileVersionInfoA@Version.dll';
70 // We need to always use ANSI version of this function, see the bottom note in GetFileDescription().
71 function VerQueryValue(pBlock:array of Byte;lpSubBlock:AnsiString;var lplpBuffer:PAnsiChar;var puLen:UINT):Boolean;
72 external 'VerQueryValueA@Version.dll';
74 // Returns the file description as stored in the VS_VERSION_INFO resource. This
75 // is used as the process name rather than using the window title, as e.g. editors
76 // might display the current file rather than the application name in the title bar.
77 function GetFileDescription(FileName:String):String;
78 var
79 Dummy,Size:DWORD;
80 Info:array of Byte;
81 Buffer:PAnsiChar;
82 BufLen:UINT;
83 BufStr:String;
84 Offset:Integer;
85 Language,Codepage,LanguageFB,CodepageFB:WORD;
86 begin
87 Size:=GetFileVersionInfoSize(Filename,Dummy);
88 if Size=0 then begin
89 Exit;
90 end;
92 SetArrayLength(Info,Size);
93 if not GetFileVersionInfo(FileName,0,Size,Info) then begin
94 Exit;
95 end;
97 // Query the language and codepage in order to query locale specific strings.
98 if not VerQueryValue(Info,'\VarFileInfo\Translation',Buffer,BufLen) then begin
99 Exit;
100 end;
102 // This will fail if "Buffer" contains inner #0 characters, in which case
103 // the "else" branch below is taken, and we are guessing some values.
104 BufStr:=Buffer;
106 if Length(BufStr)>=BufLen then begin
107 Offset:=1;
108 repeat
109 // Decode the WORDs from the string.
110 Language:=Ord(BufStr[Offset+1]);
111 Language:=(Language shl 8)+Ord(BufStr[Offset]);
113 Codepage:=Ord(BufStr[Offset+3]);
114 Codepage:=(Codepage shl 8)+Ord(BufStr[Offset+2]);
116 // Use the first entry or English as a fallback.
117 if (Offset=1) or (Language=$0409) then begin
118 LanguageFB:=Language;
119 CodepageFB:=Codepage;
120 end;
122 Offset:=Offset+4;
123 until (Language=GetUILanguage) or (Offset>BufLen);
125 // If we did not find the UI language, use the fallback.
126 if Language<>GetUILanguage then begin
127 Language:=LanguageFB;
128 Codepage:=CodepageFB;
129 end;
130 end else begin
131 Language:=$0000; // Process Default Language
132 Codepage:=$04b0; // 1200 (UTF-16, Little-Endian)
133 LanguageFB:=$0000; // Process Default Language
134 CodepageFB:=$04e4; // 1252 (West European, Latin)
135 end;
137 // Query the file description.
138 BufStr:=Format('\StringFileInfo\%.4x%.4x\FileDescription',[Language,Codepage]);
139 if not VerQueryValue(Info,BufStr,Buffer,BufLen) then begin
140 // Try the fallback if the first choice failed.
141 BufStr:=Format('\StringFileInfo\%.4x%.4x\FileDescription',[LanguageFB,CodepageFB]);
142 if not VerQueryValue(Info,BufStr,Buffer,BufLen) then begin
143 Exit;
144 end;
145 end;
147 // As we cannot cast PAnsiChar to a Unicode string here, we always
148 // need to use the ANSI functions for VerQueryValue etc.
149 Result:=Buffer;
150 end;
153 Code for Windows 95 and above
156 const
157 TH32CS_SNAPPROCESS = $0002;
158 TH32CS_SNAPMODULE = $0008;
159 TH32CS_SNAPMODULE32 = $0010;
161 type
162 PROCESSENTRY32=record
163 dwSize,cntUsage,th32ProcessID:DWORD;
164 th32DefaultHeapID:ULONG_PTR;
165 th32ModuleID,cntThreads,th32ParentProcessID:DWORD;
166 pcPriClassBase:LONG;
167 dwFlags:DWORD;
168 szExeFile:array[1..MAX_PATH] of Char;
169 end;
170 MODULEENTRY32=record
171 dwSize,th32ModuleID,th32ProcessID,GlblcntUsage,ProccntUsage:DWORD;
172 modBaseAddr:BYTE_PTR;
173 modBaseSize:DWORD;
174 hModule:HMODULE;
175 szModule:array[1..MAX_MODULE_NAME32+1] of Char;
176 szExePath:array[1..MAX_PATH] of Char;
177 end;
179 function CreateToolhelp32Snapshot(dwFlags,th32ProcessID:DWORD):THandle;
180 external 'CreateToolhelp32Snapshot@Kernel32.dll stdcall delayload';
182 function Process32First(hSnapshot:THandle;var lppe:PROCESSENTRY32):Boolean;
183 #ifdef UNICODE
184 external 'Process32FirstW@Kernel32.dll stdcall delayload';
185 #else
186 external 'Process32FirstA@Kernel32.dll stdcall delayload';
187 #endif
189 function Process32Next(hSnapshot:THandle;var lppe:PROCESSENTRY32):Boolean;
190 #ifdef UNICODE
191 external 'Process32NextW@Kernel32.dll stdcall delayload';
192 #else
193 external 'Process32NextA@Kernel32.dll stdcall delayload';
194 #endif
196 function Module32First(hSnapshot:THandle;var lpme:MODULEENTRY32):Boolean;
197 #ifdef UNICODE
198 external 'Module32FirstW@Kernel32.dll stdcall delayload';
199 #else
200 external 'Module32FirstA@Kernel32.dll stdcall delayload';
201 #endif
203 function Module32Next(hSnapshot:THandle;var lpme:MODULEENTRY32):Boolean;
204 #ifdef UNICODE
205 external 'Module32NextW@Kernel32.dll stdcall delayload';
206 #else
207 external 'Module32NextA@Kernel32.dll stdcall delayload';
208 #endif
210 // Returns a list of running processes that currectly use the specified module.
211 // The module may be a filename to a DLL with or without path.
212 function FindProcessesUsingModules_Win95(Modules:TArrayOfString;var Processes:ProcessList):DWORD;
214 Success:Boolean;
215 ProcSnap:THandle;
216 ProcEntry:PROCESSENTRY32;
217 ModSnap:THandle;
218 ModEntry:MODULEENTRY32;
219 ModPath,ProcPath:String;
220 i:Longint;
221 begin
222 SetArrayLength(Processes,0);
223 Result:=0;
225 ProcSnap:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
226 if ProcSnap=INVALID_HANDLE_VALUE then begin
227 Exit;
228 end;
230 // Compare strings case-insensitively.
231 for i:=0 to GetArraylength(Modules)-1 do begin
232 Modules[i]:=Lowercase(Modules[i]);
233 end;
235 // Loop over the processes in the system.
236 ProcEntry.dwSize:=SizeOf(ProcEntry);
237 Success:=Process32First(ProcSnap,ProcEntry);
239 while Success do begin
240 if ProcEntry.th32ProcessID>0 then begin
241 ModSnap:=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE or TH32CS_SNAPMODULE32,ProcEntry.th32ProcessID);
242 if ModSnap<>INVALID_HANDLE_VALUE then begin
243 // Loop over the modules in the process.
244 ModEntry.dwSize:=SizeOf(ModEntry);
245 Success:=Module32First(ModSnap,ModEntry);
247 // Assume the first module always is the executable itself.
248 ProcPath:=ArrayToString(ModEntry.szExePath);
249 Success:=Module32Next(ModSnap,ModEntry);
251 while Success do begin
252 ModPath:=ArrayToString(ModEntry.szExePath);
254 for i:=0 to GetArraylength(Modules)-1 do begin
255 if Pos(Modules[i],Lowercase(ModPath))>0 then begin
256 i:=GetArrayLength(Processes);
257 SetArrayLength(Processes,i+1);
258 Processes[i].ID:=ProcEntry.th32ProcessID;
259 Processes[i].Path:=ProcPath;
260 Processes[i].Name:=GetFileDescription(ProcPath);
261 if Length(Processes[i].Name)=0 then begin
262 Processes[i].Name:=ExtractFileName(ProcPath);
263 end;
264 Processes[i].Restartable:=False;
265 end;
266 end;
268 Success:=Module32Next(ModSnap,ModEntry);
269 end;
271 CloseHandle(ModSnap);
272 end;
273 end;
275 Success:=Process32Next(ProcSnap,ProcEntry);
276 end;
278 CloseHandle(ProcSnap);
280 Result:=1;
281 end;
283 // Returns a list of running processes that currectly use the specified module.
284 // The module may be a filename to a DLL with or without path.
285 function FindProcessesUsingModule_Win95(Module:String;var Processes:ProcessList):DWORD;
287 Modules:TArrayOfString;
288 begin
289 SetArrayLength(Modules,1);
290 Modules[0]:=Module;
291 Result:=FindProcessesUsingModules_Win95(Modules,Processes);
292 end;
295 Code for Windows 2000 and above
298 function EnumProcesses(pProcessIds:IdList;cb:DWORD;var pBytesReturned:DWORD):Boolean;
299 external 'EnumProcesses@Psapi.dll stdcall delayload';
301 function EnumProcessModules(hProcess:THandle;lphModule:IdList;cb:DWORD;var lpcbNeeded:DWORD):Boolean;
302 external 'EnumProcessModules@Psapi.dll stdcall delayload';
304 // Wrapper for EnumProcesses() that returns process IDs as a list.
305 function GetProcessList(var List:IdList):Boolean;
307 Size:Longint;
308 Bytes:DWORD;
309 begin
310 // Start with space for 64 processes.
311 Bytes:=32*SizeOf(Bytes);
313 repeat
314 Size:=Bytes*2;
315 SetArrayLength(List,Size/SizeOf(Bytes));
316 Result:=EnumProcesses(List,Size,Bytes);
317 until (Bytes<Size) or (not Result);
319 if Result then begin
320 SetArrayLength(List,Bytes/SizeOf(Bytes));
321 end else begin
322 SetArrayLength(List,0);
323 end;
324 end;
326 // Wrapper for EnumProcessModules() that returns module IDs as a list.
327 function GetModuleList(Process:THandle;var List:IdList):Boolean;
329 Size:Longint;
330 Bytes:DWORD;
331 begin
332 // Start with space for 64 modules.
333 Bytes:=32*SizeOf(Bytes);
335 repeat
336 Size:=Bytes*2;
337 SetArrayLength(List,Size/SizeOf(Bytes));
338 Result:=EnumProcessModules(Process,List,Size,Bytes);
339 until (Bytes<Size) or (not Result);
341 if Result then begin
342 SetArrayLength(List,Bytes/SizeOf(Bytes));
343 end else begin
344 SetArrayLength(List,0);
345 end;
346 end;
348 function OpenProcess(dwDesiredAccess:DWORD;bInheritHandle:BOOL;dwProcessId:DWORD):THandle;
349 external 'OpenProcess@Kernel32.dll stdcall delayload';
351 function GetModuleFileNameEx(hProcess:THandle;hModule:HMODULE;lpFilename:String;nSize:DWORD):DWORD;
352 #ifdef UNICODE
353 external 'GetModuleFileNameExW@Psapi.dll stdcall delayload';
354 #else
355 external 'GetModuleFileNameExA@Psapi.dll stdcall delayload';
356 #endif
358 // Returns a list of running processes that currectly use one of the specified modules.
359 // Each module may be a filename to a DLL with or without path.
360 function FindProcessesUsingModules_Win2000(Modules:TArrayOfString;var Processes:ProcessList):DWORD;
362 ProcList,ModList:IdList;
363 p,m,i:Longint;
364 Process:THandle;
365 Path:String;
366 PathLength:DWORD;
367 begin
368 SetArrayLength(Processes,0);
369 Result:=0;
371 if not GetProcessList(ProcList) then begin
372 Exit;
373 end;
375 // Compare strings case-insensitively.
376 for i:=0 to GetArraylength(Modules)-1 do begin
377 Modules[i]:=Lowercase(Modules[i]);
378 end;
380 for p:=0 to GetArraylength(ProcList)-1 do begin
381 Process:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,ProcList[p]);
382 if Process<>0 then begin
383 if GetModuleList(Process,ModList) then begin
384 for m:=0 to GetArraylength(ModList)-1 do begin
385 SetLength(Path,MAX_PATH);
386 PathLength:=GetModuleFileNameEx(Process,ModList[m],Path,MAX_PATH);
387 SetLength(Path,PathLength);
389 for i:=0 to GetArraylength(Modules)-1 do begin
390 if Pos(Modules[i],Lowercase(Path))>0 then begin
391 SetLength(Path,MAX_PATH);
392 PathLength:=GetModuleFileNameEx(Process,0,Path,MAX_PATH);
393 SetLength(Path,PathLength);
395 i:=GetArrayLength(Processes);
396 SetArrayLength(Processes,i+1);
397 Processes[i].ID:=ProcList[p];
398 Processes[i].Path:=Path;
399 Processes[i].Name:=GetFileDescription(Path);
400 if Length(Processes[i].Name)=0 then begin
401 Processes[i].Name:=ExtractFileName(Path);
402 end;
403 Processes[i].Restartable:=False;
404 end;
405 end;
406 end;
407 end;
408 CloseHandle(Process);
409 end;
410 end;
412 Result:=1;
413 end;
415 // Returns a list of running processes that currectly use the specified module.
416 // The module may be a filename to a DLL with or without path.
417 function FindProcessesUsingModule_Win2000(Module:String;var Processes:ProcessList):DWORD;
419 Modules:TArrayOfString;
420 begin
421 SetArrayLength(Modules,1);
422 Modules[0]:=Module;
423 Result:=FindProcessesUsingModules_Win2000(Modules,Processes);
424 end;
427 Code for Windows Vista and above
430 const
431 CCH_RM_SESSION_KEY = 32;
432 CCH_RM_MAX_APP_NAME = 255;
433 CCH_RM_MAX_SVC_NAME = 63;
435 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.
436 RmMainWindow = 1; // A Windows application run as a stand-alone process that displays a top-level window.
437 RmOtherWindow = 2; // A Windows application that does not run as a stand-alone process and does not display a top-level window.
438 RmService = 3; // The application is a Windows service.
439 RmExplorer = 4; // The application is Windows Explorer.
440 RmConsole = 5; // The application is a stand-alone console application.
441 RmCritical = 1000; // A system restart is required to complete the installation because a process cannot be shut down.
443 RmStatusUnknown = $0000;
444 RmStatusRunning = $0001;
445 RmStatusStopped = $0002;
446 RmStatusStoppedOther = $0004;
447 RmStatusRestarted = $0008;
448 RmStatusErrorOnStop = $0010;
449 RmStatusErrorOnRestart = $0020;
450 RmStatusShutdownMasked = $0040;
451 RmStatusRestartMasked = $0080;
453 RmForceShutdown = $0001;
454 RmShutdownOnlyRegistered = $0010;
456 type
457 SessionKey=array[1..CCH_RM_SESSION_KEY+1] of Char;
459 FILETIME=record
460 dwLowDateTime,dwHighDateTime:DWORD;
461 end;
462 RM_UNIQUE_PROCESS=record
463 dwProcessId:DWORD;
464 ProcessStartTime:FILETIME;
465 end;
466 RM_APP_TYPE=DWORD;
467 RM_PROCESS_INFO=record
468 Process:RM_UNIQUE_PROCESS;
469 strAppName:array[1..CCH_RM_MAX_APP_NAME+1] of Char;
470 strServiceShortName:array[1..CCH_RM_MAX_SVC_NAME+1] of Char;
471 ApplicationType:RM_APP_TYPE;
472 AppStatus:ULONG;
473 TSSessionId:DWORD;
474 bRestartable:BOOL;
475 end;
476 RM_WRITE_STATUS_CALLBACK=DWORD;
478 function RmStartSession(var pSessionHandle:DWORD;dwSessionFlags:DWORD;strSessionKey:SessionKey):DWORD;
479 external 'RmStartSession@Rstrtmgr.dll stdcall delayload';
481 function RmEndSession(dwSessionHandle:DWORD):DWORD;
482 external 'RmEndSession@Rstrtmgr.dll stdcall delayload';
484 function RmRegisterResources(dwSessionHandle:DWORD;hFiles:UINT;rgsFilenames:TArrayOfString;nApplications:UINT;rgApplications:array of RM_UNIQUE_PROCESS;nServices:UINT;rgsServiceNames:TArrayOfString):DWORD;
485 external 'RmRegisterResources@Rstrtmgr.dll stdcall delayload';
487 function RmGetList(dwSessionHandle:DWORD;var pnProcInfoNeeded,pnProcInfo:UINT;rgAffectedApps:array of RM_PROCESS_INFO;lpdwRebootReasons:IdList):DWORD;
488 external 'RmGetList@Rstrtmgr.dll stdcall delayload';
490 function RmShutdown(dwSessionHandle:DWORD;lActionFlags:ULONG;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD;
491 external 'RmShutdown@Rstrtmgr.dll stdcall delayload';
493 function RmRestart(dwSessionHandle:DWORD;dwRestartFlags:DWORD;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD;
494 external 'RmRestart@Rstrtmgr.dll stdcall delayload';
496 // Returns a list of running processes that currectly use one of the specified modules.
497 // Each module has to be a full path and filename to a DLL.
498 function FindProcessesUsingModules_WinVista(Modules:TArrayOfString;var Processes:ProcessList):DWORD;
500 Handle:DWORD;
501 Name:SessionKey;
502 Apps:array of RM_UNIQUE_PROCESS;
503 Services:TArrayOfString;
504 Process:THandle;
505 Path:String;
506 PathLength:DWORD;
507 Needed,Have,i:UINT;
508 AppList:array of RM_PROCESS_INFO;
509 ReasonList:IdList;
510 Success:DWORD;
511 begin
512 SetArrayLength(Processes,0);
513 Result:=0;
515 // NULL-terminate the array of chars.
516 Name[CCH_RM_SESSION_KEY+1]:=#0;
517 if RmStartSession(Handle,0,Name)<>ERROR_SUCCESS then begin
518 Exit;
519 end;
521 if RmRegisterResources(Handle,GetArrayLength(Modules),Modules,0,Apps,0,Services)=ERROR_SUCCESS then begin
522 // Reallocate the arrays until they are large enough to hold the process information.
523 Needed:=1;
524 repeat
525 Have:=Needed;
526 SetArrayLength(AppList,Have);
527 SetArrayLength(ReasonList,Have);
528 Success:=RmGetList(Handle,Needed,Have,AppList,ReasonList);
529 until (Have>=Needed) and (Success<>ERROR_MORE_DATA);
531 if (Success=ERROR_SUCCESS) and (Needed>0) then begin
532 for i:=0 to Needed-1 do begin
533 Process:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,AppList[i].Process.dwProcessId);
534 if Process<>0 then begin
535 SetLength(Path,MAX_PATH);
536 PathLength:=GetModuleFileNameEx(Process,0,Path,MAX_PATH);
537 SetLength(Path,PathLength);
539 Have:=GetArrayLength(Processes);
540 SetArrayLength(Processes,Have+1);
541 Processes[Have].ID:=AppList[i].Process.dwProcessId;
542 Processes[Have].Path:=Path;
543 Processes[Have].Name:=ArrayToString(AppList[i].strAppName);
544 Processes[Have].Restartable:=AppList[i].bRestartable;
546 CloseHandle(Process);
547 end;
548 end;
549 Result:=Handle;
550 end;
551 end;
552 end;
554 // Returns a list of running processes that currectly use the specified module.
555 // The module has to be a full path and filename to a DLL.
556 function FindProcessesUsingModule_WinVista(Module:String;var Processes:ProcessList):DWORD;
558 Modules:TArrayOfString;
559 begin
560 SetArrayLength(Modules,1);
561 Modules[0]:=Module;
562 Result:=FindProcessesUsingModules_WinVista(Modules,Processes);
563 end;
566 Wrapper code
569 // Returns a list of running processes that currectly use one of the specified modules.
570 // Automatically calls the best implementation for the running OS. The return value is
571 // non-zero on success, and equals the Restart Manager session handle on Vista and above.
572 function FindProcessesUsingModules(Modules:TArrayOfString;var Processes:ProcessList):DWORD;
574 Version:TWindowsVersion;
575 begin
576 GetWindowsVersionEx(Version);
578 if (Version.Major<5) or (not Version.NTPlatform) then begin
579 Result:=FindProcessesUsingModules_Win95(Modules,Processes);
580 end else if Version.Major<6 then begin
581 Result:=FindProcessesUsingModules_Win2000(Modules,Processes);
582 end else begin
583 Result:=FindProcessesUsingModules_WinVista(Modules,Processes);
584 end;
585 end;
587 // Returns a list of running processes that currectly use the specified module.
588 // Automatically calls the best implementation for the running OS. The return value is
589 // non-zero on success, and equals the Restart Manager session handle on Vista and above.
590 function FindProcessesUsingModule(Module:String;var Processes:ProcessList):DWORD;
592 Version:TWindowsVersion;
593 begin
594 GetWindowsVersionEx(Version);
596 if (Version.Major<5) or (not Version.NTPlatform) then begin
597 Result:=FindProcessesUsingModule_Win95(Module,Processes);
598 end else if Version.Major<6 then begin
599 Result:=FindProcessesUsingModule_Win2000(Module,Processes);
600 end else begin
601 Result:=FindProcessesUsingModule_WinVista(Module,Processes);
602 end;
603 end;
606 Helper code
609 // Tries to replace an in-use file, e.g. a registered shell extension, by
610 // renaming it and then renaming the new file to the original name. Optionally,
611 // performs (un-)registering via regsvr32.
612 function ReplaceInUseFile(CurFile,NewFile:String;Register:Boolean;var ErrorMsg:String):Boolean;
614 CurFilePath,CurFileName,NewFileName:String;
615 CurFileStem,CurFileTemp:String;
616 UnregisterFailed,RenameFailed:Boolean;
617 begin
618 Result:=False;
620 // Note that CurFile may not exist, in which case NewFile is just renamed.
621 if not FileExists(NewFile) then begin
622 Exit;
623 end;
625 CurFilePath:=ExtractFilePath(CurFile);
626 CurFileName:=ExtractFileName(CurFile);
627 NewFileName:=ExtractFileName(NewFile);
629 // Get the file name without extension or period and use that as a suffix
630 // for the temporary file.
631 CurFileStem:=ChangeFileExt(CurFileName,'');
632 CurFileTemp:=GenerateUniqueName(CurFilePath,'.'+CurFileStem);
634 // Clean-up by trying to delete any previously renamed temporary files.
635 DelTree(CurFilePath+'\*.'+CurFileStem,False,True,False);
637 UnregisterFailed:=False;
638 RenameFailed:=False;
640 if FileExists(CurFile) then begin
641 if Register and (not UnregisterServer(Is64BitInstallMode,CurFile,False)) then begin
642 UnregisterFailed:=True;
643 end;
645 if (not DeleteFile(CurFile)) and (not RenameFile(CurFile,CurFileTemp)) then begin
646 RenameFailed:=True;
647 end;
648 end;
650 if not RenameFile(NewFile,CurFile) then begin
651 ErrorMsg:='Unable to install a new version of "'+CurFileName+'". ' +
652 'Please finish the installation manually by following theses steps on the command line:' + #13 + #13;
653 if FileExists(CurFile) then begin
654 if UnregisterFailed then begin
655 ErrorMsg := ErrorMsg + '- run "regsvr32 /u ' + CurFileName + '",' + #13;
656 end;
657 if RenameFailed then begin
658 ErrorMsg := ErrorMsg + '- rename "' + CurFileName + '" to something else,' + #13;
659 end;
660 end;
661 ErrorMsg := ErrorMsg + '- rename "' + NewFileName + '" to "' + CurFileName + '",' + #13;
662 ErrorMsg := ErrorMsg + '- run "regsvr32 ' + CurFileName + '".';
663 end else begin
664 if Register then begin
665 RegisterServer(Is64BitInstallMode,CurFile,False);
666 end;
667 Result:=True;
668 end;
669 end;