Skip to content

Feature/local build and release scripts - #3413

Open
lakshamanr wants to merge 7 commits into
mRemoteNG:v1.78.2-devfrom
lakshamanr:feature/local-build-and-release-scripts
Open

Feature/local build and release scripts#3413
lakshamanr wants to merge 7 commits into
mRemoteNG:v1.78.2-devfrom
lakshamanr:feature/local-build-and-release-scripts

Conversation

@lakshamanr

Copy link
Copy Markdown

Description

Motivation and Context

How Has This Been Tested?

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Changed feature (non-breaking change which changes functionality)
  • Changed feature (breaking change which changes functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Updated translation

Checklist:

  • I have read the CONTRIBUTING document.
  • My code follows the code style of this project.
  • All Tests within VisualStudio are passing
  • This pull request does not target the master branch.
  • I have updated the changelog file accordingly, if necessary.
  • I have updated the documentation accordingly, if necessary.

@lakshamanr

Copy link
Copy Markdown
Author

n

lakshamanrokade1991 and others added 7 commits August 7, 2026 20:32
- build-and-run.bat: locates MSBuild via vswhere, builds mRemoteNG.csproj
  (Debug|x64) and launches mRemoteNG.exe for local dev iteration.
- release-msi.bat: builds the app (Release|x64) and packages the existing
  WiX v3 installer project (mRemoteNGInstaller/Installer) into
  mRemoteNG-Installer.msi, overriding WixTargetsPath/WixCATargetsPath and
  SolutionDir so the build works standalone (outside the .sln) and with
  VS2026's relocated MSBuildExtensionsPath32.
- nuget.config: clears NuGet sources to nuget.org only, so restores don't
  depend on unrelated private feeds that may be configured globally.
- mRemoteNG.csproj: guard the T4 TextTemplating targets import with
  Exists(...) since Properties/AssemblyInfo.cs is already generated and
  checked in; avoids requiring the VS Text Template Transformation
  component on machines that only need to build, not regenerate it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restores the fixed pixel resolution options (800x600 through 3840x2160)
that were removed from RDPResolutions in commit cf1709b, and adds two
larger options (5120x2880, 7680x4320) for high-DPI displays.

- RDPResolutions.cs: re-add Res800x600...Res3840x2160 plus new
  Res5120x2880/Res7680x4320 members.
- RdpExtensions.cs: restore GetResolutionRectangle() to parse the fixed
  size out of the enum member name.
- RdpProtocol.cs SetResolution(): add a default case for fixed
  resolutions. Keeps the control docked (Fill) and just sets
  DesktopWidth/DesktopHeight; the RDP ActiveX control's own native
  scrollbars handle a session larger than the visible panel. Undocking
  and manually resizing the control (like FitToWindow does) causes a
  blank/partially-rendered session because the AxHost wrapper can't
  propagate a large one-shot Size jump to the RDP control's internal
  rendering surface.
- RdpProtocol8.cs DoResizeControl(): update comment; fixed resolutions
  are skipped the same way FitToWindow already was, no dynamic resize.
- Tests: cover GetResolutionRectangle() for fixed/mode values, CSV/XML
  serialization round-trip, and DataTableDeserializer for the restored
  enum members.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sion)

Adds a UseMultiMon connection property, following the existing pattern used
for RDP display toggles (e.g. CacheBitmaps): wired through
AbstractConnectionRecord/ConnectionInfo, inheritance, XML/CSV serialization,
and localization.

RdpProtocol.SetResolution() honors it ahead of the resolution switch so it
overrides SmartSize/FitToWindow/Fullscreen, forcing true full screen and
enabling IMsRdpClientNonScriptable5.UseMultimon (RDP client 8.1+, older
clients fall back to single-monitor size). A fixed pixel resolution (e.g.
Res7680x4320) is still honored exactly for DesktopWidth/DesktopHeight rather
than being overridden by the local monitors' combined bounds; only
Fullscreen/SmartSize/FitToWindow (which have no explicit WxH of their own)
fall back to SystemInformation.VirtualScreen.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a 'Show Password' item to the property grid right-click menu that
reveals the plaintext value of password fields (Password,
RDGatewayPassword, VNCProxyPassword). Enabled only when the selected
grid item is a password property with a non-empty value.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use all my monitors now hands the RDP connection off to mstsc.exe with 'use multimon:i:1' (via a temp .rdp + staged Credential Manager entry), because the embedded RDP ActiveX control can only go fullscreen on a single monitor. This spans every physical monitor like the built-in Remote Desktop client.

