1
0
Эх сурвалжийг харах

Bug 83: MSI installation package

https://winscp.net/tracker/83

Source commit: 599e90f2406250f67542a77f1594218f68a7ac67
Martin Prikryl 2 жил өмнө
parent
commit
75dd266823

+ 4 - 0
deployment/winscp.isl

@@ -60,6 +60,10 @@ Currency=USD
 ImportSites=Looking for sites to import...
 AcceptButton=&Accept
 IncompleteTranslation=You are about to use an incomplete translation. It is completed by %1%% only.%n%nUntranslated parts of the user interface will be shown in their original English version.%n%nVisit WinSCP website at winscp.net to check, if newer version of the translation is available.
+MsiInstallation=WinSCP is already installed on this system using an MSI installer. Please uninstall that before running this standalone executable installer.
+
+; MSI (sic)
+InnoSetupInstallation=WinSCP is already installed on this system using a standalone executable installer. Please uninstall that before running this MSI installer.
 
 [CustomOptions]
 TranslationCompleteness=100

+ 7 - 0
deployment/winscpsetup.iss

@@ -809,6 +809,13 @@ begin
     end;
   end;
 
+  if IsMsiProductInstalled('{029F9450-CFEF-4408-A2BB-B69ECE29EB18}', 0) and
+     (not CmdLineParamExists('/OverrideMsi')) then
+  begin
+    MsgBox(CustomMessage('MsiInstallation'), mbError, MB_OK);
+    Abort;
+  end;
+
   Result := True;
 end;
 

+ 1 - 1
source/WinSCP.cbproj

@@ -49,7 +49,7 @@
 	</PropertyGroup>
 	<PropertyGroup Condition="'$(Base)'!=''">
 		<_TCHARMapping>wchar_t</_TCHARMapping>
-		<AllPackageLibs>vcl.lib;rtl.lib;vclx.lib;ws2_32.lib;secur32.lib;My.lib;DriveDir.lib;DragDropP.lib;tb2k.lib;tbxp.lib;bcbie.lib;Crypt32.lib;PngComponents.lib;xmlrtl.lib;vclactnband.lib;vclimg.lib;winhttp.lib;jcl.lib;vclie.lib;urlmon.lib;shlwapi.lib;powrprof.lib;soaprtl.lib;fmx.lib;dbrtl.lib;inet.lib;psapi.lib</AllPackageLibs>
+		<AllPackageLibs>vcl.lib;rtl.lib;vclx.lib;ws2_32.lib;secur32.lib;My.lib;DriveDir.lib;DragDropP.lib;tb2k.lib;tbxp.lib;bcbie.lib;Crypt32.lib;PngComponents.lib;xmlrtl.lib;vclactnband.lib;vclimg.lib;winhttp.lib;jcl.lib;vclie.lib;urlmon.lib;shlwapi.lib;powrprof.lib;soaprtl.lib;fmx.lib;dbrtl.lib;inet.lib;psapi.lib;msi.lib</AllPackageLibs>
 		<BCC_AllWarnings>true</BCC_AllWarnings>
 		<BCC_ExtendedErrorInfo>true</BCC_ExtendedErrorInfo>
 		<BCC_OptimizeForSpeed>true</BCC_OptimizeForSpeed>

+ 43 - 4
source/windows/Setup.cpp

@@ -34,6 +34,7 @@
 #include <Soap.HTTPUtil.hpp>
 #include <Web.HTTPApp.hpp>
 #include <System.IOUtils.hpp>
+#include <WinApi.h>
 //---------------------------------------------------------------------------
 #define KEY _T("SYSTEM\\CurrentControlSet\\Control\\") \
             _T("Session Manager\\Environment")
@@ -805,11 +806,12 @@ UnicodeString __fastcall ProgramUrl(UnicodeString URL)
     FORMAT(L"%d.%d.%d.%d",
       (HIWORD(FileInfo->dwFileVersionMS), LOWORD(FileInfo->dwFileVersionMS),
        HIWORD(FileInfo->dwFileVersionLS), LOWORD(FileInfo->dwFileVersionLS)));
+  int IsInstalledFlag = (IsInstalled() ? 1 : (IsInstalledMsi() ? 2 : 0));
   UnicodeString Params =
     FORMAT(L"v=%s&lang=%s&isinstalled=%d",
       (CurrentVersionStr,
       GUIConfiguration->AppliedLocaleHex,
-      int(IsInstalled())));
+      IsInstalledFlag));
 
   if (Configuration->IsUnofficial)
   {
@@ -1983,6 +1985,12 @@ bool __fastcall AnyOtherInstanceOfSelf()
   return Result;
 }
 //---------------------------------------------------------------------------
+static bool DoIsPathToExe(const UnicodeString & Path)
+{
+  UnicodeString ExePath = ExcludeTrailingBackslash(ExtractFilePath(Application->ExeName));
+  return IsPathToSameFile(ExePath, Path);
+}
+//---------------------------------------------------------------------------
 static bool __fastcall DoIsInstalled(HKEY RootKey)
 {
   std::unique_ptr<TRegistry> Registry(new TRegistry(KEY_READ));
@@ -1992,21 +2000,52 @@ static bool __fastcall DoIsInstalled(HKEY RootKey)
   if (Result)
   {
     UnicodeString InstallPath = ExcludeTrailingBackslash(Registry->ReadString(L"Inno Setup: App Path"));
-    UnicodeString ExePath = ExcludeTrailingBackslash(ExtractFilePath(Application->ExeName));
     Result =
       !InstallPath.IsEmpty() &&
-      IsPathToSameFile(ExePath, InstallPath);
+      DoIsPathToExe(InstallPath);
   }
   return Result;
 }
 //---------------------------------------------------------------------------
