📄 install.pas
字号:
{ Skip the file or fall back to time stamp comparison }
if not(foCompareTimeStamp in CurFile^.Options) then begin
Log('Same version. Skipping.');
goto Skip;
end;
AllowTimeStampComparison := True;
end;
end;
end;
end
else begin
Log('Version of existing file: (none)');
{ If neither the existing file nor our file have version info,
allow time stamp comparison }
if not CurFileVersionInfoValid then
AllowTimeStampComparison := True;
end;
end
else begin
{ When foIgnoreVersion is in Options, always allow time stamp
comparison }
AllowTimeStampComparison := True;
end;
{ Fall back to comparing time stamps if needed }
if AllowTimeStampComparison and
(foCompareTimeStamp in CurFile^.Options) then begin
if not CurFileDateValid or not ExistingFileDateValid then begin
{ If we failed to read one of the time stamps, do the safe thing
and just skip the file }
Log('Couldn''t read time stamp. Skipping.');
goto Skip;
end;
if CompareFileTime(ExistingFileDate, CurFileDate) = 0 then begin
{ Same time stamp }
Log('Same time stamp. Skipping.');
goto Skip;
end;
if CompareFileTime(ExistingFileDate, CurFileDate) > 0 then begin
{ Existing file has a later time stamp }
if not(foPromptIfOlder in CurFile^.Options) or
(MsgBox(DestFile + SNewLine2 + SetupMessages[msgExistingFileNewer],
'', mbError, MB_YESNO) <> ID_NO) then begin
Log('Existing file has a later time stamp. Skipping.');
goto Skip;
end;
end;
end;
LastOperation := '';
{ If file already exists and foConfirmOverwrite is in Options, ask
the user if it's OK to overwrite }
if (foConfirmOverwrite in CurFile^.Options) and
(MsgBox(DestFile + SNewLine2 + SetupMessages[msgFileExists],
'', mbConfirmation, MB_YESNO) <> ID_YES) then begin
Log('User opted not to overwrite the existing file. Skipping.');
goto Skip;
end;
{ Check if existing file is read-only }
while True do begin
ExistingFileAttr := GetFileAttributes(PChar(DestFile));
if (ExistingFileAttr <> -1) and
(ExistingFileAttr and FILE_ATTRIBUTE_READONLY <> 0) then begin
if not(foOverwriteReadOnly in CurFile^.Options) and
AbortRetryIgnoreMsgBox(DestFile, SetupMessages[msgExistingFileReadOnly]) then begin
Log('User opted not to strip the existing file''s read-only attribute. Skipping.');
goto Skip;
end;
LastOperation := SetupMessages[msgErrorChangingAttr];
if SetFileAttributes(PChar(DestFile),
ExistingFileAttr and not FILE_ATTRIBUTE_READONLY) then
Log('Stripped read-only attribute.')
else
Log('Failed to strip read-only attribute.');
if foOverwriteReadOnly in CurFile^.Options then
Break; { don't retry }
end
else
Break;
end;
end
else begin
if (foOnlyIfDestFileExists in CurFile^.Options) and not DestFileExistedBefore then begin
Log('Skipping due to "onlyifdestfileexists" flag.');
goto Skip;
end;
end;
Log('Installing the file.');
{ Locate source file }
SourceFile := ASourceFile;
{ If the file is compressed in the setup package, has the same file
already been copied somewhere else? If so, just make a duplicate of
that file instead of extracting it over again. }
if (SourceFile = '') and
(FileLocationFilenames[CurFile^.LocationEntry] <> '') and
NewFileExists(FileLocationFilenames[CurFile^.LocationEntry]) then
SourceFile := FileLocationFilenames[CurFile^.LocationEntry];
AllowFileToBeDuplicated := (SourceFile = '');
{ Extract or copy the file to a temporary file. Create the destination
file's directory if it didn't already exist. }
LastOperation := SetupMessages[msgErrorCreatingTemp];
TempFile := GenerateUniqueName(PathExtractPath(PathExpand(DestFile)), '.tmp');
MakeDir(PathExtractPath(TempFile), []);
DestF := TFile.Create(TempFile, fdCreateAlways, faWrite, fsNone);
try
TempFileLeftOver := True;
try
ProgressUpdated := True;
LastOperation := SetupMessages[msgErrorReadingSource];
if SourceFile = '' then begin
{ Decompress a file }
FileExtractor.SeekTo(CurFileLocation^, ExtractorProgressProc);
LastOperation := SetupMessages[msgErrorCopying];
FileExtractor.DecompressFile(CurFileLocation^, DestF, ExtractorProgressProc,
not (foDontVerifyChecksum in CurFile^.Options));
end
else begin
{ Copy an external file, or a duplicated non-external file }
SourceF := TFile.Create(SourceFile, fdOpenExisting, faRead, fsRead);
try
LastOperation := SetupMessages[msgErrorCopying];
if Assigned(CurFileLocation) then
CopySourceFileToDestFile(SourceF, DestF, CurFileLocation^.OriginalSize)
else
CopySourceFileToDestFile(SourceF, DestF, AExternalSize);
finally
SourceF.Free;
end;
end;
except
{ If an exception occurred, put progress meter back to where it was }
ProgressUpdated := False;
SetProgress(PreviousProgress);
raise;
end;
{ Set time/date stamp }
SetFileTime(DestF.Handle, nil, nil, @CurFileDate);
{ If it's the uninstall program, bind the messages }
if CurFile^.FileType = ftUninstExe then begin
AllowFileToBeDuplicated := False;
MarkExeHeader(DestF, SetupExeModeUninstaller);
if not DetachedUninstMsgFile then
BindUninstallMsgDataToExe(DestF);
end;
finally
DestF.Free;
end;
{ If it's a font, unregister the existing one to ensure that Windows
'notices' the file is being replaced, and to increase the chances
of the file being unlocked/closed before we replace it. }
if CurFile^.InstallFontName <> '' then begin
LastOperation := '';
FontFilename := ShortenOrExpandFontFilename(DestFile);
if DestFileExistedBefore then
RemoveFontResource(PChar(FontFilename));
end;
{ Delete existing version of file, if any. If it can't be deleted
because it's in use and the "restartreplace" flag was specified
on the entry, register it to be replaced when the system is
restarted. }
if DestFileExists and (CurFile^.FileType <> ftUninstExe) then begin
LastOperation := SetupMessages[msgErrorReplacingExistingFile];
RetriesLeft := 4;
while not DeleteFile(DestFile) do begin
{ Couldn't delete the existing file... }
LastError := GetLastError;
{ If the file inexplicably vanished, it's not a problem }
if LastError = ERROR_FILE_NOT_FOUND then
Break;
{ Does the error code indicate that it is possibly in use? }
if (LastError = ERROR_ACCESS_DENIED) or
(LastError = ERROR_SHARING_VIOLATION) then begin
if foRestartReplace in CurFile^.Options then begin
LogFmt('The existing file appears to be in use (%d). ' +
'Will replace on restart.', [LastError]);
LastOperation := SetupMessages[msgErrorRestartReplace];
NeedsRestart := True;
RestartReplace(TempFile, DestFile);
ReplaceOnRestart := True;
Break;
end;
{ Automatically retry }
if RetriesLeft > 0 then begin
LogFmt('The existing file appears to be in use (%d). ' +
'Retrying.', [LastError]);
Dec(RetriesLeft);
Sleep(1000);
ProcessEvents;
Continue;
end;
end;
{ Some other error occurred, or we ran out of tries }
SetLastError(LastError);
Win32ErrorMsg('DeleteFile');
end;
end;
{ Rename the temporary file to the new name now, unless the file is
to be replaced when the system is restarted, or if the file is the
uninstall program and an existing uninstall program already exists.
Then set any file attributes. }
if ReplaceOnRestart or
((CurFile^.FileType = ftUninstExe) and DestFileExistedBefore) then begin
if CurFile^.FileType = ftUninstExe then
UninstallTempExeFilename := TempFile;
TempFileLeftOver := False;
LastOperation := '';
Log('Leaving temporary file in place for now.');
if AllowFileToBeDuplicated then
SetFileLocationFilename(CurFile^.LocationEntry, TempFile);
AddAttributesToFile(TempFile, CurFile^.Attribs);
end
else begin
LastOperation := SetupMessages[msgErrorRenamingTemp];
if not MoveFile(PChar(TempFile), PChar(DestFile)) then
Win32ErrorMsg('MoveFile');
TempFileLeftOver := False;
TempFile := '';
LastOperation := '';
Log('Successfully installed the file.');
if AllowFileToBeDuplicated then
SetFileLocationFilename(CurFile^.LocationEntry, DestFile);
if foDeleteAfterInstall in CurFile^.Options then
DeleteFilesAfterInstallList.Add(DestFile);
{ Set file attributes *after* renaming the file since Novell
reportedly can't rename read-only files. }
AddAttributesToFile(DestFile, CurFile^.Attribs);
end;
{ If it's a font, register it }
if CurFile^.InstallFontName <> '' then begin
LastOperation := '';
LogFmt('Registering file as a font ("%s")', [CurFile^.InstallFontName]);
InstallFont(FontFilename, CurFile^.InstallFontName, not ReplaceOnRestart);
DeleteFlags := DeleteFlags or utDeleteFile_IsFont;
end;
{ There were no errors ... }
LastOperation := '';
if CurFile^.FileType <> ftUninstExe then begin
{ ... so add to undo list, unless it's the uninstall program. But
don't add foSharedFile files to the undo list yet. }
if not(foUninsNeverUninstall in CurFile^.Options) and
not(foSharedFile in CurFile^.Options) then begin
UninstLog.Add(utDeleteFile, [DestFile, TempFile,
CurFile^.InstallFontName, FontFilename], DeleteFlags);
end;
end
else begin
if UninstallTempExeFilename = '' then
UninstallExeCreated := ueNew
else
UninstallExeCreated := ueReplaced;
end;
Skip:
{ If foRegisterServer or foRegisterTypeLib is in Options, add the
file to RegisterFilesList for registering later.
Don't attempt to register if the file doesn't exist (which can
happen if the foOnlyIfDestFileExists flag is used). }
if ((foRegisterServer in CurFile^.Options) or
(foRegisterTypeLib in CurFile^.Options)) and
NewFileExists(DestFile) then begin
LastOperation := '';
if foRegisterTypeLib in CurFile^.Options then
Log('Will register the file (a type library) later.')
else
Log('Will register the file (a DLL/OCX) later.');
New(RegisterRec);
RegisterRec^.Filename := DestFile;
RegisterRec^.TypeLib := foRegisterTypeLib in CurFile^.Options;
RegisterRec^.NoErrorMessages := foNoRegError in CurFile^.Options;
RegisterFilesList.Add(RegisterRec);
end;
{ If foSharedFile is in Options, increment the reference count in the
registry for the file }
if foSharedFile in CurFile^.Options then begin
LastOperation := '';
Log('Incrementing shared file count.');
IncrementSharedCount(DestFile, DestFileExistedBefore);
if not(foUninsNeverUninstall in CurFile^.Options) then begin
DeleteFlags := DeleteFlags or utDeleteFile_SharedFile;
if foUninsNoSharedFilePrompt in CurFile^.Options then
DeleteFlags := DeleteFlags or utDeleteFile_NoSharedFilePrompt;
UninstLog.Add(utDeleteFile, [DestFile, TempFile,
CurFile^.InstallFontName, FontFilename], DeleteFlags);
end
else
UninstLog.Add(utDecrementSharedCount, [DestFile], 0);
end;
{ Apply permissions (even if the file wasn't replaced) }
LastOperation := '';
if TempFile <> '' then
ApplyPermissions(TempFile, CurFile^.PermissionsEntry)
else
ApplyPermissions(DestFile, CurFile^.PermissionsEntry);
except
if ExceptObject is EAbort then
raise;
Failed := GetExceptMessage;
end;
finally
{ If an exception occurred before TempFile was cleaned up, delete it now }
if TempFileLeftOver then
DeleteFile(TempFile);
end;
{ Was there an exception? Display error message and offer to retry }
if Failed <> '' then begin
if (CurFile^.FileType = ftUninstExe) and (UninstallTempExeFilename <> '') then begin
DeleteFile(UninstallTempExeFilename);
UninstallTempExeFilename := '';
UninstallExeCreated := ueNone;
end;
if LastOperation <> '' then
LastOperation := LastOperation + SNewLine;
if not AbortRetryIgnoreMsgBox(DestFile + SNewLine2 + LastOperation + Failed,
SetupMessages[msgFileAbortRetryIgnore]) then begin
if ProgressUpdated then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -