[MWB] Fix file transfer not working in service mode (#37542)

* [MWB] Fix file transfer not working in service mode

* Spellcheck issues
This commit is contained in:
Ani
2025-02-20 11:58:29 +01:00
committed by GitHub
parent 727de3e1fc
commit fa4471a9e6
3 changed files with 73 additions and 6 deletions

View File

@@ -9,6 +9,7 @@ using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Windows.Forms;
@@ -443,7 +444,31 @@ namespace MouseWithoutBorders
{
_ = Common.ImpersonateLoggedOnUserAndDoSomething(() =>
{
Setting.Values.Username = WindowsIdentity.GetCurrent(true).Name;
// See: https://stackoverflow.com/questions/19487541/how-to-get-windows-user-name-from-sessionid
static string GetUsernameBySessionId(int sessionId)
{
string username = "SYSTEM";
if (NativeMethods.WTSQuerySessionInformation(IntPtr.Zero, sessionId, NativeMethods.WTSInfoClass.WTSUserName, out nint buffer, out int strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer);
NativeMethods.WTSFreeMemory(buffer);
if (NativeMethods.WTSQuerySessionInformation(IntPtr.Zero, sessionId, NativeMethods.WTSInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = @$"{Marshal.PtrToStringAnsi(buffer)}\{username}";
NativeMethods.WTSFreeMemory(buffer);
}
}
return username;
}
// The most direct way to fetch the username is WindowsIdentity.GetCurrent(true).Name
// but GetUserName can run within an ExecutionContext.SuppressFlow block, which creates issues
// with WindowsIdentity.GetCurrent.
// See: https://stackoverflow.com/questions/76998988/exception-when-using-executioncontext-suppressflow-in-net-7
// So we use WTSQuerySessionInformation as a workaround.
Setting.Values.Username = GetUsernameBySessionId(Process.GetCurrentProcess().SessionId);
});
}
else

View File

@@ -5,7 +5,6 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Principal;
@@ -39,23 +38,31 @@ namespace MouseWithoutBorders
}
else
{
// SuppressFlow fixes an issue on service mode, where WTSQueryUserToken runs successfully once and then fails
// on subsequent calls. The reason appears to be an unknown issue with reverting the impersonation,
// meaning that subsequent impersonation attempts run as the logged-on user and fail.
// This is a workaround.
using var asyncFlowControl = System.Threading.ExecutionContext.SuppressFlow();
uint dwSessionId;
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero;
try
{
dwSessionId = (uint)Process.GetCurrentProcess().SessionId;
uint rv = NativeMethods.WTSQueryUserToken(dwSessionId, ref hUserToken);
Logger.LogDebug("WTSQueryUserToken returned " + rv.ToString(CultureInfo.CurrentCulture));
var lastError = rv == 0 ? Marshal.GetLastWin32Error() : 0;
Logger.LogDebug($"{nameof(NativeMethods.WTSQueryUserToken)} returned {rv.ToString(CultureInfo.CurrentCulture)}");
if (rv == 0)
{
Logger.LogDebug($"WTSQueryUserToken failed with: {Marshal.GetLastWin32Error()}.");
Logger.Log($"{nameof(NativeMethods.WTSQueryUserToken)} failed with: {lastError}.");
return false;
}
if (!NativeMethods.DuplicateToken(hUserToken, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref hUserTokenDup))
{
Logger.TelemetryLogTrace($"DuplicateToken Failed! {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
Logger.TelemetryLogTrace($"{nameof(NativeMethods.DuplicateToken)} Failed! {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return false;

View File

@@ -75,6 +75,41 @@ namespace MouseWithoutBorders.Class
[DllImport("kernel32.dll")]
internal static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
internal static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTSInfoClass infoClass, out IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
internal static extern void WTSFreeMemory(IntPtr pointer);
internal enum WTSInfoClass
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType,
WTSIdleTime,
WTSLogonTime,
WTSIncomingBytes,
WTSOutgoingBytes,
WTSIncomingFrames,
WTSOutgoingFrames,
WTSClientInfo,
WTSSessionInfo,
}
#endif
[DllImport("user32.dll", SetLastError = true)]
@@ -812,7 +847,7 @@ namespace MouseWithoutBorders.Class
// [DllImport("kernel32.dll", SetLastError = true)]
// internal static extern IntPtr CreateToolhelp32Snapshot(UInt32 dwFlags, UInt32 th32ProcessID);
[DllImport("Wtsapi32.dll")]
[DllImport("Wtsapi32.dll", SetLastError = true)]
internal static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "1", Justification = "Dotnet port with style preservation")]