123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- unit Setup.DotNetFunc;
- {
- Inno Setup
- Copyright (C) 1997-2020 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- .NET functions
- Fusion code based on LibFusion.pas by RemObject Software
- License:
- // LibFusion.pas
- // copyright (c) 2009 by RemObjects Software
- //
- // Uses InnoSetup License
- Also see https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/fusion
- https://docs.microsoft.com/en-us/windows/win32/sbscs/side-by-side-assembly-api
- IsDotNetInstalled code based on http://www.kynosarges.de/DotNetVersion.html by Cristoph Nahr
- License:
- // I’m placing this small bit of code in the public domain, so you may embed it in your own
- // projects, modify and redistribute it as you see fit.
- Also see https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed
- }
- interface
- uses
- Ole2, SysUtils, Windows, Shared.CommonFunc, Shared.DotNetVersion;
- type
- IAssemblyCache = class(Ole2.IUnknown)
- function UninstallAssembly(dwFlags: Integer; pszAssemblyName: PWideChar; pvReserved: Integer; var pulDisposition: Integer): Integer; virtual; stdcall; abstract;
- function QueryAssemblyInfo(dwFlags: Integer; pszAssemblyName: PWideChar; pAsmInfo: Integer): Integer; virtual; stdcall; abstract;
- function CreateAssemblyCacheItem(dwFlags: Integer; pvReserved: Integer; var ppAsmItem: Integer; pszAssemblyName: PWideChar): Integer; virtual; stdcall; abstract;
- function CreateAssemblyScavenger(var ppAsmScavenger: Pointer): Integer; virtual; stdcall; abstract;
- function InstallAssembly(dwFlags: Integer; pszManifestFilePath: PWideChar; pvReserved: Integer): Integer; virtual; stdcall; abstract;
- end;
- TAssemblyCacheInfo = class
- private
- fDll: THandle;
- fCache: IAssemblyCache;
- public
- constructor Create(const RegView: TRegView);
- destructor Destroy; override;
- property Cache: IAssemblyCache read FCache;
- procedure InstallAssembly(const FileName: string);
- procedure UninstallAssembly(const StrongAssemblyName: string); // Full name! in 'AssemblyName, version=1.0.0.0, culture=neutral, publickeytoken=abcdef123456' format
- end;
- TDotNetBaseVersion = (netbase11, netbase20, netbase40, netbaseHighestKnown);
- function GetDotNetInstallRoot(const RegView: TRegView): String;
- function GetDotNetVersionInstallRoot(const RegView: TRegView; const Version: TDotNetBaseVersion): String;
- function IsDotNetInstalled(const RegView: TRegView; const MinVersion: TDotNetVersion; const MinServicePack: DWORD): Boolean;
- implementation
- uses
- Setup.InstFunc, PathFunc;
- var
- DotNetRoot: array [TRegView] of String;
- DotNetVersionRoot: array [TRegView, TDotNetBaseVersion] of String;
- { GetDotNet(Version)InstallRoot }
- function GetDotNetInstallRoot(const RegView: TRegView): String;
- var
- K: HKEY;
- begin
- if DotNetRoot[RegView] = '' then begin
- if RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\.NETFramework', 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
- RegQueryStringValue(K, 'InstallRoot', DotNetRoot[RegView]);
- RegCloseKey(K);
- end;
- if DotNetRoot[RegView] = '' then
- InternalError('.NET Framework not found');
- end;
- Result := DotNetRoot[RegView];
- end;
- function GetDotNetVersionInstallRoot(const RegView: TRegView; const Version: TDotNetBaseVersion): String;
- const
- VersionStrings: array [TDotNetBaseVersion] of String = ('1.1', '2.0', '4.0', '');
- var
- K: HKEY;
- begin
- if DotNetVersionRoot[RegView, Version] = '' then begin
- GetDotNetInstallRoot(RegView);
- if (Version in [netbase40, netbaseHighestKnown]) and (RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\.NETFramework\Policy\v4.0', 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS) then begin
- DotNetVersionRoot[RegView, Version] := AddBackslash(DotNetRoot[RegView]) + 'v4.0.30319';
- RegCloseKey(K);
- end else if (Version in [netbase20, netbaseHighestKnown]) and (RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\.NETFramework\Policy\v2.0', 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS) then begin
- DotNetVersionRoot[RegView, Version] := AddBackslash(DotNetRoot[RegView]) + 'v2.0.50727';
- RegCloseKey(K);
- end else if (Version in [netbase11, netbaseHighestKnown]) and (RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\.NETFramework\Policy\v1.1', 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS) then begin
- DotNetVersionRoot[RegView, Version] := AddBackslash(DotNetRoot[RegView]) + 'v1.1.4322';
- RegCloseKey(K);
- end;
- if DotNetVersionRoot[RegView, Version] = '' then begin
- if Version <> netbaseHighestKnown then
- InternalError(Format('.NET Framework version %s not found', [VersionStrings[Version]]))
- else
- InternalError('.NET Framework not found');
- end;
- end;
- Result := DotNetVersionRoot[RegView, Version];
- end;
- { TAssemblyCacheInfo }
- constructor TAssemblyCacheInfo.Create(const RegView: TRegView);
- type
- TCreateAssemblyCache = function (var ppAsmCache: IAssemblyCache; dwReserved: Integer): Integer; stdcall;
- var
- FileName: string;
- Proc: TCreateAssemblyCache;
- begin
- inherited Create;
- FileName := AddBackslash(GetDotNetVersionInstallRoot(RegView, netbaseHighestKnown)) + 'Fusion.dll';
- fDll := SafeLoadLibrary(PChar(FileName), SEM_NOOPENFILEERRORBOX);
- if fDll = 0 then
- InternalError(Format('Failed to load .NET Framework DLL "%s"', [FileName]));
- Proc := GetProcAddress(fDll, 'CreateAssemblyCache');
- if not Assigned(Proc) then
- InternalError('Failed to get address of .NET Framework CreateAssemblyCache function');
- Proc(fCache, 0);
- if fCache = nil then
- InternalError('.NET Framework CreateAssemblyCache function failed');
- end;
- destructor TAssemblyCacheInfo.Destroy;
- begin
- if fCache <> nil then
- fCache.Release;
- fCache := nil;
- FreeLibrary(fDll);
- inherited Destroy;
- end;
- procedure TAssemblyCacheInfo.InstallAssembly(const FileName: string);
- const
- IASSEMBLYCACHE_INSTALL_FLAG_FORCE_REFRESH = 2;
- var
- lOleString: PWideChar;
- OleResult: HRESULT;
- begin
- lOleString := StringToOleStr(FileName);
- try
- OleResult := fCache.InstallAssembly(IASSEMBLYCACHE_INSTALL_FLAG_FORCE_REFRESH, lOleString, 0);
- if Failed(OleResult) then
- RaiseOleError('InstallAssembly', OleResult);
- finally
- SysFreeString(lOleString);
- end;
- end;
- procedure TAssemblyCacheInfo.UninstallAssembly(
- const StrongAssemblyName: string);
- var
- lOleString: PWideChar;
- OleResult: HRESULT;
- begin
- lOleString := StringToOleStr(StrongAssemblyName);
- try
- OleResult := fCache.UninstallAssembly(0, lOleString, 0, Integer(nil^));
- if Failed(OleResult) then
- RaiseOleError('UninstallAssembly', OleResult);
- finally
- SysFreeString(lOleString);
- end;
- end;
- { IsDotNetDetected }
- function IsDotNetInstalled(const RegView: TRegView; const MinVersion: TDotNetVersion; const MinServicePack: DWORD): Boolean;
- function GetVersionString(const Version: TDotNetVersion): String;
- begin
- case Version of
- net11: Result := 'v1.1';
- net20: Result := 'v2.0';
- net30: Result := 'v3.0';
- net35: Result := 'v3.5';
- net4Client: Result := 'v4\Client';
- net4Full: Result := 'v4\Full';
- net45: Result := 'v4.5';
- net451: Result := 'v4.5.1';
- net452: Result := 'v4.5.2';
- net46: Result := 'v4.6';
- net461: Result := 'v4.6.1';
- net462: Result := 'v4.6.2';
- net47: Result := 'v4.7';
- net471: Result := 'v4.7.1';
- net472: Result := 'v4.7.2';
- net48: Result := 'v4.8';
- net481: Result := 'v4.8.1';
- else
- InternalError('IsDotNetDetected: Invalid Version');
- end;
- end;
- function QueryDWord(const SubKey, ValueName: String; var Value: DWORD): Boolean;
- var
- K: HKEY;
- Typ, Size: DWORD;
- begin
- if RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, PChar(SubKey), 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
- Size := SizeOf(Value);
- Result := (RegQueryValueEx(K, PChar(ValueName), nil, @Typ, @Value, @Size) = ERROR_SUCCESS) and (Typ = REG_DWORD);
- RegCloseKey(K);
- end else
- Result := False;
- end;
- var
- VersionString, VersionKey, SubKey: String;
- Install, InstalledRelease, InstalledServicePack, RequiredRelease: DWORD;
- Success: Boolean;
- begin
- VersionString := GetVersionString(MinVersion);
- RequiredRelease := 0;
- // .NET 1.1 and 2.0 embed release number in version key
- if VersionString = 'v1.1' then
- VersionKey := 'v1.1.4322'
- else if VersionString = 'v2.0' then
- VersionKey := 'v2.0.50727'
- else begin
- // .NET 4.5 and newer install as update to .NET 4.0 Full
- if Pos('v4.', VersionString) = 1 then begin
- VersionKey := 'v4\Full';
- if VersionString = 'v4.5' then
- RequiredRelease := 378389
- else if VersionString = 'v4.5.1' then
- RequiredRelease := 378675 // 378758 on Windows 8 and older
- else if VersionString = 'v4.5.2' then
- RequiredRelease := 379893
- else if VersionString = 'v4.6' then
- RequiredRelease := 393295 // 393297 on Windows 8.1 and older
- else if VersionString = 'v4.6.1' then
- RequiredRelease := 394254 // 394271 before Win10 November Update
- else if VersionString = 'v4.6.2' then
- RequiredRelease := 394802 // 394806 before Win10 Anniversary Update
- else if VersionString = 'v4.7' then
- RequiredRelease := 460798 // 460805 before Win10 Creators Update
- else if VersionString = 'v4.7.1' then
- RequiredRelease := 461308 // 461310 before Win10 Fall Creators Update
- else if VersionString = 'v4.7.2' then
- RequiredRelease := 461808 // 461814 before Win10 April 2018 Update
- else if VersionString = 'v4.8' then
- RequiredRelease := 528040 // 528049 before Win10 May 2019 Update
- else if VersionString = 'v4.8.1' then
- RequiredRelease := 533320 // 533325 before Win11 2022 Update
- else
- InternalError('IsDotNetDetected: Invalid VersionString');
- end else
- VersionKey := VersionString;
- end;
- SubKey := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + VersionKey;
- if Pos('v3.0', VersionString) = 1 then
- Success := QueryDWord(SubKey + '\Setup', 'InstallSuccess', Install)
- else
- Success := QueryDWord(SubKey, 'Install', Install);
- if Success and (Install = 1) then begin
- if Pos('v4', VersionString) = 1 then
- Success := QueryDWord(SubKey, 'Servicing', InstalledServicePack)
- else
- Success := QueryDWord(SubKey, 'SP', InstalledServicePack);
- if Success and (InstalledServicePack >= MinServicePack) then begin
- if RequiredRelease > 0 then
- Success := QueryDWord(SubKey, 'Release', InstalledRelease) and (InstalledRelease >= RequiredRelease);
- Result := Success;
- end else
- Result := False;
- end else
- Result := False;
- end;
- end.
|