Bläddra i källkod

Merge branch 'ismsiproductinstalled' into main

# Conflicts:
#	whatsnew.htm
Martijn Laan 4 år sedan
förälder
incheckning
aecb90c83b
5 ändrade filer med 140 tillägg och 10 borttagningar
  1. 23 1
      ISHelp/isxfunc.xml
  2. 90 0
      Projects/Msi.pas
  3. 6 4
      Projects/ScriptFunc.pas
  4. 14 3
      Projects/ScriptFunc_R.pas
  5. 7 2
      whatsnew.htm

+ 23 - 1
ISHelp/isxfunc.xml

@@ -580,6 +580,19 @@ begin
   Result := IsDotNetInstalled(net462, 0); //Returns True if .NET Framework version 4.6.2 is installed, or a compatible version such as 4.8
   Result := IsDotNetInstalled(net462, 0); //Returns True if .NET Framework version 4.6.2 is installed, or a compatible version such as 4.8
   if not Result then
   if not Result then
     SuppressibleMsgBox(FmtMessage(SetupMessage(msgWinVersionTooLowError), ['.NET Framework', '4.6.2']), mbCriticalError, MB_OK, IDOK);
     SuppressibleMsgBox(FmtMessage(SetupMessage(msgWinVersionTooLowError), ['.NET Framework', '4.6.2']), mbCriticalError, MB_OK, IDOK);
+end;</pre></example>
+      </function>    
+      <function>
+        <name>IsMsiProductInstalled</name>
+        <prototype>function IsMsiProductInstalled(const UpgradeCode: String; const PackedMinVersion: Int64): Boolean;</prototype>
+        <description><p>Returns True if a MSI product with the specified UpgradeCode and PackedMinVersion is installed.</p>
+<p>If there are multiple products installed with the specified UpgradeCode only the version of the first product returned by the system is checked.</p>
+<p>An exception will be raised if an error occurs.</p></description>
+        <example><pre>function InitializeSetup: Boolean;
+begin
+  Result := IsMsiProductInstalled('{20400CF0-DE7C-327E-9AE4-F0F38D9085F8}', PackVersionComponents(12, 0, 0, 0)); //Returns True if Visual C++ 2013 Redistributable (x64) is installed
+  if not Result then
+    SuppressibleMsgBox(FmtMessage(SetupMessage(msgWinVersionTooLowError), ['Visual C++ 2013 Redistributable (x64)', '12.0']), mbCriticalError, MB_OK, IDOK);
 end;</pre></example>
 end;</pre></example>
       </function>    
       </function>    
     </subcategory>
     </subcategory>
@@ -2062,7 +2075,16 @@ end;</pre>
         <name>VersionToStr</name>
         <name>VersionToStr</name>
         <prototype>function VersionToStr(const Version: Int64): String;</prototype>
         <prototype>function VersionToStr(const Version: Int64): String;</prototype>
         <description><p>Returns the specified packed version as a string (in "0.0.0.0" format).</p></description>
         <description><p>Returns the specified packed version as a string (in "0.0.0.0" format).</p></description>
-        <seealso><p><link topic="isxfunc_PackVersionNumbers">PackVersionNumbers</link><br />
+        <seealso><p><link topic="isxfunc_StrToVersion">StrToVersion</link><br />
+<link topic="isxfunc_UnpackVersionNumbers">UnpackVersionNumbers</link><br />
+<link topic="isxfunc_UnpackVersionComponents">UnpackVersionComponents</link></p></seealso>
+      </function>
+      <function>
+        <name>StrToVersion</name>
+        <prototype>function StrToVersion(const Version: String; var Version: Int64): Boolean;</prototype>
+        <description><p>Returns the specified string (in "0.0.0.0" format) as a packed version. Returns True if successful, False otherwise.</p></description>
+        <seealso><p><link topic="isxfunc_VersionToStr">VersionToStr</link><br />
+<link topic="isxfunc_PackVersionNumbers">PackVersionNumbers</link><br />
 <link topic="isxfunc_PackVersionComponents">PackVersionComponents</link></p></seealso>
 <link topic="isxfunc_PackVersionComponents">PackVersionComponents</link></p></seealso>
       </function>
       </function>
     </subcategory>
     </subcategory>

+ 90 - 0
Projects/Msi.pas

@@ -0,0 +1,90 @@
+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
+    Inc(VersionStringSize);
+    SetLength(VersionString, VersionStringSize);
+    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;

+ 7 - 2
whatsnew.htm

@@ -30,11 +30,16 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
 
 
 <p><a name="6.1.3"></a><span class="ver">6.1.3-dev </span><span class="date">(?)</span></p>
 <p><a name="6.1.3"></a><span class="ver">6.1.3-dev </span><span class="date">(?)</span></p>
 <ul>
 <ul>
-  <li>Pascal Scripting change: Added new <tt>ItemFontStyle</tt> and <tt>SubItemFontStyle</tt> properties to the <tt>TNewCheckListBox</tt> support class. See the <i><a href="https://jrsoftware.github.io/issrc/Examples/CodeClasses.iss">CodeClasses.iss</a></i> example script for an example.</li>
+  <li>Pascal Scripting changes:
+  <ul>
+    <li>Added new <tt>ItemFontStyle</tt> and <tt>SubItemFontStyle</tt> properties to the <tt>TNewCheckListBox</tt> support class. See the <i><a href="https://jrsoftware.github.io/issrc/Examples/CodeClasses.iss">CodeClasses.iss</a></i> example script for an example.</li>
+    <li>Added new <tt>IsMsiProductInstalled</tt> and <tt>StrToVersion</tt> support functions.</li>
+  </ul>
+  </li>
   <li>Compiler IDE change: <i>Fix:</i> Autocomplete support for event functions listed some procedures as functions.</li>
   <li>Compiler IDE change: <i>Fix:</i> Autocomplete support for event functions listed some procedures as functions.</li>
 </ul>
 </ul>
 
 
-<p>Contributions via <a href="https://github.com/jrsoftware/issrc" target="_blank">GitHub</a>: <b>Thanks to Sergii Leonov for their contributions.</b></p>
+<p>Contributions via <a href="https://github.com/jrsoftware/issrc" target="_blank">GitHub</a>: <b>Thanks to Sergii Leonov and Dom Gries for their contributions.</b></p>
 
 
 <p><a name="6.1.2"></a><span class="ver">6.1.2 </span><span class="date">(2020-11-15)</span></p>
 <p><a name="6.1.2"></a><span class="ver">6.1.2 </span><span class="date">(2020-11-15)</span></p>
 <ul>
 <ul>