refactor(msi): pass the app name to the printer custom actions

preprocess.py rewrote the CustomActions sources per customer so the printer
carried the app name, which meant the dll was recompiled for every custom
client and, worse, left the app name baked into a compiled binary.

Pass it through CustomActionData instead. Only the printer and its port ever
varied: the INF path and the driver name ship under their stock names and
preprocess.py already forced the driver name back to RustDesk, so a single
build of the dll now serves every custom client.

Both actions treat the name as optional and fall back to the stock name, so a
package built before this still installs and uninstalls its printer.

This also unblocks patching a prebuilt msi template, which cannot work while a
compiled dll contains the app name: replacing a string inside a PE would shift
everything after it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7fBdTwziR5BHTkSz7Tzcm
This commit is contained in:
rustdesk
2026-08-05 20:47:38 +08:00
parent 63f8177132
commit be24391993
5 changed files with 64 additions and 25 deletions

View File

@@ -18,6 +18,9 @@ void UninstallDriver(LPCWSTR hardwareId, BOOL &rebootRequired);
namespace RemotePrinter
{
VOID installUpdatePrinter(const std::wstring& installFolder);
VOID uninstallPrinter();
// `appName` names the printer and its port. It is passed in rather than compiled
// in so that a single dll serves every custom client; an empty value keeps the
// stock "RustDesk Printer" name.
VOID installUpdatePrinter(const std::wstring& installFolder, const std::wstring& appName);
VOID uninstallPrinter(const std::wstring& appName);
}

View File