-bool __fastcall IsInstalled()
+bool IsInstalled()
 {
   return
     DoIsInstalled(HKEY_LOCAL_MACHINE) ||
     DoIsInstalled(HKEY_CURRENT_USER);
 }
 //---------------------------------------------------------------------------
+static int GIsInstalledMsi = -1;
+bool IsInstalledMsi()
+{
+  if (GIsInstalledMsi < 0)
+  {
+    GIsInstalledMsi = 0;
+    wchar_t ProductCode[MAX_GUID_CHARS + 1];
+    if (MsiEnumRelatedProducts(L"{029F9450-CFEF-4408-A2BB-B69ECE29EB18}", 0, 0, ProductCode) == ERROR_SUCCESS)
+    {
+      UnicodeString InstallPath;
+      InstallPath.SetLength(MAX_PATH);
+      unsigned long Size = InstallPath.Length() + 1;
+      int ErrorCode = MsiGetProductInfo(ProductCode, INSTALLPROPERTY_INSTALLLOCATION, InstallPath.c_str(), &Size);
+      if (ErrorCode == ERROR_MORE_DATA)
+      {
+        InstallPath.SetLength(Size);
+        Size++;
+        ErrorCode = MsiGetProductInfo(ProductCode, INSTALLPROPERTY_INSTALLLOCATION, InstallPath.c_str(), &Size);
+      }
+      if (ErrorCode == ERROR_SUCCESS)
+      {
+        InstallPath.SetLength(Size);
+        if (DoIsPathToExe(InstallPath))
+        {
+          GIsInstalledMsi = 1;
+        }
+      }
+    }
+  }
+  return (GIsInstalledMsi > 0);
+}
+//---------------------------------------------------------------------------
 static TStringList * __fastcall TextToTipList(const UnicodeString & Text)
 {
   std::unique_ptr<TStringList> List(new TStringList());

+ 2 - 1
source/windows/Setup.h

@@ -25,7 +25,8 @@ void __fastcall StopUpdateThread();
 UnicodeString __fastcall CampaignUrl(UnicodeString URL);
 void __fastcall UpdateJumpList(TStrings * SessionNames, TStrings * WorkspaceNames);
 bool __fastcall AnyOtherInstanceOfSelf();
-bool __fastcall IsInstalled();
+bool IsInstalled();
+bool IsInstalledMsi();
 UnicodeString __fastcall ProgramUrl(UnicodeString URL);
 void __fastcall AutoShowNewTip();
 bool __fastcall AnyTips();

+ 20 - 0
source/windows/WinApi.h

@@ -192,4 +192,24 @@ public:
 #define SES_EX_HANDLEFRIENDLYURL (0x100)
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+// msi.h (available, but does not compile)
+extern "C"
+{
+#define MAX_GUID_CHARS  38
+#define INSTALLPROPERTY_INSTALLLOCATION       __TEXT("InstallLocation")
+UINT WINAPI MsiEnumRelatedProductsW(
+  __in LPCWSTR  lpUpgradeCode,                               // upgrade code of products to enumerate
+  __reserved DWORD     dwReserved,                            // reserved, must be 0
+  __in DWORD     iProductIndex,                               // 0-based index into registered products
+  __out_ecount(MAX_GUID_CHARS+1)  LPWSTR   lpProductBuf);    // buffer of char count: 39 (size of string GUID)
+#define MsiEnumRelatedProducts  MsiEnumRelatedProductsW
+//---------------------------------------------------------------------------
+UINT WINAPI MsiGetProductInfoW(
+  __in LPCWSTR   szProduct,                              // product code
+  __in LPCWSTR   szAttribute,                            // attribute name, case-sensitive
+  __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf,     // returned value, NULL if not desired
+  __inout_opt                     LPDWORD pcchValueBuf);  // in/out buffer character count
+#define MsiGetProductInfo  MsiGetProductInfoW
+}
+//---------------------------------------------------------------------------
 #endif  // WinApiH

+ 1 - 0
source/windows/WinMain.cpp

@@ -603,6 +603,7 @@ void __fastcall UpdateStaticUsage()
   bool InProgramFiles = AnsiSameText(ExeName.SubString(1, ProgramsFolder.Length()), ProgramsFolder);
   Configuration->Usage->Set(L"InProgramFiles", InProgramFiles);
   Configuration->Usage->Set(L"IsInstalled", IsInstalled());
+  Configuration->Usage->Set(L"IsInstalledMsi", IsInstalledMsi());
   Configuration->Usage->Set(L"Wine", IsWine());
   Configuration->Usage->Set(L"NetFrameworkVersion", GetNetVersionStr());
   Configuration->Usage->Set(L"NetCoreVersion", GetNetCoreVersionStr());