f8ead215d8
build-windows / build-hello-agent-x64 (push) Successful in 5m41s
A single-binary, Flutter-free remote-support agent that speaks the stock
RustDesk wire protocol. Designed for one-line MDM deployment against a
self-hosted rustdesk-server: a supporter using the unmodified rustdesk.exe
client connects, the controlled-side user gets a native Win32 approval
prompt, click Yes / No.
CLI surface
hello-agent.exe --install # register + start service
hello-agent.exe --uninstall # stop, delete, clean up
hello-agent.exe --config <BLOB> # admin-UI deploy string
hello-agent.exe --install --config <BLOB> # MDM one-liner
--config accepts both forms emitted by the rustdesk-server admin UI: the
reversed-base64 deploy string and the host=,key=,api=,relay= filename
form. Decoded via the upstream custom_server module, persisted via
hbb_common::config::Config::set_option.
Architecture
--service runs as a Session 0 LocalSystem service. It polls
WTSGetActiveConsoleSessionId and (re)spawns hello-agent.exe --server
into the active console session via librustdesk::platform::run_as_user,
handling the Session 0 → user-session token impersonation.
--server is the worker. It boots three concurrent components:
1. cm_popup: an IPC listener on the rustdesk `_cm` named pipe
2. librustdesk::start_server(true, false): the upstream protocol
stack — rendezvous mediator, NAT punch, IPC server, screen
capture, login validation, hbbs_http heartbeat / sysinfo sync
3. (implicit) ApproveMode::Click is pinned in config, so every
incoming connection routes through cm_popup
The popup mechanism reuses an existing upstream contract without any
patches to the protocol code: when a peer connects with no password,
Connection::start in the upstream code calls try_start_cm_ipc, which
ipc::connect-s the `_cm` pipe before falling back to spawning a Flutter
CM child. Since cm_popup is up first, step 1 succeeds; we read the
Data::Login{authorized:false} frame, show MessageBoxTimeoutW (Yes/No,
60s, top-most, system-modal), and reply Data::Authorize or Data::Close.
Source tree
src/main.rs CLI dispatcher + run_server() composition
src/cli.rs hand-rolled argv parser + unit tests
src/service.rs windows-service install/uninstall/dispatcher
src/config_import.rs --config blob decoding + persistence
src/cm_popup.rs _cm IPC listener + Win32 approval dialog
Vendoring
The upstream RustDesk crate is vendored under vendor/rustdesk/ — full
workspace including libs/{hbb_common, scrap, enigo, clipboard,
virtual_display, remote_printer}. This makes the build self-contained
(no submodules, no sibling-repo checkout in CI) and gives us freedom to
fork in a different direction later. Excluded from the vendor: .git,
target/, flutter/, appimage/, flatpak/, fastlane/, docs/, examples/,
ci/, build.py, Dockerfile, upstream README/CLAUDE/AGENTS/GEMINI.
One local divergence vs. upstream: vendor/rustdesk/src/lib.rs flips
`mod custom_server` → `pub mod custom_server` so config_import.rs can
call get_custom_server_from_string without going through the
ui_interface shim. Documented in README.md → "Re-syncing the vendored
copy".
CI
.gitea/workflows/build-windows.yml builds on a self-hosted Windows
runner with Rust 1.75, LLVM 15.0.6 (libclang for bindgen via libvpx-sys),
and a vcpkg cache. The vendored vcpkg.json drives x64-windows-static
deps. The workflow stages the resulting hello-agent.exe into
SignOutput\, reports authenticode signing status (warns on unsigned),
and uploads as artifact. ~15 min full build, faster on incremental.
Out of scope for this commit: Linux/macOS builds, code signing, MSI
packaging, coexistence with stock rustdesk on the same box (currently
shares the RustDesk APP_NAME and config dir).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
155 lines
12 KiB
XML
155 lines
12 KiB
XML
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
|
|
xmlns:fire="http://wixtoolset.org/schemas/v4/wxs/firewall">
|
|
<Fragment>
|
|
|
|
<?include ../Includes.wxi?>
|
|
|
|
<DirectoryRef Id="INSTALLFOLDER_INNER" FileSource="$(var.BuildDir)">
|
|
<Component Id="App.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4">
|
|
<File Id="App.exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes">
|
|
<!--<fire:FirewallException Id="AppEx" Name="$(var.Product) Service" Scope="any" IgnoreFailure="yes" />-->
|
|
</File>
|
|
</Component>
|
|
</DirectoryRef>
|
|
|
|
<CustomAction Id="RemoveInstallFolder.SetParam" Return="check" Property="RemoveInstallFolder" Value="[INSTALLFOLDER_INNER]" />
|
|
<CustomAction Id="AddFirewallRules.SetParam" Return="check" Property="AddFirewallRules" Value="1[INSTALLFOLDER_INNER]$(var.Product).exe" />
|
|
<CustomAction Id="RemoveFirewallRules.SetParam" Return="check" Property="RemoveFirewallRules" Value="0[INSTALLFOLDER_INNER]$(var.Product).exe" />
|
|
<CustomAction Id="CreateStartService.SetParam" Return="check" Property="CreateStartService" Value="$(var.Product);"[INSTALLFOLDER_INNER]$(var.Product).exe" --service" />
|
|
<CustomAction Id="TryStopDeleteService.SetParam" Return="check" Property="TryStopDeleteService" Value="$(var.Product)" />
|
|
|
|
<CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="App.exe" />
|
|
<CustomAction Id="LaunchAppTray" ExeCommand=" --tray" Return="asyncNoWait" FileRef="App.exe" />
|
|
<Property Id="TerminateProcesses" Value="AppTest.exe" />
|
|
<CustomAction Id="TerminateProcesses.SetParam" Return="check" Property="TerminateProcesses" Value="$(var.Product).exe" />
|
|
<CustomAction Id="TerminateBrokers.SetParam" Return="check" Property="TerminateProcesses" Value="RuntimeBroker_rustdesk.exe" />
|
|
<CustomAction Id="SetPropertyIsServiceRunning.SetParam.AppName" Return="check" Property="AppName" Value="$(var.Product)" />
|
|
<CustomAction Id="SetPropertyIsServiceRunning.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
|
|
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigFile" Return="check" Property="ConfigFile" Value="[AppDataFolder]$(var.Product)\config\$(var.Product)2.toml" />
|
|
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" />
|
|
<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]" />
|
|
<InstallExecuteSequence>
|
|
|
|
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
|
|
<Custom Action="SetPropertyIsServiceRunning.SetParam.AppName" Before="SetPropertyIsServiceRunning" Condition="Installed" />
|
|
<Custom Action="SetPropertyIsServiceRunning.SetParam.PropertyName" Before="SetPropertyIsServiceRunning" Condition="Installed" />
|
|
|
|
<Custom Action="SetPropertyServiceStop" After="InstallInitialize" Condition="NOT Installed" />
|
|
<Custom Action="SetPropertyServiceStop.SetParam.ConfigFile" Before="SetPropertyServiceStop" Condition="NOT Installed" />
|
|
<Custom Action="SetPropertyServiceStop.SetParam.ConfigKey" Before="SetPropertyServiceStop" Condition="NOT Installed" />
|
|
<Custom Action="SetPropertyServiceStop.SetParam.PropertyName" Before="SetPropertyServiceStop" Condition="NOT Installed" />
|
|
|
|
<!-- Do not call CreateStartService if is uninstalling. -->
|
|
<!-- (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE) means uninstalling. -->
|
|
<Custom Action="CreateStartService" Before="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'") AND (NOT CC_CONNECTION_TYPE="outgoing")" />
|
|
<Custom Action="CreateStartService.SetParam" Before="CreateStartService" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'") AND (NOT CC_CONNECTION_TYPE="outgoing")" />
|
|
|
|
<Custom Action="CustomActionHello" Before="InstallFinalize" />
|
|
|
|
<!--Shortcut is in InstallValidate section. So we just let it be created, then try delete if stopping service.-->
|
|
<Custom Action="TryDeleteStartupShortcut" After="InstallFinalize" Condition="STOP_SERVICE="'Y'"" />
|
|
<Custom Action="TryDeleteStartupShortcut.SetParam" Before="SetPropertyIsServiceRunning" Condition="STOP_SERVICE="'Y'"" />
|
|
|
|
<!-- Launch ClientLauncher if installing or already installed and not uninstalling -->
|
|
<!-- https://learn.microsoft.com/en-us/windows/win32/msi/uilevel -->
|
|
<Custom Action="LaunchApp" After="InstallFinalize" Condition="(NOT UILevel=2) AND (NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) "/>
|
|
<Custom Action="LaunchAppTray" After="InstallFinalize" Condition="(LAUNCH_TRAY_APP="Y" OR LAUNCH_TRAY_APP="1") AND (NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'") AND (NOT CC_CONNECTION_TYPE="outgoing")"/>
|
|
|
|
<!-- https://learn.microsoft.com/en-us/windows/win32/msi/operating-system-property-values -->
|
|
<!-- We have to use `VersionNT` to instead of `IsWindows10OrGreater()` in the custom action.
|
|
Because `IsWindows10OrGreater()` requires the manifest file to be embedded in the executable/dll file.
|
|
Even I have embedded the manifest file, it still does not work correctly in my case.
|
|
https://learn.microsoft.com/en-us/windows/win32/sysinfo/version-helper-apis -->
|
|
<!-- VersionNT >= 603 means can't differentiate between Windows 8.1 and Windows 10.
|
|
Some msi packages reset the `VersionNT` value to 1000 on Windows 10.
|
|
https://www.advancedinstaller.com/user-guide/qa-OS-dependent-install.html -->
|
|
<!-- Remote printer also works on Win8.1 in my test. -->
|
|
<Custom Action="InstallPrinter" Before="InstallFinalize" Condition="VersionNT >= 603 AND PRINTER = 1 OR PRINTER = "Y" OR PRINTER = "y"" />
|
|
<Custom Action="InstallPrinter.SetParam" Before="InstallPrinter" Condition="VersionNT >= 603" />
|
|
|
|
<!--Workaround of "fire:FirewallException". If Outbound="Yes" or Outbound="true", the following error occurs.-->
|
|
<!--ExecFirewallExceptions: Error 0x80070057: failed to add app to the authorized apps list-->
|
|
<Custom Action="AddFirewallRules" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
|
<Custom Action="AddFirewallRules.SetParam" Before="AddFirewallRules" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
|
|
|
<Custom Action="AddRegSoftwareSASGeneration" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE) AND (NOT CC_CONNECTION_TYPE="outgoing")"/>
|
|
|
|
<Custom Action="RemoveInstallFolder" Before="RemoveFiles"/>
|
|
<Custom Action="RemoveInstallFolder.SetParam" Before="RemoveInstallFolder"/>
|
|
<Custom Action="TryStopDeleteService" Before="RemoveInstallFolder.SetParam" />
|
|
<Custom Action="TryStopDeleteService.SetParam" Before="TryStopDeleteService" />
|
|
|
|
<Custom Action="RemoveFirewallRules" Before="RemoveFiles"/>
|
|
<Custom Action="RemoveFirewallRules.SetParam" Before="RemoveFirewallRules"/>
|
|
|
|
<Custom Action="UninstallPrinter" Before="RemoveInstallFolder" Condition="VersionNT >= 603" />
|
|
|
|
<Custom Action="TerminateProcesses" Before="RemoveInstallFolder"/>
|
|
<Custom Action="TerminateProcesses.SetParam" Before="TerminateProcesses"/>
|
|
<Custom Action="TerminateBrokers" Before="RemoveInstallFolder"/>
|
|
<Custom Action="TerminateBrokers.SetParam" Before="TerminateBrokers"/>
|
|
<Custom Action="RemoveAmyuniIdd" Before="RemoveInstallFolder"/>
|
|
<Custom Action="RemoveAmyuniIdd.SetParam" Before="RemoveAmyuniIdd"/>
|
|
</InstallExecuteSequence>
|
|
|
|
<!-- Shortcuts -->
|
|
<DirectoryRef Id="App.StartMenu">
|
|
<Component Id="App.StartMenu" Guid="30F6D57A-B805-4DA4-A071-05A3B22400CA">
|
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu" Type="string" Value="1" KeyPath="yes" />
|
|
<RemoveFolder Id="Remove.App.StartMenu" On="uninstall" />
|
|
</Component>
|
|
</DirectoryRef>
|
|
|
|
<DirectoryRef Id="App.StartMenu">
|
|
<Component Id="App.StartMenu.Shortcut" Guid="43ABCAC7-E47D-42D8-A408-25EC70DBB993" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y"">
|
|
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
|
|
<!--
|
|
Fix ICE 38 by adding a dummy registry key that is the key for this shortcut.
|
|
https://learn.microsoft.com/en-us/windows/win32/msi/ice38
|
|
-->
|
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
|
</Component>
|
|
|
|
<Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y"">
|
|
<Shortcut Id="App.StartMenu.ShortcutUninstall" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
|
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.ShortcutUninstall" Type="string" Value="1" KeyPath="yes" />
|
|
</Component>
|
|
</DirectoryRef>
|
|
<StandardDirectory Id="DesktopFolder">
|
|
<Component Id="App.Desktop.Shortcut" Guid="CA8FB7AA-17F7-4E36-A58A-5A016A303709" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = "Y" OR DESKTOPSHORTCUTS = "y"">
|
|
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
|
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
|
</Component>
|
|
</StandardDirectory>
|
|
<StandardDirectory Id="StartupFolder">
|
|
<Component Id="App.StartupFolder.ShortcutTray" Guid="B1D1E2BB-E53E-E159-DB7C-744D5C726A8C" Condition="STARTUPSHORTCUTS = 1 AND (NOT CC_CONNECTION_TYPE="outgoing")">
|
|
<Shortcut Id="App.StartupFolder.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!App.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
|
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartupFolder.ShortcutTray" Type="string" Value="1" KeyPath="yes" />
|
|
</Component>
|
|
</StandardDirectory>
|
|
|
|
<!--<DirectoryRef Id="INSTALLFOLDER_INNER">
|
|
<Component Id="App.UninstallShortcut" Guid="FB0F2AC7-2AE5-4C54-B860-5E472620B6B1">
|
|
<Shortcut Id="App.UninstallShortcut" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
|
|
</Component>
|
|
</DirectoryRef>-->
|
|
|
|
<ComponentGroup Id="Components" Directory="INSTALLFOLDER_INNER">
|
|
<ComponentRef Id="App.exe" />
|
|
<ComponentRef Id="App.Desktop.Shortcut" />
|
|
<!--<ComponentRef Id="App.UninstallShortcut" />-->
|
|
<ComponentRef Id="App.StartMenu.Shortcut" />
|
|
<ComponentRef Id="App.StartMenu.ShortcutUninstall" />
|
|
<ComponentRef Id="App.StartupFolder.ShortcutTray" />
|
|
|
|
<!--$AutoComonentStart$-->
|
|
<!--$AutoComponentEnd$-->
|
|
|
|
</ComponentGroup>
|
|
|
|
</Fragment>
|
|
</Wix>
|