diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.cs b/src/modules/MouseWithoutBorders/App/Class/Common.cs
deleted file mode 100644
index 9a34500b52..0000000000
--- a/src/modules/MouseWithoutBorders/App/Class/Common.cs
+++ /dev/null
@@ -1,1661 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Net.Sockets;
-using System.Runtime.InteropServices;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading;
-using System.Windows.Forms;
-
-using Microsoft.PowerToys.Settings.UI.Library;
-
-//
-// Most of the helper methods.
-//
-//
-// 2008 created by Truong Do (ductdo).
-// 2009-... modified by Truong Do (TruongDo).
-// 2023- Included in PowerToys.
-//
-using MouseWithoutBorders.Class;
-using MouseWithoutBorders.Core;
-using MouseWithoutBorders.Exceptions;
-
-using Clipboard = MouseWithoutBorders.Core.Clipboard;
-using Thread = MouseWithoutBorders.Core.Thread;
-
-// Log is enough
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckClipboard()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckForDesktopSwitchEvent(System.Boolean)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SetAsStartupItem(System.Boolean)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#HelperThread()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetMyStorageDir()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#MouseEvent(MouseWithoutBorders.MOUSEDATA)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#KeybdEvent(MouseWithoutBorders.KEYBDDATA)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ImpersonateLoggedOnUserAndDoSomething(System.Threading.ThreadStart)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#StartMouseWithoutBordersService()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#HookClipboard()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReceiveClipboardData(MouseWithoutBorders.DATA,System.Boolean)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReceiverCallback(System.Object)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ConnectAndGetData(System.Object)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckNewVersion()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#StartServiceAndSendLogoffSignal()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetScreenConfig()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CaptureScreen()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#InitEncryption()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ToggleIcon()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetNameAndIPAddresses()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#Cleanup()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "type", Target = "MouseWithoutBorders.Common", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.Common.#ConnectAndGetData(System.Object)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.Common.#ProcessPackage(MouseWithoutBorders.DATA)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SetOEMBackground(System.Boolean)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#get_Machine_Pool()", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SetOEMBackground(System.Boolean,System.String)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetNewImageAndSaveTo(System.String,System.String)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateLowIntegrityProcess(System.String,System.String,System.Int32)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#LogAll()", MessageId = "System.String.Format(System.IFormatProvider,System.String,System.Object[])", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckForDesktopSwitchEvent(System.Boolean)", MessageId = "MouseWithoutBorders.NativeMethods.SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#DragDropStep04()", MessageId = "MouseWithoutBorders.NativeMethods.SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateLowIntegrityProcess(System.String,System.String,System.Int32)", MessageId = "MouseWithoutBorders.NativeMethods.WaitForSingleObject(System.IntPtr,System.Int32)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#GetText(System.IntPtr)", MessageId = "MouseWithoutBorders.NativeMethods.GetWindowText(System.IntPtr,System.Text.StringBuilder,System.Int32)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#ImpersonateLoggedOnUserAndDoSomething(System.Threading.ThreadStart)", MessageId = "MouseWithoutBorders.NativeMethods.WTSQueryUserToken(System.UInt32,System.IntPtr@)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateLowIntegrityProcess(System.String,System.String,System.Int32,System.Boolean,System.Int64)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateProcessInInputDesktopSession(System.String,System.String,System.String,System.Boolean,System.Int16)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SkSend(MouseWithoutBorders.DATA,System.Boolean,System.Int32)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReceiveClipboardDataUsingTCP(MouseWithoutBorders.DATA,System.Boolean,System.Net.Sockets.Socket)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#UpdateMachineMatrix(MouseWithoutBorders.DATA)", Justification = "Dotnet port with style preservation")]
-[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReopenSockets(System.Boolean)", Justification = "Dotnet port with style preservation")]
-
-namespace MouseWithoutBorders
-{
- internal partial class Common
- {
- internal Common()
- {
- }
-
- private static InputHook hook;
- private static FrmMatrix matrixForm;
- private static FrmInputCallback inputCallbackForm;
- private static FrmAbout aboutForm;
-#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
- internal static Thread helper;
- internal static int screenWidth;
- internal static int screenHeight;
-#pragma warning restore SA1307
- private static int lastX;
- private static int lastY;
-
- private static bool mainFormVisible = true;
- private static bool runOnLogonDesktop;
- private static bool runOnScrSaverDesktop;
-
-#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
- internal static int[] toggleIcons;
- internal static int toggleIconsIndex;
-#pragma warning restore SA1307
- internal const int TOGGLE_ICONS_SIZE = 4;
- internal const int ICON_ONE = 0;
- internal const int ICON_ALL = 1;
- internal const int ICON_SMALL_CLIPBOARD = 2;
- internal const int ICON_BIG_CLIPBOARD = 3;
- internal const int ICON_ERROR = 4;
- internal const int JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999;
-
- internal const int NETWORK_STREAM_BUF_SIZE = 1024 * 1024;
- internal static readonly EventWaitHandle EvSwitch = new(false, EventResetMode.AutoReset);
- private static Point lastPos;
-#pragma warning disable SA1307 // Accessible fields should begin with upper-case names
- internal static int switchCount;
-#pragma warning restore SA1307
- private static long lastReconnectByHotKeyTime;
-#pragma warning disable SA1307 // Accessible fields should begin with upper-case names
- internal static int tcpPort;
-#pragma warning restore SA1307
- private static bool secondOpenSocketTry;
- private static string binaryName;
-
- internal static Process CurrentProcess { get; set; }
-
- internal static bool HotkeyMatched(int vkCode, bool winDown, bool ctrlDown, bool altDown, bool shiftDown, HotkeySettings hotkey)
- {
- return !hotkey.IsEmpty() && (vkCode == hotkey.Code) && (!hotkey.Win || winDown) && (!hotkey.Alt || altDown) && (!hotkey.Shift || shiftDown) && (!hotkey.Ctrl || ctrlDown);
- }
-
- public static string BinaryName
- {
- get => Common.binaryName;
- set => Common.binaryName = value;
- }
-
- public static bool SecondOpenSocketTry
- {
- get => Common.secondOpenSocketTry;
- set => Common.secondOpenSocketTry = value;
- }
-
- public static long LastReconnectByHotKeyTime
- {
- get => Common.lastReconnectByHotKeyTime;
- set => Common.lastReconnectByHotKeyTime = value;
- }
-
- public static int SwitchCount
- {
- get => Common.switchCount;
- set => Common.switchCount = value;
- }
-
- public static Point LastPos
- {
- get => Common.lastPos;
- set => Common.lastPos = value;
- }
-
- internal static FrmAbout AboutForm
- {
- get => Common.aboutForm;
- set => Common.aboutForm = value;
- }
-
- internal static FrmInputCallback InputCallbackForm
- {
- get => Common.inputCallbackForm;
- set => Common.inputCallbackForm = value;
- }
-
- public static int PaintCount { get; set; }
-
- internal static bool RunOnScrSaverDesktop
- {
- get => Common.runOnScrSaverDesktop;
- set => Common.runOnScrSaverDesktop = value;
- }
-
- internal static bool RunOnLogonDesktop
- {
- get => Common.runOnLogonDesktop;
- set => Common.runOnLogonDesktop = value;
- }
-
- internal static bool RunWithNoAdminRight { get; set; }
-
- internal static int LastX
- {
- get => Common.lastX;
- set => Common.lastX = value;
- }
-
- internal static int LastY
- {
- get => Common.lastY;
- set => Common.lastY = value;
- }
-
- internal static int[] ToggleIcons => Common.toggleIcons;
-
- internal static int ScreenHeight => Common.screenHeight;
-
- internal static int ScreenWidth => Common.screenWidth;
-
- internal static bool Is64bitOS
- {
- get; set;
-
- // set { Common.is64bitOS = value; }
- }
-
- internal static int ToggleIconsIndex
- {
- // get { return Common.toggleIconsIndex; }
- set => Common.toggleIconsIndex = value;
- }
-
- internal static InputHook Hook
- {
- get => Common.hook;
- set => Common.hook = value;
- }
-
- internal static SocketStuff Sk { get; set; }
-
- internal static FrmScreen MainForm { get; set; }
-
- internal static FrmMouseCursor MouseCursorForm { get; set; }
-
- internal static FrmMatrix MatrixForm
- {
- get => Common.matrixForm;
- set => Common.matrixForm = value;
- }
-
- internal static ID DesMachineID
- {
- get => MachineStuff.desMachineID;
-
- set
- {
- MachineStuff.desMachineID = value;
- MachineStuff.DesMachineName = MachineStuff.NameFromID(MachineStuff.desMachineID);
- }
- }
-
- internal static ID MachineID => (ID)Setting.Values.MachineId;
-
- internal static string MachineName { get; set; }
-
- internal static bool MainFormVisible
- {
- get => Common.mainFormVisible;
- set => Common.mainFormVisible = value;
- }
-
- internal static Mutex SocketMutex { get; set; } // Synchronization between MouseWithoutBorders running in different desktops
-
- // TODO: For telemetry only, to be removed.
- private static int socketMutexBalance;
-
- internal static void ReleaseSocketMutex()
- {
- if (SocketMutex != null)
- {
- Logger.LogDebug("SOCKET MUTEX BEGIN RELEASE.");
-
- try
- {
- _ = Interlocked.Decrement(ref socketMutexBalance);
- SocketMutex.ReleaseMutex();
- }
- catch (ApplicationException e)
- {
- // The current thread does not own the mutex, the thread acquired it will own it.
- Logger.TelemetryLogTrace($"{nameof(ReleaseSocketMutex)}: {e.Message}. {Thread.CurrentThread.ManagedThreadId}/{UIThreadID}.", SeverityLevel.Warning);
- }
-
- Logger.LogDebug("SOCKET MUTEX RELEASED.");
- }
- else
- {
- Logger.LogDebug("SOCKET MUTEX NULL.");
- }
- }
-
- internal static void AcquireSocketMutex()
- {
- if (SocketMutex != null)
- {
- Logger.LogDebug("SOCKET MUTEX BEGIN WAIT.");
- int waitTimeout = 60000; // TcpListener.Stop may take very long to complete for some reason.
-
- int socketMutexBalance = int.MinValue;
-
- bool acquireMutex = ExecuteAndTrace(
- "Waiting for sockets to close",
- () =>
- {
- socketMutexBalance = Interlocked.Increment(ref Common.socketMutexBalance);
- _ = SocketMutex.WaitOne(waitTimeout); // The app now requires .Net 4.0. Note: .Net20RTM does not have the one-parameter version of the API.
- },
- TimeSpan.FromSeconds(5));
-
- // Took longer than expected.
- if (!acquireMutex)
- {
- Process[] ps = Process.GetProcessesByName(Common.BinaryName);
- Logger.TelemetryLogTrace($"Balance: {socketMutexBalance}, Active: {WinAPI.IsMyDesktopActive()}, Sid/Console: {Process.GetCurrentProcess().SessionId}/{NativeMethods.WTSGetActiveConsoleSessionId()}, Desktop/Input: {WinAPI.GetMyDesktop()}/{WinAPI.GetInputDesktop()}, count: {ps?.Length}.", SeverityLevel.Warning);
- }
-
- Logger.LogDebug("SOCKET MUTEX ENDED.");
- }
- else
- {
- Logger.LogDebug("SOCKET MUTEX NULL.");
- }
- }
-
- internal static bool BlockingUI { get; private set; }
-
- internal static bool ExecuteAndTrace(string actionName, Action action, TimeSpan timeout, bool restart = false)
- {
- bool rv = true;
- Logger.LogDebug(actionName);
- bool done = false;
-
- BlockingUI = true;
-
- if (restart)
- {
- Common.MainForm.Text = Setting.Values.MyIdEx;
-
- /* closesocket() rarely gets stuck for some reason inside ntdll!ZwClose ...=>... afd!AfdCleanupCore.
- * There is no good workaround for it so far, still working with [Winsock 2.0 Discussions] to address the issue.
- * */
- new Thread(
- () =>
- {
- for (int i = 0; i < timeout.TotalSeconds; i++)
- {
- Thread.Sleep(1000);
-
- if (done)
- {
- return;
- }
- }
-
- Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}, restarting the process.", SeverityLevel.Warning, true);
-
- string desktop = WinAPI.GetMyDesktop();
- MachineStuff.oneInstanceCheck?.Close();
- _ = Process.Start(Application.ExecutablePath, desktop);
- Logger.LogDebug($"Started on desktop {desktop}");
-
- Process.GetCurrentProcess().KillProcess(true);
- },
- $"{actionName} watchdog").Start();
- }
-
- Stopwatch timer = Stopwatch.StartNew();
-
- try
- {
- action();
- }
- finally
- {
- done = true;
- BlockingUI = false;
-
- if (restart)
- {
- Common.MainForm.Text = Setting.Values.MyID;
- }
-
- timer.Stop();
-
- if (timer.Elapsed > timeout)
- {
- rv = false;
-
- if (!restart)
- {
- Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}: {(long)timer.Elapsed.TotalSeconds}.", SeverityLevel.Warning);
- }
- }
- }
-
- return rv;
- }
-
- internal static byte[] GetBytes(string st)
- {
- return ASCIIEncoding.ASCII.GetBytes(st);
- }
-
- internal static string GetString(byte[] bytes)
- {
- return ASCIIEncoding.ASCII.GetString(bytes);
- }
-
- internal static byte[] GetBytesU(string st)
- {
- return ASCIIEncoding.Unicode.GetBytes(st);
- }
-
- internal static string GetStringU(byte[] bytes)
- {
- return ASCIIEncoding.Unicode.GetString(bytes);
- }
-
- internal static int UIThreadID { get; set; }
-
- internal static void DoSomethingInUIThread(Action action, bool blocking = false)
- {
- InvokeInFormThread(MainForm, UIThreadID, action, blocking);
- }
-
- internal static int InputCallbackThreadID { get; set; }
-
- internal static void DoSomethingInTheInputCallbackThread(Action action, bool blocking = true)
- {
- InvokeInFormThread(InputCallbackForm, InputCallbackThreadID, action, blocking);
- }
-
- private static void InvokeInFormThread(System.Windows.Forms.Form form, int threadId, Action action, bool blocking)
- {
- if (form != null)
- {
- int currentThreadId = Thread.CurrentThread.ManagedThreadId;
-
- if (currentThreadId == threadId)
- {
- action();
- }
- else
- {
- bool done = false;
-
- try
- {
- Action callback = () =>
- {
- try
- {
- action();
- }
- catch (Exception e)
- {
- Logger.Log(e);
- }
- finally
- {
- done = true;
- }
- };
- _ = form.BeginInvoke(callback);
- }
- catch (Exception e)
- {
- done = true;
- Logger.Log(e);
- }
-
- while (blocking && !done)
- {
- Thread.Sleep(16);
-
- if (currentThreadId == UIThreadID || currentThreadId == InputCallbackThreadID)
- {
- Application.DoEvents();
- }
- }
- }
- }
- }
-
- private static readonly Lock InputSimulationLock = new();
-
- internal static void DoSomethingInTheInputSimulationThread(ThreadStart target)
- {
- /*
- * For some reason, SendInput may hit deadlock if it is called in the InputHookProc thread.
- * For now leave it as is in the caller thread which is the socket receiver thread.
- * */
-
- // SendInput is thread-safe but few users seem to hit a deadlock occasionally, probably a Windows bug.
- lock (InputSimulationLock)
- {
- target();
- }
- }
-
- internal static void SendPackage(ID des, PackageType packageType)
- {
- DATA package = new();
- package.Type = packageType;
- package.Des = des;
- package.MachineName = MachineName;
-
- SkSend(package, null, false);
- }
-
- internal static void SendHeartBeat(bool initial = false)
- {
- SendPackage(ID.ALL, initial && Encryption.GeneratedKey ? PackageType.Heartbeat_ex : PackageType.Heartbeat);
- }
-
- private static long lastSendNextMachine;
-
- internal static void SendNextMachine(ID hostMachine, ID nextMachine, Point requestedXY)
- {
- Logger.LogDebug($"SendNextMachine: Host machine: {hostMachine}, Next machine: {nextMachine}, Requested XY: {requestedXY}");
-
- if (GetTick() - lastSendNextMachine < 100)
- {
- Logger.LogDebug("Machine switching in progress."); // "Move Mouse relatively" mode, slow machine/network, quick/busy hand.
- return;
- }
-
- lastSendNextMachine = GetTick();
-
- DATA package = new();
- package.Type = PackageType.NextMachine;
-
- package.Des = hostMachine;
-
- package.Md.X = requestedXY.X;
- package.Md.Y = requestedXY.Y;
- package.Md.WheelDelta = (int)nextMachine;
-
- SkSend(package, null, false);
-
- Logger.LogDebug("SendNextMachine done.");
- }
-
- private static ulong lastInputEventCount;
- private static ulong lastRealInputEventCount;
-
- internal static void SendAwakeBeat()
- {
- if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && WinAPI.IsMyDesktopActive() &&
- Setting.Values.BlockScreenSaver && lastRealInputEventCount != Event.RealInputEventCount)
- {
- SendPackage(ID.ALL, PackageType.Awake);
- }
- else
- {
- SendHeartBeat();
- }
-
- lastInputEventCount = Event.InputEventCount;
- lastRealInputEventCount = Event.RealInputEventCount;
- }
-
- internal static void HumanBeingDetected()
- {
- if (lastInputEventCount == Event.InputEventCount)
- {
- if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && WinAPI.IsMyDesktopActive())
- {
- PokeMyself();
- }
- }
-
- lastInputEventCount = Event.InputEventCount;
- }
-
- internal static void PokeMyself()
- {
- int x, y = 0;
-
- for (int i = 0; i < 10; i++)
- {
- x = Encryption.Ran.Next(-9, 10);
- InputSimulation.MoveMouseRelative(x, y);
- Thread.Sleep(50);
- InputSimulation.MoveMouseRelative(-x, -y);
- Thread.Sleep(50);
-
- if (lastInputEventCount != Event.InputEventCount)
- {
- break;
- }
- }
- }
-
- internal static void InitLastInputEventCount()
- {
- lastInputEventCount = Event.InputEventCount;
- lastRealInputEventCount = Event.RealInputEventCount;
- }
-
- internal static void SendHello()
- {
- SendPackage(ID.ALL, PackageType.Hello);
- }
-
- /*
- internal static void SendHi()
- {
- SendPackage(IP.ALL, PackageType.hi);
- }
- * */
-
- internal static void SendByeBye()
- {
- Logger.LogDebug($"{nameof(SendByeBye)}");
- SendPackage(ID.ALL, PackageType.ByeBye);
- }
-
- internal static void SendClipboardBeat()
- {
- SendPackage(ID.ALL, PackageType.Clipboard);
- }
-
- internal static void ProcessByeByeMessage(DATA package)
- {
- if (package.Src == MachineStuff.desMachineID)
- {
- MachineStuff.SwitchToMachine(MachineName.Trim());
- }
-
- _ = MachineStuff.RemoveDeadMachines(package.Src);
- }
-
- internal static long GetTick() // ms
- {
- return DateTime.Now.Ticks / 10000;
- }
-
- internal static void SetToggleIcon(int[] toggleIcons)
- {
- Logger.LogDebug($"{nameof(SetToggleIcon)}: {toggleIcons?.FirstOrDefault()}");
- Common.toggleIcons = toggleIcons;
- toggleIconsIndex = 0;
- }
-
- internal static string CaptureScreen()
- {
- try
- {
- string fileName = GetMyStorageDir() + @"ScreenCaptureByMouseWithoutBorders.png";
- int w = MachineStuff.desktopBounds.Right - MachineStuff.desktopBounds.Left;
- int h = MachineStuff.desktopBounds.Bottom - MachineStuff.desktopBounds.Top;
- Bitmap bm = new(w, h);
- Graphics g = Graphics.FromImage(bm);
- Size s = new(w, h);
- g.CopyFromScreen(MachineStuff.desktopBounds.Left, MachineStuff.desktopBounds.Top, 0, 0, s);
- bm.Save(fileName, ImageFormat.Png);
- bm.Dispose();
- return fileName;
- }
- catch (Exception e)
- {
- Logger.Log(e);
- return null;
- }
- }
-
- internal static void PrepareScreenCapture()
- {
- Common.DoSomethingInUIThread(() =>
- {
- if (!DragDrop.MouseDown && Helper.SendMessageToHelper(0x401, IntPtr.Zero, IntPtr.Zero) > 0)
- {
- Common.MMSleep(0.2);
- InputSimulation.SendKey(new KEYBDDATA() { wVk = (int)VK.SNAPSHOT });
- InputSimulation.SendKey(new KEYBDDATA() { dwFlags = (int)WM.LLKHF.UP, wVk = (int)VK.SNAPSHOT });
-
- Logger.LogDebug("PrepareScreenCapture: SNAPSHOT simulated.");
-
- _ = NativeMethods.MoveWindow(
- (IntPtr)NativeMethods.FindWindow(null, Helper.HELPER_FORM_TEXT),
- MachineStuff.DesktopBounds.Left,
- MachineStuff.DesktopBounds.Top,
- MachineStuff.DesktopBounds.Right - MachineStuff.DesktopBounds.Left,
- MachineStuff.DesktopBounds.Bottom - MachineStuff.DesktopBounds.Top,
- false);
-
- _ = Helper.SendMessageToHelper(0x406, IntPtr.Zero, IntPtr.Zero, false);
- }
- else
- {
- Logger.Log("PrepareScreenCapture: Validation failed.");
- }
- });
- }
-
- internal static void OpenImage(string file)
- {
- // We want to run mspaint under the user account who ran explorer.exe (who logged in this current input desktop)
-
- // ImpersonateLoggedOnUserAndDoSomething(delegate()
- // {
- // Process.Start("explorer", "\"" + file + "\"");
- // });
- _ = Launch.CreateProcessInInputDesktopSession(
- "\"" + Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe") +
- "\"",
- "\"" + file + "\"",
- WinAPI.GetInputDesktop(),
- 1);
-
- // CreateNormalIntegrityProcess(Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe") +
- // " \"" + file + "\"");
-
- // We don't want to run mspaint as local system account
- /*
- ProcessStartInfo s = new ProcessStartInfo(
- Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe"),
- "\"" + file + "\"");
- s.WindowStyle = ProcessWindowStyle.Maximized;
- Process.Start(s);
- * */
- }
-
- internal static void SendImage(string machine, string file)
- {
- Clipboard.LastDragDropFile = file;
-
- // Send ClipboardCapture
- if (machine.Equals("All", StringComparison.OrdinalIgnoreCase))
- {
- SendPackage(ID.ALL, PackageType.ClipboardCapture);
- }
- else
- {
- ID id = MachineStuff.MachinePool.ResolveID(machine);
- if (id != ID.NONE)
- {
- SendPackage(id, PackageType.ClipboardCapture);
- }
- }
- }
-
- internal static void SendImage(ID src, string file)
- {
- Clipboard.LastDragDropFile = file;
-
- // Send ClipboardCapture
- SendPackage(src, PackageType.ClipboardCapture);
- }
-
- internal static void ShowToolTip(string tip, int timeOutInMilliseconds = 5000, ToolTipIcon icon = ToolTipIcon.Info, bool showBalloonTip = true, bool forceEvenIfHidingOldUI = false)
- {
- if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
- {
- DoSomethingInUIThread(() =>
- {
- if (Setting.Values.FirstRun)
- {
- MachineStuff.Settings?.ShowTip(icon, tip, timeOutInMilliseconds);
- }
-
- Common.MatrixForm?.ShowTip(icon, tip, timeOutInMilliseconds);
-
- if (showBalloonTip)
- {
- if (MainForm != null)
- {
- MainForm.ShowToolTip(tip, timeOutInMilliseconds, forceEvenIfHidingOldUI: forceEvenIfHidingOldUI);
- }
- else
- {
- Logger.Log(tip);
- }
- }
- });
- }
- }
-
- private static FrmMessage topMostMessageForm;
-
- internal static void ToggleShowTopMostMessage(string text, string bigText, int timeOut)
- {
- DoSomethingInUIThread(() =>
- {
- if (topMostMessageForm == null)
- {
- topMostMessageForm = new FrmMessage(text, bigText, timeOut);
- topMostMessageForm.Show();
- }
- else
- {
- FrmMessage currentMessageForm = topMostMessageForm;
- topMostMessageForm = null;
- currentMessageForm.Close();
- }
- });
- }
-
- internal static void HideTopMostMessage()
- {
- DoSomethingInUIThread(() =>
- {
- topMostMessageForm?.Close();
- });
- }
-
- internal static void NullTopMostMessage()
- {
- DoSomethingInUIThread(() =>
- {
- if (topMostMessageForm != null)
- {
- topMostMessageForm = null;
- }
- });
- }
-
- internal static bool IsTopMostMessageNotNull()
- {
- return topMostMessageForm != null;
- }
-
- private static bool TestSend(TcpSk t)
- {
- ID remoteMachineID;
-
- if (t.Status == SocketStatus.Connected)
- {
- try
- {
- DATA package = new();
- package.Type = PackageType.Hi;
- package.Des = remoteMachineID = (ID)t.MachineId;
- package.MachineName = MachineName;
-
- _ = Sk.TcpSend(t, package);
- t.EncryptedStream?.Flush();
-
- return true;
- }
- catch (ExpectedSocketException)
- {
- t.BackingSocket = null; // To be removed at CloseAnUnusedSocket()
- }
- }
-
- t.Status = SocketStatus.SendError;
- return false;
- }
-
- internal static bool IsConnectedTo(ID remoteMachineID)
- {
- bool updateClientSockets = false;
-
- if (remoteMachineID == MachineID)
- {
- return true;
- }
-
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- if (sk.TcpSockets != null)
- {
- foreach (TcpSk t in sk.TcpSockets)
- {
- if (t.Status == SocketStatus.Connected && (uint)remoteMachineID == t.MachineId)
- {
- if (TestSend(t))
- {
- return true;
- }
- else
- {
- updateClientSockets = true;
- }
- }
- }
- }
- }
- }
-
- if (updateClientSockets)
- {
- MachineStuff.UpdateClientSockets(nameof(IsConnectedTo));
- }
-
- return false;
- }
-
-#if DEBUG
- private static long minSendTime = long.MaxValue;
- private static long avgSendTime;
- private static long maxSendTime;
- private static long totalSendCount;
- private static long totalSendTime;
-#endif
-
- internal static void SkSend(DATA data, uint? exceptDes, bool includeHandShakingSockets)
- {
- bool connected = false;
-
- SocketStuff sk = Sk;
-
- if (sk != null)
- {
-#if DEBUG
- long startStop = DateTime.Now.Ticks;
- totalSendCount++;
-#endif
-
- try
- {
- data.Id = Interlocked.Increment(ref Package.PackageID);
-
- bool updateClientSockets = false;
-
- lock (sk.TcpSocketsLock)
- {
- foreach (TcpSk t in sk.TcpSockets)
- {
- if (t != null && t.BackingSocket != null && (t.Status == SocketStatus.Connected || (t.Status == SocketStatus.Handshaking && includeHandShakingSockets)))
- {
- if (t.MachineId == (uint)data.Des || (data.Des == ID.ALL && t.MachineId != exceptDes && MachineStuff.InMachineMatrix(t.MachineName)))
- {
- try
- {
- sk.TcpSend(t, data);
-
- if (data.Des != ID.ALL)
- {
- connected = true;
- }
- }
- catch (ExpectedSocketException)
- {
- t.BackingSocket = null; // To be removed at CloseAnUnusedSocket()
- updateClientSockets = true;
- }
- catch (Exception e)
- {
- Logger.Log(e);
- t.BackingSocket = null; // To be removed at CloseAnUnusedSocket()
- updateClientSockets = true;
- }
- }
- }
- }
- }
-
- if (!connected && data.Des != ID.ALL)
- {
- Logger.LogDebug("********** No active connection found for the remote machine! **********" + data.Des.ToString());
-
- if (data.Des == ID.NONE || MachineStuff.RemoveDeadMachines(data.Des))
- {
- // SwitchToMachine(MachineName.Trim());
- MachineStuff.NewDesMachineID = DesMachineID = MachineID;
- MachineStuff.SwitchLocation.X = Event.XY_BY_PIXEL + Event.myLastX;
- MachineStuff.SwitchLocation.Y = Event.XY_BY_PIXEL + Event.myLastY;
- MachineStuff.SwitchLocation.ResetCount();
- EvSwitch.Set();
- }
- }
-
- if (updateClientSockets)
- {
- MachineStuff.UpdateClientSockets("SkSend");
- }
- }
- catch (Exception e)
- {
- Logger.Log(e);
- }
-
-#if DEBUG
- startStop = DateTime.Now.Ticks - startStop;
- totalSendTime += startStop;
- if (startStop < minSendTime)
- {
- minSendTime = startStop;
- }
-
- if (startStop > maxSendTime)
- {
- maxSendTime = startStop;
- }
-
- avgSendTime = totalSendTime / totalSendCount;
-#endif
- }
- else
- {
- Package.PackageSent.Nil++;
- }
- }
-
- internal static void CloseAnUnusedSocket()
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- if (sk.TcpSockets != null)
- {
- TcpSk tobeRemoved = null;
-
- foreach (TcpSk t in sk.TcpSockets)
- {
- if ((t.Status != SocketStatus.Connected && t.BirthTime < GetTick() - SocketStuff.CONNECT_TIMEOUT) || t.BackingSocket == null)
- {
- Logger.LogDebug("CloseAnUnusedSocket: " + t.MachineName + ":" + t.MachineId + "|" + t.Status.ToString());
- tobeRemoved = t;
-
- if (t.BackingSocket != null)
- {
- try
- {
- t.BackingSocket.Close();
- }
- catch (Exception e)
- {
- Logger.Log(e);
- }
- }
-
- break; // Each time we try to remove one socket only.
- }
- }
-
- if (tobeRemoved != null)
- {
- _ = sk.TcpSockets.Remove(tobeRemoved);
- }
- }
- }
- }
- }
-
- internal static bool AtLeastOneSocketConnected()
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- if (sk.TcpSockets != null)
- {
- foreach (TcpSk t in sk.TcpSockets)
- {
- if (t.Status == SocketStatus.Connected)
- {
- Logger.LogDebug("AtLeastOneSocketConnected returning true: " + t.MachineName);
- return true;
- }
- }
- }
- }
- }
-
- Logger.LogDebug("AtLeastOneSocketConnected returning false.");
- return false;
- }
-
- internal static Socket AtLeastOneServerSocketConnected()
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- if (sk.TcpSockets != null)
- {
- foreach (TcpSk t in sk.TcpSockets)
- {
- if (!t.IsClient && t.Status == SocketStatus.Connected)
- {
- Logger.LogDebug("AtLeastOneServerSocketConnected returning true: " + t.MachineName);
- return t.BackingSocket;
- }
- }
- }
- }
- }
-
- Logger.LogDebug("AtLeastOneServerSocketConnected returning false.");
- return null;
- }
-
- internal static TcpSk GetConnectedClientSocket()
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- return sk.TcpSockets?.FirstOrDefault(item => item.IsClient && item.Status == SocketStatus.Connected);
- }
- }
- else
- {
- return null;
- }
- }
-
- internal static bool AtLeastOneSocketEstablished()
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- if (sk.TcpSockets != null)
- {
- foreach (TcpSk t in sk.TcpSockets)
- {
- if (t.BackingSocket != null && t.BackingSocket.Connected)
- {
- if (TestSend(t))
- {
- Logger.LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning true: {t.MachineName}");
- return true;
- }
- }
- }
- }
- }
- }
-
- Logger.LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning false.");
- return false;
- }
-
- internal static bool IsConnectedByAClientSocketTo(string machineName)
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- foreach (TcpSk t in sk.TcpSockets)
- {
- if (t != null && t.IsClient && t.Status == SocketStatus.Connected
- && t.BackingSocket != null && t.MachineName.Equals(machineName, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- internal static IPAddress GetConnectedClientSocketIPAddressFor(string machineName)
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- return sk.TcpSockets.FirstOrDefault(t => t != null && t.IsClient && t.Status == SocketStatus.Connected
- && t.Address != null && t.MachineName.Equals(machineName, StringComparison.OrdinalIgnoreCase))
- ?.Address;
- }
- }
-
- return null;
- }
-
- internal static bool IsConnectingByAClientSocketTo(string machineName, IPAddress ip)
- {
- SocketStuff sk = Common.Sk;
-
- if (sk != null)
- {
- lock (sk.TcpSocketsLock)
- {
- foreach (TcpSk t in sk.TcpSockets)
- {
- if (t != null && t.IsClient && t.Status == SocketStatus.Connecting
- && t.BackingSocket != null && t.MachineName.Equals(machineName, StringComparison.OrdinalIgnoreCase)
- && t.Address.ToString().Equals(ip.ToString(), StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- internal static void UpdateSetupMachineMatrix(string desMachine)
- {
- int machineCt = 0;
-
- foreach (string m in MachineStuff.MachineMatrix)
- {
- if (!string.IsNullOrEmpty(m.Trim()))
- {
- machineCt++;
- }
- }
-
- if (machineCt < 2 && MachineStuff.Settings != null && (MachineStuff.Settings.GetCurrentPage() is SetupPage1 || MachineStuff.Settings.GetCurrentPage() is SetupPage2b))
- {
- MachineStuff.MachineMatrix = new string[MachineStuff.MAX_MACHINE] { Common.MachineName.Trim(), desMachine, string.Empty, string.Empty };
- Logger.LogDebug("UpdateSetupMachineMatrix: " + string.Join(",", MachineStuff.MachineMatrix));
-
- Common.DoSomethingInUIThread(
- () =>
- {
- MachineStuff.Settings.SetControlPage(new SetupPage4());
- },
- true);
- }
- }
-
- internal static void ReopenSockets(bool byUser)
- {
- DoSomethingInUIThread(
- () =>
- {
- try
- {
- SocketStuff tmpSk = Sk;
-
- if (tmpSk != null)
- {
- Sk = null; // TODO: This looks redundant.
- tmpSk.Close(byUser);
- }
-
- Sk = new SocketStuff(tcpPort, byUser);
- }
- catch (Exception e)
- {
- Sk = null;
- Logger.Log(e);
- }
-
- if (Sk != null)
- {
- if (byUser)
- {
- SocketStuff.ClearBadIPs();
- }
-
- MachineStuff.UpdateClientSockets("ReopenSockets");
- }
- },
- true);
-
- if (Sk == null)
- {
- return;
- }
-
- Common.DoSomethingInTheInputCallbackThread(() =>
- {
- if (Common.Hook != null)
- {
- Common.Hook.Stop();
- Common.Hook = null;
- }
-
- if (byUser)
- {
- Common.InputCallbackForm.Close();
- Common.InputCallbackForm = null;
- Program.StartInputCallbackThread();
- }
- else
- {
- Common.InputCallbackForm.InstallKeyboardAndMouseHook();
- }
- });
- }
-
- internal static string GetMyStorageDir()
- {
- string st = string.Empty;
-
- try
- {
- if (RunOnLogonDesktop || RunOnScrSaverDesktop)
- {
- st = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
- if (!Directory.Exists(st))
- {
- _ = Directory.CreateDirectory(st);
- }
-
- st += @"\" + Common.BinaryName;
- if (!Directory.Exists(st))
- {
- _ = Directory.CreateDirectory(st);
- }
-
- st += @"\ScreenCaptures\";
- if (!Directory.Exists(st))
- {
- _ = Directory.CreateDirectory(st);
- }
- }
- else
- {
- _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() =>
- {
- st = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\" + Common.BinaryName;
- if (!Directory.Exists(st))
- {
- _ = Directory.CreateDirectory(st);
- }
-
- st += @"\ScreenCaptures\";
- if (!Directory.Exists(st))
- {
- _ = Directory.CreateDirectory(st);
- }
- });
- }
-
- Logger.LogDebug("GetMyStorageDir: " + st);
-
- // Delete old files.
- foreach (FileInfo fi in new DirectoryInfo(st).GetFiles())
- {
- if (fi.CreationTime.AddDays(1) < DateTime.Now)
- {
- fi.Delete();
- }
- }
-
- return st;
- }
- catch (Exception e)
- {
- Logger.Log(e);
-
- if (string.IsNullOrEmpty(st) || !st.Contains(Common.BinaryName))
- {
- st = Path.GetTempPath();
- }
-
- return st;
- }
- }
-
- internal static void GetMachineName()
- {
- string machine_Name = string.Empty;
-
- try
- {
- machine_Name = Dns.GetHostName();
- Logger.LogDebug("GetHostName = " + machine_Name);
- }
- catch (Exception e)
- {
- Logger.Log(e);
-
- if (string.IsNullOrEmpty(machine_Name))
- {
- machine_Name = "RANDOM" + Encryption.Ran.Next().ToString(CultureInfo.CurrentCulture);
- }
- }
-
- if (machine_Name.Length > 32)
- {
- machine_Name = machine_Name[..32];
- }
-
- Common.MachineName = machine_Name.Trim();
-
- Logger.LogDebug($"========== {nameof(GetMachineName)} ended!");
- }
-
- private static string GetNetworkName(NetworkInterface networkInterface)
- {
- return $"{networkInterface.Name} | {networkInterface.Description.Replace(":", "-")}";
- }
-
- internal static string GetRemoteStringIP(Socket s, bool throwException = false)
- {
- if (s == null)
- {
- return string.Empty;
- }
-
- string ip;
-
- try
- {
- ip = (s?.RemoteEndPoint as IPEndPoint)?.Address?.ToString();
-
- if (string.IsNullOrEmpty(ip))
- {
- return string.Empty;
- }
- }
- catch (ObjectDisposedException e)
- {
- Logger.Log($"{nameof(GetRemoteStringIP)}: The socket could have been disposed by other threads, error: {e.Message}");
-
- if (throwException)
- {
- throw;
- }
-
- return string.Empty;
- }
- catch (SocketException e)
- {
- Logger.Log($"{nameof(GetRemoteStringIP)}: {e.Message}");
-
- if (throwException)
- {
- throw;
- }
-
- return string.Empty;
- }
-
- return ip;
- }
-
- internal static void CloseAllFormsAndHooks()
- {
- if (Hook != null)
- {
- Hook.Stop();
- Hook = null;
- if (InputCallbackForm != null)
- {
- DoSomethingInTheInputCallbackThread(() =>
- {
- InputCallbackForm.Close();
- InputCallbackForm = null;
- });
- }
- }
-
- if (MainForm != null)
- {
- MainForm.Destroy();
- MainForm = null;
- }
-
- if (MatrixForm != null)
- {
- MatrixForm.Close();
- MatrixForm = null;
- }
-
- if (AboutForm != null)
- {
- AboutForm.Close();
- AboutForm = null;
- }
- }
-
- internal static void MoveMouseToCenter()
- {
- Logger.LogDebug("+++++ MoveMouseToCenter");
- InputSimulation.MoveMouse(
- MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2),
- MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2));
- }
-
- internal static void HideMouseCursor(bool byHideMouseMessage)
- {
- Common.LastPos = new Point(
- MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2),
- Setting.Values.HideMouse ? 4 : MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2));
-
- if ((MachineStuff.desMachineID != MachineID && MachineStuff.desMachineID != ID.ALL) || byHideMouseMessage)
- {
- _ = NativeMethods.SetCursorPos(Common.LastPos.X, Common.LastPos.Y);
- _ = NativeMethods.GetCursorPos(ref Common.lastPos);
- Logger.LogDebug($"+++++ HideMouseCursor, byHideMouseMessage = {byHideMouseMessage}");
- }
-
- CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue);
- }
-
- internal static string GetText(IntPtr hWnd)
- {
- int length = NativeMethods.GetWindowTextLength(hWnd);
- StringBuilder sb = new(length + 1);
- int rv = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity);
- Logger.LogDebug("GetWindowText returned " + rv.ToString(CultureInfo.CurrentCulture));
- return sb.ToString();
- }
-
- public static string GetWindowClassName(IntPtr hWnd)
- {
- StringBuilder buffer = new(128);
- _ = NativeMethods.GetClassName(hWnd, buffer, buffer.Capacity);
- return buffer.ToString();
- }
-
- internal static void MMSleep(double secs)
- {
- for (int i = 0; i < secs * 10; i++)
- {
- Application.DoEvents();
- Thread.Sleep(100);
- }
- }
-
- internal static void UpdateMultipleModeIconAndMenu()
- {
- MainForm?.UpdateMultipleModeIconAndMenu();
- }
-
- internal static void SendOrReceiveARandomDataBlockPerInitialIV(Stream st, bool send = true)
- {
- byte[] ranData = new byte[Encryption.SymAlBlockSize];
-
- try
- {
- if (send)
- {
- ranData = RandomNumberGenerator.GetBytes(Encryption.SymAlBlockSize);
- st.Write(ranData, 0, ranData.Length);
- }
- else
- {
- int toRead = ranData.Length;
- int read = st.ReadEx(ranData, 0, toRead);
-
- if (read != toRead)
- {
- Logger.LogDebug("Stream has no more data after reading {0} bytes.", read);
- }
- }
- }
- catch (IOException e)
- {
- string log = $"{nameof(SendOrReceiveARandomDataBlockPerInitialIV)}: Exception {(send ? "writing" : "reading")} to the socket stream: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
- Logger.Log(log);
-
- if (e.InnerException is not (SocketException or ObjectDisposedException))
- {
- throw;
- }
- }
- }
-
- private static bool DisableEasyMouseWhenForegroundWindowIsFullscreenSetting()
- {
- return Setting.Values.DisableEasyMouseWhenForegroundWindowIsFullscreen;
- }
-
- private static bool IsAppIgnoredByEasyMouseFullscreenCheck(IntPtr foregroundWindowHandle)
- {
- if (NativeMethods.GetWindowThreadProcessId(foregroundWindowHandle, out var processId) == 0)
- {
- Logger.LogDebug($"GetWindowThreadProcessId failed with error : {Marshal.GetLastWin32Error()}");
- return false;
- }
-
- var processHandle = NativeMethods.OpenProcess(0x1000, false, processId);
- if (processHandle == IntPtr.Zero)
- {
- return false;
- }
-
- uint maxPath = 260;
- var nameBuffer = new char[maxPath];
- if (!NativeMethods.QueryFullProcessImageName(
- processHandle, NativeMethods.QUERY_FULL_PROCESS_NAME_FLAGS.DEFAULT, nameBuffer, ref maxPath))
- {
- Logger.LogDebug($"QueryFullProcessImageName failed with error : {Marshal.GetLastWin32Error()}");
- NativeMethods.CloseHandle(processHandle);
- return false;
- }
-
- NativeMethods.CloseHandle(processHandle);
-
- var name = new string(nameBuffer, 0, (int)maxPath);
-
- var excludedApps = Setting.Values.EasyMouseFullscreenSwitchBlockExcludedApps;
-
- return excludedApps.Contains(Path.GetFileNameWithoutExtension(name), StringComparer.OrdinalIgnoreCase)
- || excludedApps.Contains(Path.GetFileName(name), StringComparer.OrdinalIgnoreCase);
- }
-
- internal static bool IsEasyMouseBlockedByFullscreenWindow()
- {
- var shellHandle = NativeMethods.GetShellWindow();
- var desktopHandle = NativeMethods.GetDesktopWindow();
- var foregroundHandle = NativeMethods.GetForegroundWindow();
-
- // If the foreground window is either the desktop or the Windows shell, we are not in fullscreen mode.
- if (foregroundHandle.Equals(shellHandle) || foregroundHandle.Equals(desktopHandle))
- {
- return false;
- }
-
- if (NativeMethods.SHQueryUserNotificationState(out var userNotificationState) != 0)
- {
- Logger.LogDebug($"SHQueryUserNotificationState failed with error : {Marshal.GetLastWin32Error()}");
- return false;
- }
-
- switch (userNotificationState)
- {
- // An application running in full screen mode, check if the foreground window is
- // listed as ignored in the settings.
- case NativeMethods.USER_NOTIFICATION_STATE.BUSY:
- case NativeMethods.USER_NOTIFICATION_STATE.RUNNING_D3D_FULL_SCREEN:
- case NativeMethods.USER_NOTIFICATION_STATE.PRESENTATION_MODE:
- return !IsAppIgnoredByEasyMouseFullscreenCheck(foregroundHandle);
-
- // No full screen app running.
- case NativeMethods.USER_NOTIFICATION_STATE.NOT_PRESENT:
- case NativeMethods.USER_NOTIFICATION_STATE.ACCEPTS_NOTIFICATIONS:
- case NativeMethods.USER_NOTIFICATION_STATE.QUIET_TIME:
- // Cannot determine
- case NativeMethods.USER_NOTIFICATION_STATE.APP:
- default:
- return false;
- }
- }
-
- ///
- /// Check if a machine switch triggered by EasyMouse would be allowed to proceed due to other settings.
- ///
- /// A boolean that tells us if the switch isn't blocked by any other settings
- internal static bool IsEasyMouseSwitchAllowed()
- {
- // Never prevent a switch if we are not moving out of the host machine.
- if (!DisableEasyMouseWhenForegroundWindowIsFullscreenSetting() || DesMachineID != MachineID)
- {
- return true;
- }
-
- // Check if the switch is blocked by a full-screen window running in the foreground
- return !IsEasyMouseBlockedByFullscreenWindow();
- }
- }
-}
diff --git a/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs b/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs
index 62360b4795..15ac6fb8b8 100644
--- a/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs
+++ b/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs
@@ -18,12 +18,12 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.VisualStudio.Threading;
+using MouseWithoutBorders.Core;
using Newtonsoft.Json;
using StreamJsonRpc;
#if !MM_HELPER
using MouseWithoutBorders.Class;
-using MouseWithoutBorders.Core;
#endif
using SystemClipboard = System.Windows.Forms.Clipboard;
@@ -246,11 +246,11 @@ WellKnownSidType.AuthenticatedUserSid, null);
CancellationToken cancellationToken = _serverTaskCancellationSource.Token;
IpcChannel.StartIpcServer(ChannelName + "/" + RemoteObjectName, cancellationToken);
- Common.IpcChannelCreated = true;
+ IpcChannelHelper.IpcChannelCreated = true;
}
catch (Exception e)
{
- Common.IpcChannelCreated = false;
+ IpcChannelHelper.IpcChannelCreated = false;
Common.ShowToolTip("Error setting up clipboard sharing, clipboard sharing will not work!", 5000, ToolTipIcon.Error);
Logger.Log(e);
}
@@ -405,7 +405,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
try
{
- rv = Common.Retry(nameof(SystemClipboard.ContainsFileDropList), () => { return SystemClipboard.ContainsFileDropList(); }, (log) => Log(log));
+ rv = IpcChannelHelper.Retry(nameof(SystemClipboard.ContainsFileDropList), () => { return SystemClipboard.ContainsFileDropList(); }, (log) => Log(log));
}
catch (ExternalException e)
{
@@ -427,7 +427,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
try
{
- rv = Common.Retry(nameof(SystemClipboard.ContainsImage), () => { return SystemClipboard.ContainsImage(); }, (log) => Log(log));
+ rv = IpcChannelHelper.Retry(nameof(SystemClipboard.ContainsImage), () => { return SystemClipboard.ContainsImage(); }, (log) => Log(log));
}
catch (ExternalException e)
{
@@ -449,7 +449,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
try
{
- rv = Common.Retry(nameof(SystemClipboard.ContainsText), () => { return SystemClipboard.ContainsText(); }, (log) => Log(log));
+ rv = IpcChannelHelper.Retry(nameof(SystemClipboard.ContainsText), () => { return SystemClipboard.ContainsText(); }, (log) => Log(log));
}
catch (ExternalException e)
{
@@ -471,7 +471,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
try
{
- rv = Common.Retry(nameof(SystemClipboard.GetFileDropList), () => { return SystemClipboard.GetFileDropList(); }, (log) => Log(log));
+ rv = IpcChannelHelper.Retry(nameof(SystemClipboard.GetFileDropList), () => { return SystemClipboard.GetFileDropList(); }, (log) => Log(log));
}
catch (ExternalException e)
{
@@ -493,7 +493,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
try
{
- rv = Common.Retry(nameof(SystemClipboard.GetImage), () => { return SystemClipboard.GetImage(); }, (log) => Log(log));
+ rv = IpcChannelHelper.Retry(nameof(SystemClipboard.GetImage), () => { return SystemClipboard.GetImage(); }, (log) => Log(log));
}
catch (ExternalException e)
{
@@ -515,7 +515,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
try
{
- rv = Common.Retry(nameof(SystemClipboard.GetText), () => { return SystemClipboard.GetText(format); }, (log) => Log(log));
+ rv = IpcChannelHelper.Retry(nameof(SystemClipboard.GetText), () => { return SystemClipboard.GetText(format); }, (log) => Log(log));
}
catch (ExternalException e)
{
@@ -539,7 +539,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
{
try
{
- _ = Common.Retry(
+ _ = IpcChannelHelper.Retry(
nameof(SystemClipboard.SetImage),
() =>
{
@@ -568,7 +568,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
{
try
{
- _ = Common.Retry(
+ _ = IpcChannelHelper.Retry(
nameof(SystemClipboard.SetText),
() =>
{
@@ -600,44 +600,4 @@ WellKnownSidType.AuthenticatedUserSid, null);
{
internal const int QUIT_CMD = 0x409;
}
-
- internal sealed partial class Common
- {
- internal static bool IpcChannelCreated { get; set; }
-
- internal static T Retry(string name, Func func, Action log, Action preRetry = null)
- {
- int count = 0;
-
- do
- {
- try
- {
- T rv = func();
-
- if (count > 0)
- {
- log($"Trace: {name} has been successful after {count} retry.");
- }
-
- return rv;
- }
- catch (Exception)
- {
- count++;
-
- preRetry?.Invoke();
-
- if (count > 10)
- {
- throw;
- }
-
- Application.DoEvents();
- Thread.Sleep(200);
- }
- }
- while (true);
- }
- }
}
diff --git a/src/modules/MouseWithoutBorders/App/Core/Clipboard.cs b/src/modules/MouseWithoutBorders/App/Core/Clipboard.cs
index e557ff4a37..db16ac8b4d 100644
--- a/src/modules/MouseWithoutBorders/App/Core/Clipboard.cs
+++ b/src/modules/MouseWithoutBorders/App/Core/Clipboard.cs
@@ -1036,7 +1036,7 @@ internal static class Clipboard
{
try
{
- _ = Common.Retry(
+ _ = IpcChannelHelper.Retry(
nameof(SystemClipboard.SetFileDropList),
() =>
{
@@ -1073,7 +1073,7 @@ internal static class Clipboard
{
try
{
- _ = Common.Retry(
+ _ = IpcChannelHelper.Retry(
nameof(SystemClipboard.SetImage),
() =>
{
@@ -1104,7 +1104,7 @@ internal static class Clipboard
{
try
{
- _ = Common.Retry(
+ _ = IpcChannelHelper.Retry(
nameof(SystemClipboard.SetText),
() =>
{
diff --git a/src/modules/MouseWithoutBorders/App/Core/Common.cs b/src/modules/MouseWithoutBorders/App/Core/Common.cs
new file mode 100644
index 0000000000..3c2206f2ab
--- /dev/null
+++ b/src/modules/MouseWithoutBorders/App/Core/Common.cs
@@ -0,0 +1,1654 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+
+using Microsoft.PowerToys.Settings.UI.Library;
+using MouseWithoutBorders.Class;
+using MouseWithoutBorders.Exceptions;
+
+using Clipboard = MouseWithoutBorders.Core.Clipboard;
+using Thread = MouseWithoutBorders.Core.Thread;
+
+// Log is enough
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckClipboard()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckForDesktopSwitchEvent(System.Boolean)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SetAsStartupItem(System.Boolean)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#HelperThread()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetMyStorageDir()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#MouseEvent(MouseWithoutBorders.MOUSEDATA)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#KeybdEvent(MouseWithoutBorders.KEYBDDATA)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ImpersonateLoggedOnUserAndDoSomething(System.Threading.ThreadStart)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#StartMouseWithoutBordersService()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#HookClipboard()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReceiveClipboardData(MouseWithoutBorders.DATA,System.Boolean)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReceiverCallback(System.Object)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ConnectAndGetData(System.Object)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckNewVersion()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#StartServiceAndSendLogoffSignal()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetScreenConfig()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CaptureScreen()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#InitEncryption()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ToggleIcon()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetNameAndIPAddresses()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#Cleanup()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "type", Target = "MouseWithoutBorders.Common", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.Common.#ConnectAndGetData(System.Object)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.Common.#ProcessPackage(MouseWithoutBorders.DATA)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SetOEMBackground(System.Boolean)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#get_Machine_Pool()", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SetOEMBackground(System.Boolean,System.String)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#GetNewImageAndSaveTo(System.String,System.String)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateLowIntegrityProcess(System.String,System.String,System.Int32)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#LogAll()", MessageId = "System.String.Format(System.IFormatProvider,System.String,System.Object[])", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#CheckForDesktopSwitchEvent(System.Boolean)", MessageId = "MouseWithoutBorders.NativeMethods.SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#DragDropStep04()", MessageId = "MouseWithoutBorders.NativeMethods.SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateLowIntegrityProcess(System.String,System.String,System.Int32)", MessageId = "MouseWithoutBorders.NativeMethods.WaitForSingleObject(System.IntPtr,System.Int32)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#GetText(System.IntPtr)", MessageId = "MouseWithoutBorders.NativeMethods.GetWindowText(System.IntPtr,System.Text.StringBuilder,System.Int32)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.Common.#ImpersonateLoggedOnUserAndDoSomething(System.Threading.ThreadStart)", MessageId = "MouseWithoutBorders.NativeMethods.WTSQueryUserToken(System.UInt32,System.IntPtr@)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateLowIntegrityProcess(System.String,System.String,System.Int32,System.Boolean,System.Int64)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#CreateProcessInInputDesktopSession(System.String,System.String,System.String,System.Boolean,System.Int16)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#SkSend(MouseWithoutBorders.DATA,System.Boolean,System.Int32)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReceiveClipboardDataUsingTCP(MouseWithoutBorders.DATA,System.Boolean,System.Net.Sockets.Socket)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#UpdateMachineMatrix(MouseWithoutBorders.DATA)", Justification = "Dotnet port with style preservation")]
+[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Common.#ReopenSockets(System.Boolean)", Justification = "Dotnet port with style preservation")]
+
+//
+// Most of the helper methods.
+//
+//
+// 2008 created by Truong Do (ductdo).
+// 2009-... modified by Truong Do (TruongDo).
+// 2023- Included in PowerToys.
+//
+namespace MouseWithoutBorders.Core;
+
+internal static class Common
+{
+ private static InputHook hook;
+ private static FrmMatrix matrixForm;
+ private static FrmInputCallback inputCallbackForm;
+ private static FrmAbout aboutForm;
+#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
+ internal static Thread helper;
+ internal static int screenWidth;
+ internal static int screenHeight;
+#pragma warning restore SA1307
+ private static int lastX;
+ private static int lastY;
+
+ private static bool mainFormVisible = true;
+ private static bool runOnLogonDesktop;
+ private static bool runOnScrSaverDesktop;
+
+#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
+ internal static int[] toggleIcons;
+ internal static int toggleIconsIndex;
+#pragma warning restore SA1307
+ internal const int TOGGLE_ICONS_SIZE = 4;
+ internal const int ICON_ONE = 0;
+ internal const int ICON_ALL = 1;
+ internal const int ICON_SMALL_CLIPBOARD = 2;
+ internal const int ICON_BIG_CLIPBOARD = 3;
+ internal const int ICON_ERROR = 4;
+ internal const int JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999;
+
+ internal const int NETWORK_STREAM_BUF_SIZE = 1024 * 1024;
+ internal static readonly EventWaitHandle EvSwitch = new(false, EventResetMode.AutoReset);
+ private static Point lastPos;
+#pragma warning disable SA1307 // Accessible fields should begin with upper-case names
+ internal static int switchCount;
+#pragma warning restore SA1307
+ private static long lastReconnectByHotKeyTime;
+#pragma warning disable SA1307 // Accessible fields should begin with upper-case names
+ internal static int tcpPort;
+#pragma warning restore SA1307
+ private static bool secondOpenSocketTry;
+ private static string binaryName;
+
+ internal static Process CurrentProcess { get; set; }
+
+ internal static bool HotkeyMatched(int vkCode, bool winDown, bool ctrlDown, bool altDown, bool shiftDown, HotkeySettings hotkey)
+ {
+ return !hotkey.IsEmpty() && (vkCode == hotkey.Code) && (!hotkey.Win || winDown) && (!hotkey.Alt || altDown) && (!hotkey.Shift || shiftDown) && (!hotkey.Ctrl || ctrlDown);
+ }
+
+ internal static string BinaryName
+ {
+ get => Common.binaryName;
+ set => Common.binaryName = value;
+ }
+
+ internal static bool SecondOpenSocketTry
+ {
+ get => Common.secondOpenSocketTry;
+ set => Common.secondOpenSocketTry = value;
+ }
+
+ internal static long LastReconnectByHotKeyTime
+ {
+ get => Common.lastReconnectByHotKeyTime;
+ set => Common.lastReconnectByHotKeyTime = value;
+ }
+
+ internal static int SwitchCount
+ {
+ get => Common.switchCount;
+ set => Common.switchCount = value;
+ }
+
+ internal static Point LastPos
+ {
+ get => Common.lastPos;
+ set => Common.lastPos = value;
+ }
+
+ internal static FrmAbout AboutForm
+ {
+ get => Common.aboutForm;
+ set => Common.aboutForm = value;
+ }
+
+ internal static FrmInputCallback InputCallbackForm
+ {
+ get => Common.inputCallbackForm;
+ set => Common.inputCallbackForm = value;
+ }
+
+ internal static int PaintCount { get; set; }
+
+ internal static bool RunOnScrSaverDesktop
+ {
+ get => Common.runOnScrSaverDesktop;
+ set => Common.runOnScrSaverDesktop = value;
+ }
+
+ internal static bool RunOnLogonDesktop
+ {
+ get => Common.runOnLogonDesktop;
+ set => Common.runOnLogonDesktop = value;
+ }
+
+ internal static bool RunWithNoAdminRight { get; set; }
+
+ internal static int LastX
+ {
+ get => Common.lastX;
+ set => Common.lastX = value;
+ }
+
+ internal static int LastY
+ {
+ get => Common.lastY;
+ set => Common.lastY = value;
+ }
+
+ internal static int[] ToggleIcons => Common.toggleIcons;
+
+ internal static int ScreenHeight => Common.screenHeight;
+
+ internal static int ScreenWidth => Common.screenWidth;
+
+ internal static bool Is64bitOS
+ {
+ get; set;
+
+ // set { Common.is64bitOS = value; }
+ }
+
+ internal static int ToggleIconsIndex
+ {
+ // get { return Common.toggleIconsIndex; }
+ set => Common.toggleIconsIndex = value;
+ }
+
+ internal static InputHook Hook
+ {
+ get => Common.hook;
+ set => Common.hook = value;
+ }
+
+ internal static SocketStuff Sk { get; set; }
+
+ internal static FrmScreen MainForm { get; set; }
+
+ internal static FrmMouseCursor MouseCursorForm { get; set; }
+
+ internal static FrmMatrix MatrixForm
+ {
+ get => Common.matrixForm;
+ set => Common.matrixForm = value;
+ }
+
+ internal static ID DesMachineID
+ {
+ get => MachineStuff.desMachineID;
+
+ set
+ {
+ MachineStuff.desMachineID = value;
+ MachineStuff.DesMachineName = MachineStuff.NameFromID(MachineStuff.desMachineID);
+ }
+ }
+
+ internal static ID MachineID => (ID)Setting.Values.MachineId;
+
+ internal static string MachineName { get; set; }
+
+ internal static bool MainFormVisible
+ {
+ get => Common.mainFormVisible;
+ set => Common.mainFormVisible = value;
+ }
+
+ internal static Mutex SocketMutex { get; set; } // Synchronization between MouseWithoutBorders running in different desktops
+
+ // TODO: For telemetry only, to be removed.
+ private static int socketMutexBalance;
+
+ internal static void ReleaseSocketMutex()
+ {
+ if (SocketMutex != null)
+ {
+ Logger.LogDebug("SOCKET MUTEX BEGIN RELEASE.");
+
+ try
+ {
+ _ = Interlocked.Decrement(ref socketMutexBalance);
+ SocketMutex.ReleaseMutex();
+ }
+ catch (ApplicationException e)
+ {
+ // The current thread does not own the mutex, the thread acquired it will own it.
+ Logger.TelemetryLogTrace($"{nameof(ReleaseSocketMutex)}: {e.Message}. {Thread.CurrentThread.ManagedThreadId}/{UIThreadID}.", SeverityLevel.Warning);
+ }
+
+ Logger.LogDebug("SOCKET MUTEX RELEASED.");
+ }
+ else
+ {
+ Logger.LogDebug("SOCKET MUTEX NULL.");
+ }
+ }
+
+ internal static void AcquireSocketMutex()
+ {
+ if (SocketMutex != null)
+ {
+ Logger.LogDebug("SOCKET MUTEX BEGIN WAIT.");
+ int waitTimeout = 60000; // TcpListener.Stop may take very long to complete for some reason.
+
+ int socketMutexBalance = int.MinValue;
+
+ bool acquireMutex = ExecuteAndTrace(
+ "Waiting for sockets to close",
+ () =>
+ {
+ socketMutexBalance = Interlocked.Increment(ref Common.socketMutexBalance);
+ _ = SocketMutex.WaitOne(waitTimeout); // The app now requires .Net 4.0. Note: .Net20RTM does not have the one-parameter version of the API.
+ },
+ TimeSpan.FromSeconds(5));
+
+ // Took longer than expected.
+ if (!acquireMutex)
+ {
+ Process[] ps = Process.GetProcessesByName(Common.BinaryName);
+ Logger.TelemetryLogTrace($"Balance: {socketMutexBalance}, Active: {WinAPI.IsMyDesktopActive()}, Sid/Console: {Process.GetCurrentProcess().SessionId}/{NativeMethods.WTSGetActiveConsoleSessionId()}, Desktop/Input: {WinAPI.GetMyDesktop()}/{WinAPI.GetInputDesktop()}, count: {ps?.Length}.", SeverityLevel.Warning);
+ }
+
+ Logger.LogDebug("SOCKET MUTEX ENDED.");
+ }
+ else
+ {
+ Logger.LogDebug("SOCKET MUTEX NULL.");
+ }
+ }
+
+ internal static bool BlockingUI { get; private set; }
+
+ internal static bool ExecuteAndTrace(string actionName, Action action, TimeSpan timeout, bool restart = false)
+ {
+ bool rv = true;
+ Logger.LogDebug(actionName);
+ bool done = false;
+
+ BlockingUI = true;
+
+ if (restart)
+ {
+ Common.MainForm.Text = Setting.Values.MyIdEx;
+
+ /* closesocket() rarely gets stuck for some reason inside ntdll!ZwClose ...=>... afd!AfdCleanupCore.
+ * There is no good workaround for it so far, still working with [Winsock 2.0 Discussions] to address the issue.
+ * */
+ new Thread(
+ () =>
+ {
+ for (int i = 0; i < timeout.TotalSeconds; i++)
+ {
+ Thread.Sleep(1000);
+
+ if (done)
+ {
+ return;
+ }
+ }
+
+ Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}, restarting the process.", SeverityLevel.Warning, true);
+
+ string desktop = WinAPI.GetMyDesktop();
+ MachineStuff.oneInstanceCheck?.Close();
+ _ = Process.Start(Application.ExecutablePath, desktop);
+ Logger.LogDebug($"Started on desktop {desktop}");
+
+ Process.GetCurrentProcess().KillProcess(true);
+ },
+ $"{actionName} watchdog").Start();
+ }
+
+ Stopwatch timer = Stopwatch.StartNew();
+
+ try
+ {
+ action();
+ }
+ finally
+ {
+ done = true;
+ BlockingUI = false;
+
+ if (restart)
+ {
+ Common.MainForm.Text = Setting.Values.MyID;
+ }
+
+ timer.Stop();
+
+ if (timer.Elapsed > timeout)
+ {
+ rv = false;
+
+ if (!restart)
+ {
+ Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}: {(long)timer.Elapsed.TotalSeconds}.", SeverityLevel.Warning);
+ }
+ }
+ }
+
+ return rv;
+ }
+
+ internal static byte[] GetBytes(string st)
+ {
+ return ASCIIEncoding.ASCII.GetBytes(st);
+ }
+
+ internal static string GetString(byte[] bytes)
+ {
+ return ASCIIEncoding.ASCII.GetString(bytes);
+ }
+
+ internal static byte[] GetBytesU(string st)
+ {
+ return ASCIIEncoding.Unicode.GetBytes(st);
+ }
+
+ internal static string GetStringU(byte[] bytes)
+ {
+ return ASCIIEncoding.Unicode.GetString(bytes);
+ }
+
+ internal static int UIThreadID { get; set; }
+
+ internal static void DoSomethingInUIThread(Action action, bool blocking = false)
+ {
+ InvokeInFormThread(MainForm, UIThreadID, action, blocking);
+ }
+
+ internal static int InputCallbackThreadID { get; set; }
+
+ internal static void DoSomethingInTheInputCallbackThread(Action action, bool blocking = true)
+ {
+ InvokeInFormThread(InputCallbackForm, InputCallbackThreadID, action, blocking);
+ }
+
+ private static void InvokeInFormThread(System.Windows.Forms.Form form, int threadId, Action action, bool blocking)
+ {
+ if (form != null)
+ {
+ int currentThreadId = Thread.CurrentThread.ManagedThreadId;
+
+ if (currentThreadId == threadId)
+ {
+ action();
+ }
+ else
+ {
+ bool done = false;
+
+ try
+ {
+ Action callback = () =>
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception e)
+ {
+ Logger.Log(e);
+ }
+ finally
+ {
+ done = true;
+ }
+ };
+ _ = form.BeginInvoke(callback);
+ }
+ catch (Exception e)
+ {
+ done = true;
+ Logger.Log(e);
+ }
+
+ while (blocking && !done)
+ {
+ Thread.Sleep(16);
+
+ if (currentThreadId == UIThreadID || currentThreadId == InputCallbackThreadID)
+ {
+ Application.DoEvents();
+ }
+ }
+ }
+ }
+ }
+
+ private static readonly Lock InputSimulationLock = new();
+
+ internal static void DoSomethingInTheInputSimulationThread(ThreadStart target)
+ {
+ /*
+ * For some reason, SendInput may hit deadlock if it is called in the InputHookProc thread.
+ * For now leave it as is in the caller thread which is the socket receiver thread.
+ * */
+
+ // SendInput is thread-safe but few users seem to hit a deadlock occasionally, probably a Windows bug.
+ lock (InputSimulationLock)
+ {
+ target();
+ }
+ }
+
+ internal static void SendPackage(ID des, PackageType packageType)
+ {
+ DATA package = new();
+ package.Type = packageType;
+ package.Des = des;
+ package.MachineName = MachineName;
+
+ SkSend(package, null, false);
+ }
+
+ internal static void SendHeartBeat(bool initial = false)
+ {
+ SendPackage(ID.ALL, initial && Encryption.GeneratedKey ? PackageType.Heartbeat_ex : PackageType.Heartbeat);
+ }
+
+ private static long lastSendNextMachine;
+
+ internal static void SendNextMachine(ID hostMachine, ID nextMachine, Point requestedXY)
+ {
+ Logger.LogDebug($"SendNextMachine: Host machine: {hostMachine}, Next machine: {nextMachine}, Requested XY: {requestedXY}");
+
+ if (GetTick() - lastSendNextMachine < 100)
+ {
+ Logger.LogDebug("Machine switching in progress."); // "Move Mouse relatively" mode, slow machine/network, quick/busy hand.
+ return;
+ }
+
+ lastSendNextMachine = GetTick();
+
+ DATA package = new();
+ package.Type = PackageType.NextMachine;
+
+ package.Des = hostMachine;
+
+ package.Md.X = requestedXY.X;
+ package.Md.Y = requestedXY.Y;
+ package.Md.WheelDelta = (int)nextMachine;
+
+ SkSend(package, null, false);
+
+ Logger.LogDebug("SendNextMachine done.");
+ }
+
+ private static ulong lastInputEventCount;
+ private static ulong lastRealInputEventCount;
+
+ internal static void SendAwakeBeat()
+ {
+ if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && WinAPI.IsMyDesktopActive() &&
+ Setting.Values.BlockScreenSaver && lastRealInputEventCount != Event.RealInputEventCount)
+ {
+ SendPackage(ID.ALL, PackageType.Awake);
+ }
+ else
+ {
+ SendHeartBeat();
+ }
+
+ lastInputEventCount = Event.InputEventCount;
+ lastRealInputEventCount = Event.RealInputEventCount;
+ }
+
+ internal static void HumanBeingDetected()
+ {
+ if (lastInputEventCount == Event.InputEventCount)
+ {
+ if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && WinAPI.IsMyDesktopActive())
+ {
+ PokeMyself();
+ }
+ }
+
+ lastInputEventCount = Event.InputEventCount;
+ }
+
+ private static void PokeMyself()
+ {
+ int x, y = 0;
+
+ for (int i = 0; i < 10; i++)
+ {
+ x = Encryption.Ran.Next(-9, 10);
+ InputSimulation.MoveMouseRelative(x, y);
+ Thread.Sleep(50);
+ InputSimulation.MoveMouseRelative(-x, -y);
+ Thread.Sleep(50);
+
+ if (lastInputEventCount != Event.InputEventCount)
+ {
+ break;
+ }
+ }
+ }
+
+ internal static void InitLastInputEventCount()
+ {
+ lastInputEventCount = Event.InputEventCount;
+ lastRealInputEventCount = Event.RealInputEventCount;
+ }
+
+ internal static void SendHello()
+ {
+ SendPackage(ID.ALL, PackageType.Hello);
+ }
+
+ /*
+ internal static void SendHi()
+ {
+ SendPackage(IP.ALL, PackageType.hi);
+ }
+ * */
+
+ internal static void SendByeBye()
+ {
+ Logger.LogDebug($"{nameof(SendByeBye)}");
+ SendPackage(ID.ALL, PackageType.ByeBye);
+ }
+
+ internal static void SendClipboardBeat()
+ {
+ SendPackage(ID.ALL, PackageType.Clipboard);
+ }
+
+ internal static void ProcessByeByeMessage(DATA package)
+ {
+ if (package.Src == MachineStuff.desMachineID)
+ {
+ MachineStuff.SwitchToMachine(MachineName.Trim());
+ }
+
+ _ = MachineStuff.RemoveDeadMachines(package.Src);
+ }
+
+ internal static long GetTick() // ms
+ {
+ return DateTime.Now.Ticks / 10000;
+ }
+
+ internal static void SetToggleIcon(int[] toggleIcons)
+ {
+ Logger.LogDebug($"{nameof(SetToggleIcon)}: {toggleIcons?.FirstOrDefault()}");
+ Common.toggleIcons = toggleIcons;
+ toggleIconsIndex = 0;
+ }
+
+ internal static string CaptureScreen()
+ {
+ try
+ {
+ string fileName = GetMyStorageDir() + @"ScreenCaptureByMouseWithoutBorders.png";
+ int w = MachineStuff.desktopBounds.Right - MachineStuff.desktopBounds.Left;
+ int h = MachineStuff.desktopBounds.Bottom - MachineStuff.desktopBounds.Top;
+ Bitmap bm = new(w, h);
+ Graphics g = Graphics.FromImage(bm);
+ Size s = new(w, h);
+ g.CopyFromScreen(MachineStuff.desktopBounds.Left, MachineStuff.desktopBounds.Top, 0, 0, s);
+ bm.Save(fileName, ImageFormat.Png);
+ bm.Dispose();
+ return fileName;
+ }
+ catch (Exception e)
+ {
+ Logger.Log(e);
+ return null;
+ }
+ }
+
+ private static void PrepareScreenCapture()
+ {
+ Common.DoSomethingInUIThread(() =>
+ {
+ if (!DragDrop.MouseDown && Helper.SendMessageToHelper(0x401, IntPtr.Zero, IntPtr.Zero) > 0)
+ {
+ Common.MMSleep(0.2);
+ InputSimulation.SendKey(new KEYBDDATA() { wVk = (int)VK.SNAPSHOT });
+ InputSimulation.SendKey(new KEYBDDATA() { dwFlags = (int)WM.LLKHF.UP, wVk = (int)VK.SNAPSHOT });
+
+ Logger.LogDebug("PrepareScreenCapture: SNAPSHOT simulated.");
+
+ _ = NativeMethods.MoveWindow(
+ (IntPtr)NativeMethods.FindWindow(null, Helper.HELPER_FORM_TEXT),
+ MachineStuff.DesktopBounds.Left,
+ MachineStuff.DesktopBounds.Top,
+ MachineStuff.DesktopBounds.Right - MachineStuff.DesktopBounds.Left,
+ MachineStuff.DesktopBounds.Bottom - MachineStuff.DesktopBounds.Top,
+ false);
+
+ _ = Helper.SendMessageToHelper(0x406, IntPtr.Zero, IntPtr.Zero, false);
+ }
+ else
+ {
+ Logger.Log("PrepareScreenCapture: Validation failed.");
+ }
+ });
+ }
+
+ internal static void OpenImage(string file)
+ {
+ // We want to run mspaint under the user account who ran explorer.exe (who logged in this current input desktop)
+
+ // ImpersonateLoggedOnUserAndDoSomething(delegate()
+ // {
+ // Process.Start("explorer", "\"" + file + "\"");
+ // });
+ _ = Launch.CreateProcessInInputDesktopSession(
+ "\"" + Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe") +
+ "\"",
+ "\"" + file + "\"",
+ WinAPI.GetInputDesktop(),
+ 1);
+
+ // CreateNormalIntegrityProcess(Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe") +
+ // " \"" + file + "\"");
+
+ // We don't want to run mspaint as local system account
+ /*
+ ProcessStartInfo s = new ProcessStartInfo(
+ Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe"),
+ "\"" + file + "\"");
+ s.WindowStyle = ProcessWindowStyle.Maximized;
+ Process.Start(s);
+ * */
+ }
+
+ internal static void SendImage(string machine, string file)
+ {
+ Clipboard.LastDragDropFile = file;
+
+ // Send ClipboardCapture
+ if (machine.Equals("All", StringComparison.OrdinalIgnoreCase))
+ {
+ SendPackage(ID.ALL, PackageType.ClipboardCapture);
+ }
+ else
+ {
+ ID id = MachineStuff.MachinePool.ResolveID(machine);
+ if (id != ID.NONE)
+ {
+ SendPackage(id, PackageType.ClipboardCapture);
+ }
+ }
+ }
+
+ internal static void SendImage(ID src, string file)
+ {
+ Clipboard.LastDragDropFile = file;
+
+ // Send ClipboardCapture
+ SendPackage(src, PackageType.ClipboardCapture);
+ }
+
+ internal static void ShowToolTip(string tip, int timeOutInMilliseconds = 5000, ToolTipIcon icon = ToolTipIcon.Info, bool showBalloonTip = true, bool forceEvenIfHidingOldUI = false)
+ {
+ if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
+ {
+ DoSomethingInUIThread(() =>
+ {
+ if (Setting.Values.FirstRun)
+ {
+ MachineStuff.Settings?.ShowTip(icon, tip, timeOutInMilliseconds);
+ }
+
+ Common.MatrixForm?.ShowTip(icon, tip, timeOutInMilliseconds);
+
+ if (showBalloonTip)
+ {
+ if (MainForm != null)
+ {
+ MainForm.ShowToolTip(tip, timeOutInMilliseconds, forceEvenIfHidingOldUI: forceEvenIfHidingOldUI);
+ }
+ else
+ {
+ Logger.Log(tip);
+ }
+ }
+ });
+ }
+ }
+
+ private static FrmMessage topMostMessageForm;
+
+ internal static void ToggleShowTopMostMessage(string text, string bigText, int timeOut)
+ {
+ DoSomethingInUIThread(() =>
+ {
+ if (topMostMessageForm == null)
+ {
+ topMostMessageForm = new FrmMessage(text, bigText, timeOut);
+ topMostMessageForm.Show();
+ }
+ else
+ {
+ FrmMessage currentMessageForm = topMostMessageForm;
+ topMostMessageForm = null;
+ currentMessageForm.Close();
+ }
+ });
+ }
+
+ internal static void HideTopMostMessage()
+ {
+ DoSomethingInUIThread(() =>
+ {
+ topMostMessageForm?.Close();
+ });
+ }
+
+ internal static void NullTopMostMessage()
+ {
+ DoSomethingInUIThread(() =>
+ {
+ if (topMostMessageForm != null)
+ {
+ topMostMessageForm = null;
+ }
+ });
+ }
+
+ internal static bool IsTopMostMessageNotNull()
+ {
+ return topMostMessageForm != null;
+ }
+
+ private static bool TestSend(TcpSk t)
+ {
+ ID remoteMachineID;
+
+ if (t.Status == SocketStatus.Connected)
+ {
+ try
+ {
+ DATA package = new();
+ package.Type = PackageType.Hi;
+ package.Des = remoteMachineID = (ID)t.MachineId;
+ package.MachineName = MachineName;
+
+ _ = Sk.TcpSend(t, package);
+ t.EncryptedStream?.Flush();
+
+ return true;
+ }
+ catch (ExpectedSocketException)
+ {
+ t.BackingSocket = null; // To be removed at CloseAnUnusedSocket()
+ }
+ }
+
+ t.Status = SocketStatus.SendError;
+ return false;
+ }
+
+ internal static bool IsConnectedTo(ID remoteMachineID)
+ {
+ bool updateClientSockets = false;
+
+ if (remoteMachineID == MachineID)
+ {
+ return true;
+ }
+
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ if (sk.TcpSockets != null)
+ {
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if (t.Status == SocketStatus.Connected && (uint)remoteMachineID == t.MachineId)
+ {
+ if (TestSend(t))
+ {
+ return true;
+ }
+ else
+ {
+ updateClientSockets = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (updateClientSockets)
+ {
+ MachineStuff.UpdateClientSockets(nameof(IsConnectedTo));
+ }
+
+ return false;
+ }
+
+#if DEBUG
+ private static long minSendTime = long.MaxValue;
+ private static long avgSendTime;
+ private static long maxSendTime;
+ private static long totalSendCount;
+ private static long totalSendTime;
+#endif
+
+ internal static void SkSend(DATA data, uint? exceptDes, bool includeHandShakingSockets)
+ {
+ bool connected = false;
+
+ SocketStuff sk = Sk;
+
+ if (sk != null)
+ {
+#if DEBUG
+ long startStop = DateTime.Now.Ticks;
+ totalSendCount++;
+#endif
+
+ try
+ {
+ data.Id = Interlocked.Increment(ref Package.PackageID);
+
+ bool updateClientSockets = false;
+
+ lock (sk.TcpSocketsLock)
+ {
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if (t != null && t.BackingSocket != null && (t.Status == SocketStatus.Connected || (t.Status == SocketStatus.Handshaking && includeHandShakingSockets)))
+ {
+ if (t.MachineId == (uint)data.Des || (data.Des == ID.ALL && t.MachineId != exceptDes && MachineStuff.InMachineMatrix(t.MachineName)))
+ {
+ try
+ {
+ sk.TcpSend(t, data);
+
+ if (data.Des != ID.ALL)
+ {
+ connected = true;
+ }
+ }
+ catch (ExpectedSocketException)
+ {
+ t.BackingSocket = null; // To be removed at CloseAnUnusedSocket()
+ updateClientSockets = true;
+ }
+ catch (Exception e)
+ {
+ Logger.Log(e);
+ t.BackingSocket = null; // To be removed at CloseAnUnusedSocket()
+ updateClientSockets = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (!connected && data.Des != ID.ALL)
+ {
+ Logger.LogDebug("********** No active connection found for the remote machine! **********" + data.Des.ToString());
+
+ if (data.Des == ID.NONE || MachineStuff.RemoveDeadMachines(data.Des))
+ {
+ // SwitchToMachine(MachineName.Trim());
+ MachineStuff.NewDesMachineID = DesMachineID = MachineID;
+ MachineStuff.SwitchLocation.X = Event.XY_BY_PIXEL + Event.myLastX;
+ MachineStuff.SwitchLocation.Y = Event.XY_BY_PIXEL + Event.myLastY;
+ MachineStuff.SwitchLocation.ResetCount();
+ EvSwitch.Set();
+ }
+ }
+
+ if (updateClientSockets)
+ {
+ MachineStuff.UpdateClientSockets("SkSend");
+ }
+ }
+ catch (Exception e)
+ {
+ Logger.Log(e);
+ }
+
+#if DEBUG
+ startStop = DateTime.Now.Ticks - startStop;
+ totalSendTime += startStop;
+ if (startStop < minSendTime)
+ {
+ minSendTime = startStop;
+ }
+
+ if (startStop > maxSendTime)
+ {
+ maxSendTime = startStop;
+ }
+
+ avgSendTime = totalSendTime / totalSendCount;
+#endif
+ }
+ else
+ {
+ Package.PackageSent.Nil++;
+ }
+ }
+
+ internal static void CloseAnUnusedSocket()
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ if (sk.TcpSockets != null)
+ {
+ TcpSk tobeRemoved = null;
+
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if ((t.Status != SocketStatus.Connected && t.BirthTime < GetTick() - SocketStuff.CONNECT_TIMEOUT) || t.BackingSocket == null)
+ {
+ Logger.LogDebug("CloseAnUnusedSocket: " + t.MachineName + ":" + t.MachineId + "|" + t.Status.ToString());
+ tobeRemoved = t;
+
+ if (t.BackingSocket != null)
+ {
+ try
+ {
+ t.BackingSocket.Close();
+ }
+ catch (Exception e)
+ {
+ Logger.Log(e);
+ }
+ }
+
+ break; // Each time we try to remove one socket only.
+ }
+ }
+
+ if (tobeRemoved != null)
+ {
+ _ = sk.TcpSockets.Remove(tobeRemoved);
+ }
+ }
+ }
+ }
+ }
+
+ internal static bool AtLeastOneSocketConnected()
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ if (sk.TcpSockets != null)
+ {
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if (t.Status == SocketStatus.Connected)
+ {
+ Logger.LogDebug("AtLeastOneSocketConnected returning true: " + t.MachineName);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ Logger.LogDebug("AtLeastOneSocketConnected returning false.");
+ return false;
+ }
+
+ private static Socket AtLeastOneServerSocketConnected()
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ if (sk.TcpSockets != null)
+ {
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if (!t.IsClient && t.Status == SocketStatus.Connected)
+ {
+ Logger.LogDebug("AtLeastOneServerSocketConnected returning true: " + t.MachineName);
+ return t.BackingSocket;
+ }
+ }
+ }
+ }
+ }
+
+ Logger.LogDebug("AtLeastOneServerSocketConnected returning false.");
+ return null;
+ }
+
+ internal static TcpSk GetConnectedClientSocket()
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ return sk.TcpSockets?.FirstOrDefault(item => item.IsClient && item.Status == SocketStatus.Connected);
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ internal static bool AtLeastOneSocketEstablished()
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ if (sk.TcpSockets != null)
+ {
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if (t.BackingSocket != null && t.BackingSocket.Connected)
+ {
+ if (TestSend(t))
+ {
+ Logger.LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning true: {t.MachineName}");
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Logger.LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning false.");
+ return false;
+ }
+
+ internal static bool IsConnectedByAClientSocketTo(string machineName)
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if (t != null && t.IsClient && t.Status == SocketStatus.Connected
+ && t.BackingSocket != null && t.MachineName.Equals(machineName, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ internal static IPAddress GetConnectedClientSocketIPAddressFor(string machineName)
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ return sk.TcpSockets.FirstOrDefault(t => t != null && t.IsClient && t.Status == SocketStatus.Connected
+ && t.Address != null && t.MachineName.Equals(machineName, StringComparison.OrdinalIgnoreCase))
+ ?.Address;
+ }
+ }
+
+ return null;
+ }
+
+ internal static bool IsConnectingByAClientSocketTo(string machineName, IPAddress ip)
+ {
+ SocketStuff sk = Common.Sk;
+
+ if (sk != null)
+ {
+ lock (sk.TcpSocketsLock)
+ {
+ foreach (TcpSk t in sk.TcpSockets)
+ {
+ if (t != null && t.IsClient && t.Status == SocketStatus.Connecting
+ && t.BackingSocket != null && t.MachineName.Equals(machineName, StringComparison.OrdinalIgnoreCase)
+ && t.Address.ToString().Equals(ip.ToString(), StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ internal static void UpdateSetupMachineMatrix(string desMachine)
+ {
+ int machineCt = 0;
+
+ foreach (string m in MachineStuff.MachineMatrix)
+ {
+ if (!string.IsNullOrEmpty(m.Trim()))
+ {
+ machineCt++;
+ }
+ }
+
+ if (machineCt < 2 && MachineStuff.Settings != null && (MachineStuff.Settings.GetCurrentPage() is SetupPage1 || MachineStuff.Settings.GetCurrentPage() is SetupPage2b))
+ {
+ MachineStuff.MachineMatrix = new string[MachineStuff.MAX_MACHINE] { Common.MachineName.Trim(), desMachine, string.Empty, string.Empty };
+ Logger.LogDebug("UpdateSetupMachineMatrix: " + string.Join(",", MachineStuff.MachineMatrix));
+
+ Common.DoSomethingInUIThread(
+ () =>
+ {
+ MachineStuff.Settings.SetControlPage(new SetupPage4());
+ },
+ true);
+ }
+ }
+
+ internal static void ReopenSockets(bool byUser)
+ {
+ DoSomethingInUIThread(
+ () =>
+ {
+ try
+ {
+ SocketStuff tmpSk = Sk;
+
+ if (tmpSk != null)
+ {
+ Sk = null; // TODO: This looks redundant.
+ tmpSk.Close(byUser);
+ }
+
+ Sk = new SocketStuff(tcpPort, byUser);
+ }
+ catch (Exception e)
+ {
+ Sk = null;
+ Logger.Log(e);
+ }
+
+ if (Sk != null)
+ {
+ if (byUser)
+ {
+ SocketStuff.ClearBadIPs();
+ }
+
+ MachineStuff.UpdateClientSockets("ReopenSockets");
+ }
+ },
+ true);
+
+ if (Sk == null)
+ {
+ return;
+ }
+
+ Common.DoSomethingInTheInputCallbackThread(() =>
+ {
+ if (Common.Hook != null)
+ {
+ Common.Hook.Stop();
+ Common.Hook = null;
+ }
+
+ if (byUser)
+ {
+ Common.InputCallbackForm.Close();
+ Common.InputCallbackForm = null;
+ Program.StartInputCallbackThread();
+ }
+ else
+ {
+ Common.InputCallbackForm.InstallKeyboardAndMouseHook();
+ }
+ });
+ }
+
+ internal static string GetMyStorageDir()
+ {
+ string st = string.Empty;
+
+ try
+ {
+ if (RunOnLogonDesktop || RunOnScrSaverDesktop)
+ {
+ st = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
+ if (!Directory.Exists(st))
+ {
+ _ = Directory.CreateDirectory(st);
+ }
+
+ st += @"\" + Common.BinaryName;
+ if (!Directory.Exists(st))
+ {
+ _ = Directory.CreateDirectory(st);
+ }
+
+ st += @"\ScreenCaptures\";
+ if (!Directory.Exists(st))
+ {
+ _ = Directory.CreateDirectory(st);
+ }
+ }
+ else
+ {
+ _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() =>
+ {
+ st = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\" + Common.BinaryName;
+ if (!Directory.Exists(st))
+ {
+ _ = Directory.CreateDirectory(st);
+ }
+
+ st += @"\ScreenCaptures\";
+ if (!Directory.Exists(st))
+ {
+ _ = Directory.CreateDirectory(st);
+ }
+ });
+ }
+
+ Logger.LogDebug("GetMyStorageDir: " + st);
+
+ // Delete old files.
+ foreach (FileInfo fi in new DirectoryInfo(st).GetFiles())
+ {
+ if (fi.CreationTime.AddDays(1) < DateTime.Now)
+ {
+ fi.Delete();
+ }
+ }
+
+ return st;
+ }
+ catch (Exception e)
+ {
+ Logger.Log(e);
+
+ if (string.IsNullOrEmpty(st) || !st.Contains(Common.BinaryName))
+ {
+ st = Path.GetTempPath();
+ }
+
+ return st;
+ }
+ }
+
+ internal static void GetMachineName()
+ {
+ string machine_Name = string.Empty;
+
+ try
+ {
+ machine_Name = Dns.GetHostName();
+ Logger.LogDebug("GetHostName = " + machine_Name);
+ }
+ catch (Exception e)
+ {
+ Logger.Log(e);
+
+ if (string.IsNullOrEmpty(machine_Name))
+ {
+ machine_Name = "RANDOM" + Encryption.Ran.Next().ToString(CultureInfo.CurrentCulture);
+ }
+ }
+
+ if (machine_Name.Length > 32)
+ {
+ machine_Name = machine_Name[..32];
+ }
+
+ Common.MachineName = machine_Name.Trim();
+
+ Logger.LogDebug($"========== {nameof(GetMachineName)} ended!");
+ }
+
+ private static string GetNetworkName(NetworkInterface networkInterface)
+ {
+ return $"{networkInterface.Name} | {networkInterface.Description.Replace(":", "-")}";
+ }
+
+ internal static string GetRemoteStringIP(Socket s, bool throwException = false)
+ {
+ if (s == null)
+ {
+ return string.Empty;
+ }
+
+ string ip;
+
+ try
+ {
+ ip = (s?.RemoteEndPoint as IPEndPoint)?.Address?.ToString();
+
+ if (string.IsNullOrEmpty(ip))
+ {
+ return string.Empty;
+ }
+ }
+ catch (ObjectDisposedException e)
+ {
+ Logger.Log($"{nameof(GetRemoteStringIP)}: The socket could have been disposed by other threads, error: {e.Message}");
+
+ if (throwException)
+ {
+ throw;
+ }
+
+ return string.Empty;
+ }
+ catch (SocketException e)
+ {
+ Logger.Log($"{nameof(GetRemoteStringIP)}: {e.Message}");
+
+ if (throwException)
+ {
+ throw;
+ }
+
+ return string.Empty;
+ }
+
+ return ip;
+ }
+
+ internal static void CloseAllFormsAndHooks()
+ {
+ if (Hook != null)
+ {
+ Hook.Stop();
+ Hook = null;
+ if (InputCallbackForm != null)
+ {
+ DoSomethingInTheInputCallbackThread(() =>
+ {
+ InputCallbackForm.Close();
+ InputCallbackForm = null;
+ });
+ }
+ }
+
+ if (MainForm != null)
+ {
+ MainForm.Destroy();
+ MainForm = null;
+ }
+
+ if (MatrixForm != null)
+ {
+ MatrixForm.Close();
+ MatrixForm = null;
+ }
+
+ if (AboutForm != null)
+ {
+ AboutForm.Close();
+ AboutForm = null;
+ }
+ }
+
+ internal static void MoveMouseToCenter()
+ {
+ Logger.LogDebug("+++++ MoveMouseToCenter");
+ InputSimulation.MoveMouse(
+ MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2),
+ MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2));
+ }
+
+ internal static void HideMouseCursor(bool byHideMouseMessage)
+ {
+ Common.LastPos = new Point(
+ MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2),
+ Setting.Values.HideMouse ? 4 : MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2));
+
+ if ((MachineStuff.desMachineID != MachineID && MachineStuff.desMachineID != ID.ALL) || byHideMouseMessage)
+ {
+ _ = NativeMethods.SetCursorPos(Common.LastPos.X, Common.LastPos.Y);
+ _ = NativeMethods.GetCursorPos(ref Common.lastPos);
+ Logger.LogDebug($"+++++ HideMouseCursor, byHideMouseMessage = {byHideMouseMessage}");
+ }
+
+ CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue);
+ }
+
+ internal static string GetText(IntPtr hWnd)
+ {
+ int length = NativeMethods.GetWindowTextLength(hWnd);
+ StringBuilder sb = new(length + 1);
+ int rv = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity);
+ Logger.LogDebug("GetWindowText returned " + rv.ToString(CultureInfo.CurrentCulture));
+ return sb.ToString();
+ }
+
+ private static string GetWindowClassName(IntPtr hWnd)
+ {
+ StringBuilder buffer = new(128);
+ _ = NativeMethods.GetClassName(hWnd, buffer, buffer.Capacity);
+ return buffer.ToString();
+ }
+
+ internal static void MMSleep(double secs)
+ {
+ for (int i = 0; i < secs * 10; i++)
+ {
+ Application.DoEvents();
+ Thread.Sleep(100);
+ }
+ }
+
+ internal static void UpdateMultipleModeIconAndMenu()
+ {
+ MainForm?.UpdateMultipleModeIconAndMenu();
+ }
+
+ internal static void SendOrReceiveARandomDataBlockPerInitialIV(Stream st, bool send = true)
+ {
+ byte[] ranData = new byte[Encryption.SymAlBlockSize];
+
+ try
+ {
+ if (send)
+ {
+ ranData = RandomNumberGenerator.GetBytes(Encryption.SymAlBlockSize);
+ st.Write(ranData, 0, ranData.Length);
+ }
+ else
+ {
+ int toRead = ranData.Length;
+ int read = st.ReadEx(ranData, 0, toRead);
+
+ if (read != toRead)
+ {
+ Logger.LogDebug("Stream has no more data after reading {0} bytes.", read);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ string log = $"{nameof(SendOrReceiveARandomDataBlockPerInitialIV)}: Exception {(send ? "writing" : "reading")} to the socket stream: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)";
+ Logger.Log(log);
+
+ if (e.InnerException is not (SocketException or ObjectDisposedException))
+ {
+ throw;
+ }
+ }
+ }
+
+ private static bool DisableEasyMouseWhenForegroundWindowIsFullscreenSetting()
+ {
+ return Setting.Values.DisableEasyMouseWhenForegroundWindowIsFullscreen;
+ }
+
+ private static bool IsAppIgnoredByEasyMouseFullscreenCheck(IntPtr foregroundWindowHandle)
+ {
+ if (NativeMethods.GetWindowThreadProcessId(foregroundWindowHandle, out var processId) == 0)
+ {
+ Logger.LogDebug($"GetWindowThreadProcessId failed with error : {Marshal.GetLastWin32Error()}");
+ return false;
+ }
+
+ var processHandle = NativeMethods.OpenProcess(0x1000, false, processId);
+ if (processHandle == IntPtr.Zero)
+ {
+ return false;
+ }
+
+ uint maxPath = 260;
+ var nameBuffer = new char[maxPath];
+ if (!NativeMethods.QueryFullProcessImageName(
+ processHandle, NativeMethods.QUERY_FULL_PROCESS_NAME_FLAGS.DEFAULT, nameBuffer, ref maxPath))
+ {
+ Logger.LogDebug($"QueryFullProcessImageName failed with error : {Marshal.GetLastWin32Error()}");
+ NativeMethods.CloseHandle(processHandle);
+ return false;
+ }
+
+ NativeMethods.CloseHandle(processHandle);
+
+ var name = new string(nameBuffer, 0, (int)maxPath);
+
+ var excludedApps = Setting.Values.EasyMouseFullscreenSwitchBlockExcludedApps;
+
+ return excludedApps.Contains(Path.GetFileNameWithoutExtension(name), StringComparer.OrdinalIgnoreCase)
+ || excludedApps.Contains(Path.GetFileName(name), StringComparer.OrdinalIgnoreCase);
+ }
+
+ private static bool IsEasyMouseBlockedByFullscreenWindow()
+ {
+ var shellHandle = NativeMethods.GetShellWindow();
+ var desktopHandle = NativeMethods.GetDesktopWindow();
+ var foregroundHandle = NativeMethods.GetForegroundWindow();
+
+ // If the foreground window is either the desktop or the Windows shell, we are not in fullscreen mode.
+ if (foregroundHandle.Equals(shellHandle) || foregroundHandle.Equals(desktopHandle))
+ {
+ return false;
+ }
+
+ if (NativeMethods.SHQueryUserNotificationState(out var userNotificationState) != 0)
+ {
+ Logger.LogDebug($"SHQueryUserNotificationState failed with error : {Marshal.GetLastWin32Error()}");
+ return false;
+ }
+
+ switch (userNotificationState)
+ {
+ // An application running in full screen mode, check if the foreground window is
+ // listed as ignored in the settings.
+ case NativeMethods.USER_NOTIFICATION_STATE.BUSY:
+ case NativeMethods.USER_NOTIFICATION_STATE.RUNNING_D3D_FULL_SCREEN:
+ case NativeMethods.USER_NOTIFICATION_STATE.PRESENTATION_MODE:
+ return !IsAppIgnoredByEasyMouseFullscreenCheck(foregroundHandle);
+
+ // No full screen app running.
+ case NativeMethods.USER_NOTIFICATION_STATE.NOT_PRESENT:
+ case NativeMethods.USER_NOTIFICATION_STATE.ACCEPTS_NOTIFICATIONS:
+ case NativeMethods.USER_NOTIFICATION_STATE.QUIET_TIME:
+ // Cannot determine
+ case NativeMethods.USER_NOTIFICATION_STATE.APP:
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Check if a machine switch triggered by EasyMouse would be allowed to proceed due to other settings.
+ ///
+ /// A boolean that tells us if the switch isn't blocked by any other settings
+ internal static bool IsEasyMouseSwitchAllowed()
+ {
+ // Never prevent a switch if we are not moving out of the host machine.
+ if (!DisableEasyMouseWhenForegroundWindowIsFullscreenSetting() || DesMachineID != MachineID)
+ {
+ return true;
+ }
+
+ // Check if the switch is blocked by a full-screen window running in the foreground
+ return !IsEasyMouseBlockedByFullscreenWindow();
+ }
+}
diff --git a/src/modules/MouseWithoutBorders/App/Core/Helper.cs b/src/modules/MouseWithoutBorders/App/Core/Helper.cs
index 8c291fb417..2d97d91123 100644
--- a/src/modules/MouseWithoutBorders/App/Core/Helper.cs
+++ b/src/modules/MouseWithoutBorders/App/Core/Helper.cs
@@ -295,9 +295,9 @@ internal static class Helper
return;
}
- if (!Common.IpcChannelCreated)
+ if (!IpcChannelHelper.IpcChannelCreated)
{
- Logger.TelemetryLogTrace($"{nameof(Common.IpcChannelCreated)} = {Common.IpcChannelCreated}. {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
+ Logger.TelemetryLogTrace($"{nameof(IpcChannelHelper.IpcChannelCreated)} = {IpcChannelHelper.IpcChannelCreated}. {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
return;
}
diff --git a/src/modules/MouseWithoutBorders/App/Core/IpcChannelHelper.cs b/src/modules/MouseWithoutBorders/App/Core/IpcChannelHelper.cs
new file mode 100644
index 0000000000..7e6bfd0217
--- /dev/null
+++ b/src/modules/MouseWithoutBorders/App/Core/IpcChannelHelper.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Threading;
+using System.Windows.Forms;
+
+#if !MM_HELPER
+using Thread = MouseWithoutBorders.Core.Thread;
+#endif
+
+namespace MouseWithoutBorders.Core;
+
+internal static class IpcChannelHelper
+{
+ internal static bool IpcChannelCreated { get; set; }
+
+ internal static T Retry(string name, Func func, Action log, Action preRetry = null)
+ {
+ int count = 0;
+
+ do
+ {
+ try
+ {
+ T rv = func();
+
+ if (count > 0)
+ {
+ log($"Trace: {name} has been successful after {count} retry.");
+ }
+
+ return rv;
+ }
+ catch (Exception)
+ {
+ count++;
+
+ preRetry?.Invoke();
+
+ if (count > 10)
+ {
+ throw;
+ }
+
+ Application.DoEvents();
+ Thread.Sleep(200);
+ }
+ }
+ while (true);
+ }
+}
diff --git a/src/modules/MouseWithoutBorders/App/Core/Logger.cs b/src/modules/MouseWithoutBorders/App/Core/Logger.cs
index 4d39983c35..334d269400 100644
--- a/src/modules/MouseWithoutBorders/App/Core/Logger.cs
+++ b/src/modules/MouseWithoutBorders/App/Core/Logger.cs
@@ -198,7 +198,6 @@ internal static class Logger
}
Logger.DumpProgramLogs(sb, level);
- Logger.DumpOtherLogs(sb, level);
Logger.DumpStaticTypes(sb, level);
log = string.Format(
@@ -240,19 +239,16 @@ internal static class Logger
_ = Logger.PrivateDump(sb, AllLogs, "[Program logs]\r\n===============\r\n", 0, level, false);
}
- internal static void DumpOtherLogs(StringBuilder sb, int level)
- {
- _ = Logger.PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, level, false);
- }
-
internal static void DumpStaticTypes(StringBuilder sb, int level)
{
var staticTypes = new List
{
typeof(Clipboard),
+ typeof(Common),
typeof(DragDrop),
typeof(Encryption),
typeof(Event),
+ typeof(IpcChannelHelper),
typeof(InitAndCleanup),
typeof(Helper),
typeof(Launch),
diff --git a/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2b.cs b/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2b.cs
index 5649ae8d7f..bb802aa159 100644
--- a/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2b.cs
+++ b/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2b.cs
@@ -2,6 +2,8 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using MouseWithoutBorders.Core;
+
namespace MouseWithoutBorders
{
public partial class SetupPage2b : SettingsFormPage
diff --git a/src/modules/MouseWithoutBorders/App/Form/frmAbout.cs b/src/modules/MouseWithoutBorders/App/Form/frmAbout.cs
index 3a7ae9901c..069c428589 100644
--- a/src/modules/MouseWithoutBorders/App/Form/frmAbout.cs
+++ b/src/modules/MouseWithoutBorders/App/Form/frmAbout.cs
@@ -15,6 +15,8 @@ using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
+using MouseWithoutBorders.Core;
+
namespace MouseWithoutBorders
{
internal partial class FrmAbout : System.Windows.Forms.Form, IDisposable
diff --git a/src/modules/MouseWithoutBorders/App/Form/frmMessage.cs b/src/modules/MouseWithoutBorders/App/Form/frmMessage.cs
index 2abb18f932..e258df9709 100644
--- a/src/modules/MouseWithoutBorders/App/Form/frmMessage.cs
+++ b/src/modules/MouseWithoutBorders/App/Form/frmMessage.cs
@@ -6,6 +6,8 @@ using System;
using System.Globalization;
using System.Windows.Forms;
+using MouseWithoutBorders.Core;
+
namespace MouseWithoutBorders
{
public partial class FrmMessage : System.Windows.Forms.Form
diff --git a/src/modules/MouseWithoutBorders/App/Form/frmMouseCursor.cs b/src/modules/MouseWithoutBorders/App/Form/frmMouseCursor.cs
index b4c2f13efd..e5b8095d18 100644
--- a/src/modules/MouseWithoutBorders/App/Form/frmMouseCursor.cs
+++ b/src/modules/MouseWithoutBorders/App/Form/frmMouseCursor.cs
@@ -6,6 +6,7 @@ using System;
using System.Windows.Forms;
using MouseWithoutBorders.Class;
+using MouseWithoutBorders.Core;
namespace MouseWithoutBorders
{
diff --git a/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj b/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj
index f97c0c44f3..981266c0cb 100644
--- a/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj
+++ b/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj
@@ -49,6 +49,9 @@
IClipboardHelper.cs
+
+ IpcChannelHelper.cs
+
diff --git a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt
index 34a83830cd..3a36e9b3d2 100644
--- a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt
+++ b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt
@@ -1,9 +1,38 @@
[Program logs]
===============
= System.String[]
-[Other Logs]
+[Clipboard]
+===============
+Comma = System.Char[]
+--System.Char[] = System.Char[]: N/A
+Star = System.Char[]
+--System.Char[] = System.Char[]: N/A
+NullSeparator = System.Char[]
+--System.Char[] = System.Char[]: N/A
+lastClipboardEventTime = 0
+clipboardCopiedTime = 0
+k__BackingField = NONE
+k__BackingField = 0
+k__BackingField = False
+lastClipboardObject =
+k__BackingField = False
+ClipboardThreadOldLock = Lock
+--_owningThreadId = 0
+--_state = 0
+--_recursionCount = 0
+--_spinCount = 22
+--_waiterStartTimeMs = 0
+--s_contentionCount = 0
+--s_maxSpinCount = 22
+--s_minSpinCountForAdaptiveSpin = -100
+BIG_CLIPBOARD_DATA_TIMEOUT = 30000
+MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
+MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
+TEXT_HEADER_SIZE = 12
+DATA_SIZE = 48
+TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
+[Common]
===============
- = MouseWithoutBorders.Common
screenWidth = 0
screenHeight = 0
lastX = 0
@@ -46,7 +75,6 @@ avgSendTime = 0
maxSendTime = 0
totalSendCount = 0
totalSendTime = 0
-k__BackingField = False
TOGGLE_ICONS_SIZE = 4
ICON_ONE = 0
ICON_ALL = 1
@@ -55,36 +83,6 @@ ICON_BIG_CLIPBOARD = 3
ICON_ERROR = 4
JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999
NETWORK_STREAM_BUF_SIZE = 1048576
-[Clipboard]
-===============
-Comma = System.Char[]
---System.Char[] = System.Char[]: N/A
-Star = System.Char[]
---System.Char[] = System.Char[]: N/A
-NullSeparator = System.Char[]
---System.Char[] = System.Char[]: N/A
-lastClipboardEventTime = 0
-clipboardCopiedTime = 0
-k__BackingField = NONE
-k__BackingField = 0
-k__BackingField = False
-lastClipboardObject =
-k__BackingField = False
-ClipboardThreadOldLock = Lock
---_owningThreadId = 0
---_state = 0
---_recursionCount = 0
---_spinCount = 22
---_waiterStartTimeMs = 0
---s_contentionCount = 0
---s_maxSpinCount = 22
---s_minSpinCountForAdaptiveSpin = -100
-BIG_CLIPBOARD_DATA_TIMEOUT = 30000
-MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
-MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
-TEXT_HEADER_SIZE = 12
-DATA_SIZE = 48
-TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
[DragDrop]
===============
isDragging = False
@@ -174,6 +172,9 @@ actualLastPos = {X=0,Y=0}
--Empty = {X=0,Y=0}
myLastX = 0
myLastY = 0
+[IpcChannelHelper]
+===============
+k__BackingField = False
[InitAndCleanup]
===============
initDone = False
@@ -440,6 +441,7 @@ WM_LBUTTONDBLCLK = 515
WM_RBUTTONDBLCLK = 518
WM_MBUTTONDBLCLK = 521
WM_MOUSEWHEEL = 522
+WM_MOUSEHWHEEL = 526
WM_KEYDOWN = 256
WM_KEYUP = 257
WM_SYSKEYDOWN = 260
diff --git a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs
index 6ca983724c..f7cd3b461a 100644
--- a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs
+++ b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs
@@ -117,7 +117,6 @@ public static class LoggerTests
// copied from DumpObjects in Logger.cs
var sb = new StringBuilder(1000000);
Logger.DumpProgramLogs(sb, settingsDumpObjectsLevel);
- Logger.DumpOtherLogs(sb, settingsDumpObjectsLevel);
Logger.DumpStaticTypes(sb, settingsDumpObjectsLevel);
var actual = sb.ToString();