فهرست منبع

Merge branch 'ismsiproductinstalled' into main

# Conflicts:
#	whatsnew.htm
Martijn Laan 4 سال پیش
والد
کامیت
aecb90c83b
5فایلهای تغییر یافته به همراه140 افزوده شده و 10 حذف شده
  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
   if not Result then
     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>
       </function>    
     </subcategory>
@@ -2062,7 +2075,16 @@ end;</pre>
         <name>VersionToStr</name>
         <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>
-        <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>
       </function>
     </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 }
-  VerInfoTable: array [0..10] of AnsiString =
+  VerInfoTable: array [0..11] of AnsiString =
   (
     'function GetVersionNumbers(const Filename: String; var VersionMS, VersionLS: Cardinal): 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;',
     'procedure UnpackVersionNumbers(const Version: Int64; var VersionMS, VersionLS: Cardinal);',
     '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 }
@@ -345,7 +346,7 @@ const
   );
 
   { Other }
-  OtherTable: array [0..31] of AnsiString =
+  OtherTable: array [0..32] of AnsiString =
   (
     'procedure BringToFrontAndRestore;',
     'function WizardDirValue: String;',
@@ -378,7 +379,8 @@ const
     'function EnableFsRedirection(const Enable: Boolean): Boolean;',
     'function GetUninstallProgressForm: TUninstallProgressForm;',
     '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

+ 14 - 3
Projects/ScriptFunc_R.pas

@@ -27,7 +27,7 @@ uses
   Struct, ScriptDlg, Main, PathFunc, CmnFunc, CmnFunc2, FileClass, RedirFunc,
   Install, InstFunc, InstFnc2, Msgs, MsgIDs, NewDisk, BrowseFunc, Wizard, VerInfo,
   SetupTypes, Int64Em, MD5, SHA1, Logging, SetupForm, RegDLL, Helper,
-  SpawnClient, UninstProgressForm, ASMInline, DotNet;
+  SpawnClient, UninstProgressForm, ASMInline, DotNet, Msi;
 
 var
   ScaleBaseUnitsInitialized: Boolean;
@@ -1559,6 +1559,12 @@ begin
     VersionNumbers.LS := UInt64(Stack.GetInt64(PStart-1)) and $FFFFFFFF;
     Stack.SetString(PStart, Format('%u.%u.%u.%u', [VersionNumbers.MS shr 16,
       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
     Result := False;
 end;
@@ -1878,6 +1884,7 @@ var
   S: String;
   AnsiS: AnsiString;
   Arr: TPSVariantIFC;
+  ErrorCode: Cardinal;
 begin
   PStart := Stack.Count-1;
   Result := True;
@@ -2009,9 +2016,13 @@ begin
   end else if Proc.Name = 'GETUNINSTALLPROGRESSFORM' then begin
     Stack.SetClass(PStart, GetUninstallProgressForm);
   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
-   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
     Result := False;
 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>
 <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>
 </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>
 <ul>