@@ -1022,6 +1022,7 @@ UINT __stdcall InstallPrinter(
int nResult = 0;
LPWSTR installFolder = NULL;
LPWSTR appName = NULL;
LPWSTR pwz = NULL;
LPWSTR pwzData = NULL;
@@ -1035,11 +1036,21 @@ UINT __stdcall InstallPrinter(
hr = WcaReadStringFromCaData(&pwz, &installFolder);
ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz);
// Names the printer. Optional, so that a package built before this was passed in
// still installs, keeping the stock printer name.
if (FAILED(WcaReadStringFromCaData(&pwz, &appName)))
{
ReleaseNullStr(appName);
}
WcaLog(LOGMSG_STANDARD, "Try to install RD printer in : %ls", installFolder);
RemotePrinter::installUpdatePrinter(installFolder);
RemotePrinter::installUpdatePrinter(installFolder, appName ? appName : L"");
WcaLog(LOGMSG_STANDARD, "Install RD printer done");
LExit:
if (appName) {
ReleaseStr(appName);
}
if (pwzData) {
ReleaseStr(pwzData);
}
@@ -1054,14 +1065,36 @@ UINT __stdcall UninstallPrinter(
HRESULT hr = S_OK;
DWORD er = ERROR_SUCCESS;
LPWSTR appName = NULL;
LPWSTR pwz = NULL;
LPWSTR pwzData = NULL;
hr = WcaInitialize(hInstall, "UninstallPrinter");
ExitOnFailure(hr, "Failed to initialize");
// Must match the name install used, otherwise the printer is left behind. Absent
// on packages built before this was passed in, where it was the stock name.
if (SUCCEEDED(WcaGetProperty(L"CustomActionData", &pwzData)))
{
pwz = pwzData;
if (FAILED(WcaReadStringFromCaData(&pwz, &appName)))
{
ReleaseNullStr(appName);
}
}
WcaLog(LOGMSG_STANDARD, "Try to uninstall RD printer");
RemotePrinter::uninstallPrinter();
RemotePrinter::uninstallPrinter(appName ? appName : L"");
WcaLog(LOGMSG_STANDARD, "Uninstall RD printer done");
LExit:
if (appName) {
ReleaseStr(appName);
}
if (pwzData) {
ReleaseStr(pwzData);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}

View File

@@ -18,12 +18,19 @@ namespace RemotePrinter
{
#define HRESULT_ERR_ELEMENT_NOT_FOUND 0x80070490
// The driver files and the driver name ship with the app under their stock names
// and stay fixed for every custom client. Only the printer and its port carry the
// app name, and that arrives at runtime so one dll serves every custom client.
LPCWCH RD_DRIVER_INF_PATH = L"drivers\\RustDeskPrinterDriver\\RustDeskPrinterDriver.inf";
LPCWCH RD_PRINTER_PORT = L"RustDesk Printer";
LPCWCH RD_PRINTER_NAME = L"RustDesk Printer";
LPCWCH RD_PRINTER_DRIVER_NAME = L"RustDesk v4 Printer Driver";
LPCWCH RD_DEFAULT_APP_NAME = L"RustDesk";
LPCWCH XCV_MONITOR_LOCAL_PORT = L",XcvMonitor Local Port";
static std::wstring printerNameOf(const std::wstring &appName)
{
return (appName.empty() ? std::wstring(RD_DEFAULT_APP_NAME) : appName) + L" Printer";
}
using FuncEnum = std::function<BOOL(DWORD level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)>;
template <typename T, typename R>
using FuncOnData = std::function<std::shared_ptr<R>(const T &)>;
@@ -458,8 +465,12 @@ namespace RemotePrinter
// We should not check the driver version because the driver is deployed with the application.
// It's better to uninstall the existing driver and install the driver from the application.
// 3. Add the printer.
VOID installUpdatePrinter(const std::wstring &installFolder)
VOID installUpdatePrinter(const std::wstring &installFolder, const std::wstring &appName)
{
const std::wstring printerName = printerNameOf(appName);
const LPCWCH RD_PRINTER_NAME = printerName.c_str();
const LPCWCH RD_PRINTER_PORT = printerName.c_str();
const std::wstring infFile = installFolder + L"\\" + RemotePrinter::RD_DRIVER_INF_PATH;
if (!FileExists(infFile))
{
@@ -505,13 +516,15 @@ namespace RemotePrinter
}
}
VOID uninstallPrinter()
VOID uninstallPrinter(const std::wstring &appName)
{
deletePrinter(RD_PRINTER_NAME);
const std::wstring printerName = printerNameOf(appName);
deletePrinter(printerName.c_str());
WcaLog(LOGMSG_STANDARD, "Deleted the printer\n");
uninstallDriver(RD_PRINTER_DRIVER_NAME);
WcaLog(LOGMSG_STANDARD, "Uninstalled the printer driver\n");
checkDeleteLocalPort(RD_PRINTER_PORT);
checkDeleteLocalPort(printerName.c_str());
WcaLog(LOGMSG_STANDARD, "Deleted the local port\n");
}
}

View File

@@ -30,7 +30,10 @@
<CustomAction Id="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER_INNER]" />
<CustomAction Id="InstallPrinter.SetParam" Return="check" Property="InstallPrinter" Value="[INSTALLFOLDER_INNER]" />
<!-- [~] separates the fields the custom action reads back. ProductName names the
printer, so the dll carries no app name and one build serves every custom client. -->
<CustomAction Id="InstallPrinter.SetParam" Return="check" Property="InstallPrinter" Value="[INSTALLFOLDER_INNER][~][ProductName]" />
<CustomAction Id="UninstallPrinter.SetParam" Return="check" Property="UninstallPrinter" Value="[ProductName]" />
<InstallExecuteSequence>
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
@@ -86,6 +89,7 @@
<Custom Action="RemoveFirewallRules.SetParam" Before="RemoveFirewallRules"/>
<Custom Action="UninstallPrinter" Before="RemoveRuntimeGeneratedFiles" Condition="VersionNT &gt;= 603" />
<Custom Action="UninstallPrinter.SetParam" Before="UninstallPrinter" Condition="VersionNT &gt;= 603" />
<Custom Action="TerminateProcesses" Before="RemoveRuntimeGeneratedFiles"/>
<Custom Action="TerminateProcesses.SetParam" Before="TerminateProcesses"/>

View File

@@ -10,7 +10,6 @@ import subprocess
import re
import platform
from pathlib import Path
from itertools import chain
import shutil
g_indent_unit = "\t"
@@ -189,18 +188,6 @@ def replace_app_name_in_langs(app_name):
with open(file_path, "w", encoding="utf-8") as f:
f.writelines(lines)
def replace_app_name_in_custom_actions(app_name):
custion_actions_dir = Path(sys.argv[0]).parent.joinpath("CustomActions")
for file_path in chain(custion_actions_dir.glob("*.cpp"), custion_actions_dir.glob("*.h")):
with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines()
for i, line in enumerate(lines):
line = re.sub(r"\bRustDesk\b", app_name, line)
line = line.replace(f"{app_name} v4 Printer Driver", "RustDesk v4 Printer Driver")
lines[i] = line
with open(file_path, "w", encoding="utf-8") as f:
f.writelines(lines)
def gen_upgrade_info():
def func(lines, index_start):
indent = g_indent_unit * 3
@@ -557,4 +544,3 @@ if __name__ == "__main__":
sys.exit(-1)
replace_app_name_in_langs(args.app_name)
replace_app_name_in_custom_actions(args.app_name)