diff --git a/.gitignore b/.gitignore index 301974b66a..c2300810f6 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ xcuserdata /packages/ /win32/distrib-* +/win32/uninst-helper.dll /*.snap /poedit_source.tar.bz2 diff --git a/Poedit.vcxproj b/Poedit.vcxproj index c15fedbc08..f48fe55b71 100644 --- a/Poedit.vcxproj +++ b/Poedit.vcxproj @@ -1,5 +1,6 @@  + @@ -357,5 +358,6 @@ + \ No newline at end of file diff --git a/packages.config b/packages.config index f51d96f1d4..684d7059c7 100644 --- a/packages.config +++ b/packages.config @@ -3,5 +3,6 @@ + \ No newline at end of file diff --git a/win32/distrib.proj b/win32/distrib.proj new file mode 100644 index 0000000000..4d6581949e --- /dev/null +++ b/win32/distrib.proj @@ -0,0 +1,73 @@ + + + + + + + + + + $(MSBuildProjectDirectory)\..\packages\MSBuildTasks.1.5.0.235\tools + + + + + ../x64/$(Configuration) + "$(VCToolsInstallDir)\bin\HostX64\x86" + "$(VCToolsInstallDir)\lib\x86" + ..\packages\Tools.InnoSetup.6.2.2\tools\ISCC.exe + /dCONFIG=$(Configuration) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + call "$(VCINSTALLDIR)\Auxiliary\Build\vcvarsall.bat" x64_x86 || exit /b 666 + cl /LD /O1 /MT /W3 uninst-helper.c || exit /b 666 + + + + + + + + + + + + + + diff --git a/win32/poedit.iss b/win32/poedit.iss index 104bfce643..f9c326df4e 100644 --- a/win32/poedit.iss +++ b/win32/poedit.iss @@ -35,6 +35,8 @@ #define VERSION "3.4.2" #define VERSION_WIN VERSION + "." + Str(POEDIT_GIT_BUILD_NUMBER) +#define APP_ID "{68EB2C37-083A-4303-B5D8-41FA67E50B8F}" + #ifndef CRT_REDIST #define CRT_REDIST GetEnv("VCToolsRedistDir") + "\x64\Microsoft.VC143.CRT" #endif @@ -70,7 +72,7 @@ ShowLanguageDialog=no DisableWelcomePage=true AllowUNCPath=true InternalCompressLevel=ultra -AppID={{68EB2C37-083A-4303-B5D8-41FA67E50B8F} +AppID={{#APP_ID} VersionInfoVersion={#VERSION_WIN} VersionInfoTextVersion={#VERSION} AppCopyright=Copyright © 1999-2023 Vaclav Slavik @@ -99,6 +101,7 @@ VersionInfoProductTextVersion={#VERSION} DisableDirPage=auto [Files] +Source: win32\uninst-helper.dll; Flags: dontcopy Source: {#BINDIR}\Poedit.exe; DestDir: {app}; Flags: ignoreversion Source: {#BINDIR}\*.dll; DestDir: {app}; Flags: ignoreversion Source: {#BINDIR}\icudt*.dat; DestDir: {app} @@ -209,6 +212,7 @@ Name: "ukrainian"; MessagesFile: "compiler:Languages\Ukrainian.isl" [CustomMessages] OpenAfterInstall=Open Poedit after installation +Uninstall32bit=Uninstalling 32-bit version of Poedit... brazilianportuguese.OpenAfterInstall=Abrir o Poedit após a instalação catalan.OpenAfterInstall=Obre el Poedit després de la instal·lació corsican.OpenAfterInstall=Apre Poedit dopu à l'installazione @@ -229,3 +233,48 @@ slovenian.OpenAfterInstall=Odpri Poedit po namestitvi spanish.OpenAfterInstall=Abrir Poedit tras la instalación turkish.OpenAfterInstall=Kurulumdan sonra Poedit'i aç ukrainian.OpenAfterInstall=Відкрити Poedit після встановлення + +[Code] + +const UninstallKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{#APP_ID}_is1'; + +function SafelyUninstall32BitVersion(uninstallerExe: String): Integer; +external 'SafelyUninstall32BitVersion@files:uninst-helper.dll cdecl setuponly'; + +function Find32bitUninstallerPath(): String; +var + uninst: String; +begin + if (not RegKeyExists(HKCU64, UninstallKey)) and (not RegKeyExists(HKLM64, UninstallKey)) and RegKeyExists(HKLM32, UninstallKey) then + begin + RegQueryStringValue(HKLM32, UninstallKey, 'UninstallString', uninst); + Result := RemoveQuotes(uninst); + end + else + Result := ''; +end; + + +procedure Uninstall32bitVersionIfPresent; +var + uninstaller: String; +begin + uninstaller := Find32bitUninstallerPath(); + if (uninstaller <> '') then + begin + Log('Migrating from 32bit install, uninstalling via ' + uninstaller); + WizardForm.StatusLabel.Caption := CustomMessage('Uninstall32bit'); + SafelyUninstall32BitVersion(uninstaller); + end; +end; + + +procedure CurStepChanged(CurStep: TSetupStep); +begin + if (CurStep = ssInstall) then + begin + Uninstall32bitVersionIfPresent(); + end; +end; + + diff --git a/win32/uninst-helper.c b/win32/uninst-helper.c new file mode 100644 index 0000000000..668c9b1207 --- /dev/null +++ b/win32/uninst-helper.c @@ -0,0 +1,93 @@ +/* + * This file is part of Poedit (https://poedit.net) + * + * Copyright (C) 2024 Vaclav Slavik + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _M_IX86 +#error This file must be compiled for 32bit x86 target +#endif + +// This DLL is used by the uninstaller to remove Poedit's 32bit version during upgrades. +// +// Inno Setup treats 64bit and 32bit versions as separate applications, so it can't +// upgrade existing install seamlessly; we need to remove the old version first. +// +// Unfortunately, the installer for older versions of Poedit uninstalled HKCU registry +// keys, i.e. deleted user settings; that wouldn't be good during a simple upgrade. +// Hence this DLL: it is loaded by the uninstaller and it removes the old version, +// while taking care to preserve user settings. + +#define UNICODE +#include +#pragma comment(lib, "advapi32.lib") + + +__declspec(dllexport) int SafelyUninstall32BitVersion(LPCWSTR uninstallerExe) +{ + // check if the uninstaller file exists: + if (GetFileAttributesW(uninstallerExe) == INVALID_FILE_ATTRIBUTES) + return 0; + + // rename the HKCU\Software\Vaclav Slavik\Poedit key to *.backup: + HKEY key = 0; + if (SUCCEEDED(RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Vaclav Slavik\\Poedit", 0, KEY_ALL_ACCESS, &key))) + { + if (FAILED(RegRenameKey(key, NULL, L"Poedit.backup"))) + { + RegCloseKey(key); + key = 0; + } + } + + // run the uninstaller: + wchar_t cmdline[10240]; + wcscpy_s(cmdline, 10240, uninstallerExe); + wcscat_s(cmdline, 10240, L" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART"); + + STARTUPINFOW si = { sizeof(si) }; + PROCESS_INFORMATION pi; + if (CreateProcessW(uninstallerExe, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) + { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + // Inno Setup's uninstaller spawns a subprocess in order to be able to delete itself, so wait until + // the uninstaller is removed (or 60 seconds pass, whichever comes first): + for (int i = 0; i < 600; i++) + { + if (GetFileAttributesW(uninstallerExe) == INVALID_FILE_ATTRIBUTES) + break; + Sleep(100); + } + + // rename the backup key back: + if (key) + { + RegRenameKey(key, NULL, L"Poedit"); + RegCloseKey(key); + } + + return 1; +}