| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- unit Setup.PathRedir;
- {
- Inno Setup
- Copyright (C) 1997-2026 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- The ApplyPathRedirRules function rewrites paths containing System32,
- SysWOW64, and Sysnative to achieve the same effect as disabling/enabling
- WOW64 file system redirection.
- Before rewriting, the path is expanded and converted to a super
- (extended-length) path. If the rfNormalPath flag is specified, the path is
- converted back to a normal path if possible (even if a super path was
- passed to the function). Use rfNormalPath only in cases where a super
- path is known to not work properly.
- Used only by the Setup project.
- }
- interface
- uses
- Windows, SysUtils;
- type
- TPathRedirFlags = set of (rfNormalPath);
- TPathRedirTargetProcess = (tpCurrent, tpNativeBit, tp32Bit,
- tp32BitPreferSystem32);
- function ApplyPathRedirRules(const A64Bit: Boolean; const APath: String;
- const ATargetProcess: TPathRedirTargetProcess;
- const AFlags: TPathRedirFlags = []): String;
- procedure InitializePathRedir(const AWindows64Bit: Boolean;
- const ASystem32Path, ASysWow64Path, ASysNativePath: String);
- implementation
- uses
- PathFunc,
- Setup.InstFunc;
- type
- TPathRedir = class
- strict private
- FWindows64Bit: Boolean;
- FSystem32Path, FSysWow64Path, FSysNativePath: String;
- public
- constructor Create(const AWindows64Bit: Boolean;
- const ASystem32Path, ASysWow64Path, ASysNativePath: String);
- function ApplyRules(const A64Bit: Boolean; const APath: String;
- const AFlags: TPathRedirFlags;
- const ATargetProcess: TPathRedirTargetProcess): String;
- end;
- var
- [volatile] PathRedirInstance: TPathRedir;
- [volatile] PathRedirActiveUseCount: Integer;
- procedure InitializePathRedir(const AWindows64Bit: Boolean;
- const ASystem32Path, ASysWow64Path, ASysNativePath: String);
- begin
- const LInstance = TPathRedir.Create(AWindows64Bit, ASystem32Path,
- ASysWow64Path, ASysNativePath);
- MemoryBarrier;
- if AtomicCmpExchange(Pointer(PathRedirInstance), Pointer(LInstance), nil) <> nil then begin
- LInstance.Free;
- InternalError('PathRedir: Already initialized');
- end;
- end;
- function ApplyPathRedirRules(const A64Bit: Boolean; const APath: String;
- const ATargetProcess: TPathRedirTargetProcess;
- const AFlags: TPathRedirFlags = []): String;
- begin
- while True do begin
- const CurCount = PathRedirActiveUseCount;
- if CurCount < 0 then
- InternalError('PathRedir: Unit was finalized');
- if AtomicCmpExchange(PathRedirActiveUseCount, CurCount + 1, CurCount) = CurCount then
- Break;
- end;
- MemoryBarrier;
- try
- if PathRedirInstance = nil then
- InternalError('PathRedir: Not initialized');
- Result := PathRedirInstance.ApplyRules(A64Bit, APath, AFlags, ATargetProcess);
- finally
- MemoryBarrier;
- AtomicDecrement(PathRedirActiveUseCount);
- end;
- end;
- { TPathRedir }
- constructor TPathRedir.Create(const AWindows64Bit: Boolean;
- const ASystem32Path, ASysWow64Path, ASysNativePath: String);
- procedure CheckAndAssignPath(var OutPath: String; const Dir, Title: String);
- begin
- var TestExpandedPath: String;
- if (Length(Dir) >= 4) and PathCharIsDriveLetter(Dir[1]) and
- (Dir[2] = ':') and (Dir[3] = '\') and
- PathExpand(Dir, TestExpandedPath) and
- PathSame(Dir, TestExpandedPath) and
- not PathCharIsSlash(Dir[High(Dir)]) then begin
- OutPath := '\\?\' + Dir;
- Exit;
- end;
- InternalErrorFmt('Path for %s directory is invalid: "%s"', [Title, Dir]);
- end;
- begin
- inherited Create;
- if AWindows64Bit then begin
- CheckAndAssignPath(FSystem32Path, ASystem32Path, 'System32');
- CheckAndAssignPath(FSysWow64Path, ASysWow64Path, 'SysWOW64');
- CheckAndAssignPath(FSysNativePath, ASysNativePath, 'Sysnative');
- end;
- FWindows64Bit := AWindows64Bit;
- end;
- function TPathRedir.ApplyRules(const A64Bit: Boolean; const APath: String;
- const AFlags: TPathRedirFlags;
- const ATargetProcess: TPathRedirTargetProcess): String;
- procedure SubstitutePath(var Path: String; const FromDir, ToDir: String);
- begin
- { Just an extra layer of safety }
- if (FromDir = '') or (ToDir = '') then
- InternalError('PathRedir: SubstitutePath received invalid parameter');
- const PathLen = Length(Path);
- const FromDirLen = Length(FromDir);
- if (PathLen = FromDirLen) or
- ((PathLen > FromDirLen) and (Path[Low(Path) + FromDirLen] = '\')) then
- if PathStartsWith(Path, FromDir) then
- Path := ToDir + Copy(Path, FromDirLen+1, Maxint);
- end;
- begin
- if APath = '' then
- InternalError('PathRedir: Called with empty path string');
- { Windows supports an undocumented "\??\" prefix that works like "\\?\".
- However, PathExpand (GetFullPathName) doesn't understand it and will
- prepend the current drive (e.g., "C:\??\"). So don't allow it. }
- if PathStartsWith(APath, '\??\') then
- InternalError('PathRedir: "\??\" prefix not allowed');
- var NewPath: String;
- if not PathConvertNormalToSuper(APath, NewPath, True) then
- InternalError('PathRedir: PathConvertNormalToSuper failed');
- if FWindows64Bit then begin
- { Running on 64-bit Windows }
- const TargetProcess64Bit =
- {$IFDEF WIN64} (ATargetProcess = tpCurrent) or {$ENDIF}
- (ATargetProcess = tpNativeBit);
- if A64Bit then begin
- { It's a 64-bit path (i.e., System32 means 64-bit system directory).
- System32 -> Sysnative: When target process is 32-bit. }
- if not TargetProcess64Bit then
- SubstitutePath(NewPath, FSystem32Path, FSysNativePath);
- end else begin
- { It's a 32-bit path (i.e., System32 means 32-bit system directory).
- SysWOW64 -> System32: In special tp32BitPreferSystem32 case only.
- System32 -> SysWOW64: Otherwise.
- If you're wondering why it does the latter not only for a 64-bit
- target process but also for a 32-bit target process:
- - GenerateUninstallInfoFilename makes use of this rewrite, see its
- comments.
- - It also helps 32-bit target processes avoid some exceptions that
- apply to System32 but not to SysWOW64. For example: certain
- System32 subdirectories are exempt from redirection. This is not
- the case for SysWOW64. }
- if ATargetProcess = tp32BitPreferSystem32 then
- SubstitutePath(NewPath, FSysWow64Path, FSystem32Path)
- else
- SubstitutePath(NewPath, FSystem32Path, FSysWow64Path);
- end;
- { Sysnative -> System32: When process is 64-bit, regardless of path
- bitness (because the Sysnative alias never works in 64-bit processes). }
- if TargetProcess64Bit then
- SubstitutePath(NewPath, FSysNativePath, FSystem32Path);
- end else begin
- { Running on 32-bit Windows; no substitutions are made }
- if A64Bit then
- InternalError('PathRedir: A64Bit=True but not running 64-bit Windows');
- end;
- if rfNormalPath in AFlags then
- NewPath := PathConvertSuperToNormal(NewPath);
- { Save memory: Return reference to passed-in string if no changes were made }
- if NewPath = APath then
- Result := APath
- else
- Result := NewPath;
- end;
- initialization
- finalization
- if AtomicExchange(PathRedirActiveUseCount, -1) = 0 then begin
- MemoryBarrier;
- FreeAndNil(PathRedirInstance);
- end;
- end.
|