Bladeren bron

Add IsMsiProductInstalled and also StrToVersion. See #391. Todo: doc/whatsnew/credit.

Martijn Laan 4 jaren geleden
bovenliggende
commit
59acd63e47
3 gewijzigde bestanden met toevoegingen van 109 en 7 verwijderingen
  1. 89 0
      Projects/Msi.pas
  2. 6 4
      Projects/ScriptFunc.pas
  3. 14 3
      Projects/ScriptFunc_R.pas

+ 89 - 0
Projects/Msi.pas

@@ -0,0 +1,89 @@
+unit Msi;
+
+{
+  Inno Setup
+  Copyright (C) 1997-2020 Jordan Russell
+  Portions by Martijn Laan
+  For conditions of distribution and use, see LICENSE.TXT.
+
+  MSI functions
+}
+
+interface
+
+function IsMsiProductInstalled(const UpgradeCode: String; const PackedMinVersion: Int64; var ErrorCode: Cardinal): Boolean;
+
+implementation
+
+uses
+  Windows, SysUtils, CmnFunc2, PathFunc, VerInfo;
+
+var
+  MsiLoaded: Boolean;
+  MsiLibrary: HMODULE;
+  MsiEnumRelatedProductsFunc: function(lpUpgradeCode: PChar; dwReserved, iProductIndex: DWORD; lpProductBuf: PChar): UINT; stdcall;
+  MsiGetProductInfoFunc: function(szProduct, szAttribute, lpValueBuf: PChar; var pcchValueBuf: DWORD): UINT; stdcall;
+  MsiLibraryLastError, MsiEnumRelatedProductsFuncLastError, MsiGetProductInfoFuncLastError: Cardinal;
+
+function IsMsiProductInstalled(const UpgradeCode: String; const PackedMinVersion: Int64; var ErrorCode: Cardinal): Boolean;
+var
+  ProductCode: array[0..38] of Char;
+  VersionStringSize: DWORD;
+  VersionString: String;
+  VersionNumbers: TFileVersionNumbers;
+  PackedVersion: Int64;
+begin
+  Result := False;
+
+  if not MsiLoaded then begin
+    MsiLibrary := SafeLoadLibrary(AddBackslash(GetSystemDir) + 'msi.dll', SEM_NOOPENFILEERRORBOX);
+    if MsiLibrary <> 0 then begin
+      MsiEnumRelatedProductsFunc := GetProcAddress(MsiLibrary, 'MsiEnumRelatedProductsW');
+      MsiEnumRelatedProductsFuncLastError := GetLastError;
+      MsiGetProductInfoFunc := GetProcAddress(MsiLibrary, 'MsiGetProductInfoW');
+      MsiGetProductInfoFuncLastError := GetLastError;
+    end else
+      MsiLibraryLastError := GetLastError;
+    MsiLoaded := True;
+  end;
+
+  if MsiLibrary = 0 then
+    ErrorCode := MsiLibraryLastError
+  else if not Assigned(MsiEnumRelatedProductsFunc) then
+    ErrorCode := MsiEnumRelatedProductsFuncLastError
+  else if not Assigned(MsiGetProductInfoFunc) then
+    ErrorCode := MsiGetProductInfoFuncLastError
+  else
+    ErrorCode := ERROR_SUCCESS;
+  if ErrorCode <> ERROR_SUCCESS then
+    Exit;
+
+  ErrorCode := MsiEnumRelatedProductsFunc(PChar(UpgradeCode), 0, 0, ProductCode);
+  if ErrorCode <> ERROR_SUCCESS then begin
+    if ErrorCode = ERROR_NO_MORE_ITEMS then
+      ErrorCode := ERROR_SUCCESS;  { Not installed so should just return False without an error }
+    Exit;
+  end;
+
+  VersionStringSize := 16;
+  SetLength(VersionString, VersionStringSize);
+  ErrorCode := MsiGetProductInfoFunc(ProductCode, 'VersionString', PChar(VersionString), VersionStringSize);
+  if ErrorCode = ERROR_MORE_DATA then begin
+    SetLength(VersionString, VersionStringSize+1);
+    ErrorCode := MsiGetProductInfoFunc(ProductCode, 'VersionString', PChar(VersionString), VersionStringSize);
+  end;
+  if ErrorCode <> ERROR_SUCCESS then
+    Exit;
+  SetLength(VersionString, VersionStringSize);
+
+  if not StrToVersionNumbers(VersionString, VersionNumbers) then begin
+    ErrorCode := ERROR_BAD_FORMAT;
+    Exit;
+  end;
+
+  PackedVersion := (Int64(VersionNumbers.MS) shl 32) or VersionNumbers.LS;
+  Result := PackedVersion >= PackedMinVersion;
+  ErrorCode := ERROR_SUCCESS;
+end;
+
+end.

