mirror of
https://github.com/koho/frpmgr.git
synced 2025-10-20 16:03:47 +08:00
Support reinstallation of the same version
This commit is contained in:
@ -243,9 +243,10 @@ __declspec(dllexport) UINT __stdcall EvaluateFrpServices(MSIHANDLE installer)
|
||||
record = MsiCreateRecord(5);
|
||||
if (!record)
|
||||
continue;
|
||||
BOOL start = services[i].ServiceStatusProcess.dwWin32ExitCode == ERROR_FAIL_NOACTION_REBOOT;
|
||||
MsiRecordSetStringW(record, 1, identifier);
|
||||
MsiRecordSetStringW(record, 2, services[i].lpServiceName);
|
||||
MsiRecordSetInteger(record, 3, msidbServiceControlEventStop | msidbServiceControlEventUninstallStop | (legacy == 0 ? msidbServiceControlEventDelete : 0) | msidbServiceControlEventUninstallDelete);
|
||||
MsiRecordSetInteger(record, 3, (start ? msidbServiceControlEventStart : msidbServiceControlEventStop) | msidbServiceControlEventUninstallStop | (legacy == 0 ? msidbServiceControlEventDelete : 0) | msidbServiceControlEventUninstallDelete);
|
||||
MsiRecordSetStringW(record, 4, L"frpmgr.exe");
|
||||
MsiRecordSetInteger(record, 5, 1);
|
||||
ret = MsiViewExecute(view, record);
|
||||
|
@ -43,8 +43,8 @@ if not defined TARGET_%ARCH% (
|
||||
|
||||
:build_actions
|
||||
%WINDRES% -DVERSION_ARRAY=%VERSION:.=,% -DVERSION_STR=%VERSION% -o %PLAT_DIR%\actions.res.obj -i actions\version.rc -O coff -c 65001 || exit /b 1
|
||||
set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -municode -DUNICODE -D_UNICODE -DNDEBUG
|
||||
set LDFLAGS=-shared -s -Wl,--kill-at -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols
|
||||
set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -municode -DUNICODE -D_UNICODE -DNDEBUG
|
||||
set LDFLAGS=-shared -s -Wl,--kill-at -Wl,--major-os-version=6 -Wl,--minor-os-version=2 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=2 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols
|
||||
set LDLIBS=-lmsi -lole32 -lshlwapi -lshell32 -ladvapi32
|
||||
%CC% %CFLAGS% %LDFLAGS% -o %PLAT_DIR%\actions.dll actions\actions.c %PLAT_DIR%\actions.res.obj %LDLIBS% || exit /b 1
|
||||
goto :eof
|
||||
@ -83,9 +83,9 @@ if not defined TARGET_%ARCH% (
|
||||
echo ERROR: UpgradeCode was not found.
|
||||
exit /b 1
|
||||
)
|
||||
set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -municode -DUNICODE -D_UNICODE -DNDEBUG -DUPGRADE_CODE=L\"{%UPGRADE_CODE%}\" -DVERSION=L\"%VERSION%\"
|
||||
set LDFLAGS=-s -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -mwindows
|
||||
set LDLIBS=-lmsi -lole32 -lshlwapi -ladvapi32 -luser32
|
||||
set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -municode -DUNICODE -D_UNICODE -DNDEBUG -DUPGRADE_CODE=L\"{%UPGRADE_CODE%}\" -DVERSION=L\"%VERSION%\"
|
||||
set LDFLAGS=-s -Wl,--major-os-version=6 -Wl,--minor-os-version=2 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=2 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -mwindows
|
||||
set LDLIBS=-lmsi -lole32 -lshlwapi -ladvapi32 -luser32 -lcomctl32
|
||||
%CC% %CFLAGS% %LDFLAGS% -o %PLAT_DIR%\setup.exe setup\setup.c %PLAT_DIR%\setup.res.obj %LDLIBS% || exit /b 1
|
||||
goto :eof
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
Detect previous install folder if it's a upgrade
|
||||
-->
|
||||
<SetProperty Id="INSTALLFOLDER" Value="[PREVINSTALLFOLDER]" After="AppSearch" Sequence="first">
|
||||
WIX_UPGRADE_DETECTED
|
||||
PREVINSTALLFOLDER
|
||||
</SetProperty>
|
||||
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
||||
@ -125,7 +125,7 @@
|
||||
-->
|
||||
<CustomAction Id="EvaluateFrpServices" BinaryKey="actions.dll" DllEntry="EvaluateFrpServices" />
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="EvaluateFrpServices" After="InstallInitialize">NOT (UPGRADINGPRODUCTCODE AND (REMOVE="ALL"))</Custom>
|
||||
<Custom Action="EvaluateFrpServices" After="InstallInitialize">NOT ((UPGRADINGPRODUCTCODE OR SAVESTATE) AND (REMOVE="ALL"))</Custom>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<!--
|
||||
@ -143,7 +143,7 @@
|
||||
<CustomAction Id="RemoveFrpFiles" BinaryKey="actions.dll" DllEntry="RemoveFrpFiles" Impersonate="no" Execute="deferred" />
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="RemoveFrpFiles.SetProperty" After="DeleteServices" />
|
||||
<Custom Action="RemoveFrpFiles" After="RemoveFrpFiles.SetProperty">(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>
|
||||
<Custom Action="RemoveFrpFiles" After="RemoveFrpFiles.SetProperty">(NOT UPGRADINGPRODUCTCODE) AND (NOT SAVESTATE) AND (REMOVE="ALL")</Custom>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<!--
|
||||
|
@ -7,4 +7,10 @@
|
||||
#define IDC_LANG_COMBO 1000
|
||||
#define IDC_STATIC -1
|
||||
|
||||
#define IDS_TITLE 200
|
||||
#define IDS_MANAGEMENT 201
|
||||
#define IDS_OPERATION 202
|
||||
#define IDS_REINSTALL 203
|
||||
#define IDS_UNINSTALL 204
|
||||
|
||||
#endif
|
||||
|
@ -133,3 +133,63 @@ LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_MODERN
|
||||
LANG_DIALOG_TEMPLATE(
|
||||
TITLE_ES_ES, "Seleccione el idioma para la instalación entre las opciones siguientes.", "Aceptar", "Cancelar"
|
||||
)
|
||||
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TITLE TITLE_EN_US
|
||||
IDS_MANAGEMENT "Manage Current Product"
|
||||
IDS_OPERATION "Select the action you want to perform."
|
||||
IDS_REINSTALL "Reinstall with ""%1""%rThis operation requires suspending the running services.\0 "
|
||||
IDS_UNINSTALL "Uninstall"
|
||||
END
|
||||
|
||||
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TITLE TITLE_ZH_CN
|
||||
IDS_MANAGEMENT "管理当前产品"
|
||||
IDS_OPERATION "选择希望执行的操作。"
|
||||
IDS_REINSTALL "以 “%1” 重新安装%r此操作需要暂停正在运行的服务。\0 "
|
||||
IDS_UNINSTALL "卸载"
|
||||
END
|
||||
|
||||
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TITLE TITLE_ZH_TW
|
||||
IDS_MANAGEMENT "管理現行產品"
|
||||
IDS_OPERATION "選取您要執行的作業。"
|
||||
IDS_REINSTALL "以「%1」重新安裝%r此操作需要暫停正在運作的服務。\0 "
|
||||
IDS_UNINSTALL "移除"
|
||||
END
|
||||
|
||||
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TITLE TITLE_JA_JP
|
||||
IDS_MANAGEMENT "現在のインストールの管理"
|
||||
IDS_OPERATION "実行するアクションを選択します。"
|
||||
IDS_REINSTALL "「%1」で再インストールします%rこの操作では実行中のサービスを一時停止する必要があります。\0 "
|
||||
IDS_UNINSTALL "アンインストール"
|
||||
END
|
||||
|
||||
LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TITLE TITLE_KO_KR
|
||||
IDS_MANAGEMENT "현재 제품 관리"
|
||||
IDS_OPERATION "수행하고자 하는 작업을 선택하세요."
|
||||
IDS_REINSTALL """%1""로 다시 설치%r이 작업을 수행하려면 실행 중인 서비스를 중단해야 합니다.\0 "
|
||||
IDS_UNINSTALL "제거하다"
|
||||
END
|
||||
|
||||
LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_MODERN
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TITLE TITLE_ES_ES
|
||||
IDS_MANAGEMENT "Administrar el producto actual"
|
||||
IDS_OPERATION "Seleccione la acción que desea realizar."
|
||||
IDS_REINSTALL "Reinstalar con ""%1""%rEsta operación requiere suspender los servicios en ejecución.\0 "
|
||||
IDS_UNINSTALL "Desinstalar"
|
||||
END
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <msi.h>
|
||||
#include <shlwapi.h>
|
||||
#include <sddl.h>
|
||||
#include <commctrl.h>
|
||||
#include <stdio.h>
|
||||
#include "resource.h"
|
||||
|
||||
@ -42,6 +43,42 @@ static INT MatchLanguageCode(LPWSTR langCode)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static LPWSTR FormatString(HINSTANCE hInstance, UINT uID, ...)
|
||||
{
|
||||
LPWSTR pBuffer = NULL, pFormat;
|
||||
int n = LoadStringW(hInstance, uID, (LPWSTR)&pFormat, 0);
|
||||
if (n < 2 || pFormat[n - 2] != L'\0')
|
||||
return NULL;
|
||||
va_list args = NULL;
|
||||
va_start(args, uID);
|
||||
FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, pFormat,
|
||||
0, 0, (LPWSTR)&pBuffer, 0, &args);
|
||||
va_end(args);
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
static HANDLE CreateReinstallEvent(LPWSTR path, DWORD pathLen)
|
||||
{
|
||||
if (!PathAppendW(path, L"frpmgr.exe"))
|
||||
return NULL;
|
||||
HANDLE hFile = CreateFileW(path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
path[pathLen] = L'\0';
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
FILE_ID_INFO fileId;
|
||||
BOOL ret = GetFileInformationByHandleEx(hFile, FileIdInfo, &fileId, sizeof(fileId));
|
||||
CloseHandle(hFile);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
CHAR name[_countof("Global\\") + sizeof(fileId) * 2];
|
||||
int n = sprintf_s(name, _countof(name), "Global\\%llx", fileId.VolumeSerialNumber);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
for (size_t i = 0; i < sizeof(fileId.FileId); i++)
|
||||
n += sprintf_s(&name[n], _countof(name) - n, "%02x", fileId.FileId.Identifier[i]);
|
||||
return CreateEventA(NULL, TRUE, FALSE, name);
|
||||
}
|
||||
|
||||
static INT GetApplicationLanguage(LPWSTR path, DWORD pathLen)
|
||||
{
|
||||
if (!PathAppendW(path, L"lang.config"))
|
||||
@ -144,7 +181,7 @@ INT_PTR CALLBACK LanguageDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM l
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
static int cleanup(void)
|
||||
static int Cleanup(void)
|
||||
{
|
||||
if (msiFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -159,13 +196,10 @@ static int cleanup(void)
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
|
||||
{
|
||||
INT langIndex = -1;
|
||||
BOOL installed = FALSE, showDlg = TRUE;
|
||||
BOOL installed = FALSE, reinstall = FALSE, showDlg = TRUE;
|
||||
Product product = {
|
||||
.path = { 0 },
|
||||
.pathLen = _countof(product.path),
|
||||
.lang = { 0 },
|
||||
.langLen = _countof(product.lang),
|
||||
.version = { 0 },
|
||||
.versionLen = _countof(product.version)
|
||||
};
|
||||
|
||||
@ -224,6 +258,40 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
|
||||
if (lang == NULL)
|
||||
return 0;
|
||||
|
||||
if (installed)
|
||||
{
|
||||
LPWSTR pszButtonText = FormatString(hInstance, IDS_REINSTALL, lang->name);
|
||||
const TASKDIALOG_BUTTON buttons[] = {
|
||||
{ IDYES, pszButtonText },
|
||||
{ IDNO, MAKEINTRESOURCE(IDS_UNINSTALL) }
|
||||
};
|
||||
TASKDIALOGCONFIG config = {
|
||||
.cbSize = sizeof(config),
|
||||
.hInstance = hInstance,
|
||||
.dwFlags = TDF_USE_COMMAND_LINKS,
|
||||
.dwCommonButtons = TDCBF_CLOSE_BUTTON,
|
||||
.pszWindowTitle = MAKEINTRESOURCE(IDS_TITLE),
|
||||
.pszMainIcon = MAKEINTRESOURCE(IDI_ICON),
|
||||
.pszMainInstruction = MAKEINTRESOURCE(IDS_MANAGEMENT),
|
||||
.pszContent = MAKEINTRESOURCE(IDS_OPERATION),
|
||||
.cButtons = ARRAYSIZE(buttons),
|
||||
.pButtons = buttons,
|
||||
.nDefaultButton = IDYES
|
||||
};
|
||||
LPWSTR newLine;
|
||||
if (pszButtonText && (newLine = wcschr(pszButtonText, L'\r')))
|
||||
*newLine = L'\n';
|
||||
int nButtonPressed = 0;
|
||||
HRESULT ret = TaskDialogIndirect(&config, &nButtonPressed, NULL, NULL);
|
||||
if (pszButtonText)
|
||||
LocalFree((HLOCAL)pszButtonText);
|
||||
if (ret != S_OK)
|
||||
return 1;
|
||||
if (nButtonPressed == IDCLOSE)
|
||||
return 0;
|
||||
reinstall = nButtonPressed == IDYES;
|
||||
}
|
||||
|
||||
if (!GetWindowsDirectoryW(msiPath, _countof(msiPath)) || !PathAppendW(msiPath, L"Temp"))
|
||||
return 1;
|
||||
GUID guid;
|
||||
@ -254,7 +322,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
|
||||
LocalFree(sa.lpSecurityDescriptor);
|
||||
if (msiFile == INVALID_HANDLE_VALUE)
|
||||
return 1;
|
||||
_onexit(cleanup);
|
||||
_onexit(Cleanup);
|
||||
DWORD bytesWritten;
|
||||
BOOL ok = WriteFile(msiFile, pResData, resSize, &bytesWritten, NULL);
|
||||
CloseHandle(msiFile);
|
||||
@ -262,7 +330,21 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
|
||||
if (!ok || bytesWritten != resSize)
|
||||
return 1;
|
||||
|
||||
if (installed)
|
||||
{
|
||||
if (!reinstall)
|
||||
return MsiInstallProductW(msiPath, L"REMOVE=ALL");
|
||||
HANDLE hEvent = CreateReinstallEvent(product.path, product.pathLen);
|
||||
UINT ret = MsiInstallProductW(msiPath, L"REMOVE=ALL MSIDISABLERMRESTART=1 SAVESTATE=1");
|
||||
if (hEvent)
|
||||
CloseHandle(hEvent);
|
||||
if (ret != ERROR_SUCCESS)
|
||||
return 1;
|
||||
MsiSetInternalUI(INSTALLUILEVEL_BASIC | INSTALLUILEVEL_ENDDIALOG, NULL);
|
||||
}
|
||||
else
|
||||
MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
|
||||
|
||||
#define CMD_FORMAT L"ProductLanguage=%s PREVINSTALLFOLDER=\"%s\""
|
||||
WCHAR cmd[_countof(CMD_FORMAT) + _countof(product.path)];
|
||||
if (swprintf_s(cmd, _countof(cmd), CMD_FORMAT, lang->id, product.path) < 0)
|
||||
|
@ -5,9 +5,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/fatedier/frp/pkg/util/log"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
|
||||
"github.com/koho/frpmgr/pkg/config"
|
||||
@ -82,6 +85,9 @@ func (service *frpService) Execute(args []string, r <-chan svc.ChangeRequest, ch
|
||||
switch c.Cmd {
|
||||
case svc.Stop, svc.Shutdown:
|
||||
svr.Stop(false)
|
||||
if code := shutdownReason(path); code > 0 {
|
||||
return false, code
|
||||
}
|
||||
return
|
||||
case svc.ParamChange:
|
||||
// Reload service
|
||||
@ -125,3 +131,24 @@ func ReloadService(configPath string) error {
|
||||
_, err = service.Control(svc.ParamChange)
|
||||
return err
|
||||
}
|
||||
|
||||
func shutdownReason(path string) uint32 {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
fileID, err := winio.GetFileID(f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
name, err := syscall.UTF16PtrFromString(fmt.Sprintf("Global\\%x%x", fileID.VolumeSerialNumber, fileID.FileID))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
if h, err := windows.OpenEvent(windows.READ_CONTROL, false, name); err == nil {
|
||||
windows.CloseHandle(h)
|
||||
return uint32(windows.ERROR_FAIL_NOACTION_REBOOT)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
Reference in New Issue
Block a user