Adds a CustomResolution WidthxHeight connection property (single-monitor) that overrides the Resolution dropdown when set, wired through AbstractConnectionRecord/inheritance, XML+CSV serialization, defaults and localization. RdpProtocol.SetResolution honors it; RdpExtensions.TryParseCustomResolution validates against RDP's 200-8192px limits. Adds RdpCustomResolutionTests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three issues prevented copying from local to a remote RDP session even with Redirect Clipboard enabled:

- SetRedirection() ran SetDriveRedirection() before the RedirectClipboard assignment inside the same try/catch, so a drive-redirection failure (common pre-connect when enumerating DriveCollection for Local/Custom drives) swallowed the exception and silently skipped enabling clipboard redirection. Clipboard is now set first and drive redirection is isolated in its own try.

- frmMain joined the legacy SetClipboardViewer chain and forwarded WM_DRAWCLIPBOARD/WM_CHANGECBCHAIN with wParam/lParam swapped, corrupting the clipboard-viewer chain and interfering with the RDP control's clipboard monitor. Replaced with the non-chaining AddClipboardFormatListener / WM_CLIPBOARDUPDATE API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
If a previous mRemoteNG-Installer.msi is registered by Windows Installer,
light.exe fails with LGHT0001 (access denied). Now the script tries a plain
del first; if the file is still present it re-launches an elevated cmd to
force-delete it, then aborts with a clear message if deletion still fails.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add local build/MSI scripts and expand RDP resolution + multimon support

✨ Enhancement 🐞 Bug fix 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add local MSBuild/WiX batch scripts for dev builds and MSI packaging.
• Extend RDP settings with fixed/custom resolutions and true multimon via mstsc fallback.
• Update serialization/UI/tests for new RDP fields and improve clipboard notifications.
Diagram

