Files
EonaCat.RdpColorizer/EonaCat.RdpColorizer/RdpTargetResolver.cs
T
2026-08-06 22:29:07 +02:00

91 lines
3.3 KiB
C#

using System.Management;
using System.Text;
using System.Text.RegularExpressions;
using static EonaCat.RdpColorizer.NativeMethods;
namespace EonaCat.RdpColorizer
{
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
/// <summary>
/// Figures out which server/IP an mstsc.exe window is actually connected
/// to, so the watcher can match it against saved profiles. Prefers the
/// process command line (exact, e.g. "10.0.0.5:3389") and falls back to
/// the window title (e.g. "SERVER01 - Remote Desktop Connection") when
/// the command line can't be read - which happens if mstsc.exe is
/// running elevated or under a different user account than this app.
/// </summary>
internal static class RdpTargetResolver
{
public static string? Resolve(nint hwnd)
{
GetWindowThreadProcessId(hwnd, out int pid);
return TryGetCommandLineTarget(pid) ?? TryGetTitleTarget(hwnd);
}
private static string? TryGetCommandLineTarget(int pid)
{
try
{
using var searcher = new ManagementObjectSearcher(
$"SELECT CommandLine FROM Win32_Process WHERE ProcessId = {pid}");
foreach (ManagementObject mo in searcher.Get())
{
string? cmdLine = mo["CommandLine"]?.ToString();
if (string.IsNullOrEmpty(cmdLine))
{
continue;
}
Match match = Regex.Match(cmdLine, @"/v:([^\s""]+)", RegexOptions.IgnoreCase);
if (match.Success)
{
return Normalize(match.Groups[1].Value);
}
}
}
catch
{
// WMI query can fail for permission reasons (e.g. mstsc.exe
// running elevated); fall back to reading the window title.
}
return null;
}
private static string? TryGetTitleTarget(nint hwnd)
{
var sb = new StringBuilder(512);
GetWindowText(hwnd, sb, sb.Capacity);
string title = sb.ToString();
// Typical titles: "SERVER01 - Remote Desktop Connection" or,
// while still negotiating, just "Remote Desktop Connection".
Match match = Regex.Match(title, @"^(.*?)\s*-\s*Remote Desktop Connection", RegexOptions.IgnoreCase);
if (match.Success && !string.IsNullOrWhiteSpace(match.Groups[1].Value))
{
return Normalize(match.Groups[1].Value);
}
return null;
}
private static string Normalize(string target)
{
target = target.Trim();
// Drop a trailing ":port" (but leave bare IPv6 addresses alone -
// we only strip when there's exactly one colon, i.e. IPv4/hostname:port).
int colonCount = target.Split(':').Length - 1;
if (colonCount == 1)
{
int idx = target.IndexOf(':');
target = target[..idx];
}
return target;
}
}
}