+ 6 - 4
Projects/ScriptFunc.pas

@@ -296,7 +296,7 @@ const
   );
   );
 
 
   { VerInfo }
   { VerInfo }
-  VerInfoTable: array [0..10] of AnsiString =
+  VerInfoTable: array [0..11] of AnsiString =
   (
   (
     'function GetVersionNumbers(const Filename: String; var VersionMS, VersionLS: Cardinal): Boolean;',
     'function GetVersionNumbers(const Filename: String; var VersionMS, VersionLS: Cardinal): Boolean;',
     'function GetVersionComponents(const Filename: String; var Major, Minor, Revision, Build: Word): Boolean;',
     'function GetVersionComponents(const Filename: String; var Major, Minor, Revision, Build: Word): Boolean;',
@@ -308,7 +308,8 @@ const
     'function SamePackedVersion(const Version1, Version2: Int64): Boolean;',
     'function SamePackedVersion(const Version1, Version2: Int64): Boolean;',
     'procedure UnpackVersionNumbers(const Version: Int64; var VersionMS, VersionLS: Cardinal);',
     'procedure UnpackVersionNumbers(const Version: Int64; var VersionMS, VersionLS: Cardinal);',
     'procedure UnpackVersionComponents(const Version: Int64; var Major, Minor, Revision, Build: Word);',
     'procedure UnpackVersionComponents(const Version: Int64; var Major, Minor, Revision, Build: Word);',
-    'function VersionToStr(const Version: Int64): String;'
+    'function VersionToStr(const Version: Int64): String;',
+    'function StrToVersion(const VersionString: String; var Version: Int64): Boolean;'
   );
   );
 
 
   { Windows }
   { Windows }
@@ -345,7 +346,7 @@ const
   );
   );
 
 
   { Other }
   { Other }
-  OtherTable: array [0..31] of AnsiString =
+  OtherTable: array [0..32] of AnsiString =
   (
   (
     'procedure BringToFrontAndRestore;',
     'procedure BringToFrontAndRestore;',
     'function WizardDirValue: String;',
     'function WizardDirValue: String;',
@@ -378,7 +379,8 @@ const
     'function EnableFsRedirection(const Enable: Boolean): Boolean;',
     'function EnableFsRedirection(const Enable: Boolean): Boolean;',
     'function GetUninstallProgressForm: TUninstallProgressForm;',
     'function GetUninstallProgressForm: TUninstallProgressForm;',
     'function CreateCallback(Method: AnyMethod): Longword;',
     'function CreateCallback(Method: AnyMethod): Longword;',
-    'function IsDotNetInstalled(const MinVersion: TDotNetVersion; const MinServicePack: Cardinal): Boolean;'
+    'function IsDotNetInstalled(const MinVersion: TDotNetVersion; const MinServicePack: Cardinal): Boolean;',
+    'function IsMsiProductInstalled(const UpgradeCode: String; const PackedMinVersion: Int64): Boolean;'
   );
   );
 
 
 implementation
 implementation

+ 14 - 3
Projects/ScriptFunc_R.pas

@@ -27,7 +27,7 @@ uses
   Struct, ScriptDlg, Main, PathFunc, CmnFunc, CmnFunc2, FileClass, RedirFunc,
   Struct, ScriptDlg, Main, PathFunc, CmnFunc, CmnFunc2, FileClass, RedirFunc,
   Install, InstFunc, InstFnc2, Msgs, MsgIDs, NewDisk, BrowseFunc, Wizard, VerInfo,
   Install, InstFunc, InstFnc2, Msgs, MsgIDs, NewDisk, BrowseFunc, Wizard, VerInfo,
   SetupTypes, Int64Em, MD5, SHA1, Logging, SetupForm, RegDLL, Helper,
   SetupTypes, Int64Em, MD5, SHA1, Logging, SetupForm, RegDLL, Helper,
-  SpawnClient, UninstProgressForm, ASMInline, DotNet;
+  SpawnClient, UninstProgressForm, ASMInline, DotNet, Msi;
 
 
 var
 var
   ScaleBaseUnitsInitialized: Boolean;
   ScaleBaseUnitsInitialized: Boolean;
@@ -1559,6 +1559,12 @@ begin
     VersionNumbers.LS := UInt64(Stack.GetInt64(PStart-1)) and $FFFFFFFF;
     VersionNumbers.LS := UInt64(Stack.GetInt64(PStart-1)) and $FFFFFFFF;
     Stack.SetString(PStart, Format('%u.%u.%u.%u', [VersionNumbers.MS shr 16,
     Stack.SetString(PStart, Format('%u.%u.%u.%u', [VersionNumbers.MS shr 16,
       VersionNumbers.MS and $FFFF, VersionNumbers.LS shr 16, VersionNumbers.LS and $FFFF]));
       VersionNumbers.MS and $FFFF, VersionNumbers.LS shr 16, VersionNumbers.LS and $FFFF]));
+  end else if Proc.Name = 'STRTOVERSION' then begin
+    if StrToVersionNumbers(Stack.GetString(PStart-1), VersionNumbers) then begin
+      Stack.SetInt64(PStart-2, (Int64(VersionNumbers.MS) shl 32) or VersionNumbers.LS);
+      Stack.SetBool(PStart, True);
+    end else
+      Stack.SetBool(PStart, False);
   end else
   end else
     Result := False;
     Result := False;
 end;
 end;
@@ -1878,6 +1884,7 @@ var
   S: String;
   S: String;
   AnsiS: AnsiString;
   AnsiS: AnsiString;
   Arr: TPSVariantIFC;
   Arr: TPSVariantIFC;
+  ErrorCode: Cardinal;
 begin
 begin
   PStart := Stack.Count-1;
   PStart := Stack.Count-1;
   Result := True;
   Result := True;
@@ -2009,9 +2016,13 @@ begin
   end else if Proc.Name = 'GETUNINSTALLPROGRESSFORM' then begin
   end else if Proc.Name = 'GETUNINSTALLPROGRESSFORM' then begin
     Stack.SetClass(PStart, GetUninstallProgressForm);
     Stack.SetClass(PStart, GetUninstallProgressForm);
   end else if Proc.Name = 'CREATECALLBACK' then begin
   end else if Proc.Name = 'CREATECALLBACK' then begin
-   Stack.SetInt(PStart, CreateCallback(Stack.Items[PStart-1]));
+    Stack.SetInt(PStart, CreateCallback(Stack.Items[PStart-1]));
   end else if Proc.Name = 'ISDOTNETINSTALLED' then begin
   end else if Proc.Name = 'ISDOTNETINSTALLED' then begin
-   Stack.SetBool(PStart, IsDotNetInstalled(InstallDefaultRegView, TDotNetVersion(Stack.GetInt(PStart-1)), Stack.GetInt(PStart-2)));
+    Stack.SetBool(PStart, IsDotNetInstalled(InstallDefaultRegView, TDotNetVersion(Stack.GetInt(PStart-1)), Stack.GetInt(PStart-2)));
+  end else if Proc.Name = 'ISMSIPRODUCTINSTALLED' then begin
+    Stack.SetBool(PStart, IsMsiProductInstalled(Stack.GetString(PStart-1), Stack.GetInt64(PStart-2), ErrorCode));
+    if ErrorCode <> 0 then
+      raise Exception.Create(Win32ErrorString(ErrorCode));
   end else
   end else
     Result := False;
     Result := False;
 end;
 end;