graph TD
UI["Config UI"] --> CI["ConnectionInfo"] --> INIT["ConnectionInitiator"]
CI --> SER["CSV/XML serializers"] --> FILE["Connection files"]
INIT -->|RDP multimon| EXT{{"mstsc.exe (/multimon)"}}
INIT -->|RDP embedded| RDP["RdpProtocol (ActiveX)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use PowerShell scripts instead of .bat
  • ➕ Richer error handling and structured output
  • ➕ Easier path manipulation/quoting and environment discovery
  • ➕ More maintainable for future extension (e.g., parameters, CI parity)
  • ➖ Adds PowerShell execution policy considerations
  • ➖ Batch scripts are simpler for minimal Windows environments
2. Keep multimon inside ActiveX only (no mstsc handoff)
  • ➕ Single process/UI experience within mRemoteNG
  • ➕ Avoids writing temp .rdp files and staging credentials
  • ➖ Does not reliably achieve true multi-monitor fullscreen for docked ActiveX controls
  • ➖ Higher complexity/risk trying to emulate mstsc behavior; may remain flaky across Windows/RDP versions
3. Store custom resolution as structured width/height fields
  • ➕ Avoids string parsing and validation at connect time
  • ➕ Easier to validate in UI and to migrate formats
  • ➖ Requires schema/config versioning work and migration logic
  • ➖ More UI changes than a single freeform field

Recommendation: The current approach is reasonable: keep embedded RDP for normal sessions, and hand off only the true multimon case to mstsc.exe where Windows already implements the correct behavior. For build tooling, the .bat scripts are pragmatic for a Windows-only build pipeline; consider a future follow-up to offer a PowerShell equivalent if script complexity grows. If custom resolution usage expands, consider a structured representation to reduce parsing surface area.

Files changed (34) +1059 / -120

Enhancement (13) +397 / -10
CsvConnectionsDeserializerMremotengFormat.csDeserialize UseMultiMon and CustomResolution from CSV +23/-0

Deserialize UseMultiMon and CustomResolution from CSV

• Extends CSV parsing to read UseMultiMon and CustomResolution fields, including inheritance flags (InheritUseMultiMon/InheritCustomResolution) when present.

mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormat.cs

CsvConnectionsSerializerMremotengFormat.csSerialize UseMultiMon and CustomResolution into CSV headers/rows +6/-2

Serialize UseMultiMon and CustomResolution into CSV headers/rows

• Extends the CSV schema to include UseMultiMon and CustomResolution plus their inheritance columns, and writes the corresponding values during serialization.

mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs

XmlConnectionNodeSerializer28.csPersist UseMultiMon and CustomResolution to v2.8 XML +6/-0

Persist UseMultiMon and CustomResolution to v2.8 XML

• Adds XML attributes for CustomResolution and UseMultiMon on connection nodes and emits inheritance attributes for both flags when enabled.

mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionNodeSerializer28.cs

XmlConnectionsDeserializer.csRead UseMultiMon and CustomResolution from XML +4/-0

Read UseMultiMon and CustomResolution from XML

• Extends XML deserialization to populate UseMultiMon and CustomResolution on ConnectionInfo and their inheritance flags from the saved attributes.

mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs

AbstractConnectionRecord.csAdd RDP appearance properties: CustomResolution and UseMultiMon +23/-0

Add RDP appearance properties: CustomResolution and UseMultiMon

• Introduces new ConnectionInfo properties with localization metadata and protocol scoping so they appear in the property grid and participate in persistence.

mRemoteNG/Connection/AbstractConnectionRecord.cs

ConnectionInfo.csInitialize defaults for custom resolution and multimon +2/-0

Initialize defaults for custom resolution and multimon

• Sets CustomResolution default to empty and loads UseMultiMon default from Settings during appearance initialization.

mRemoteNG/Connection/ConnectionInfo.cs

ConnectionInfoInheritance.csAdd inheritance toggles for CustomResolution and UseMultiMon +12/-0

Add inheritance toggles for CustomResolution and UseMultiMon

• Introduces inheritable flags so containers can control whether child connections inherit these new RDP settings.

mRemoteNG/Connection/ConnectionInfoInheritance.cs

ConnectionInitiator.csRoute RDP multimon sessions to an external launcher +10/-0

Route RDP multimon sessions to an external launcher

• If Protocol is RDP and UseMultiMon is enabled, launches mstsc.exe via a helper and returns early instead of using the embedded control.

mRemoteNG/Connection/ConnectionInitiator.cs

RDPResolutions.csRestore fixed-pixel RDP resolution presets and add larger options +22/-2

Restore fixed-pixel RDP resolution presets and add larger options

• Reintroduces classic fixed resolutions (800x600 through 3840x2160) and adds 5120x2880 and 7680x4320, using Description attributes for display text.

mRemoteNG/Connection/Protocol/RDP/RDPResolutions.cs

RdpExtensions.csAdd helpers for fixed/custom resolution parsing +38/-1

Add helpers for fixed/custom resolution parsing

• Adds GetResolutionRectangle() to derive pixel sizes from fixed-resolution enum values, and TryParseCustomResolution() to validate and clamp user-entered sizes.

mRemoteNG/Connection/Protocol/RDP/RdpExtensions.cs

RdpExternalMultimonLauncher.csImplement mstsc.exe multimon handoff via temporary .rdp file +102/-0

Implement mstsc.exe multimon handoff via temporary .rdp file

• Creates a temporary .rdp file with multimon settings, optionally stages credentials via cmdkey, launches mstsc.exe, and cleans up credentials/files asynchronously.

mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs

RdpProtocol.csEnhance RDP resolution/multimon logic and harden redirection setup +117/-4

Enhance RDP resolution/multimon logic and harden redirection setup

• Reworks SetResolution() to correctly order multimon/fullscreen property assignment, support custom resolutions, and handle fixed pixel resolutions via native scrollbars. Splits redirection setup into separate try blocks so drive redirection failures don’t block other redirection flags, and logs server-accepted monitor count after connect.

mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs

ConfigWindow.csAdd PropertyGrid context menu action to show password value +32/-1

Add PropertyGrid context menu action to show password value

• Adds a context menu item that becomes enabled for password-typed properties and displays the selected password value in a dialog when invoked.

mRemoteNG/UI/Window/ConfigWindow.cs

Bug fix (3) +23 / -28
NativeMethods.csAdd modern clipboard notification APIs and WM_CLIPBOARDUPDATE +14/-0

Add modern clipboard notification APIs and WM_CLIPBOARDUPDATE

• Adds P/Invoke bindings for AddClipboardFormatListener/RemoveClipboardFormatListener and defines WM_CLIPBOARDUPDATE to replace clipboard viewer-chain usage.

mRemoteNG/App/NativeMethods.cs

RdpProtocol8.csSkip resize handling for fixed resolutions and FitToWindow +6/-2

Skip resize handling for fixed resolutions and FitToWindow

• Adjusts resize behavior so only SmartSize and Fullscreen participate in resize logic; FitToWindow and fixed pixel resolutions rely on scrollbars and do not resize dynamically.

mRemoteNG/Connection/Protocol/RDP/RdpProtocol8.cs

frmMain.csSwitch to AddClipboardFormatListener for clipboard change events +3/-26

Switch to AddClipboardFormatListener for clipboard change events

• Replaces SetClipboardViewer/clipboard chain handling with modern clipboard notifications (WM_CLIPBOARDUPDATE) to avoid breaking other clipboard listeners.

mRemoteNG/UI/Forms/frmMain.cs

Tests (6) +121 / -2
CsvConnectionsDeserializerMremotengFormatTests.csAdd CSV round-trip test for fixed RDP resolution enums +11/-0

Add CSV round-trip test for fixed RDP resolution enums

• Adds a regression test ensuring a fixed pixel resolution value can be serialized and deserialized without falling back to defaults.

mRemoteNGTests/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormatTests.cs

XmlConnectionsSerializerTests.csAdd XML round-trip test for fixed RDP resolutions +13/-0

Add XML round-trip test for fixed RDP resolutions

• Adds a test verifying fixed RDP resolution values survive serialize/deserialize through the XML format.

mRemoteNGTests/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsSerializerTests.cs

DataTableDeserializerTests.csFix legacy-resolution fallback test and add fixed-resolution round-trip +11/-1

Fix legacy-resolution fallback test and add fixed-resolution round-trip

• Updates the fallback test to use an actually unknown resolution value and adds a test ensuring fixed resolutions round-trip through DataTable deserialization.

mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs

RdpCustomResolutionTests.csAdd unit tests for custom resolution parsing/validation +40/-0

Add unit tests for custom resolution parsing/validation

• Introduces tests covering valid/invalid custom resolution strings, whitespace/separator tolerance, and bounds enforcement.

mRemoteNGTests/Connection/Protocol/RdpCustomResolutionTests.cs

RdpProtocol8ResizeTests.csAdd tests for fixed-resolution sizing and resize skipping behavior +44/-1

Add tests for fixed-resolution sizing and resize skipping behavior

• Adds tests for GetResolutionRectangle() across fixed/mode resolutions and verifies resize logic only applies to SmartSize/Fullscreen.

mRemoteNGTests/Connection/Protocol/RdpProtocol8ResizeTests.cs

SerializableConnectionInfoAllPropertiesOfType.csInclude new RDP properties in generic serialization helper model +2/-0

Include new RDP properties in generic serialization helper model

• Extends the helper type to include UseMultiMon and CustomResolution so broad serialization tests cover the new properties.

mRemoteNGTests/TestHelpers/SerializableConnectionInfoAllPropertiesOfType.cs

Documentation (3) +64 / -0
copilot-instructions.mdAdd Azure-specific Copilot contribution rules +4/-0

Add Azure-specific Copilot contribution rules

• Adds guidance for handling Azure-related requests, including invoking best-practices tooling when available.

.github/copilot-instructions.md

Language.Designer.csAdd localized string accessors for new UI labels/descriptions +45/-0

Add localized string accessors for new UI labels/descriptions

• Adds generated resource properties for UseMultiMon, CustomResolution, their descriptions, and a ShowPassword label.

mRemoteNG/Language/Language.Designer.cs

Language.resxAdd localized strings for multimon/custom resolution and Show Password +15/-0

Add localized strings for multimon/custom resolution and Show Password

• Introduces new resource entries for CustomResolution, UseMultiMon, their property descriptions, and the ShowPassword context menu label.

mRemoteNG/Language/Language.resx

Other (9) +454 / -80
build-and-run.batAdd local Debug build-and-run script using vswhere/MSBuild +32/-0

Add local Debug build-and-run script using vswhere/MSBuild

• Introduces a helper script that locates MSBuild via vswhere, restores/builds mRemoteNG (Debug|x64), and launches the produced executable for local iteration.

build-and-run.bat

App.configAdd default setting for RDP multimon usage +3/-0

Add default setting for RDP multimon usage

• Adds the ConDefaultUseMultiMon application/user setting with a default of False.

mRemoteNG/App.config

AssemblyInfo.csBump nightly build number/version metadata +4/-4

Bump nightly build number/version metadata

• Updates build number and assembly version/file version/informational version values.

mRemoteNG/Properties/AssemblyInfo.cs

Settings.Designer.csAdd generated setting ConDefaultUseMultiMon +12/-0

Add generated setting ConDefaultUseMultiMon

• Adds the strongly-typed settings property for ConDefaultUseMultiMon with default False.

mRemoteNG/Properties/Settings.Designer.cs

Settings.settingsDefine ConDefaultUseMultiMon setting +3/-0

Define ConDefaultUseMultiMon setting

• Adds the new user-scoped boolean setting entry for ConDefaultUseMultiMon.

mRemoteNG/Properties/Settings.settings

mRemoteNG.csprojGuard TextTemplating targets import for machines without T4 tooling +2/-1

Guard TextTemplating targets import for machines without T4 tooling

• Wraps the T4 TextTemplating targets import with Exists(...) so builds don’t require the VS Text Template Transformation component when generated files are already checked in.

mRemoteNG/mRemoteNG.csproj

FilesFragment.wxsUpdate WiX file harvesting to match current build outputs +325/-75

Update WiX file harvesting to match current build outputs

• Adjusts installer directory structure (e.g., adds ta locale) and updates component list to reflect changed/added dependencies (including various SQLite runtime assets and new icons) while removing obsolete entries.

mRemoteNGInstaller/Installer/Fragments/FilesFragment.wxs

nuget.configPin NuGet restore to nuget.org only +7/-0

Pin NuGet restore to nuget.org only

• Adds a repository nuget.config that clears inherited sources and uses only nuget.org to avoid dependence on developer machine private feeds.

nuget.config

release-msi.batAdd standalone Release build + WiX MSI packaging script +66/-0

Add standalone Release build + WiX MSI packaging script

• Adds a helper script to build mRemoteNG (Release|x64) and then build the WiX v3 installer project standalone by forcing WixTargetsPath/WixCATargetsPath and SolutionDir, with logic to handle locked MSI outputs.

release-msi.bat

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Cmdkey leaks RDP password 🐞 Bug ⛨ Security
Description
RdpExternalMultimonLauncher stages RDP credentials by invoking cmdkey.exe with the password in the
/pass: command-line argument, exposing the plaintext password to local process inspection. If
anything throws after staging but before the cleanup task runs, the TERMSRV credential can persist
in Windows Credential Manager longer than intended.
Code

mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[R37-40]

+                bool stagedCredential = !string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(password);
+                if (stagedCredential)
+                    RunCmdKey("/generic:TERMSRV/" + host, "/user:" + (string.IsNullOrEmpty(domain) ? user : domain + "\\" + user), "/pass:" + password);
+
Evidence
The launcher currently shells out to cmdkey.exe with /pass: + password, which places the plaintext
secret in the child process arguments. Cleanup is only performed later in a fire-and-forget Task,
and the codebase already demonstrates using Win32 credential APIs directly (CredDelete), suggesting
an in-process approach is expected/available.

mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[35-58]
mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[60-63]
mRemoteNG/Connection/Protocol/RDP/RdpCredentialCacheCleaner.cs[33-61]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`RdpExternalMultimonLauncher` passes the RDP password to `cmdkey.exe` via `/pass:<password>`, which exposes the secret in process arguments. Cleanup of the staged credential only happens in a background task after `mstsc.Start()`, so failures in between can leave a persistent cached credential.

### Issue Context
The repo already uses direct Win32 credential APIs for deletion (`CredDelete`) in `RdpCredentialCacheCleaner`, so staging should follow the same pattern (e.g., `CredWrite`) instead of spawning `cmdkey.exe`.

### Fix
- Replace `cmdkey.exe` usage with an in-process Win32 credential write (`CredWriteW`) to create/update the `TERMSRV/<hostname>` entry without putting secrets on a command line.
- Ensure staged credentials are removed on *all* failure paths:
 - Track whether staging succeeded.
 - Use `try/finally` around `mstsc.Start()` and cleanup scheduling.
 - If `mstsc` fails to start, immediately delete the staged credential.
- Keep temp `.rdp` cleanup best-effort, but ensure credential cleanup is best-effort on every path.

### Fix Focus Areas
- mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[23-63]
- mRemoteNG/Connection/Protocol/RDP/RdpCredentialCacheCleaner.cs[33-61]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Multimon skips PostExtApp 🐞 Bug ≡ Correctness
Description
When UseMultiMon is enabled, ConnectionInitiator.OpenConnection returns after launching mstsc.exe,
bypassing the normal ProtocolBase lifecycle. As a result, PostExtApp (which is triggered from
Prot_Event_Closed) will never run for multimon connections.
Code

mRemoteNG/Connection/ConnectionInitiator.cs[R99-102]

+                if (connectionInfo.Protocol == ProtocolType.RDP && connectionInfo.UseMultiMon)
+                {
+                    Protocol.RDP.RdpExternalMultimonLauncher.Launch(connectionInfo);
+                    return;
Evidence
The new early return prevents creation/connection of a ProtocolBase instance, so the only place
PostExtApp is invoked (Prot_Event_Closed) is never reached for multimon sessions.

mRemoteNG/Connection/ConnectionInitiator.cs[93-103]
mRemoteNG/Connection/ConnectionInitiator.cs[415-437]
mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[51-58]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
For RDP multimon sessions, `OpenConnection` launches `mstsc.exe` and returns early. This skips the embedded protocol lifecycle events (`Prot_Event_Closed`), which is where `PostExtApp` is executed, so multimon connections behave differently than normal connections.

### Issue Context
`PostExtApp` is started only inside `Prot_Event_Closed`, which will not fire for an externally launched `mstsc.exe` process.

### Fix
- Add a completion hook for external `mstsc.exe` sessions:
 - Option A: in `RdpExternalMultimonLauncher.Launch`, after `mstsc.WaitForExit()`, run the configured `PostExtApp` for `connectionInfo`.
 - Option B: return the `Process` (or a Task) from `Launch` and have `ConnectionInitiator` await/continue to run `PostExtApp` on completion.
- Ensure PostExtApp runs only if configured (same behavior as `Prot_Event_Closed`).

### Fix Focus Areas
- mRemoteNG/Connection/ConnectionInitiator.cs[93-103]
- mRemoteNG/Connection/ConnectionInitiator.cs[415-437]
- mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[51-58]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Show Password exposes secrets 🐞 Bug ⛨ Security
Description
ConfigWindow adds a "Show Password" context menu action that displays the stored password in
plaintext in a MessageBox. This weakens local secret handling because any user with access to the
unlocked session can reveal credentials without additional authorization or safeguards.
Code

mRemoteNG/UI/Window/ConfigWindow.cs[R723-726]

+                GridItem gridItem = _pGrid.SelectedGridItem;
+                if (gridItem?.Value is string password && !string.IsNullOrEmpty(password))
+                {
+                    MessageBox.Show(password, gridItem.Label, MessageBoxButtons.OK, MessageBoxIcon.Information);
Evidence
The menu is explicitly enabled for password-typed properties and then shows the underlying value as
plaintext via MessageBox.Show.

mRemoteNG/UI/Window/ConfigWindow.cs[682-695]
mRemoteNG/UI/Window/ConfigWindow.cs[719-733]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new context-menu action reveals password values directly via `MessageBox.Show(...)` with no confirmation, re-authentication, or other guardrails.

### Issue Context
The menu is enabled for properties marked with `PasswordPropertyTextAttribute` and then displays the raw string value.

### Fix
- Add a guard before revealing secrets, e.g.:
 - Require re-authentication / master password confirmation (if the app supports it), or
 - At minimum, require an explicit confirmation dialog warning about shoulder-surfing/screen capture.
- Consider showing the value in a dedicated dialog with a masked-by-default textbox and a deliberate “Reveal” toggle, rather than always showing plaintext.

### Fix Focus Areas
- mRemoteNG/UI/Window/ConfigWindow.cs[682-734]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. MSBuild args unquoted 🐞 Bug ☼ Reliability
Description
release-msi.bat passes /p:SolutionDir=%ROOT% to MSBuild without quotes, so builds from paths
containing spaces will mis-parse the argument and fail or behave incorrectly.
Code

release-msi.bat[54]

+"%MSBUILD%" "%ROOT%mRemoteNGInstaller\Installer\Installer.wixproj" /t:Build /p:Configuration=Release /p:Platform=x64 /p:SolutionDir=%ROOT% "/p:WixTargetsPath=%ProgramFiles(x86)%\MSBuild\Microsoft\WiX\v3.x\Wix.targets" "/p:WixCATargetsPath=%ProgramFiles(x86)%\MSBuild\Microsoft\WiX\v3.x\wix.ca.targets" /nologo /v:minimal
Evidence
ROOT is computed from the script directory and may include spaces; it is then injected into the
MSBuild command line without quotes for SolutionDir.

release-msi.bat[6-7]
release-msi.bat[51-55]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The MSI build command sets `SolutionDir` without quoting `%ROOT%`, which can contain spaces (derived from `%~dp0`). This breaks MSBuild argument parsing.

### Fix
- Quote the property value:
 - Change `/p:SolutionDir=%ROOT%` to `/p:SolutionDir="%ROOT%"`.
- (Optional) Apply the same quoting discipline to any other path-valued properties you add later.

### Fix Focus Areas
- release-msi.bat[6-7]
- release-msi.bat[51-55]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +37 to +40
bool stagedCredential = !string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(password);
if (stagedCredential)
RunCmdKey("/generic:TERMSRV/" + host, "/user:" + (string.IsNullOrEmpty(domain) ? user : domain + "\\" + user), "/pass:" + password);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Cmdkey leaks rdp password 🐞 Bug ⛨ Security

RdpExternalMultimonLauncher stages RDP credentials by invoking cmdkey.exe with the password in the
/pass: command-line argument, exposing the plaintext password to local process inspection. If
anything throws after staging but before the cleanup task runs, the TERMSRV credential can persist
in Windows Credential Manager longer than intended.
Agent Prompt
### Issue description
`RdpExternalMultimonLauncher` passes the RDP password to `cmdkey.exe` via `/pass:<password>`, which exposes the secret in process arguments. Cleanup of the staged credential only happens in a background task after `mstsc.Start()`, so failures in between can leave a persistent cached credential.

### Issue Context
The repo already uses direct Win32 credential APIs for deletion (`CredDelete`) in `RdpCredentialCacheCleaner`, so staging should follow the same pattern (e.g., `CredWrite`) instead of spawning `cmdkey.exe`.

### Fix
- Replace `cmdkey.exe` usage with an in-process Win32 credential write (`CredWriteW`) to create/update the `TERMSRV/<hostname>` entry without putting secrets on a command line.
- Ensure staged credentials are removed on *all* failure paths:
  - Track whether staging succeeded.
  - Use `try/finally` around `mstsc.Start()` and cleanup scheduling.
  - If `mstsc` fails to start, immediately delete the staged credential.
- Keep temp `.rdp` cleanup best-effort, but ensure credential cleanup is best-effort on every path.

### Fix Focus Areas
- mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[23-63]
- mRemoteNG/Connection/Protocol/RDP/RdpCredentialCacheCleaner.cs[33-61]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +99 to +102
if (connectionInfo.Protocol == ProtocolType.RDP && connectionInfo.UseMultiMon)
{
Protocol.RDP.RdpExternalMultimonLauncher.Launch(connectionInfo);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Multimon skips postextapp 🐞 Bug ≡ Correctness

When UseMultiMon is enabled, ConnectionInitiator.OpenConnection returns after launching mstsc.exe,
bypassing the normal ProtocolBase lifecycle. As a result, PostExtApp (which is triggered from
Prot_Event_Closed) will never run for multimon connections.
Agent Prompt
### Issue description
For RDP multimon sessions, `OpenConnection` launches `mstsc.exe` and returns early. This skips the embedded protocol lifecycle events (`Prot_Event_Closed`), which is where `PostExtApp` is executed, so multimon connections behave differently than normal connections.

### Issue Context
`PostExtApp` is started only inside `Prot_Event_Closed`, which will not fire for an externally launched `mstsc.exe` process.

### Fix
- Add a completion hook for external `mstsc.exe` sessions:
  - Option A: in `RdpExternalMultimonLauncher.Launch`, after `mstsc.WaitForExit()`, run the configured `PostExtApp` for `connectionInfo`.
  - Option B: return the `Process` (or a Task) from `Launch` and have `ConnectionInitiator` await/continue to run `PostExtApp` on completion.
- Ensure PostExtApp runs only if configured (same behavior as `Prot_Event_Closed`).

### Fix Focus Areas
- mRemoteNG/Connection/ConnectionInitiator.cs[93-103]
- mRemoteNG/Connection/ConnectionInitiator.cs[415-437]
- mRemoteNG/Connection/Protocol/RDP/RdpExternalMultimonLauncher.cs[51-58]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +723 to +726
GridItem gridItem = _pGrid.SelectedGridItem;
if (gridItem?.Value is string password && !string.IsNullOrEmpty(password))
{
MessageBox.Show(password, gridItem.Label, MessageBoxButtons.OK, MessageBoxIcon.Information);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Show password exposes secrets 🐞 Bug ⛨ Security

ConfigWindow adds a "Show Password" context menu action that displays the stored password in
plaintext in a MessageBox. This weakens local secret handling because any user with access to the
unlocked session can reveal credentials without additional authorization or safeguards.
Agent Prompt
### Issue description
The new context-menu action reveals password values directly via `MessageBox.Show(...)` with no confirmation, re-authentication, or other guardrails.

### Issue Context
The menu is enabled for properties marked with `PasswordPropertyTextAttribute` and then displays the raw string value.

### Fix
- Add a guard before revealing secrets, e.g.:
  - Require re-authentication / master password confirmation (if the app supports it), or
  - At minimum, require an explicit confirmation dialog warning about shoulder-surfing/screen capture.
- Consider showing the value in a dedicated dialog with a masked-by-default textbox and a deliberate “Reveal” toggle, rather than always showing plaintext.

### Fix Focus Areas
- mRemoteNG/UI/Window/ConfigWindow.cs[682-734]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread release-msi.bat
echo Building MSI installer...
:: ponytail: WixTargetsPath is forced because VS's own MSBuildExtensionsPath32 no longer
:: points at the classic shared "%ProgramFiles(x86)%\MSBuild" folder where WiX v3 installs its targets.
"%MSBUILD%" "%ROOT%mRemoteNGInstaller\Installer\Installer.wixproj" /t:Build /p:Configuration=Release /p:Platform=x64 /p:SolutionDir=%ROOT% "/p:WixTargetsPath=%ProgramFiles(x86)%\MSBuild\Microsoft\WiX\v3.x\Wix.targets" "/p:WixCATargetsPath=%ProgramFiles(x86)%\MSBuild\Microsoft\WiX\v3.x\wix.ca.targets" /nologo /v:minimal

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Msbuild args unquoted 🐞 Bug ☼ Reliability

release-msi.bat passes /p:SolutionDir=%ROOT% to MSBuild without quotes, so builds from paths
containing spaces will mis-parse the argument and fail or behave incorrectly.
Agent Prompt
### Issue description
The MSI build command sets `SolutionDir` without quoting `%ROOT%`, which can contain spaces (derived from `%~dp0`). This breaks MSBuild argument parsing.

### Fix
- Quote the property value:
  - Change `/p:SolutionDir=%ROOT%` to `/p:SolutionDir="%ROOT%"`.
- (Optional) Apply the same quoting discipline to any other path-valued properties you add later.

### Fix Focus Areas
- release-msi.bat[6-7]
- release-msi.bat[51-55]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@lakshamanr
lakshamanr force-pushed the feature/local-build-and-release-scripts branch from d1cc6d4 to 7cd55b5 Compare August 7, 2026 15:13
@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
B Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants