Setup.DotNetFunc.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. unit Setup.DotNetFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2020 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. .NET functions
  8. Fusion code based on LibFusion.pas by RemObject Software
  9. License:
  10. // LibFusion.pas
  11. // copyright (c) 2009 by RemObjects Software
  12. //
  13. // Uses InnoSetup License
  14. Also see https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/fusion
  15. https://docs.microsoft.com/en-us/windows/win32/sbscs/side-by-side-assembly-api
  16. IsDotNetInstalled code based on http://www.kynosarges.de/DotNetVersion.html by Cristoph Nahr
  17. License:
  18. // I’m placing this small bit of code in the public domain, so you may embed it in your own
  19. // projects, modify and redistribute it as you see fit.
  20. Also see https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed
  21. }
  22. interface
  23. uses
  24. Ole2, SysUtils, Windows, Shared.CommonFunc, Shared.DotNetVersion;
  25. type
  26. IAssemblyCache = class(Ole2.IUnknown)
  27. function UninstallAssembly(dwFlags: Integer; pszAssemblyName: PWideChar; pvReserved: Integer; var pulDisposition: Integer): Integer; virtual; stdcall; abstract;
  28. function QueryAssemblyInfo(dwFlags: Integer; pszAssemblyName: PWideChar; pAsmInfo: Integer): Integer; virtual; stdcall; abstract;
  29. function CreateAssemblyCacheItem(dwFlags: Integer; pvReserved: Integer; var ppAsmItem: Integer; pszAssemblyName: PWideChar): Integer; virtual; stdcall; abstract;
  30. function CreateAssemblyScavenger(var ppAsmScavenger: Pointer): Integer; virtual; stdcall; abstract;
  31. function InstallAssembly(dwFlags: Integer; pszManifestFilePath: PWideChar; pvReserved: Integer): Integer; virtual; stdcall; abstract;
  32. end;
  33. TAssemblyCacheInfo = class
  34. private
  35. fDll: THandle;
  36. fCache: IAssemblyCache;
  37. public
  38. constructor Create(const RegView: TRegView);
  39. destructor Destroy; override;
  40. property Cache: IAssemblyCache read FCache;
  41. procedure InstallAssembly(const FileName: string);
  42. procedure UninstallAssembly(const StrongAssemblyName: string); // Full name! in 'AssemblyName, version=1.0.0.0, culture=neutral, publickeytoken=abcdef123456' format
  43. end;
  44. TDotNetBaseVersion = (netbase11, netbase20, netbase40, netbaseHighestKnown);
  45. function GetDotNetInstallRoot(const RegView: TRegView): String;
  46. function GetDotNetVersionInstallRoot(const RegView: TRegView; const Version: TDotNetBaseVersion): String;
  47. function IsDotNetInstalled(const RegView: TRegView; const MinVersion: TDotNetVersion; const MinServicePack: DWORD): Boolean;
  48. implementation
  49. uses
  50. Setup.InstFunc, PathFunc;
  51. var
  52. DotNetRoot: array [TRegView] of String;
  53. DotNetVersionRoot: array [TRegView, TDotNetBaseVersion] of String;
  54. { GetDotNet(Version)InstallRoot }
  55. function GetDotNetInstallRoot(const RegView: TRegView): String;
  56. var
  57. K: HKEY;
  58. begin
  59. if DotNetRoot[RegView] = '' then begin
  60. if RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\.NETFramework', 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
  61. RegQueryStringValue(K, 'InstallRoot', DotNetRoot[RegView]);
  62. RegCloseKey(K);
  63. end;
  64. if DotNetRoot[RegView] = '' then
  65. InternalError('.NET Framework not found');
  66. end;
  67. Result := DotNetRoot[RegView];
  68. end;
  69. function GetDotNetVersionInstallRoot(const RegView: TRegView; const Version: TDotNetBaseVersion): String;
  70. const
  71. VersionStrings: array [TDotNetBaseVersion] of String = ('1.1', '2.0', '4.0', '');
  72. var
  73. K: HKEY;
  74. begin
  75. if DotNetVersionRoot[RegView, Version] = '' then begin
  76. GetDotNetInstallRoot(RegView);
  77. 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
  78. DotNetVersionRoot[RegView, Version] := AddBackslash(DotNetRoot[RegView]) + 'v4.0.30319';
  79. RegCloseKey(K);
  80. 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
  81. DotNetVersionRoot[RegView, Version] := AddBackslash(DotNetRoot[RegView]) + 'v2.0.50727';
  82. RegCloseKey(K);
  83. 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
  84. DotNetVersionRoot[RegView, Version] := AddBackslash(DotNetRoot[RegView]) + 'v1.1.4322';
  85. RegCloseKey(K);
  86. end;
  87. if DotNetVersionRoot[RegView, Version] = '' then begin
  88. if Version <> netbaseHighestKnown then
  89. InternalError(Format('.NET Framework version %s not found', [VersionStrings[Version]]))
  90. else
  91. InternalError('.NET Framework not found');
  92. end;
  93. end;
  94. Result := DotNetVersionRoot[RegView, Version];
  95. end;
  96. { TAssemblyCacheInfo }
  97. constructor TAssemblyCacheInfo.Create(const RegView: TRegView);
  98. type
  99. TCreateAssemblyCache = function (var ppAsmCache: IAssemblyCache; dwReserved: Integer): Integer; stdcall;
  100. var
  101. FileName: string;
  102. Proc: TCreateAssemblyCache;
  103. begin
  104. inherited Create;
  105. FileName := AddBackslash(GetDotNetVersionInstallRoot(RegView, netbaseHighestKnown)) + 'Fusion.dll';
  106. fDll := SafeLoadLibrary(PChar(FileName), SEM_NOOPENFILEERRORBOX);
  107. if fDll = 0 then
  108. InternalError(Format('Failed to load .NET Framework DLL "%s"', [FileName]));
  109. Proc := GetProcAddress(fDll, 'CreateAssemblyCache');
  110. if not Assigned(Proc) then
  111. InternalError('Failed to get address of .NET Framework CreateAssemblyCache function');
  112. Proc(fCache, 0);
  113. if fCache = nil then
  114. InternalError('.NET Framework CreateAssemblyCache function failed');
  115. end;
  116. destructor TAssemblyCacheInfo.Destroy;
  117. begin
  118. if fCache <> nil then
  119. fCache.Release;
  120. fCache := nil;
  121. FreeLibrary(fDll);
  122. inherited Destroy;
  123. end;
  124. procedure TAssemblyCacheInfo.InstallAssembly(const FileName: string);
  125. const
  126. IASSEMBLYCACHE_INSTALL_FLAG_FORCE_REFRESH = 2;
  127. var
  128. lOleString: PWideChar;
  129. OleResult: HRESULT;
  130. begin
  131. lOleString := StringToOleStr(FileName);
  132. try
  133. OleResult := fCache.InstallAssembly(IASSEMBLYCACHE_INSTALL_FLAG_FORCE_REFRESH, lOleString, 0);
  134. if Failed(OleResult) then
  135. RaiseOleError('InstallAssembly', OleResult);
  136. finally
  137. SysFreeString(lOleString);
  138. end;
  139. end;
  140. procedure TAssemblyCacheInfo.UninstallAssembly(
  141. const StrongAssemblyName: string);
  142. var
  143. lOleString: PWideChar;
  144. OleResult: HRESULT;
  145. begin
  146. lOleString := StringToOleStr(StrongAssemblyName);
  147. try
  148. OleResult := fCache.UninstallAssembly(0, lOleString, 0, Integer(nil^));
  149. if Failed(OleResult) then
  150. RaiseOleError('UninstallAssembly', OleResult);
  151. finally
  152. SysFreeString(lOleString);
  153. end;
  154. end;
  155. { IsDotNetDetected }
  156. function IsDotNetInstalled(const RegView: TRegView; const MinVersion: TDotNetVersion; const MinServicePack: DWORD): Boolean;
  157. function GetVersionString(const Version: TDotNetVersion): String;
  158. begin
  159. case Version of
  160. net11: Result := 'v1.1';
  161. net20: Result := 'v2.0';
  162. net30: Result := 'v3.0';
  163. net35: Result := 'v3.5';
  164. net4Client: Result := 'v4\Client';
  165. net4Full: Result := 'v4\Full';
  166. net45: Result := 'v4.5';
  167. net451: Result := 'v4.5.1';
  168. net452: Result := 'v4.5.2';
  169. net46: Result := 'v4.6';
  170. net461: Result := 'v4.6.1';
  171. net462: Result := 'v4.6.2';
  172. net47: Result := 'v4.7';
  173. net471: Result := 'v4.7.1';
  174. net472: Result := 'v4.7.2';
  175. net48: Result := 'v4.8';
  176. net481: Result := 'v4.8.1';
  177. else
  178. InternalError('IsDotNetDetected: Invalid Version');
  179. end;
  180. end;
  181. function QueryDWord(const SubKey, ValueName: String; var Value: DWORD): Boolean;
  182. var
  183. K: HKEY;
  184. Typ, Size: DWORD;
  185. begin
  186. if RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, PChar(SubKey), 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
  187. Size := SizeOf(Value);
  188. Result := (RegQueryValueEx(K, PChar(ValueName), nil, @Typ, @Value, @Size) = ERROR_SUCCESS) and (Typ = REG_DWORD);
  189. RegCloseKey(K);
  190. end else
  191. Result := False;
  192. end;
  193. var
  194. VersionString, VersionKey, SubKey: String;
  195. Install, InstalledRelease, InstalledServicePack, RequiredRelease: DWORD;
  196. Success: Boolean;
  197. begin
  198. VersionString := GetVersionString(MinVersion);
  199. RequiredRelease := 0;
  200. // .NET 1.1 and 2.0 embed release number in version key
  201. if VersionString = 'v1.1' then
  202. VersionKey := 'v1.1.4322'
  203. else if VersionString = 'v2.0' then
  204. VersionKey := 'v2.0.50727'
  205. else begin
  206. // .NET 4.5 and newer install as update to .NET 4.0 Full
  207. if Pos('v4.', VersionString) = 1 then begin
  208. VersionKey := 'v4\Full';
  209. if VersionString = 'v4.5' then
  210. RequiredRelease := 378389
  211. else if VersionString = 'v4.5.1' then
  212. RequiredRelease := 378675 // 378758 on Windows 8 and older
  213. else if VersionString = 'v4.5.2' then
  214. RequiredRelease := 379893
  215. else if VersionString = 'v4.6' then
  216. RequiredRelease := 393295 // 393297 on Windows 8.1 and older
  217. else if VersionString = 'v4.6.1' then
  218. RequiredRelease := 394254 // 394271 before Win10 November Update
  219. else if VersionString = 'v4.6.2' then
  220. RequiredRelease := 394802 // 394806 before Win10 Anniversary Update
  221. else if VersionString = 'v4.7' then
  222. RequiredRelease := 460798 // 460805 before Win10 Creators Update
  223. else if VersionString = 'v4.7.1' then
  224. RequiredRelease := 461308 // 461310 before Win10 Fall Creators Update
  225. else if VersionString = 'v4.7.2' then
  226. RequiredRelease := 461808 // 461814 before Win10 April 2018 Update
  227. else if VersionString = 'v4.8' then
  228. RequiredRelease := 528040 // 528049 before Win10 May 2019 Update
  229. else if VersionString = 'v4.8.1' then
  230. RequiredRelease := 533320 // 533325 before Win11 2022 Update
  231. else
  232. InternalError('IsDotNetDetected: Invalid VersionString');
  233. end else
  234. VersionKey := VersionString;
  235. end;
  236. SubKey := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + VersionKey;
  237. if Pos('v3.0', VersionString) = 1 then
  238. Success := QueryDWord(SubKey + '\Setup', 'InstallSuccess', Install)
  239. else
  240. Success := QueryDWord(SubKey, 'Install', Install);
  241. if Success and (Install = 1) then begin
  242. if Pos('v4', VersionString) = 1 then
  243. Success := QueryDWord(SubKey, 'Servicing', InstalledServicePack)
  244. else
  245. Success := QueryDWord(SubKey, 'SP', InstalledServicePack);
  246. if Success and (InstalledServicePack >= MinServicePack) then begin
  247. if RequiredRelease > 0 then
  248. Success := QueryDWord(SubKey, 'Release', InstalledRelease) and (InstalledRelease >= RequiredRelease);
  249. Result := Success;
  250. end else
  251. Result := False;
  252. end else
  253. Result := False;
  254. end;
  255. end.