diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs deleted file mode 100644 index 51be48ec5a..0000000000 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs +++ /dev/null @@ -1,1162 +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.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Drawing; -using System.Globalization; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Net.Sockets; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; - -using Microsoft.PowerToys.Telemetry; - -// -// Clipboard related routines. -// -// -// 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 SystemClipboard = System.Windows.Forms.Clipboard; -using Thread = MouseWithoutBorders.Core.Thread; - -namespace MouseWithoutBorders -{ - internal partial class Common - { - internal static readonly char[] Comma = new char[] { ',' }; - internal static readonly char[] Star = new char[] { '*' }; - internal static readonly char[] NullSeparator = new char[] { '\0' }; - - internal const uint BIG_CLIPBOARD_DATA_TIMEOUT = 30000; - private const uint MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1024 * 1024; // 1MB - private const uint MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 100 * 1024 * 1024; // 100MB - private const int TEXT_HEADER_SIZE = 12; - private const int DATA_SIZE = 48; - private const string TEXT_TYPE_SEP = "{4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}"; - private static long lastClipboardEventTime; - private static string lastMachineWithClipboardData; - private static string lastDragDropFile; -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter - internal static long clipboardCopiedTime; -#pragma warning restore SA1307 - - internal static ID LastIDWithClipboardData { get; set; } - - internal static string LastDragDropFile - { - get => Common.lastDragDropFile; - set => Common.lastDragDropFile = value; - } - - internal static string LastMachineWithClipboardData - { - get => Common.lastMachineWithClipboardData; - set => Common.lastMachineWithClipboardData = value; - } - - internal static long LastClipboardEventTime - { - get => Common.lastClipboardEventTime; - set => Common.lastClipboardEventTime = value; - } - - internal static IntPtr NextClipboardViewer { get; set; } - - internal static bool IsClipboardDataImage { get; private set; } - - internal static byte[] LastClipboardData { get; private set; } - - private static object lastClipboardObject = string.Empty; - - internal static bool HasSwitchedMachineSinceLastCopy { get; set; } - - internal static bool CheckClipboardEx(ByteArrayOrString data, bool isFilePath) - { - Logger.LogDebug($"{nameof(CheckClipboardEx)}: ShareClipboard = {Setting.Values.ShareClipboard}, TransferFile = {Setting.Values.TransferFile}, data = {data}."); - Logger.LogDebug($"{nameof(CheckClipboardEx)}: {nameof(Setting.Values.OneWayClipboardMode)} = {Setting.Values.OneWayClipboardMode}."); - - if (!Setting.Values.ShareClipboard) - { - return false; - } - - if (Common.RunWithNoAdminRight && Setting.Values.OneWayClipboardMode) - { - return false; - } - - if (GetTick() - LastClipboardEventTime < 1000) - { - Logger.LogDebug("GetTick() - lastClipboardEventTime < 1000"); - LastClipboardEventTime = GetTick(); - return false; - } - - LastClipboardEventTime = GetTick(); - - try - { - IsClipboardDataImage = false; - LastClipboardData = null; - LastDragDropFile = null; - GC.Collect(); - - string stringData = null; - byte[] byteData = null; - - if (data.IsByteArray) - { - byteData = data.GetByteArray(); - } - else - { - stringData = data.GetString(); - } - - if (stringData != null) - { - if (!HasSwitchedMachineSinceLastCopy) - { - if (lastClipboardObject is string lastStringData && lastStringData.Equals(stringData, StringComparison.OrdinalIgnoreCase)) - { - Logger.LogDebug("CheckClipboardEx: Same string data."); - return false; - } - } - - HasSwitchedMachineSinceLastCopy = false; - - if (isFilePath) - { - Logger.LogDebug("Clipboard contains FileDropList"); - - if (!Setting.Values.TransferFile) - { - Logger.LogDebug("TransferFile option is unchecked."); - return false; - } - - string filePath = stringData; - - _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() => - { - if (File.Exists(filePath) || Directory.Exists(filePath)) - { - if (File.Exists(filePath) && new FileInfo(filePath).Length <= MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT) - { - Logger.LogDebug("Clipboard contains: " + filePath); - LastDragDropFile = filePath; - SendClipboardBeat(); - SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_BIG_CLIPBOARD, -1, ICON_BIG_CLIPBOARD, -1 }); - } - else - { - if (Directory.Exists(filePath)) - { - Logger.LogDebug("Clipboard contains a directory: " + filePath); - LastDragDropFile = filePath; - SendClipboardBeat(); - } - else - { - LastDragDropFile = filePath + " - File too big (greater than 100MB), please drag and drop the file instead!"; - SendClipboardBeat(); - Logger.Log("Clipboard: File too big: " + filePath); - } - - SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_ERROR, -1, ICON_ERROR, -1 }); - } - } - else - { - Logger.Log("CheckClipboardEx: File not found: " + filePath); - } - }); - } - else - { - byte[] texts = Common.GetBytesU(stringData); - - using MemoryStream ms = new(); - using (DeflateStream s = new(ms, CompressionMode.Compress, true)) - { - s.Write(texts, 0, texts.Length); - } - - Logger.LogDebug("Plain/Zip = " + texts.Length.ToString(CultureInfo.CurrentCulture) + "/" + - ms.Length.ToString(CultureInfo.CurrentCulture)); - - LastClipboardData = ms.GetBuffer(); - } - } - else if (byteData != null) - { - if (!HasSwitchedMachineSinceLastCopy) - { - if (lastClipboardObject is byte[] lastByteData && Enumerable.SequenceEqual(lastByteData, byteData)) - { - Logger.LogDebug("CheckClipboardEx: Same byte[] data."); - return false; - } - } - - HasSwitchedMachineSinceLastCopy = false; - - Logger.LogDebug("Clipboard contains image"); - IsClipboardDataImage = true; - LastClipboardData = byteData; - } - else - { - Logger.LogDebug("*** Clipboard contains something else!"); - return false; - } - - lastClipboardObject = data; - - if (LastClipboardData != null && LastClipboardData.Length > 0) - { - if (LastClipboardData.Length > MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP) - { - SendClipboardBeat(); - SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_BIG_CLIPBOARD, -1, ICON_BIG_CLIPBOARD, -1 }); - } - else - { - SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_SMALL_CLIPBOARD, -1, -1, -1 }); - SendClipboardDataUsingTCP(LastClipboardData, IsClipboardDataImage); - } - - return true; - } - } - catch (Exception e) - { - Logger.Log(e); - } - - return false; - } - - private static void SendClipboardDataUsingTCP(byte[] bytes, bool image) - { - if (Sk == null) - { - return; - } - - new Task(() => - { - // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. - // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 - using var asyncFlowControl = ExecutionContext.SuppressFlow(); - - System.Threading.Thread thread = Thread.CurrentThread; - thread.Name = $"{nameof(SendClipboardDataUsingTCP)}.{thread.ManagedThreadId}"; - Thread.UpdateThreads(thread); - int l = bytes.Length; - int index = 0; - int len; - DATA package = new(); - byte[] buf = new byte[PACKAGE_SIZE_EX]; - int dataStart = PACKAGE_SIZE_EX - DATA_SIZE; - - while (true) - { - if ((index + DATA_SIZE) > l) - { - len = l - index; - Array.Clear(buf, 0, PACKAGE_SIZE_EX); - } - else - { - len = DATA_SIZE; - } - - Array.Copy(bytes, index, buf, dataStart, len); - package.Bytes = buf; - - package.Type = image ? PackageType.ClipboardImage : PackageType.ClipboardText; - package.Des = ID.ALL; - SkSend(package, (uint)MachineID, false); - - index += DATA_SIZE; - if (index >= l) - { - break; - } - } - - package.Type = PackageType.ClipboardDataEnd; - package.Des = ID.ALL; - SkSend(package, (uint)MachineID, false); - }).Start(); - } - - internal static void ReceiveClipboardDataUsingTCP(DATA data, bool image, TcpSk tcp) - { - try - { - if (Sk == null || RunOnLogonDesktop || RunOnScrSaverDesktop) - { - return; - } - - MemoryStream m = new(); - int dataStart = PACKAGE_SIZE_EX - DATA_SIZE; - m.Write(data.Bytes, dataStart, DATA_SIZE); - int unexpectedCount = 0; - - bool done = false; - do - { - data = SocketStuff.TcpReceiveData(tcp, out int err); - - switch (data.Type) - { - case PackageType.ClipboardImage: - case PackageType.ClipboardText: - m.Write(data.Bytes, dataStart, DATA_SIZE); - break; - - case PackageType.ClipboardDataEnd: - done = true; - break; - - default: - Receiver.ProcessPackage(data, tcp); - if (++unexpectedCount > 100) - { - Logger.Log("ReceiveClipboardDataUsingTCP: unexpectedCount > 100!"); - done = true; - } - - break; - } - } - while (!done); - - LastClipboardEventTime = GetTick(); - - if (image) - { - Image im = Image.FromStream(m); - Clipboard.SetImage(im); - LastClipboardEventTime = GetTick(); - } - else - { - Common.SetClipboardData(m.GetBuffer()); - LastClipboardEventTime = GetTick(); - } - - m.Dispose(); - - SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_SMALL_CLIPBOARD, -1, ICON_SMALL_CLIPBOARD, -1 }); - } - catch (Exception e) - { - Logger.Log("ReceiveClipboardDataUsingTCP: " + e.Message); - } - } - - private static readonly Lock ClipboardThreadOldLock = new(); - private static System.Threading.Thread clipboardThreadOld; - - internal static void GetRemoteClipboard(string postAction) - { - if (!RunOnLogonDesktop && !RunOnScrSaverDesktop) - { - if (Common.LastMachineWithClipboardData == null || - Common.LastMachineWithClipboardData.Length < 1) - { - return; - } - - new Task(() => - { - // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. - // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 - using var asyncFlowControl = ExecutionContext.SuppressFlow(); - - System.Threading.Thread thread = Thread.CurrentThread; - thread.Name = $"{nameof(ConnectAndGetData)}.{thread.ManagedThreadId}"; - Thread.UpdateThreads(thread); - ConnectAndGetData(postAction); - }).Start(); - } - } - - private static Stream m; - - private static void ConnectAndGetData(object postAction) - { - if (Sk == null) - { - Logger.Log("ConnectAndGetData: Sk == null!"); - return; - } - - string remoteMachine; - TcpClient clipboardTcpClient = null; - string postAct = (string)postAction; - - Logger.LogDebug("ConnectAndGetData.postAction: " + postAct); - - ClipboardPostAction clipboardPostAct = postAct.Contains("mspaint,") ? ClipboardPostAction.Mspaint - : postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase) ? ClipboardPostAction.Desktop - : ClipboardPostAction.Other; - - try - { - remoteMachine = postAct.Contains("mspaint,") ? postAct.Split(Comma)[1] : Common.LastMachineWithClipboardData; - - remoteMachine = remoteMachine.Trim(); - - if (!IsConnectedByAClientSocketTo(remoteMachine)) - { - Logger.Log($"No potential inbound connection from {MachineName} to {remoteMachine}, ask for a push back instead."); - ID machineId = MachineStuff.MachinePool.ResolveID(remoteMachine); - - if (machineId != ID.NONE) - { - SkSend( - new DATA() - { - Type = PackageType.ClipboardAsk, - Des = machineId, - MachineName = MachineName, - PostAction = clipboardPostAct, - }, - null, - false); - } - else - { - Logger.Log($"Unable to resolve {remoteMachine} to its long IP."); - } - - return; - } - - ShowToolTip("Connecting to " + remoteMachine, 2000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); - - clipboardTcpClient = ConnectToRemoteClipboardSocket(remoteMachine); - } - catch (ThreadAbortException) - { - Logger.Log("The current thread is being aborted (1)."); - if (clipboardTcpClient != null && clipboardTcpClient.Connected) - { - clipboardTcpClient.Client.Close(); - } - - return; - } - catch (Exception e) - { - Logger.Log(e); - Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] - { - Common.ICON_BIG_CLIPBOARD, - -1, Common.ICON_BIG_CLIPBOARD, -1, - }); - ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); - return; - } - - bool clientPushData = false; - - if (!ShakeHand(ref remoteMachine, clipboardTcpClient.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref clipboardPostAct)) - { - return; - } - - ReceiveAndProcessClipboardData(remoteMachine, clipboardTcpClient.Client, enStream, deStream, postAct); - } - - internal static void ReceiveAndProcessClipboardData(string remoteMachine, Socket s, Stream enStream, Stream deStream, string postAct) - { - lock (ClipboardThreadOldLock) - { - // Do not enable two connections at the same time. - if (clipboardThreadOld != null && clipboardThreadOld.ThreadState != System.Threading.ThreadState.AbortRequested - && clipboardThreadOld.ThreadState != System.Threading.ThreadState.Aborted && clipboardThreadOld.IsAlive - && clipboardThreadOld.ManagedThreadId != Thread.CurrentThread.ManagedThreadId) - { - if (clipboardThreadOld.Join(3000)) - { - if (m != null) - { - m.Flush(); - m.Close(); - m = null; - } - } - } - - clipboardThreadOld = Thread.CurrentThread; - } - - try - { - byte[] header = new byte[1024]; - byte[] buf = new byte[NETWORK_STREAM_BUF_SIZE]; - string fileName = null; - string tempFile = "data", savingFolder = string.Empty; - Common.ToggleIconsIndex = 0; - int rv; - long receivedCount = 0; - - if ((rv = deStream.ReadEx(header, 0, header.Length)) < header.Length) - { - Logger.Log("Reading header failed: " + rv.ToString(CultureInfo.CurrentCulture)); - Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] - { - Common.ICON_BIG_CLIPBOARD, - -1, -1, -1, - }); - return; - } - - fileName = Common.GetStringU(header).Replace("\0", string.Empty); - Logger.LogDebug("Header: " + fileName); - string[] headers = fileName.Split(Star); - - if (headers.Length < 2 || !long.TryParse(headers[0], out long dataSize)) - { - Logger.Log(string.Format( - CultureInfo.CurrentCulture, - "Reading header failed: {0}:{1}", - headers.Length, - fileName)); - Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] - { - Common.ICON_BIG_CLIPBOARD, - -1, -1, -1, - }); - return; - } - - fileName = headers[1]; - - Logger.LogDebug(string.Format( - CultureInfo.CurrentCulture, - "Receiving {0}:{1} from {2}...", - Path.GetFileName(fileName), - dataSize, - remoteMachine)); - ShowToolTip( - string.Format( - CultureInfo.CurrentCulture, - "Receiving {0} from {1}...", - Path.GetFileName(fileName), - remoteMachine), - 5000, - ToolTipIcon.Info, - Setting.Values.ShowClipNetStatus); - if (fileName.StartsWith("image", StringComparison.CurrentCultureIgnoreCase) || - fileName.StartsWith("text", StringComparison.CurrentCultureIgnoreCase)) - { - m = new MemoryStream(); - } - else - { - if (postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase)) - { - _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() => - { - savingFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\MouseWithoutBorders\\"; - - if (!Directory.Exists(savingFolder)) - { - _ = Directory.CreateDirectory(savingFolder); - } - }); - - tempFile = savingFolder + Path.GetFileName(fileName); - m = new FileStream(tempFile, FileMode.Create); - } - else if (postAct.Contains("mspaint")) - { - tempFile = GetMyStorageDir() + @"ScreenCapture-" + - remoteMachine + ".png"; - m = new FileStream(tempFile, FileMode.Create); - } - else - { - tempFile = GetMyStorageDir(); - tempFile += Path.GetFileName(fileName); - m = new FileStream(tempFile, FileMode.Create); - } - - Logger.Log("==> " + tempFile); - } - - ShowToolTip( - string.Format( - CultureInfo.CurrentCulture, - "Receiving {0} from {1}...", - Path.GetFileName(fileName), - remoteMachine), - 5000, - ToolTipIcon.Info, - Setting.Values.ShowClipNetStatus); - - do - { - rv = deStream.ReadEx(buf, 0, buf.Length); - - if (rv > 0) - { - receivedCount += rv; - - if (receivedCount > dataSize) - { - rv -= (int)(receivedCount - dataSize); - } - - m.Write(buf, 0, rv); - } - - if (Common.ToggleIcons == null) - { - Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] - { - Common.ICON_SMALL_CLIPBOARD, - -1, Common.ICON_SMALL_CLIPBOARD, -1, - }); - } - - string text = string.Format(CultureInfo.CurrentCulture, "{0}KB received: {1}", m.Length / 1024, Path.GetFileName(fileName)); - - DoSomethingInUIThread(() => - { - MainForm.SetTrayIconText(text); - }); - } - while (rv > 0); - - if (m != null && fileName != null) - { - m.Flush(); - Logger.LogDebug(m.Length.ToString(CultureInfo.CurrentCulture) + " bytes received."); - Common.LastClipboardEventTime = Common.GetTick(); - string toolTipText = null; - string sizeText = m.Length >= 1024 - ? (m.Length / 1024).ToString(CultureInfo.CurrentCulture) + "KB" - : m.Length.ToString(CultureInfo.CurrentCulture) + "Bytes"; - - PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersClipboardFileTransferEvent()); - - if (fileName.StartsWith("image", StringComparison.CurrentCultureIgnoreCase)) - { - Clipboard.SetImage(Image.FromStream(m)); - toolTipText = string.Format( - CultureInfo.CurrentCulture, - "{0} {1} from {2} is in Clipboard.", - sizeText, - "image", - remoteMachine); - } - else if (fileName.StartsWith("text", StringComparison.CurrentCultureIgnoreCase)) - { - byte[] data = (m as MemoryStream).GetBuffer(); - toolTipText = string.Format( - CultureInfo.CurrentCulture, - "{0} {1} from {2} is in Clipboard.", - sizeText, - "text", - remoteMachine); - Common.SetClipboardData(data); - } - else if (tempFile != null) - { - if (postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase)) - { - toolTipText = string.Format( - CultureInfo.CurrentCulture, - "{0} {1} received from {2}!", - sizeText, - Path.GetFileName(fileName), - remoteMachine); - - _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() => - { - ProcessStartInfo startInfo = new(); - startInfo.UseShellExecute = true; - startInfo.WorkingDirectory = savingFolder; - startInfo.FileName = savingFolder; - startInfo.Verb = "open"; - _ = Process.Start(startInfo); - }); - } - else if (postAct.Contains("mspaint")) - { - m.Close(); - m = null; - OpenImage(tempFile); - toolTipText = string.Format( - CultureInfo.CurrentCulture, - "{0} {1} from {2} is in Mspaint.", - sizeText, - Path.GetFileName(tempFile), - remoteMachine); - } - else - { - StringCollection filePaths = new() - { - tempFile, - }; - Clipboard.SetFileDropList(filePaths); - toolTipText = string.Format( - CultureInfo.CurrentCulture, - "{0} {1} from {2} is in Clipboard.", - sizeText, - Path.GetFileName(fileName), - remoteMachine); - } - } - - if (!string.IsNullOrWhiteSpace(toolTipText)) - { - Common.ShowToolTip(toolTipText, 5000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); - } - - DoSomethingInUIThread(() => - { - MainForm.UpdateNotifyIcon(); - }); - - m?.Close(); - m = null; - } - } - catch (ThreadAbortException) - { - Logger.Log("The current thread is being aborted (3)."); - s.Close(); - - if (m != null) - { - m.Close(); - m = null; - } - - return; - } - catch (Exception e) - { - if (e is IOException) - { - string log = $"{nameof(ReceiveAndProcessClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Logger.Log(log); - } - else - { - Logger.Log(e); - } - - Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] - { - Common.ICON_BIG_CLIPBOARD, - -1, Common.ICON_BIG_CLIPBOARD, -1, - }); - ShowToolTip(e.Message, 1000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); - - if (m != null) - { - m.Close(); - m = null; - } - - return; - } - - s.Close(); - } - - internal static bool ShakeHand(ref string remoteName, Socket s, out Stream enStream, out Stream deStream, ref bool clientPushData, ref ClipboardPostAction postAction) - { - const int CLIPBOARD_HANDSHAKE_TIMEOUT = 30; - s.ReceiveTimeout = CLIPBOARD_HANDSHAKE_TIMEOUT * 1000; - s.NoDelay = true; - s.SendBufferSize = s.ReceiveBufferSize = 1024000; - - bool handShaken = false; - enStream = deStream = null; - - try - { - DATA package = new() - { - Type = clientPushData ? PackageType.ClipboardPush : PackageType.Clipboard, - PostAction = postAction, - Src = MachineID, - MachineName = MachineName, - }; - - byte[] buf = new byte[PACKAGE_SIZE_EX]; - - NetworkStream ns = new(s); - enStream = Common.GetEncryptedStream(ns); - Common.SendOrReceiveARandomDataBlockPerInitialIV(enStream); - Logger.LogDebug($"{nameof(ShakeHand)}: Writing header package."); - enStream.Write(package.Bytes, 0, PACKAGE_SIZE_EX); - - Logger.LogDebug($"{nameof(ShakeHand)}: Sent: clientPush={clientPushData}, postAction={postAction}."); - - deStream = Common.GetDecryptedStream(ns); - Common.SendOrReceiveARandomDataBlockPerInitialIV(deStream, false); - - Logger.LogDebug($"{nameof(ShakeHand)}: Reading header package."); - - int bytesReceived = deStream.ReadEx(buf, 0, Common.PACKAGE_SIZE_EX); - package.Bytes = buf; - - string name = "Unknown"; - - if (bytesReceived == Common.PACKAGE_SIZE_EX) - { - if (package.Type is PackageType.Clipboard or PackageType.ClipboardPush) - { - name = remoteName = package.MachineName; - - Logger.LogDebug($"{nameof(ShakeHand)}: Connection from {name}:{package.Src}"); - - if (MachineStuff.MachinePool.ResolveID(name) == package.Src && Common.IsConnectedTo(package.Src)) - { - clientPushData = package.Type == PackageType.ClipboardPush; - postAction = package.PostAction; - handShaken = true; - Logger.LogDebug($"{nameof(ShakeHand)}: Received: clientPush={clientPushData}, postAction={postAction}."); - } - else - { - Logger.LogDebug($"{nameof(ShakeHand)}: No active connection to the machine: {name}."); - } - } - else - { - Logger.LogDebug($"{nameof(ShakeHand)}: Unexpected package type: {package.Type}."); - } - } - else - { - Logger.LogDebug($"{nameof(ShakeHand)}: BytesTransferred != PACKAGE_SIZE_EX: {bytesReceived}"); - } - - if (!handShaken) - { - string msg = $"Clipboard connection rejected: {name}:{remoteName}/{package.Src}\r\n\r\nMake sure you run the same version in all machines."; - Logger.Log(msg); - Common.ShowToolTip(msg, 3000, ToolTipIcon.Warning); - Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, -1, -1 }); - } - } - catch (ThreadAbortException) - { - Logger.Log($"{nameof(ShakeHand)}: The current thread is being aborted."); - s.Close(); - } - catch (Exception e) - { - if (e is IOException) - { - string log = $"{nameof(ShakeHand)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Logger.Log(log); - } - else - { - Logger.Log(e); - } - - Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] - { - Common.ICON_BIG_CLIPBOARD, - -1, Common.ICON_BIG_CLIPBOARD, -1, - }); - MainForm.UpdateNotifyIcon(); - ShowToolTip(e.Message + "\r\n\r\nMake sure you run the same version in all machines.", 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); - - if (m != null) - { - m.Close(); - m = null; - } - } - - return handShaken; - } - - internal static TcpClient ConnectToRemoteClipboardSocket(string remoteMachine) - { - TcpClient clipboardTcpClient; - clipboardTcpClient = new TcpClient(AddressFamily.InterNetworkV6); - clipboardTcpClient.Client.DualMode = true; - - SocketStuff sk = Common.Sk; - - if (sk != null) - { - Common.DoSomethingInUIThread(() => Common.MainForm.ChangeIcon(Common.ICON_SMALL_CLIPBOARD)); - - System.Net.IPAddress ip = GetConnectedClientSocketIPAddressFor(remoteMachine); - Logger.LogDebug($"{nameof(ConnectToRemoteClipboardSocket)}Connecting to {remoteMachine}:{ip}:{sk.TcpPort}..."); - - if (ip != null) - { - clipboardTcpClient.Connect(ip, sk.TcpPort); - } - else - { - clipboardTcpClient.Connect(remoteMachine, sk.TcpPort); - } - - Logger.LogDebug($"Connected from {clipboardTcpClient.Client.LocalEndPoint}. Getting data..."); - return clipboardTcpClient; - } - else - { - throw new ExpectedSocketException($"{nameof(ConnectToRemoteClipboardSocket)}: No longer connected."); - } - } - - internal static void SetClipboardData(byte[] data) - { - if (data == null || data.Length <= 0) - { - Logger.Log("data is null or empty!"); - return; - } - - if (data.Length > 1024000) - { - ShowToolTip( - string.Format( - CultureInfo.CurrentCulture, - "Decompressing {0} clipboard data ...", - (data.Length / 1024).ToString(CultureInfo.CurrentCulture) + "KB"), - 5000, - ToolTipIcon.Info, - Setting.Values.ShowClipNetStatus); - } - - string st = string.Empty; - - using (MemoryStream ms = new(data)) - { - using DeflateStream s = new(ms, CompressionMode.Decompress, true); - const int BufferSize = 1024000; // Buffer size should be big enough, this is critical to performance! - - int rv = 0; - - do - { - byte[] buffer = new byte[BufferSize]; - rv = s.ReadEx(buffer, 0, BufferSize); - - if (rv > 0) - { - st += Common.GetStringU(buffer); - } - else - { - break; - } - } - while (true); - } - - int textTypeCount = 0; - string[] texts = st.Split(new string[] { TEXT_TYPE_SEP }, StringSplitOptions.RemoveEmptyEntries); - string tmp; - DataObject data1 = new(); - - foreach (string txt in texts) - { - if (string.IsNullOrEmpty(txt.Trim(NullSeparator))) - { - continue; - } - - tmp = txt[3..]; - - if (txt.StartsWith("RTF", StringComparison.CurrentCultureIgnoreCase)) - { - Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of RTF <-"); - data1.SetData(DataFormats.Rtf, tmp); - } - else if (txt.StartsWith("HTM", StringComparison.CurrentCultureIgnoreCase)) - { - Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of HTM <-"); - data1.SetData(DataFormats.Html, tmp); - } - else if (txt.StartsWith("TXT", StringComparison.CurrentCultureIgnoreCase)) - { - Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of TXT <-"); - data1.SetData(DataFormats.UnicodeText, tmp); - } - else - { - if (textTypeCount == 0) - { - Logger.LogDebug(((double)txt.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of UNI <-"); - data1.SetData(DataFormats.UnicodeText, txt); - } - - Logger.Log("Invalid clipboard format received!"); - } - - textTypeCount++; - } - - if (textTypeCount > 0) - { - Clipboard.SetDataObject(data1); - } - } - } - - internal static class Clipboard - { - public static void SetFileDropList(StringCollection filePaths) - { - Common.DoSomethingInUIThread(() => - { - try - { - _ = Common.Retry( - nameof(SystemClipboard.SetFileDropList), - () => - { - SystemClipboard.SetFileDropList(filePaths); - return true; - }, - (log) => Logger.TelemetryLogTrace( - log, - SeverityLevel.Information), - () => Common.LastClipboardEventTime = Common.GetTick()); - } - catch (ExternalException e) - { - Logger.Log(e); - } - catch (ThreadStateException e) - { - Logger.Log(e); - } - catch (ArgumentNullException e) - { - Logger.Log(e); - } - catch (ArgumentException e) - { - Logger.Log(e); - } - }); - } - - public static void SetImage(Image image) - { - Common.DoSomethingInUIThread(() => - { - try - { - _ = Common.Retry( - nameof(SystemClipboard.SetImage), - () => - { - SystemClipboard.SetImage(image); - return true; - }, - (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), - () => Common.LastClipboardEventTime = Common.GetTick()); - } - catch (ExternalException e) - { - Logger.Log(e); - } - catch (ThreadStateException e) - { - Logger.Log(e); - } - catch (ArgumentNullException e) - { - Logger.Log(e); - } - }); - } - - public static void SetText(string text) - { - Common.DoSomethingInUIThread(() => - { - try - { - _ = Common.Retry( - nameof(SystemClipboard.SetText), - () => - { - SystemClipboard.SetText(text); - return true; - }, - (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), - () => Common.LastClipboardEventTime = Common.GetTick()); - } - catch (ExternalException e) - { - Logger.Log(e); - } - catch (ThreadStateException e) - { - Logger.Log(e); - } - catch (ArgumentNullException e) - { - Logger.Log(e); - } - }); - } - - public static void SetDataObject(DataObject dataObject) - { - Common.DoSomethingInUIThread(() => - { - try - { - SystemClipboard.SetDataObject(dataObject, true, 10, 200); - } - catch (ExternalException e) - { - string dataFormats = string.Join(",", dataObject.GetFormats()); - Logger.Log($"{e.Message}: {dataFormats}"); - } - catch (ThreadStateException e) - { - Logger.Log(e); - } - catch (ArgumentNullException e) - { - Logger.Log(e); - } - }); - } - } -} diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.InitAndCleanup.cs b/src/modules/MouseWithoutBorders/App/Class/Common.InitAndCleanup.cs deleted file mode 100644 index 44861926e9..0000000000 --- a/src/modules/MouseWithoutBorders/App/Class/Common.InitAndCleanup.cs +++ /dev/null @@ -1,284 +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.Globalization; -using System.Linq; -using System.Net.NetworkInformation; -using System.Security.Cryptography; -using System.Threading; - -// -// Initialization and clean up. -// -// -// 2008 created by Truong Do (ductdo). -// 2009-... modified by Truong Do (TruongDo). -// 2023- Included in PowerToys. -// -using Microsoft.Win32; -using MouseWithoutBorders.Class; -using MouseWithoutBorders.Core; -using MouseWithoutBorders.Form; -using Windows.UI.Input.Preview.Injection; - -using Thread = MouseWithoutBorders.Core.Thread; - -namespace MouseWithoutBorders -{ - internal partial class Common - { - private static bool initDone; - internal static int REOPEN_WHEN_WSAECONNRESET = -10054; - internal static int REOPEN_WHEN_HOTKEY = -10055; - internal static int PleaseReopenSocket; - internal static bool ReopenSocketDueToReadError; - - internal static DateTime LastResumeSuspendTime { get; set; } = DateTime.UtcNow; - - internal static bool InitDone - { - get => Common.initDone; - set => Common.initDone = value; - } - - internal static void UpdateMachineTimeAndID() - { - Common.MachineName = Common.MachineName.Trim(); - _ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true); - } - - private static void InitializeMachinePoolFromSettings() - { - try - { - MachineInf[] info = MachinePoolHelpers.LoadMachineInfoFromMachinePoolStringSetting(Setting.Values.MachinePoolString); - for (int i = 0; i < info.Length; i++) - { - info[i].Name = info[i].Name.Trim(); - } - - MachineStuff.MachinePool.Initialize(info); - MachineStuff.MachinePool.ResetIPAddressesForDeadMachines(true); - } - catch (Exception ex) - { - Logger.Log(ex); - MachineStuff.MachinePool.Clear(); - } - } - - internal static void SetupMachineNameAndID() - { - try - { - GetMachineName(); - DesMachineID = MachineStuff.NewDesMachineID = MachineID; - - // MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test - InitializeMachinePoolFromSettings(); - - Common.MachineName = Common.MachineName.Trim(); - _ = MachineStuff.MachinePool.LearnMachine(Common.MachineName); - _ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true); - - MachineStuff.UpdateMachinePoolStringSetting(); - } - catch (Exception e) - { - Logger.Log(e); - } - } - - internal static void Init() - { - _ = Helper.GetUserName(); - Common.GeneratedKey = true; - - try - { - Common.MyKey = Setting.Values.MyKey; - int tmp = Setting.Values.MyKeyDaysToExpire; - } - catch (FormatException e) - { - Common.KeyCorrupted = true; - Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); - Logger.Log(e.Message); - } - catch (CryptographicException e) - { - Common.KeyCorrupted = true; - Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); - Logger.Log(e.Message); - } - - try - { - InputSimulation.Injector = InputInjector.TryCreate(); - if (InputSimulation.Injector != null) - { - InputSimulation.MoveMouseRelative(0, 0); - NativeMethods.InjectMouseInputAvailable = true; - } - } - catch (EntryPointNotFoundException) - { - NativeMethods.InjectMouseInputAvailable = false; - Logger.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false"); - } - - bool dummy = Setting.Values.DrawMouseEx; - Is64bitOS = IntPtr.Size == 8; - tcpPort = Setting.Values.TcpPort; - GetScreenConfig(); - PackageSent = new PackageMonitor(0); - PackageReceived = new PackageMonitor(0); - SetupMachineNameAndID(); - InitEncryption(); - CreateHelperThreads(); - - SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged); - NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged); - SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged); - PleaseReopenSocket = 9; - /* TODO: Telemetry for the matrix? */ - } - - private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) - { - Helper.WndProcCounter++; - - if (e.Mode is PowerModes.Resume or PowerModes.Suspend) - { - Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information); - LastResumeSuspendTime = DateTime.UtcNow; - MachineStuff.SwitchToMultipleMode(false, true); - } - } - - private static void CreateHelperThreads() - { - // NOTE(@yuyoyuppe): service crashes while trying to obtain this info, disabling. - /* - Thread watchDogThread = new(new ThreadStart(WatchDogThread), nameof(WatchDogThread)); - watchDogThread.Priority = ThreadPriority.Highest; - watchDogThread.Start(); - */ - - helper = new Thread(new ThreadStart(Helper.HelperThread), "Helper Thread"); - helper.SetApartmentState(ApartmentState.STA); - helper.Start(); - } - - private static void AskHelperThreadsToExit(int waitTime) - { - Helper.signalHelperToExit = true; - Helper.signalWatchDogToExit = true; - _ = EvSwitch.Set(); - - int c = 0; - if (helper != null && c < waitTime) - { - while (Helper.signalHelperToExit) - { - Thread.Sleep(1); - } - - helper = null; - } - } - - internal static void Cleanup() - { - try - { - SendByeBye(); - - // UnhookClipboard(); - AskHelperThreadsToExit(500); - MainForm.NotifyIcon.Visible = false; - MainForm.NotifyIcon.Dispose(); - CloseAllFormsAndHooks(); - - DoSomethingInUIThread(() => - { - Sk?.Close(true); - }); - } - catch (Exception e) - { - Logger.Log(e); - } - } - - private static long lastReleaseAllKeysCall; - - internal static void ReleaseAllKeys() - { - if (Math.Abs(GetTick() - lastReleaseAllKeysCall) < 2000) - { - return; - } - - lastReleaseAllKeysCall = GetTick(); - - KEYBDDATA kd; - kd.dwFlags = (int)LLKHF.UP; - - VK[] keys = new VK[] - { - VK.LSHIFT, VK.LCONTROL, VK.LMENU, VK.LWIN, VK.RSHIFT, - VK.RCONTROL, VK.RMENU, VK.RWIN, VK.SHIFT, VK.MENU, VK.CONTROL, - }; - - Logger.LogDebug("***** ReleaseAllKeys has been called! *****:"); - - foreach (VK vk in keys) - { - if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0) - { - Logger.LogDebug(vk.ToString() + " is down, release it..."); - Hook?.ResetLastSwitchKeys(); // Sticky key can turn ALL PC mode on (CtrlCtrlCtrl) - kd.wVk = (int)vk; - InputSimulation.SendKey(kd); - Hook?.ResetLastSwitchKeys(); - } - } - } - - private static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) - { - Logger.LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture)); - Helper.WndProcCounter++; - ScheduleReopenSocketsDueToNetworkChanges(!e.IsAvailable); - } - - private static void ScheduleReopenSocketsDueToNetworkChanges(bool closeSockets = true) - { - if (closeSockets) - { - // Slept/hibernated machine may still have the sockets' status as Connected:( (unchanged) so it would not re-connect after a timeout when waking up. - // Closing the sockets when it is going to sleep/hibernate will trigger the reconnection faster when it wakes up. - DoSomethingInUIThread( - () => - { - SocketStuff s = Sk; - Sk = null; - s?.Close(false); - }, - true); - } - - if (!Common.IsMyDesktopActive()) - { - PleaseReopenSocket = 0; - } - else if (PleaseReopenSocket != 10) - { - PleaseReopenSocket = 10; - } - } - } -} diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs b/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs index ed56101930..ee2d99398c 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs @@ -36,7 +36,7 @@ namespace MouseWithoutBorders internal static string ActiveDesktop => Common.activeDesktop; - private static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) + internal static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { GetScreenConfig(); } @@ -340,7 +340,7 @@ namespace MouseWithoutBorders Setting.Values.LastX = JUST_GOT_BACK_FROM_SCREEN_SAVER; if (cleanupIfExit) { - Common.Cleanup(); + InitAndCleanup.Cleanup(); } Process.GetCurrentProcess().KillProcess(); diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.cs b/src/modules/MouseWithoutBorders/App/Class/Common.cs index 0494a952fd..ba5a1655e0 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.cs @@ -33,6 +33,7 @@ using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; using MouseWithoutBorders.Exceptions; +using Clipboard = MouseWithoutBorders.Core.Clipboard; using Thread = MouseWithoutBorders.Core.Thread; // Log is enough @@ -90,8 +91,8 @@ namespace MouseWithoutBorders private static FrmMatrix matrixForm; private static FrmInputCallback inputCallbackForm; private static FrmAbout aboutForm; - private static Thread helper; #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 @@ -121,7 +122,9 @@ namespace MouseWithoutBorders internal static int switchCount; #pragma warning restore SA1307 private static long lastReconnectByHotKeyTime; - private static int tcpPort; +#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; @@ -210,7 +213,7 @@ namespace MouseWithoutBorders internal static bool Is64bitOS { - get; private set; + get; set; // set { Common.is64bitOS = value; } } @@ -611,7 +614,7 @@ namespace MouseWithoutBorders } * */ - private static void SendByeBye() + internal static void SendByeBye() { Logger.LogDebug($"{nameof(SendByeBye)}"); SendPackage(ID.ALL, PackageType.ByeBye); @@ -725,7 +728,7 @@ namespace MouseWithoutBorders internal static void SendImage(string machine, string file) { - LastDragDropFile = file; + Clipboard.LastDragDropFile = file; // Send ClipboardCapture if (machine.Equals("All", StringComparison.OrdinalIgnoreCase)) @@ -744,7 +747,7 @@ namespace MouseWithoutBorders internal static void SendImage(ID src, string file) { - LastDragDropFile = file; + Clipboard.LastDragDropFile = file; // Send ClipboardCapture SendPackage(src, PackageType.ClipboardCapture); @@ -1291,7 +1294,7 @@ namespace MouseWithoutBorders }); } - private static string GetMyStorageDir() + internal static string GetMyStorageDir() { string st = string.Empty; diff --git a/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs b/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs index 9a52f69529..62360b4795 100644 --- a/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs +++ b/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs @@ -28,6 +28,7 @@ using MouseWithoutBorders.Core; using SystemClipboard = System.Windows.Forms.Clipboard; #if !MM_HELPER +using Clipboard = MouseWithoutBorders.Core.Clipboard; using Thread = MouseWithoutBorders.Core.Thread; #endif @@ -159,7 +160,7 @@ namespace MouseWithoutBorders public void SendClipboardData(ByteArrayOrString data, bool isFilePath) { - _ = Common.CheckClipboardEx(data, isFilePath); + _ = Clipboard.CheckClipboardEx(data, isFilePath); } } #endif diff --git a/src/modules/MouseWithoutBorders/App/Class/InputHook.cs b/src/modules/MouseWithoutBorders/App/Class/InputHook.cs index 33cbe77e89..d68b1a1584 100644 --- a/src/modules/MouseWithoutBorders/App/Class/InputHook.cs +++ b/src/modules/MouseWithoutBorders/App/Class/InputHook.cs @@ -579,7 +579,7 @@ namespace MouseWithoutBorders.Class { Common.ShowToolTip("Reconnecting...", 2000); Common.LastReconnectByHotKeyTime = Common.GetTick(); - Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY; + InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY; return false; } @@ -632,7 +632,7 @@ namespace MouseWithoutBorders.Class { // Common.DoSomethingInUIThread(delegate() { - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); } // ); diff --git a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs index a991c7f64f..0bbd8014ae 100644 --- a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs +++ b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs @@ -407,7 +407,7 @@ namespace MouseWithoutBorders.Class { ResetModifiersState(Setting.Values.HotKeyLockMachine); eatKey = true; - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); _ = NativeMethods.LockWorkStation(); } } @@ -439,7 +439,7 @@ namespace MouseWithoutBorders.Class { ctrlDown = altDown = false; eatKey = true; - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); } break; @@ -449,7 +449,7 @@ namespace MouseWithoutBorders.Class { winDown = false; eatKey = true; - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); uint rv = NativeMethods.LockWorkStation(); Logger.LogDebug("LockWorkStation returned " + rv.ToString(CultureInfo.CurrentCulture)); } diff --git a/src/modules/MouseWithoutBorders/App/Class/Program.cs b/src/modules/MouseWithoutBorders/App/Class/Program.cs index 2fd8357e24..c139da46e9 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Program.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Program.cs @@ -235,7 +235,7 @@ namespace MouseWithoutBorders.Class _ = Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); Application.SetCompatibleTextRenderingDefault(false); - Common.Init(); + InitAndCleanup.Init(); Core.Helper.WndProcCounter++; var formScreen = new FrmScreen(); @@ -314,7 +314,7 @@ namespace MouseWithoutBorders.Class MachineStuff.UpdateMachinePoolStringSetting(); SocketStuff.InvalidKeyFound = false; - Common.ReopenSocketDueToReadError = true; + InitAndCleanup.ReopenSocketDueToReadError = true; Common.ReopenSockets(true); MachineStuff.SendMachineMatrix(); @@ -340,7 +340,7 @@ namespace MouseWithoutBorders.Class public void Reconnect() { SocketStuff.InvalidKeyFound = false; - Common.ReopenSocketDueToReadError = true; + InitAndCleanup.ReopenSocketDueToReadError = true; Common.ReopenSockets(true); for (int i = 0; i < 10; i++) @@ -397,7 +397,7 @@ namespace MouseWithoutBorders.Class using var asyncFlowControl = ExecutionContext.SuppressFlow(); Common.InputCallbackThreadID = Thread.CurrentThread.ManagedThreadId; - while (!Common.InitDone) + while (!InitAndCleanup.InitDone) { Thread.Sleep(100); } diff --git a/src/modules/MouseWithoutBorders/App/Class/Setting.cs b/src/modules/MouseWithoutBorders/App/Class/Setting.cs index 30b99a97d0..c9d81f2049 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Setting.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Setting.cs @@ -118,7 +118,7 @@ namespace MouseWithoutBorders.Class if (shouldReopenSockets) { SocketStuff.InvalidKeyFound = false; - Common.ReopenSocketDueToReadError = true; + InitAndCleanup.ReopenSocketDueToReadError = true; Common.ReopenSockets(true); } diff --git a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs index 8796f61dfb..c5241beddf 100644 --- a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs +++ b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs @@ -29,6 +29,7 @@ using MouseWithoutBorders.Core; // using MouseWithoutBorders.Exceptions; +using Clipboard = MouseWithoutBorders.Core.Clipboard; using Thread = MouseWithoutBorders.Core.Thread; [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[])", Justification = "Dotnet port with style preservation")] @@ -281,7 +282,7 @@ namespace MouseWithoutBorders.Class * */ Common.GetMachineName(); // IPs might have been changed - Common.UpdateMachineTimeAndID(); + InitAndCleanup.UpdateMachineTimeAndID(); Logger.LogDebug("Creating sockets..."); @@ -308,7 +309,7 @@ namespace MouseWithoutBorders.Class { Logger.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning); Program.StartService(); - Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; + InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET; } break; @@ -1248,7 +1249,7 @@ namespace MouseWithoutBorders.Class // WSAECONNRESET if (e is ExpectedSocketException se && se.ShouldReconnect) { - Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; + InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET; Logger.Log($"MainTCPRoutine: {nameof(FlagReopenSocketIfNeeded)}"); } } @@ -1306,7 +1307,7 @@ namespace MouseWithoutBorders.Class } catch (ObjectDisposedException e) { - Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; + InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET; UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed); currentSocket.Close(); Logger.Log($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads: {e.Message}"); @@ -1353,10 +1354,10 @@ namespace MouseWithoutBorders.Class * In this case, we should give ONE try to reconnect. */ - if (Common.ReopenSocketDueToReadError) + if (InitAndCleanup.ReopenSocketDueToReadError) { - Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; - Common.ReopenSocketDueToReadError = false; + InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET; + InitAndCleanup.ReopenSocketDueToReadError = false; } break; @@ -1641,7 +1642,7 @@ namespace MouseWithoutBorders.Class bool clientPushData = true; ClipboardPostAction postAction = ClipboardPostAction.Other; - bool handShaken = Common.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction); + bool handShaken = Clipboard.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction); if (!handShaken) { @@ -1656,7 +1657,7 @@ namespace MouseWithoutBorders.Class if (clientPushData) { - Common.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}"); + Clipboard.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}"); } else { @@ -1680,23 +1681,23 @@ namespace MouseWithoutBorders.Class const int CLOSE_TIMEOUT = 10; byte[] header = new byte[1024]; string headerString = string.Empty; - if (Common.LastDragDropFile != null) + if (Clipboard.LastDragDropFile != null) { string fileName = null; if (!Launch.ImpersonateLoggedOnUserAndDoSomething(() => { - if (!File.Exists(Common.LastDragDropFile)) + if (!File.Exists(Clipboard.LastDragDropFile)) { - headerString = Directory.Exists(Common.LastDragDropFile) - ? $"{0}*{Common.LastDragDropFile} - Folder is not supported, zip it first!" - : Common.LastDragDropFile.Contains("- File too big") - ? $"{0}*{Common.LastDragDropFile}" - : $"{0}*{Common.LastDragDropFile} not found!"; + headerString = Directory.Exists(Clipboard.LastDragDropFile) + ? $"{0}*{Clipboard.LastDragDropFile} - Folder is not supported, zip it first!" + : Clipboard.LastDragDropFile.Contains("- File too big") + ? $"{0}*{Clipboard.LastDragDropFile}" + : $"{0}*{Clipboard.LastDragDropFile} not found!"; } else { - fileName = Common.LastDragDropFile; + fileName = Clipboard.LastDragDropFile; headerString = $"{new FileInfo(fileName).Length}*{fileName}"; } })) @@ -1739,11 +1740,11 @@ namespace MouseWithoutBorders.Class Logger.Log(log); } } - else if (!Common.IsClipboardDataImage && Common.LastClipboardData != null) + else if (!Clipboard.IsClipboardDataImage && Clipboard.LastClipboardData != null) { try { - byte[] data = Common.LastClipboardData; + byte[] data = Clipboard.LastClipboardData; headerString = $"{data.Length}*{"text"}"; Common.GetBytesU(headerString).CopyTo(header, 0); @@ -1773,9 +1774,9 @@ namespace MouseWithoutBorders.Class Logger.Log(log); } } - else if (Common.LastClipboardData != null && Common.LastClipboardData.Length > 0) + else if (Clipboard.LastClipboardData != null && Clipboard.LastClipboardData.Length > 0) { - byte[] data = Common.LastClipboardData; + byte[] data = Clipboard.LastClipboardData; headerString = $"{data.Length}*{"image"}"; Common.GetBytesU(headerString).CopyTo(header, 0); @@ -1984,8 +1985,8 @@ namespace MouseWithoutBorders.Class { tcp = null; Setting.Values.MachineId = Common.Ran.Next(); - Common.UpdateMachineTimeAndID(); - Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY; + InitAndCleanup.UpdateMachineTimeAndID(); + InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY; Logger.TelemetryLogTrace("MachineID conflict.", SeverityLevel.Information); } diff --git a/src/modules/MouseWithoutBorders/App/Core/Clipboard.cs b/src/modules/MouseWithoutBorders/App/Core/Clipboard.cs new file mode 100644 index 0000000000..5840325941 --- /dev/null +++ b/src/modules/MouseWithoutBorders/App/Core/Clipboard.cs @@ -0,0 +1,1155 @@ +// 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.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +using Microsoft.PowerToys.Telemetry; +using MouseWithoutBorders.Class; +using MouseWithoutBorders.Exceptions; + +using SystemClipboard = System.Windows.Forms.Clipboard; + +// +// Clipboard related routines. +// +// +// 2008 created by Truong Do (ductdo). +// 2009-... modified by Truong Do (TruongDo). +// 2023- Included in PowerToys. +// +namespace MouseWithoutBorders.Core; + +internal static class Clipboard +{ + private static readonly char[] Comma = new char[] { ',' }; + private static readonly char[] Star = new char[] { '*' }; + private static readonly char[] NullSeparator = new char[] { '\0' }; + + internal const uint BIG_CLIPBOARD_DATA_TIMEOUT = 30000; + private const uint MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1024 * 1024; // 1MB + private const uint MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 100 * 1024 * 1024; // 100MB + private const int TEXT_HEADER_SIZE = 12; + private const int DATA_SIZE = 48; + private const string TEXT_TYPE_SEP = "{4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}"; + private static long lastClipboardEventTime; + private static string lastMachineWithClipboardData; + private static string lastDragDropFile; +#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter + internal static long clipboardCopiedTime; +#pragma warning restore SA1307 + + internal static ID LastIDWithClipboardData { get; set; } + + internal static string LastDragDropFile + { + get => Clipboard.lastDragDropFile; + set => Clipboard.lastDragDropFile = value; + } + + internal static string LastMachineWithClipboardData + { + get => Clipboard.lastMachineWithClipboardData; + set => Clipboard.lastMachineWithClipboardData = value; + } + + private static long LastClipboardEventTime + { + get => Clipboard.lastClipboardEventTime; + set => Clipboard.lastClipboardEventTime = value; + } + + private static IntPtr NextClipboardViewer { get; set; } + + internal static bool IsClipboardDataImage { get; private set; } + + internal static byte[] LastClipboardData { get; private set; } + + private static object lastClipboardObject = string.Empty; + + internal static bool HasSwitchedMachineSinceLastCopy { get; set; } + + internal static bool CheckClipboardEx(ByteArrayOrString data, bool isFilePath) + { + Logger.LogDebug($"{nameof(CheckClipboardEx)}: ShareClipboard = {Setting.Values.ShareClipboard}, TransferFile = {Setting.Values.TransferFile}, data = {data}."); + Logger.LogDebug($"{nameof(CheckClipboardEx)}: {nameof(Setting.Values.OneWayClipboardMode)} = {Setting.Values.OneWayClipboardMode}."); + + if (!Setting.Values.ShareClipboard) + { + return false; + } + + if (Common.RunWithNoAdminRight && Setting.Values.OneWayClipboardMode) + { + return false; + } + + if (Common.GetTick() - LastClipboardEventTime < 1000) + { + Logger.LogDebug("GetTick() - lastClipboardEventTime < 1000"); + LastClipboardEventTime = Common.GetTick(); + return false; + } + + LastClipboardEventTime = Common.GetTick(); + + try + { + IsClipboardDataImage = false; + LastClipboardData = null; + LastDragDropFile = null; + GC.Collect(); + + string stringData = null; + byte[] byteData = null; + + if (data.IsByteArray) + { + byteData = data.GetByteArray(); + } + else + { + stringData = data.GetString(); + } + + if (stringData != null) + { + if (!HasSwitchedMachineSinceLastCopy) + { + if (lastClipboardObject is string lastStringData && lastStringData.Equals(stringData, StringComparison.OrdinalIgnoreCase)) + { + Logger.LogDebug("CheckClipboardEx: Same string data."); + return false; + } + } + + HasSwitchedMachineSinceLastCopy = false; + + if (isFilePath) + { + Logger.LogDebug("Clipboard contains FileDropList"); + + if (!Setting.Values.TransferFile) + { + Logger.LogDebug("TransferFile option is unchecked."); + return false; + } + + string filePath = stringData; + + _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() => + { + if (File.Exists(filePath) || Directory.Exists(filePath)) + { + if (File.Exists(filePath) && new FileInfo(filePath).Length <= MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT) + { + Logger.LogDebug("Clipboard contains: " + filePath); + LastDragDropFile = filePath; + Common.SendClipboardBeat(); + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, Common.ICON_BIG_CLIPBOARD, -1 }); + } + else + { + if (Directory.Exists(filePath)) + { + Logger.LogDebug("Clipboard contains a directory: " + filePath); + LastDragDropFile = filePath; + Common.SendClipboardBeat(); + } + else + { + LastDragDropFile = filePath + " - File too big (greater than 100MB), please drag and drop the file instead!"; + Common.SendClipboardBeat(); + Logger.Log("Clipboard: File too big: " + filePath); + } + + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_ERROR, -1, Common.ICON_ERROR, -1 }); + } + } + else + { + Logger.Log("CheckClipboardEx: File not found: " + filePath); + } + }); + } + else + { + byte[] texts = Common.GetBytesU(stringData); + + using MemoryStream ms = new(); + using (DeflateStream s = new(ms, CompressionMode.Compress, true)) + { + s.Write(texts, 0, texts.Length); + } + + Logger.LogDebug("Plain/Zip = " + texts.Length.ToString(CultureInfo.CurrentCulture) + "/" + + ms.Length.ToString(CultureInfo.CurrentCulture)); + + LastClipboardData = ms.GetBuffer(); + } + } + else if (byteData != null) + { + if (!HasSwitchedMachineSinceLastCopy) + { + if (lastClipboardObject is byte[] lastByteData && Enumerable.SequenceEqual(lastByteData, byteData)) + { + Logger.LogDebug("CheckClipboardEx: Same byte[] data."); + return false; + } + } + + HasSwitchedMachineSinceLastCopy = false; + + Logger.LogDebug("Clipboard contains image"); + IsClipboardDataImage = true; + LastClipboardData = byteData; + } + else + { + Logger.LogDebug("*** Clipboard contains something else!"); + return false; + } + + lastClipboardObject = data; + + if (LastClipboardData != null && LastClipboardData.Length > 0) + { + if (LastClipboardData.Length > MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP) + { + Common.SendClipboardBeat(); + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, Common.ICON_BIG_CLIPBOARD, -1 }); + } + else + { + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_SMALL_CLIPBOARD, -1, -1, -1 }); + SendClipboardDataUsingTCP(LastClipboardData, IsClipboardDataImage); + } + + return true; + } + } + catch (Exception e) + { + Logger.Log(e); + } + + return false; + } + + private static void SendClipboardDataUsingTCP(byte[] bytes, bool image) + { + if (Common.Sk == null) + { + return; + } + + new Task(() => + { + // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. + // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 + using var asyncFlowControl = ExecutionContext.SuppressFlow(); + + System.Threading.Thread thread = Thread.CurrentThread; + thread.Name = $"{nameof(SendClipboardDataUsingTCP)}.{thread.ManagedThreadId}"; + Thread.UpdateThreads(thread); + int l = bytes.Length; + int index = 0; + int len; + DATA package = new(); + byte[] buf = new byte[Common.PACKAGE_SIZE_EX]; + int dataStart = Common.PACKAGE_SIZE_EX - DATA_SIZE; + + while (true) + { + if ((index + DATA_SIZE) > l) + { + len = l - index; + Array.Clear(buf, 0, Common.PACKAGE_SIZE_EX); + } + else + { + len = DATA_SIZE; + } + + Array.Copy(bytes, index, buf, dataStart, len); + package.Bytes = buf; + + package.Type = image ? PackageType.ClipboardImage : PackageType.ClipboardText; + package.Des = ID.ALL; + Common.SkSend(package, (uint)Common.MachineID, false); + + index += DATA_SIZE; + if (index >= l) + { + break; + } + } + + package.Type = PackageType.ClipboardDataEnd; + package.Des = ID.ALL; + Common.SkSend(package, (uint)Common.MachineID, false); + }).Start(); + } + + internal static void ReceiveClipboardDataUsingTCP(DATA data, bool image, TcpSk tcp) + { + try + { + if (Common.Sk == null || Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop) + { + return; + } + + MemoryStream m = new(); + int dataStart = Common.PACKAGE_SIZE_EX - DATA_SIZE; + m.Write(data.Bytes, dataStart, DATA_SIZE); + int unexpectedCount = 0; + + bool done = false; + do + { + data = SocketStuff.TcpReceiveData(tcp, out int err); + + switch (data.Type) + { + case PackageType.ClipboardImage: + case PackageType.ClipboardText: + m.Write(data.Bytes, dataStart, DATA_SIZE); + break; + + case PackageType.ClipboardDataEnd: + done = true; + break; + + default: + Receiver.ProcessPackage(data, tcp); + if (++unexpectedCount > 100) + { + Logger.Log("ReceiveClipboardDataUsingTCP: unexpectedCount > 100!"); + done = true; + } + + break; + } + } + while (!done); + + LastClipboardEventTime = Common.GetTick(); + + if (image) + { + Image im = Image.FromStream(m); + Clipboard.SetImage(im); + LastClipboardEventTime = Common.GetTick(); + } + else + { + Clipboard.SetClipboardData(m.GetBuffer()); + LastClipboardEventTime = Common.GetTick(); + } + + m.Dispose(); + + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_SMALL_CLIPBOARD, -1, Common.ICON_SMALL_CLIPBOARD, -1 }); + } + catch (Exception e) + { + Logger.Log("ReceiveClipboardDataUsingTCP: " + e.Message); + } + } + + private static readonly Lock ClipboardThreadOldLock = new(); + private static System.Threading.Thread clipboardThreadOld; + + internal static void GetRemoteClipboard(string postAction) + { + if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) + { + if (Clipboard.LastMachineWithClipboardData == null || + Clipboard.LastMachineWithClipboardData.Length < 1) + { + return; + } + + new Task(() => + { + // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. + // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 + using var asyncFlowControl = ExecutionContext.SuppressFlow(); + + System.Threading.Thread thread = Thread.CurrentThread; + thread.Name = $"{nameof(ConnectAndGetData)}.{thread.ManagedThreadId}"; + Thread.UpdateThreads(thread); + ConnectAndGetData(postAction); + }).Start(); + } + } + + private static Stream m; + + private static void ConnectAndGetData(object postAction) + { + if (Common.Sk == null) + { + Logger.Log("ConnectAndGetData: Sk == null!"); + return; + } + + string remoteMachine; + TcpClient clipboardTcpClient = null; + string postAct = (string)postAction; + + Logger.LogDebug("ConnectAndGetData.postAction: " + postAct); + + ClipboardPostAction clipboardPostAct = postAct.Contains("mspaint,") ? ClipboardPostAction.Mspaint + : postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase) ? ClipboardPostAction.Desktop + : ClipboardPostAction.Other; + + try + { + remoteMachine = postAct.Contains("mspaint,") ? postAct.Split(Comma)[1] : Clipboard.LastMachineWithClipboardData; + + remoteMachine = remoteMachine.Trim(); + + if (!Common.IsConnectedByAClientSocketTo(remoteMachine)) + { + Logger.Log($"No potential inbound connection from {Common.MachineName} to {remoteMachine}, ask for a push back instead."); + ID machineId = MachineStuff.MachinePool.ResolveID(remoteMachine); + + if (machineId != ID.NONE) + { + Common.SkSend( + new DATA() + { + Type = PackageType.ClipboardAsk, + Des = machineId, + MachineName = Common.MachineName, + PostAction = clipboardPostAct, + }, + null, + false); + } + else + { + Logger.Log($"Unable to resolve {remoteMachine} to its long IP."); + } + + return; + } + + Common.ShowToolTip("Connecting to " + remoteMachine, 2000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); + + clipboardTcpClient = ConnectToRemoteClipboardSocket(remoteMachine); + } + catch (ThreadAbortException) + { + Logger.Log("The current thread is being aborted (1)."); + if (clipboardTcpClient != null && clipboardTcpClient.Connected) + { + clipboardTcpClient.Client.Close(); + } + + return; + } + catch (Exception e) + { + Logger.Log(e); + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] + { + Common.ICON_BIG_CLIPBOARD, + -1, Common.ICON_BIG_CLIPBOARD, -1, + }); + Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); + return; + } + + bool clientPushData = false; + + if (!ShakeHand(ref remoteMachine, clipboardTcpClient.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref clipboardPostAct)) + { + return; + } + + ReceiveAndProcessClipboardData(remoteMachine, clipboardTcpClient.Client, enStream, deStream, postAct); + } + + internal static void ReceiveAndProcessClipboardData(string remoteMachine, Socket s, Stream enStream, Stream deStream, string postAct) + { + lock (ClipboardThreadOldLock) + { + // Do not enable two connections at the same time. + if (clipboardThreadOld != null && clipboardThreadOld.ThreadState != System.Threading.ThreadState.AbortRequested + && clipboardThreadOld.ThreadState != System.Threading.ThreadState.Aborted && clipboardThreadOld.IsAlive + && clipboardThreadOld.ManagedThreadId != Thread.CurrentThread.ManagedThreadId) + { + if (clipboardThreadOld.Join(3000)) + { + if (m != null) + { + m.Flush(); + m.Close(); + m = null; + } + } + } + + clipboardThreadOld = Thread.CurrentThread; + } + + try + { + byte[] header = new byte[1024]; + byte[] buf = new byte[Common.NETWORK_STREAM_BUF_SIZE]; + string fileName = null; + string tempFile = "data", savingFolder = string.Empty; + Common.ToggleIconsIndex = 0; + int rv; + long receivedCount = 0; + + if ((rv = deStream.ReadEx(header, 0, header.Length)) < header.Length) + { + Logger.Log("Reading header failed: " + rv.ToString(CultureInfo.CurrentCulture)); + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] + { + Common.ICON_BIG_CLIPBOARD, + -1, -1, -1, + }); + return; + } + + fileName = Common.GetStringU(header).Replace("\0", string.Empty); + Logger.LogDebug("Header: " + fileName); + string[] headers = fileName.Split(Star); + + if (headers.Length < 2 || !long.TryParse(headers[0], out long dataSize)) + { + Logger.Log(string.Format( + CultureInfo.CurrentCulture, + "Reading header failed: {0}:{1}", + headers.Length, + fileName)); + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] + { + Common.ICON_BIG_CLIPBOARD, + -1, -1, -1, + }); + return; + } + + fileName = headers[1]; + + Logger.LogDebug(string.Format( + CultureInfo.CurrentCulture, + "Receiving {0}:{1} from {2}...", + Path.GetFileName(fileName), + dataSize, + remoteMachine)); + Common.ShowToolTip( + string.Format( + CultureInfo.CurrentCulture, + "Receiving {0} from {1}...", + Path.GetFileName(fileName), + remoteMachine), + 5000, + ToolTipIcon.Info, + Setting.Values.ShowClipNetStatus); + if (fileName.StartsWith("image", StringComparison.CurrentCultureIgnoreCase) || + fileName.StartsWith("text", StringComparison.CurrentCultureIgnoreCase)) + { + m = new MemoryStream(); + } + else + { + if (postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase)) + { + _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() => + { + savingFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\MouseWithoutBorders\\"; + + if (!Directory.Exists(savingFolder)) + { + _ = Directory.CreateDirectory(savingFolder); + } + }); + + tempFile = savingFolder + Path.GetFileName(fileName); + m = new FileStream(tempFile, FileMode.Create); + } + else if (postAct.Contains("mspaint")) + { + tempFile = Common.GetMyStorageDir() + @"ScreenCapture-" + + remoteMachine + ".png"; + m = new FileStream(tempFile, FileMode.Create); + } + else + { + tempFile = Common.GetMyStorageDir(); + tempFile += Path.GetFileName(fileName); + m = new FileStream(tempFile, FileMode.Create); + } + + Logger.Log("==> " + tempFile); + } + + Common.ShowToolTip( + string.Format( + CultureInfo.CurrentCulture, + "Receiving {0} from {1}...", + Path.GetFileName(fileName), + remoteMachine), + 5000, + ToolTipIcon.Info, + Setting.Values.ShowClipNetStatus); + + do + { + rv = deStream.ReadEx(buf, 0, buf.Length); + + if (rv > 0) + { + receivedCount += rv; + + if (receivedCount > dataSize) + { + rv -= (int)(receivedCount - dataSize); + } + + m.Write(buf, 0, rv); + } + + if (Common.ToggleIcons == null) + { + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] + { + Common.ICON_SMALL_CLIPBOARD, + -1, Common.ICON_SMALL_CLIPBOARD, -1, + }); + } + + string text = string.Format(CultureInfo.CurrentCulture, "{0}KB received: {1}", m.Length / 1024, Path.GetFileName(fileName)); + + Common.DoSomethingInUIThread(() => + { + Common.MainForm.SetTrayIconText(text); + }); + } + while (rv > 0); + + if (m != null && fileName != null) + { + m.Flush(); + Logger.LogDebug(m.Length.ToString(CultureInfo.CurrentCulture) + " bytes received."); + Clipboard.LastClipboardEventTime = Common.GetTick(); + string toolTipText = null; + string sizeText = m.Length >= 1024 + ? (m.Length / 1024).ToString(CultureInfo.CurrentCulture) + "KB" + : m.Length.ToString(CultureInfo.CurrentCulture) + "Bytes"; + + PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersClipboardFileTransferEvent()); + + if (fileName.StartsWith("image", StringComparison.CurrentCultureIgnoreCase)) + { + Clipboard.SetImage(Image.FromStream(m)); + toolTipText = string.Format( + CultureInfo.CurrentCulture, + "{0} {1} from {2} is in Clipboard.", + sizeText, + "image", + remoteMachine); + } + else if (fileName.StartsWith("text", StringComparison.CurrentCultureIgnoreCase)) + { + byte[] data = (m as MemoryStream).GetBuffer(); + toolTipText = string.Format( + CultureInfo.CurrentCulture, + "{0} {1} from {2} is in Clipboard.", + sizeText, + "text", + remoteMachine); + Clipboard.SetClipboardData(data); + } + else if (tempFile != null) + { + if (postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase)) + { + toolTipText = string.Format( + CultureInfo.CurrentCulture, + "{0} {1} received from {2}!", + sizeText, + Path.GetFileName(fileName), + remoteMachine); + + _ = Launch.ImpersonateLoggedOnUserAndDoSomething(() => + { + ProcessStartInfo startInfo = new(); + startInfo.UseShellExecute = true; + startInfo.WorkingDirectory = savingFolder; + startInfo.FileName = savingFolder; + startInfo.Verb = "open"; + _ = Process.Start(startInfo); + }); + } + else if (postAct.Contains("mspaint")) + { + m.Close(); + m = null; + Common.OpenImage(tempFile); + toolTipText = string.Format( + CultureInfo.CurrentCulture, + "{0} {1} from {2} is in Mspaint.", + sizeText, + Path.GetFileName(tempFile), + remoteMachine); + } + else + { + StringCollection filePaths = new() + { + tempFile, + }; + Clipboard.SetFileDropList(filePaths); + toolTipText = string.Format( + CultureInfo.CurrentCulture, + "{0} {1} from {2} is in Clipboard.", + sizeText, + Path.GetFileName(fileName), + remoteMachine); + } + } + + if (!string.IsNullOrWhiteSpace(toolTipText)) + { + Common.ShowToolTip(toolTipText, 5000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); + } + + Common.DoSomethingInUIThread(() => + { + Common.MainForm.UpdateNotifyIcon(); + }); + + m?.Close(); + m = null; + } + } + catch (ThreadAbortException) + { + Logger.Log("The current thread is being aborted (3)."); + s.Close(); + + if (m != null) + { + m.Close(); + m = null; + } + + return; + } + catch (Exception e) + { + if (e is IOException) + { + string log = $"{nameof(ReceiveAndProcessClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; + Logger.Log(log); + } + else + { + Logger.Log(e); + } + + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] + { + Common.ICON_BIG_CLIPBOARD, + -1, Common.ICON_BIG_CLIPBOARD, -1, + }); + Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); + + if (m != null) + { + m.Close(); + m = null; + } + + return; + } + + s.Close(); + } + + internal static bool ShakeHand(ref string remoteName, Socket s, out Stream enStream, out Stream deStream, ref bool clientPushData, ref ClipboardPostAction postAction) + { + const int CLIPBOARD_HANDSHAKE_TIMEOUT = 30; + s.ReceiveTimeout = CLIPBOARD_HANDSHAKE_TIMEOUT * 1000; + s.NoDelay = true; + s.SendBufferSize = s.ReceiveBufferSize = 1024000; + + bool handShaken = false; + enStream = deStream = null; + + try + { + DATA package = new() + { + Type = clientPushData ? PackageType.ClipboardPush : PackageType.Clipboard, + PostAction = postAction, + Src = Common.MachineID, + MachineName = Common.MachineName, + }; + + byte[] buf = new byte[Common.PACKAGE_SIZE_EX]; + + NetworkStream ns = new(s); + enStream = Common.GetEncryptedStream(ns); + Common.SendOrReceiveARandomDataBlockPerInitialIV(enStream); + Logger.LogDebug($"{nameof(ShakeHand)}: Writing header package."); + enStream.Write(package.Bytes, 0, Common.PACKAGE_SIZE_EX); + + Logger.LogDebug($"{nameof(ShakeHand)}: Sent: clientPush={clientPushData}, postAction={postAction}."); + + deStream = Common.GetDecryptedStream(ns); + Common.SendOrReceiveARandomDataBlockPerInitialIV(deStream, false); + + Logger.LogDebug($"{nameof(ShakeHand)}: Reading header package."); + + int bytesReceived = deStream.ReadEx(buf, 0, Common.PACKAGE_SIZE_EX); + package.Bytes = buf; + + string name = "Unknown"; + + if (bytesReceived == Common.PACKAGE_SIZE_EX) + { + if (package.Type is PackageType.Clipboard or PackageType.ClipboardPush) + { + name = remoteName = package.MachineName; + + Logger.LogDebug($"{nameof(ShakeHand)}: Connection from {name}:{package.Src}"); + + if (MachineStuff.MachinePool.ResolveID(name) == package.Src && Common.IsConnectedTo(package.Src)) + { + clientPushData = package.Type == PackageType.ClipboardPush; + postAction = package.PostAction; + handShaken = true; + Logger.LogDebug($"{nameof(ShakeHand)}: Received: clientPush={clientPushData}, postAction={postAction}."); + } + else + { + Logger.LogDebug($"{nameof(ShakeHand)}: No active connection to the machine: {name}."); + } + } + else + { + Logger.LogDebug($"{nameof(ShakeHand)}: Unexpected package type: {package.Type}."); + } + } + else + { + Logger.LogDebug($"{nameof(ShakeHand)}: BytesTransferred != PACKAGE_SIZE_EX: {bytesReceived}"); + } + + if (!handShaken) + { + string msg = $"Clipboard connection rejected: {name}:{remoteName}/{package.Src}\r\n\r\nMake sure you run the same version in all machines."; + Logger.Log(msg); + Common.ShowToolTip(msg, 3000, ToolTipIcon.Warning); + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, -1, -1 }); + } + } + catch (ThreadAbortException) + { + Logger.Log($"{nameof(ShakeHand)}: The current thread is being aborted."); + s.Close(); + } + catch (Exception e) + { + if (e is IOException) + { + string log = $"{nameof(ShakeHand)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; + Logger.Log(log); + } + else + { + Logger.Log(e); + } + + Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] + { + Common.ICON_BIG_CLIPBOARD, + -1, Common.ICON_BIG_CLIPBOARD, -1, + }); + Common.MainForm.UpdateNotifyIcon(); + Common.ShowToolTip(e.Message + "\r\n\r\nMake sure you run the same version in all machines.", 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); + + if (m != null) + { + m.Close(); + m = null; + } + } + + return handShaken; + } + + internal static TcpClient ConnectToRemoteClipboardSocket(string remoteMachine) + { + TcpClient clipboardTcpClient; + clipboardTcpClient = new TcpClient(AddressFamily.InterNetworkV6); + clipboardTcpClient.Client.DualMode = true; + + SocketStuff sk = Common.Sk; + + if (sk != null) + { + Common.DoSomethingInUIThread(() => Common.MainForm.ChangeIcon(Common.ICON_SMALL_CLIPBOARD)); + + System.Net.IPAddress ip = Common.GetConnectedClientSocketIPAddressFor(remoteMachine); + Logger.LogDebug($"{nameof(ConnectToRemoteClipboardSocket)}Connecting to {remoteMachine}:{ip}:{sk.TcpPort}..."); + + if (ip != null) + { + clipboardTcpClient.Connect(ip, sk.TcpPort); + } + else + { + clipboardTcpClient.Connect(remoteMachine, sk.TcpPort); + } + + Logger.LogDebug($"Connected from {clipboardTcpClient.Client.LocalEndPoint}. Getting data..."); + return clipboardTcpClient; + } + else + { + throw new ExpectedSocketException($"{nameof(ConnectToRemoteClipboardSocket)}: No longer connected."); + } + } + + private static void SetClipboardData(byte[] data) + { + if (data == null || data.Length <= 0) + { + Logger.Log("data is null or empty!"); + return; + } + + if (data.Length > 1024000) + { + Common.ShowToolTip( + string.Format( + CultureInfo.CurrentCulture, + "Decompressing {0} clipboard data ...", + (data.Length / 1024).ToString(CultureInfo.CurrentCulture) + "KB"), + 5000, + ToolTipIcon.Info, + Setting.Values.ShowClipNetStatus); + } + + string st = string.Empty; + + using (MemoryStream ms = new(data)) + { + using DeflateStream s = new(ms, CompressionMode.Decompress, true); + const int BufferSize = 1024000; // Buffer size should be big enough, this is critical to performance! + + int rv = 0; + + do + { + byte[] buffer = new byte[BufferSize]; + rv = s.ReadEx(buffer, 0, BufferSize); + + if (rv > 0) + { + st += Common.GetStringU(buffer); + } + else + { + break; + } + } + while (true); + } + + int textTypeCount = 0; + string[] texts = st.Split(new string[] { TEXT_TYPE_SEP }, StringSplitOptions.RemoveEmptyEntries); + string tmp; + DataObject data1 = new(); + + foreach (string txt in texts) + { + if (string.IsNullOrEmpty(txt.Trim(NullSeparator))) + { + continue; + } + + tmp = txt[3..]; + + if (txt.StartsWith("RTF", StringComparison.CurrentCultureIgnoreCase)) + { + Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of RTF <-"); + data1.SetData(DataFormats.Rtf, tmp); + } + else if (txt.StartsWith("HTM", StringComparison.CurrentCultureIgnoreCase)) + { + Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of HTM <-"); + data1.SetData(DataFormats.Html, tmp); + } + else if (txt.StartsWith("TXT", StringComparison.CurrentCultureIgnoreCase)) + { + Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of TXT <-"); + data1.SetData(DataFormats.UnicodeText, tmp); + } + else + { + if (textTypeCount == 0) + { + Logger.LogDebug(((double)txt.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of UNI <-"); + data1.SetData(DataFormats.UnicodeText, txt); + } + + Logger.Log("Invalid clipboard format received!"); + } + + textTypeCount++; + } + + if (textTypeCount > 0) + { + Clipboard.SetDataObject(data1); + } + } + + private static void SetFileDropList(StringCollection filePaths) + { + Common.DoSomethingInUIThread(() => + { + try + { + _ = Common.Retry( + nameof(SystemClipboard.SetFileDropList), + () => + { + SystemClipboard.SetFileDropList(filePaths); + return true; + }, + (log) => Logger.TelemetryLogTrace( + log, + SeverityLevel.Information), + () => Clipboard.LastClipboardEventTime = Common.GetTick()); + } + catch (ExternalException e) + { + Logger.Log(e); + } + catch (ThreadStateException e) + { + Logger.Log(e); + } + catch (ArgumentNullException e) + { + Logger.Log(e); + } + catch (ArgumentException e) + { + Logger.Log(e); + } + }); + } + + private static void SetImage(Image image) + { + Common.DoSomethingInUIThread(() => + { + try + { + _ = Common.Retry( + nameof(SystemClipboard.SetImage), + () => + { + SystemClipboard.SetImage(image); + return true; + }, + (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), + () => Clipboard.LastClipboardEventTime = Common.GetTick()); + } + catch (ExternalException e) + { + Logger.Log(e); + } + catch (ThreadStateException e) + { + Logger.Log(e); + } + catch (ArgumentNullException e) + { + Logger.Log(e); + } + }); + } + + internal static void SetText(string text) + { + Common.DoSomethingInUIThread(() => + { + try + { + _ = Common.Retry( + nameof(SystemClipboard.SetText), + () => + { + SystemClipboard.SetText(text); + return true; + }, + (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), + () => Clipboard.LastClipboardEventTime = Common.GetTick()); + } + catch (ExternalException e) + { + Logger.Log(e); + } + catch (ThreadStateException e) + { + Logger.Log(e); + } + catch (ArgumentNullException e) + { + Logger.Log(e); + } + }); + } + + private static void SetDataObject(DataObject dataObject) + { + Common.DoSomethingInUIThread(() => + { + try + { + SystemClipboard.SetDataObject(dataObject, true, 10, 200); + } + catch (ExternalException e) + { + string dataFormats = string.Join(",", dataObject.GetFormats()); + Logger.Log($"{e.Message}: {dataFormats}"); + } + catch (ThreadStateException e) + { + Logger.Log(e); + } + catch (ArgumentNullException e) + { + Logger.Log(e); + } + }); + } +} diff --git a/src/modules/MouseWithoutBorders/App/Core/DragDrop.cs b/src/modules/MouseWithoutBorders/App/Core/DragDrop.cs index 6d13672d3a..2decb83261 100644 --- a/src/modules/MouseWithoutBorders/App/Core/DragDrop.cs +++ b/src/modules/MouseWithoutBorders/App/Core/DragDrop.cs @@ -83,7 +83,7 @@ internal static class DragDrop if (wParam == Common.WM_RBUTTONUP && IsDropping) { IsDropping = false; - Common.LastIDWithClipboardData = ID.NONE; + Clipboard.LastIDWithClipboardData = ID.NONE; } } @@ -193,7 +193,7 @@ internal static class DragDrop { if (!string.IsNullOrEmpty(dragFileName) && (File.Exists(dragFileName) || Directory.Exists(dragFileName))) { - Common.LastDragDropFile = dragFileName; + Clipboard.LastDragDropFile = dragFileName; /* * possibleDropMachineID is used as desID sent in DragDropStep06(); * */ @@ -270,7 +270,7 @@ internal static class DragDrop else { IsDragging = false; - Common.LastIDWithClipboardData = ID.NONE; + Clipboard.LastIDWithClipboardData = ID.NONE; } } } @@ -280,7 +280,7 @@ internal static class DragDrop Logger.LogDebug("DragDropStep10: Hide the form and get data..."); IsDropping = false; IsDragging = false; - Common.LastIDWithClipboardData = ID.NONE; + Clipboard.LastIDWithClipboardData = ID.NONE; Common.DoSomethingInUIThread(() => { @@ -288,7 +288,7 @@ internal static class DragDrop }); PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersDragAndDropEvent()); - Common.GetRemoteClipboard("desktop"); + Clipboard.GetRemoteClipboard("desktop"); } internal static void DragDropStep11() @@ -298,8 +298,8 @@ internal static class DragDrop IsDropping = false; IsDragging = false; DragMachine = (ID)1; - Common.LastIDWithClipboardData = ID.NONE; - Common.LastDragDropFile = null; + Clipboard.LastIDWithClipboardData = ID.NONE; + Clipboard.LastDragDropFile = null; MouseDown = false; } @@ -307,7 +307,7 @@ internal static class DragDrop { Logger.LogDebug("DragDropStep12: ClipboardDragDropEnd received"); IsDropping = false; - Common.LastIDWithClipboardData = ID.NONE; + Clipboard.LastIDWithClipboardData = ID.NONE; Common.DoSomethingInUIThread(() => { diff --git a/src/modules/MouseWithoutBorders/App/Core/Event.cs b/src/modules/MouseWithoutBorders/App/Core/Event.cs index c30c59e547..1e6ee3e371 100644 --- a/src/modules/MouseWithoutBorders/App/Core/Event.cs +++ b/src/modules/MouseWithoutBorders/App/Core/Event.cs @@ -78,7 +78,7 @@ internal static class Event // if they are, check that there is no application running in fullscreen mode before switching. if (!p.IsEmpty && Common.IsEasyMouseSwitchAllowed()) { - Common.HasSwitchedMachineSinceLastCopy = true; + Clipboard.HasSwitchedMachineSinceLastCopy = true; Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, @@ -218,10 +218,10 @@ internal static class Event if (MachineStuff.desMachineID == Common.MachineID) { - if (Common.GetTick() - Common.clipboardCopiedTime < Common.BIG_CLIPBOARD_DATA_TIMEOUT) + if (Common.GetTick() - Clipboard.clipboardCopiedTime < Clipboard.BIG_CLIPBOARD_DATA_TIMEOUT) { - Common.clipboardCopiedTime = 0; - Common.GetRemoteClipboard("PrepareToSwitchToMachine"); + Clipboard.clipboardCopiedTime = 0; + Clipboard.GetRemoteClipboard("PrepareToSwitchToMachine"); } } else diff --git a/src/modules/MouseWithoutBorders/App/Core/Helper.cs b/src/modules/MouseWithoutBorders/App/Core/Helper.cs index 4122fbe31b..bd66c9a83f 100644 --- a/src/modules/MouseWithoutBorders/App/Core/Helper.cs +++ b/src/modules/MouseWithoutBorders/App/Core/Helper.cs @@ -119,7 +119,7 @@ internal static class Helper if (MachineStuff.NewDesMachineID == Common.MachineID) { - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); } } } @@ -317,7 +317,7 @@ internal static class Helper Common.GetInputDesktop(), 0); - Common.HasSwitchedMachineSinceLastCopy = true; + Clipboard.HasSwitchedMachineSinceLastCopy = true; // Common.CreateLowIntegrityProcess("\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", string.Empty, 0, false, 0); var processes = Process.GetProcessesByName(HelperProcessName); diff --git a/src/modules/MouseWithoutBorders/App/Core/InitAndCleanup.cs b/src/modules/MouseWithoutBorders/App/Core/InitAndCleanup.cs new file mode 100644 index 0000000000..963775cbca --- /dev/null +++ b/src/modules/MouseWithoutBorders/App/Core/InitAndCleanup.cs @@ -0,0 +1,278 @@ +// 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.Globalization; +using System.Net.NetworkInformation; +using System.Security.Cryptography; +using System.Threading; + +using Microsoft.Win32; +using MouseWithoutBorders.Class; +using Windows.UI.Input.Preview.Injection; + +// +// Initialization and clean up. +// +// +// 2008 created by Truong Do (ductdo). +// 2009-... modified by Truong Do (TruongDo). +// 2023- Included in PowerToys. +// +namespace MouseWithoutBorders.Core; + +internal static class InitAndCleanup +{ + private static bool initDone; + internal static int REOPEN_WHEN_WSAECONNRESET = -10054; + internal static int REOPEN_WHEN_HOTKEY = -10055; + internal static int PleaseReopenSocket; + internal static bool ReopenSocketDueToReadError; + + private static DateTime LastResumeSuspendTime { get; set; } = DateTime.UtcNow; + + internal static bool InitDone + { + get => InitAndCleanup.initDone; + set => InitAndCleanup.initDone = value; + } + + internal static void UpdateMachineTimeAndID() + { + Common.MachineName = Common.MachineName.Trim(); + _ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true); + } + + private static void InitializeMachinePoolFromSettings() + { + try + { + MachineInf[] info = MachinePoolHelpers.LoadMachineInfoFromMachinePoolStringSetting(Setting.Values.MachinePoolString); + for (int i = 0; i < info.Length; i++) + { + info[i].Name = info[i].Name.Trim(); + } + + MachineStuff.MachinePool.Initialize(info); + MachineStuff.MachinePool.ResetIPAddressesForDeadMachines(true); + } + catch (Exception ex) + { + Logger.Log(ex); + MachineStuff.MachinePool.Clear(); + } + } + + private static void SetupMachineNameAndID() + { + try + { + Common.GetMachineName(); + Common.DesMachineID = MachineStuff.NewDesMachineID = Common.MachineID; + + // MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test + InitializeMachinePoolFromSettings(); + + Common.MachineName = Common.MachineName.Trim(); + _ = MachineStuff.MachinePool.LearnMachine(Common.MachineName); + _ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true); + + MachineStuff.UpdateMachinePoolStringSetting(); + } + catch (Exception e) + { + Logger.Log(e); + } + } + + internal static void Init() + { + _ = Helper.GetUserName(); + Common.GeneratedKey = true; + + try + { + Common.MyKey = Setting.Values.MyKey; + int tmp = Setting.Values.MyKeyDaysToExpire; + } + catch (FormatException e) + { + Common.KeyCorrupted = true; + Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); + Logger.Log(e.Message); + } + catch (CryptographicException e) + { + Common.KeyCorrupted = true; + Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); + Logger.Log(e.Message); + } + + try + { + InputSimulation.Injector = InputInjector.TryCreate(); + if (InputSimulation.Injector != null) + { + InputSimulation.MoveMouseRelative(0, 0); + NativeMethods.InjectMouseInputAvailable = true; + } + } + catch (EntryPointNotFoundException) + { + NativeMethods.InjectMouseInputAvailable = false; + Logger.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false"); + } + + bool dummy = Setting.Values.DrawMouseEx; + Common.Is64bitOS = IntPtr.Size == 8; + Common.tcpPort = Setting.Values.TcpPort; + Common.GetScreenConfig(); + Common.PackageSent = new PackageMonitor(0); + Common.PackageReceived = new PackageMonitor(0); + SetupMachineNameAndID(); + Common.InitEncryption(); + CreateHelperThreads(); + + SystemEvents.DisplaySettingsChanged += new EventHandler(Common.SystemEvents_DisplaySettingsChanged); + NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged); + SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged); + PleaseReopenSocket = 9; + /* TODO: Telemetry for the matrix? */ + } + + private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) + { + Helper.WndProcCounter++; + + if (e.Mode is PowerModes.Resume or PowerModes.Suspend) + { + Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information); + LastResumeSuspendTime = DateTime.UtcNow; + MachineStuff.SwitchToMultipleMode(false, true); + } + } + + private static void CreateHelperThreads() + { + // NOTE(@yuyoyuppe): service crashes while trying to obtain this info, disabling. + /* + Thread watchDogThread = new(new ThreadStart(WatchDogThread), nameof(WatchDogThread)); + watchDogThread.Priority = ThreadPriority.Highest; + watchDogThread.Start(); + */ + + Common.helper = new Thread(new ThreadStart(Helper.HelperThread), "Helper Thread"); + Common.helper.SetApartmentState(ApartmentState.STA); + Common.helper.Start(); + } + + private static void AskHelperThreadsToExit(int waitTime) + { + Helper.signalHelperToExit = true; + Helper.signalWatchDogToExit = true; + _ = Common.EvSwitch.Set(); + + int c = 0; + if (Common.helper != null && c < waitTime) + { + while (Helper.signalHelperToExit) + { + Thread.Sleep(1); + } + + Common.helper = null; + } + } + + internal static void Cleanup() + { + try + { + Common.SendByeBye(); + + // UnhookClipboard(); + AskHelperThreadsToExit(500); + Common.MainForm.NotifyIcon.Visible = false; + Common.MainForm.NotifyIcon.Dispose(); + Common.CloseAllFormsAndHooks(); + + Common.DoSomethingInUIThread(() => + { + Common.Sk?.Close(true); + }); + } + catch (Exception e) + { + Logger.Log(e); + } + } + + private static long lastReleaseAllKeysCall; + + internal static void ReleaseAllKeys() + { + if (Math.Abs(Common.GetTick() - lastReleaseAllKeysCall) < 2000) + { + return; + } + + lastReleaseAllKeysCall = Common.GetTick(); + + KEYBDDATA kd; + kd.dwFlags = (int)Common.LLKHF.UP; + + VK[] keys = new VK[] + { + VK.LSHIFT, VK.LCONTROL, VK.LMENU, VK.LWIN, VK.RSHIFT, + VK.RCONTROL, VK.RMENU, VK.RWIN, VK.SHIFT, VK.MENU, VK.CONTROL, + }; + + Logger.LogDebug("***** ReleaseAllKeys has been called! *****:"); + + foreach (VK vk in keys) + { + if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0) + { + Logger.LogDebug(vk.ToString() + " is down, release it..."); + Common.Hook?.ResetLastSwitchKeys(); // Sticky key can turn ALL PC mode on (CtrlCtrlCtrl) + kd.wVk = (int)vk; + InputSimulation.SendKey(kd); + Common.Hook?.ResetLastSwitchKeys(); + } + } + } + + private static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) + { + Logger.LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture)); + Helper.WndProcCounter++; + ScheduleReopenSocketsDueToNetworkChanges(!e.IsAvailable); + } + + private static void ScheduleReopenSocketsDueToNetworkChanges(bool closeSockets = true) + { + if (closeSockets) + { + // Slept/hibernated machine may still have the sockets' status as Connected:( (unchanged) so it would not re-connect after a timeout when waking up. + // Closing the sockets when it is going to sleep/hibernate will trigger the reconnection faster when it wakes up. + Common.DoSomethingInUIThread( + () => + { + SocketStuff s = Common.Sk; + Common.Sk = null; + s?.Close(false); + }, + true); + } + + if (!Common.IsMyDesktopActive()) + { + PleaseReopenSocket = 0; + } + else if (PleaseReopenSocket != 10) + { + PleaseReopenSocket = 10; + } + } +} diff --git a/src/modules/MouseWithoutBorders/App/Core/Logger.cs b/src/modules/MouseWithoutBorders/App/Core/Logger.cs index 6635de59f0..86ce7605b5 100644 --- a/src/modules/MouseWithoutBorders/App/Core/Logger.cs +++ b/src/modules/MouseWithoutBorders/App/Core/Logger.cs @@ -247,22 +247,24 @@ internal static class Logger internal static void DumpStaticTypes(StringBuilder sb, int level) { - sb.AppendLine($"[{nameof(DragDrop)}]\r\n==============="); - Logger.DumpType(sb, typeof(DragDrop), 0, level); - sb.AppendLine($"[{nameof(Event)}]\r\n==============="); - Logger.DumpType(sb, typeof(Event), 0, level); - sb.AppendLine($"[{nameof(Helper)}]\r\n==============="); - Logger.DumpType(sb, typeof(Helper), 0, level); - sb.AppendLine($"[{nameof(Launch)}]\r\n==============="); - Logger.DumpType(sb, typeof(Launch), 0, level); - sb.AppendLine($"[{nameof(Logger)}]\r\n==============="); - Logger.DumpType(sb, typeof(Logger), 0, level); - sb.AppendLine($"[{nameof(MachineStuff)}]\r\n==============="); - Logger.DumpType(sb, typeof(MachineStuff), 0, level); - sb.AppendLine($"[{nameof(Receiver)}]\r\n==============="); - Logger.DumpType(sb, typeof(Receiver), 0, level); - sb.AppendLine($"[{nameof(Service)}]\r\n==============="); - Logger.DumpType(sb, typeof(Service), 0, level); + var staticTypes = new List + { + typeof(Clipboard), + typeof(DragDrop), + typeof(Event), + typeof(InitAndCleanup), + typeof(Helper), + typeof(Launch), + typeof(Logger), + typeof(MachineStuff), + typeof(Receiver), + typeof(Service), + }; + foreach (var staticType in staticTypes) + { + sb.AppendLine(CultureInfo.InvariantCulture, $"[{staticType.Name}]\r\n==============="); + Logger.DumpType(sb, staticType, 0, level); + } } internal static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool stop) diff --git a/src/modules/MouseWithoutBorders/App/Core/MachineStuff.cs b/src/modules/MouseWithoutBorders/App/Core/MachineStuff.cs index 144102629f..e5263aa788 100644 --- a/src/modules/MouseWithoutBorders/App/Core/MachineStuff.cs +++ b/src/modules/MouseWithoutBorders/App/Core/MachineStuff.cs @@ -992,7 +992,7 @@ internal static class MachineStuff Setting.Values.MatrixOneRow = !((package.Type & PackageType.MatrixTwoRowFlag) == PackageType.MatrixTwoRowFlag); MachineMatrix = MachineMatrix; // Save - Common.ReopenSocketDueToReadError = true; + InitAndCleanup.ReopenSocketDueToReadError = true; UpdateClientSockets("UpdateMachineMatrix"); @@ -1044,7 +1044,7 @@ internal static class MachineStuff Common.MoveMouseToCenter(); } - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); Common.UpdateMultipleModeIconAndMenu(); } diff --git a/src/modules/MouseWithoutBorders/App/Core/Receiver.cs b/src/modules/MouseWithoutBorders/App/Core/Receiver.cs index 303bec4ff0..1b1e0730b0 100644 --- a/src/modules/MouseWithoutBorders/App/Core/Receiver.cs +++ b/src/modules/MouseWithoutBorders/App/Core/Receiver.cs @@ -157,7 +157,7 @@ internal static class Receiver if (!p.IsEmpty) { - Common.HasSwitchedMachineSinceLastCopy = true; + Clipboard.HasSwitchedMachineSinceLastCopy = true; Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, @@ -274,7 +274,7 @@ internal static class Receiver Common.PackageReceived.Clipboard++; if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { - Common.clipboardCopiedTime = Common.GetTick(); + Clipboard.clipboardCopiedTime = Common.GetTick(); GetNameOfMachineWithClipboardData(package); SignalBigClipboardData(); } @@ -282,10 +282,10 @@ internal static class Receiver break; case PackageType.MachineSwitched: - if (Common.GetTick() - Common.clipboardCopiedTime < Common.BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == Common.MachineID)) + if (Common.GetTick() - Clipboard.clipboardCopiedTime < Clipboard.BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == Common.MachineID)) { - Common.clipboardCopiedTime = 0; - Common.GetRemoteClipboard("PackageType.MachineSwitched"); + Clipboard.clipboardCopiedTime = 0; + Clipboard.GetRemoteClipboard("PackageType.MachineSwitched"); } break; @@ -297,7 +297,7 @@ internal static class Receiver if (package.Des == Common.MachineID || package.Des == ID.ALL) { GetNameOfMachineWithClipboardData(package); - Common.GetRemoteClipboard("mspaint," + Common.LastMachineWithClipboardData); + Clipboard.GetRemoteClipboard("mspaint," + Clipboard.LastMachineWithClipboardData); } } @@ -326,10 +326,10 @@ internal static class Receiver Thread.UpdateThreads(thread); string remoteMachine = package.MachineName; - System.Net.Sockets.TcpClient client = Common.ConnectToRemoteClipboardSocket(remoteMachine); + System.Net.Sockets.TcpClient client = Clipboard.ConnectToRemoteClipboardSocket(remoteMachine); bool clientPushData = true; - if (Common.ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction)) + if (Clipboard.ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction)) { SocketStuff.SendClipboardData(client.Client, enStream); } @@ -360,7 +360,7 @@ internal static class Receiver case PackageType.ClipboardText: case PackageType.ClipboardImage: - Common.clipboardCopiedTime = 0; + Clipboard.clipboardCopiedTime = 0; if (package.Type == PackageType.ClipboardImage) { Common.PackageReceived.ClipboardImage++; @@ -372,7 +372,7 @@ internal static class Receiver if (tcp != null) { - Common.ReceiveClipboardDataUsingTCP( + Clipboard.ReceiveClipboardDataUsingTCP( package, package.Type == PackageType.ClipboardImage, tcp); @@ -381,10 +381,10 @@ internal static class Receiver break; case PackageType.HideMouse: - Common.HasSwitchedMachineSinceLastCopy = true; + Clipboard.HasSwitchedMachineSinceLastCopy = true; Common.HideMouseCursor(true); Helper.MainFormDotEx(false); - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); break; default: @@ -405,11 +405,11 @@ internal static class Receiver internal static void GetNameOfMachineWithClipboardData(DATA package) { - Common.LastIDWithClipboardData = package.Src; - List matchingMachines = MachineStuff.MachinePool.TryFindMachineByID(Common.LastIDWithClipboardData); + Clipboard.LastIDWithClipboardData = package.Src; + List matchingMachines = MachineStuff.MachinePool.TryFindMachineByID(Clipboard.LastIDWithClipboardData); if (matchingMachines.Count >= 1) { - Common.LastMachineWithClipboardData = matchingMachines[0].Name.Trim(); + Clipboard.LastMachineWithClipboardData = matchingMachines[0].Name.Trim(); } /* diff --git a/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs b/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs index 84d464d33d..97884d4821 100644 --- a/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs +++ b/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs @@ -84,7 +84,7 @@ namespace MouseWithoutBorders if ((connectedClientSocket = Common.GetConnectedClientSocket()) != null) { ShowStatus($"Connected from local IP Address: {connectedClientSocket.Address}."); - Common.UpdateMachineTimeAndID(); + InitAndCleanup.UpdateMachineTimeAndID(); Common.MMSleep(1); connected = true; diff --git a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs index ca095d8140..66301c52cb 100644 --- a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs +++ b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs @@ -22,6 +22,8 @@ using Microsoft.PowerToys.Telemetry; // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; + +using Clipboard = MouseWithoutBorders.Core.Clipboard; using Timer = System.Windows.Forms.Timer; [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#buttonOK_Click(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] @@ -110,7 +112,7 @@ namespace MouseWithoutBorders { SocketStuff.InvalidKeyFound = false; showInvalidKeyMessage = false; - Common.ReopenSocketDueToReadError = true; + InitAndCleanup.ReopenSocketDueToReadError = true; Common.ReopenSockets(true); for (int i = 0; i < 10; i++) @@ -780,7 +782,7 @@ namespace MouseWithoutBorders ShowUpdateMessage(); - Common.HasSwitchedMachineSinceLastCopy = true; + Clipboard.HasSwitchedMachineSinceLastCopy = true; } private void CheckBoxDisableCAD_CheckedChanged(object sender, EventArgs e) diff --git a/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs b/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs index b01a653f60..1ab0ce8cc7 100644 --- a/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs +++ b/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs @@ -139,13 +139,13 @@ namespace MouseWithoutBorders { if (cleanup) { - Common.Cleanup(); + InitAndCleanup.Cleanup(); } Helper.WndProcCounter++; if (!Common.RunOnScrSaverDesktop) { - Common.ReleaseAllKeys(); + InitAndCleanup.ReleaseAllKeys(); } Helper.RunDDHelper(true); @@ -412,7 +412,7 @@ namespace MouseWithoutBorders count = 0; - Common.InitDone = true; + InitAndCleanup.InitDone = true; #if SHOW_ON_WINLOGON if (Common.RunOnLogonDesktop) { @@ -423,39 +423,39 @@ namespace MouseWithoutBorders if ((count % 2) == 0) { - if (Common.PleaseReopenSocket == 10 || (Common.PleaseReopenSocket > 0 && count > 0 && count % 300 == 0)) + if (InitAndCleanup.PleaseReopenSocket == 10 || (InitAndCleanup.PleaseReopenSocket > 0 && count > 0 && count % 300 == 0)) { - if (!Common.AtLeastOneSocketEstablished() || Common.PleaseReopenSocket == 10) + if (!Common.AtLeastOneSocketEstablished() || InitAndCleanup.PleaseReopenSocket == 10) { Thread.Sleep(1000); - if (Common.PleaseReopenSocket > 0) + if (InitAndCleanup.PleaseReopenSocket > 0) { - Common.PleaseReopenSocket--; + InitAndCleanup.PleaseReopenSocket--; } // Double check. if (!Common.AtLeastOneSocketEstablished()) { Common.GetMachineName(); - Logger.LogDebug("Common.pleaseReopenSocket: " + Common.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture)); + Logger.LogDebug("Common.pleaseReopenSocket: " + InitAndCleanup.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture)); Common.ReopenSockets(false); MachineStuff.NewDesMachineID = Common.DesMachineID = Common.MachineID; } } else { - Common.PleaseReopenSocket = 0; + InitAndCleanup.PleaseReopenSocket = 0; } } - if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_HOTKEY) + if (InitAndCleanup.PleaseReopenSocket == InitAndCleanup.REOPEN_WHEN_HOTKEY) { - Common.PleaseReopenSocket = 0; + InitAndCleanup.PleaseReopenSocket = 0; Common.ReopenSockets(true); } - else if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_WSAECONNRESET) + else if (InitAndCleanup.PleaseReopenSocket == InitAndCleanup.REOPEN_WHEN_WSAECONNRESET) { - Common.PleaseReopenSocket = 0; + InitAndCleanup.PleaseReopenSocket = 0; Thread.Sleep(1000); MachineStuff.UpdateClientSockets("REOPEN_WHEN_WSAECONNRESET"); } 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 3c933bfff1..1bbd8ba49c 100644 --- a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt +++ b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt @@ -4,28 +4,6 @@ [Other Logs] =============== = MouseWithoutBorders.Common -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 screenWidth = 0 screenHeight = 0 lastX = 0 @@ -99,17 +77,6 @@ LegalKeyDictionary = Concurrent.ConcurrentDictionary`2[System.String,System.Byte --_budget = ???????????? --_growLockArray = True --_comparerIsDefaultForClasses = False -initDone = False -REOPEN_WHEN_WSAECONNRESET = -10054 -REOPEN_WHEN_HOTKEY = -10055 -PleaseReopenSocket = 0 -ReopenSocketDueToReadError = False -k__BackingField = ???????????? ---_dateData = ???????????? ---MinValue = 01/01/0001 00:00:00 ---MaxValue = 31/12/9999 23:59:59 ---UnixEpoch = 01/01/1970 00:00:00 -lastReleaseAllKeysCall = 0 PackageSent = MouseWithoutBorders.PackageMonitor --Keyboard = 0 --Mouse = 0 @@ -153,12 +120,6 @@ p = {X=0,Y=0} --y = 0 --Empty = {X=0,Y=0} k__BackingField = False -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} TOGGLE_ICONS_SIZE = 4 ICON_ONE = 0 ICON_ALL = 1 @@ -195,6 +156,36 @@ WM_KEYDOWN = 256 WM_KEYUP = 257 WM_SYSKEYDOWN = 260 WM_SYSKEYUP = 261 +[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 @@ -249,6 +240,19 @@ actualLastPos = {X=0,Y=0} --Empty = {X=0,Y=0} myLastX = 0 myLastY = 0 +[InitAndCleanup] +=============== +initDone = False +REOPEN_WHEN_WSAECONNRESET = -10054 +REOPEN_WHEN_HOTKEY = -10055 +PleaseReopenSocket = 0 +ReopenSocketDueToReadError = False +k__BackingField = ???????????? +--_dateData = ???????????? +--MinValue = 01/01/0001 00:00:00 +--MaxValue = 31/12/9999 23:59:59 +--UnixEpoch = 01/01/1970 00:00:00 +lastReleaseAllKeysCall = 0 [Helper] =============== signalHelperToExit = False