[New Utility]Mouse Without Borders

* Integrate Mouse Without Borders into PowerToys

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Andrey Nekrasov
2023-05-15 23:32:26 +01:00
committed by Jaime Bernardo
parent a0b9af039d
commit 29eebe16a4
304 changed files with 37234 additions and 133 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,405 @@
// 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.Drawing;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.PowerToys.Telemetry;
// <summary>
// Drag/Drop implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
namespace MouseWithoutBorders
{
/* Common.DragDrop.cs
* Drag&Drop is one complicated implementation of the tool with some tricks.
*
* SEQUENCE OF EVENTS:
* DragDropStep01: MachineX: Remember mouse down state since it could be a start of a dragging
* DragDropStep02: MachineY: Send an message to the MachineX to ask it to check if it is
* doing drag/drop
* DragDropStep03: MachineX: Got explorerDragDrop, send WM_CHECK_EXPLORER_DRAG_DROP to its mainForm
* DragDropStep04: MachineX: Show Mouse Without Borders Helper form at mouse cursor to get DragEnter event.
* DragDropStepXX: MachineX: Mouse Without Borders Helper: Called by DragEnter, check if dragging a single file,
* remember the file (set as its window caption)
* DragDropStep05: MachineX: Get the file name from Mouse Without Borders Helper, hide Mouse Without Borders Helper window
* DragDropStep06: MachineX: Broadcast a message saying that it has some drag file.
* DragDropStep08: MachineY: Got ClipboardDragDrop, isDropping set, get the MachineX name from the package.
* DragDropStep09: MachineY: Since isDropping is true, show up the drop form (looks like an icon).
* DragDropStep10: MachineY: MouseUp, set isDropping to false, hide the drop "icon" and get data.
* DragDropStep11: MachineX: Mouse move back without drop event, cancelling drag/dop
* SendClipboardBeatDragDropEnd
* DragDropStep12: MachineY: Hide the drop "icon" when received ClipboardDragDropEnd.
*
* FROM VERSION 1.6.3: Drag/Drop is temporary removed, Drop action cannot be done from a lower integrity app to a higher one.
* We have to run a helper process...
* http://forums.microsoft.com/MSDN/ShowPost.aspx?PageIndex=1&SiteID=1&PageID=1&PostID=736086
*
* 2008.10.28: Trying to restore the Drag/Drop feature by adding the drag/drop helper process. Coming in version
* 1.6.5
* */
internal partial class Common
{
private static bool isDragging;
internal static bool IsDragging
{
get => Common.isDragging;
set => Common.isDragging = value;
}
internal static void DragDropStep01(int wParam)
{
if (!Setting.Values.TransferFile)
{
return;
}
if (wParam == WM_LBUTTONDOWN)
{
MouseDown = true;
DragMachine = desMachineID;
dropMachineID = ID.NONE;
LogDebug("DragDropStep01: MouseDown");
}
else if (wParam == WM_LBUTTONUP)
{
MouseDown = false;
LogDebug("DragDropStep01: MouseUp");
}
if (wParam == WM_RBUTTONUP && IsDropping)
{
IsDropping = false;
LastIDWithClipboardData = ID.NONE;
}
}
internal static void DragDropStep02()
{
if (desMachineID == MachineID)
{
LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent to myself");
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
else
{
SendCheckExplorerDragDrop();
LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent");
}
}
internal static void DragDropStep03(DATA package)
{
if (RunOnLogonDesktop || RunOnScrSaverDesktop)
{
return;
}
if (package.Des == MachineID || package.Des == ID.ALL)
{
LogDebug("DragDropStep03: ExplorerDragDrop Received.");
dropMachineID = package.Src; // Drop machine is the machine that sent ExplorerDragDrop
if (MouseDown || IsDropping)
{
LogDebug("DragDropStep03: Mouse is down, check if dragging...sending WM_CHECK_EXPLORER_DRAG_DROP to myself...");
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
}
}
private static int dragDropStep05ExCalledByIpc;
internal static void DragDropStep04()
{
if (!IsDropping)
{
IntPtr h = (IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT);
if (h.ToInt32() > 0)
{
_ = Interlocked.Exchange(ref dragDropStep05ExCalledByIpc, 0);
MainForm.Hide();
MainFormVisible = false;
Point p = default;
// NativeMethods.SetWindowText(h, "");
_ = NativeMethods.SetWindowPos(h, NativeMethods.HWND_TOPMOST, 0, 0, 0, 0, NativeMethods.SWP_SHOWWINDOW);
for (int i = -10; i < 10; i++)
{
if (dragDropStep05ExCalledByIpc > 0)
{
LogDebug("DragDropStep04: DragDropStep05ExCalledByIpc.");
break;
}
_ = NativeMethods.GetCursorPos(ref p);
LogDebug("DragDropStep04: Moving Mouse Without Borders Helper to (" + p.X.ToString(CultureInfo.CurrentCulture) + ", " + p.Y.ToString(CultureInfo.CurrentCulture) + ")");
_ = NativeMethods.SetWindowPos(h, NativeMethods.HWND_TOPMOST, p.X - 100 + i, p.Y - 100 + i, 200, 200, 0);
_ = NativeMethods.SendMessage(h, 0x000F, IntPtr.Zero, IntPtr.Zero); // WM_PAINT
Thread.Sleep(20);
Application.DoEvents();
// if (GetText(h).Length > 1) break;
}
}
else
{
LogDebug("DragDropStep04: Mouse without Borders Helper not found!");
}
}
else
{
LogDebug("DragDropStep04: IsDropping == true, skip checking");
}
LogDebug("DragDropStep04: Got WM_CHECK_EXPLORER_DRAG_DROP, done with processing jump to DragDropStep05...");
}
internal static void DragDropStep05Ex(string dragFileName)
{
LogDebug("DragDropStep05 called.");
_ = Interlocked.Exchange(ref dragDropStep05ExCalledByIpc, 1);
if (RunOnLogonDesktop || RunOnScrSaverDesktop)
{
return;
}
if (!IsDropping)
{
_ = Common.ImpersonateLoggedOnUserAndDoSomething(() =>
{
if (!string.IsNullOrEmpty(dragFileName) && (File.Exists(dragFileName) || Directory.Exists(dragFileName)))
{
Common.LastDragDropFile = dragFileName;
/*
* possibleDropMachineID is used as desID sent in DragDropStep06();
* */
if (dropMachineID == ID.NONE)
{
dropMachineID = newDesMachineID;
}
DragDropStep06();
LogDebug("DragDropStep05: File dragging: " + dragFileName);
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)1, (IntPtr)0);
}
else
{
LogDebug("DragDropStep05: File not found: [" + dragFileName + "]");
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)0, (IntPtr)0);
}
LogDebug("DragDropStep05: WM_HIDE_DDHelper sent");
});
}
else
{
LogDebug("DragDropStep05: IsDropping == true, change drop machine...");
IsDropping = false;
MainFormVisible = true; // WM_HIDE_DRAG_DROP
SendDropBegin(); // To dropMachineID set in DragDropStep03
}
MouseDown = false;
}
internal static void DragDropStep06()
{
IsDragging = true;
LogDebug("DragDropStep06: SendClipboardBeatDragDrop");
SendClipboardBeatDragDrop();
SendDropBegin();
}
internal static void DragDropStep08(DATA package)
{
GetNameOfMachineWithClipboardData(package);
LogDebug("DragDropStep08: ClipboardDragDrop Received. machine with drag file was set");
}
internal static void DragDropStep08_2(DATA package)
{
if (package.Des == MachineID && !RunOnLogonDesktop && !RunOnScrSaverDesktop)
{
IsDropping = true;
dropMachineID = MachineID;
LogDebug("DragDropStep08_2: ClipboardDragDropOperation Received. IsDropping set");
}
}
internal static void DragDropStep09(int wParam)
{
if (wParam == WM_MOUSEMOVE && IsDropping)
{
// Show/Move form
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_SHOW_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
else if (wParam == WM_LBUTTONUP && (IsDropping || IsDragging))
{
if (IsDropping)
{
// Hide form, get data
DragDropStep10();
}
else
{
IsDragging = false;
LastIDWithClipboardData = ID.NONE;
}
}
}
internal static void DragDropStep10()
{
LogDebug("DragDropStep10: Hide the form and get data...");
IsDropping = false;
IsDragging = false;
LastIDWithClipboardData = ID.NONE;
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersDragAndDropEvent());
GetRemoteClipboard("desktop");
}
internal static void DragDropStep11()
{
LogDebug("DragDropStep11: Mouse drag coming back, canceling drag/drop");
SendClipboardBeatDragDropEnd();
IsDropping = false;
IsDragging = false;
DragMachine = (ID)1;
LastIDWithClipboardData = ID.NONE;
LastDragDropFile = null;
MouseDown = false;
}
internal static void DragDropStep12()
{
LogDebug("DragDropStep12: ClipboardDragDropEnd received");
IsDropping = false;
LastIDWithClipboardData = ID.NONE;
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
internal static void SendCheckExplorerDragDrop()
{
DATA package = new();
package.Type = PackageType.ExplorerDragDrop;
/*
* package.src = newDesMachineID:
* sent from the master machine but the src must be the
* new des machine since the previous des machine will get this and set
* to possibleDropMachineID in DragDropStep3()
* */
package.Src = newDesMachineID;
package.Des = desMachineID;
package.MachineName = MachineName;
SkSend(package, null, false);
}
private static void ChangeDropMachine()
{
// desMachineID = current drop machine
// newDesMachineID = new drop machine
// 1. Cancelling dropping in current drop machine
if (dropMachineID == MachineID)
{
// Drag/Drop coming through me
IsDropping = false;
}
else
{
// Drag/Drop coming back
SendClipboardBeatDragDropEnd();
}
// 2. SendClipboardBeatDragDrop to new drop machine
// new drop machine is not me
if (newDesMachineID != MachineID)
{
dropMachineID = newDesMachineID;
SendDropBegin();
}
// New drop machine is me
else
{
IsDropping = true;
}
}
internal static void SendClipboardBeatDragDrop()
{
SendPackage(ID.ALL, PackageType.ClipboardDragDrop);
}
internal static void SendDropBegin()
{
LogDebug("SendDropBegin...");
SendPackage(dropMachineID, PackageType.ClipboardDragDropOperation);
}
internal static void SendClipboardBeatDragDropEnd()
{
if (desMachineID != MachineID)
{
SendPackage(desMachineID, PackageType.ClipboardDragDropEnd);
}
}
private static bool isDropping;
private static ID dragMachine;
internal static ID DragMachine
{
get => Common.dragMachine;
set => Common.dragMachine = value;
}
internal static bool IsDropping
{
get => Common.isDropping;
set => Common.isDropping = value;
}
internal static bool MouseDown { get; set; }
}
}

View File

@@ -0,0 +1,242 @@
// 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.
// <summary>
// Encrypt/decrypt implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace MouseWithoutBorders
{
internal partial class Common
{
private static SymmetricAlgorithm symAl;
private static string myKey;
private static uint magicNumber;
private static Random ran = new(); // Used for non encryption related functionality.
internal const int SymAlBlockSize = 16;
/// <summary>
/// This is used for the first encryption block, the following blocks will be combined with the cipher text of the previous block.
/// Thus identical blocks in the socket stream would be encrypted to different cipher text blocks.
/// The first block is a handshake one containing random data.
/// Related Unit Test: TestEncryptDecrypt
/// </summary>
internal static readonly string InitialIV = ulong.MaxValue.ToString(CultureInfo.InvariantCulture);
internal static Random Ran
{
get => Common.ran ??= new Random();
set => Common.ran = value;
}
internal static uint MagicNumber
{
get => Common.magicNumber;
set => Common.magicNumber = value;
}
internal static string MyKey
{
get => Common.myKey;
set
{
if (Common.myKey != value)
{
Common.myKey = value;
_ = Task.Factory.StartNew(
() => Common.GenLegalKey(),
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default); // Cache the key to improve UX.
}
}
}
internal static string KeyDisplayedText(string key)
{
string displayedValue = string.Empty;
int i = 0;
do
{
int length = Math.Min(4, key.Length - i);
displayedValue += string.Concat(key.AsSpan(i, length), " ");
i += 4;
}
while (i < key.Length - 1);
return displayedValue.Trim();
}
internal static bool GeneratedKey { get; set; }
internal static bool KeyCorrupted { get; set; }
internal static void InitEncryption()
{
try
{
if (symAl == null)
{
#pragma warning disable SYSLIB0021 // No proper replacement for now
symAl = new AesCryptoServiceProvider();
#pragma warning restore SYSLIB0021
symAl.KeySize = 256;
symAl.BlockSize = SymAlBlockSize * 8;
symAl.Padding = PaddingMode.Zeros;
symAl.Mode = CipherMode.CBC;
symAl.GenerateIV();
}
}
catch (Exception e)
{
Log(e);
}
}
private static readonly ConcurrentDictionary<string, byte[]> LegalKeyDictionary = new(StringComparer.OrdinalIgnoreCase);
internal static byte[] GenLegalKey()
{
byte[] rv;
string myKey = Common.MyKey;
if (!LegalKeyDictionary.ContainsKey(myKey))
{
Rfc2898DeriveBytes key = new(
myKey,
Common.GetBytesU(InitialIV),
50000,
HashAlgorithmName.SHA512);
rv = key.GetBytes(32);
_ = LegalKeyDictionary.AddOrUpdate(myKey, rv, (k, v) => rv);
}
else
{
rv = LegalKeyDictionary[myKey];
}
return rv;
}
private static byte[] GenLegalIV()
{
string st = InitialIV;
int ivLength = symAl.IV.Length;
if (st.Length > ivLength)
{
st = st[..ivLength];
}
else if (st.Length < ivLength)
{
st = st.PadRight(ivLength, ' ');
}
return GetBytes(st);
}
internal static Stream GetEncryptedStream(Stream encryptedStream)
{
ICryptoTransform encryptor;
encryptor = symAl.CreateEncryptor(GenLegalKey(), GenLegalIV());
return new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write);
}
internal static Stream GetDecryptedStream(Stream encryptedStream)
{
ICryptoTransform decryptor;
decryptor = symAl.CreateDecryptor(GenLegalKey(), GenLegalIV());
return new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read);
}
internal static uint Get24BitHash(string st)
{
if (string.IsNullOrEmpty(st))
{
return 0;
}
byte[] bytes = new byte[PACKAGE_SIZE];
for (int i = 0; i < PACKAGE_SIZE; i++)
{
if (i < st.Length)
{
bytes[i] = (byte)st[i];
}
}
var hash = SHA512.Create();
byte[] hashValue = hash.ComputeHash(bytes);
for (int i = 0; i < 50000; i++)
{
hashValue = hash.ComputeHash(hashValue);
}
Common.LogDebug(string.Format(CultureInfo.CurrentCulture, "magic: {0},{1},{2}", hashValue[0], hashValue[1], hashValue[^1]));
hash.Clear();
return (uint)((hashValue[0] << 23) + (hashValue[1] << 16) + (hashValue[^1] << 8) + hashValue[2]);
}
internal static string GetDebugInfo(string st)
{
return string.IsNullOrEmpty(st) ? st : ((byte)(Common.GetBytesU(st).Sum(value => value) % 256)).ToString(CultureInfo.InvariantCulture);
}
internal static string CreateDefaultKey()
{
return CreateRandomKey();
}
private const int PW_LENGTH = 16;
public static string CreateRandomKey()
{
// Not including characters like "'`O0& since they are confusing to users.
string[] chars = new[] { "abcdefghjkmnpqrstuvxyz", "ABCDEFGHJKMNPQRSTUVXYZ", "123456789", "~!@#$%^*()_-+=:;<,>.?/\\|[]" };
char[][] charactersUsedForKey = chars.Select(charset => Enumerable.Range(0, charset.Length - 1).Select(i => charset[i]).ToArray()).ToArray();
byte[] randomData = new byte[1];
string key = string.Empty;
do
{
foreach (string set in chars)
{
randomData = RandomNumberGenerator.GetBytes(1);
key += set[randomData[0] % set.Length];
if (key.Length >= PW_LENGTH)
{
break;
}
}
}
while (key.Length < PW_LENGTH);
return key;
}
internal static bool IsKeyValid(string key, out string error)
{
error = string.IsNullOrEmpty(key) || key.Length < 16
? "Key must have at least 16 characters in length (spaces are discarded). Key must be auto generated in one of the machines."
: null;
return error == null;
}
}
}

View File

@@ -0,0 +1,272 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
// <summary>
// Keyboard/Mouse hook callback implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Form;
namespace MouseWithoutBorders
{
internal partial class Common
{
private static readonly DATA KeybdPackage = new();
private static readonly DATA MousePackage = new();
private static ulong inputEventCount;
private static ulong invalidPackageCount;
internal static int MOVE_MOUSE_RELATIVE = 100000;
internal static int XY_BY_PIXEL = 300000;
static Common()
{
}
internal static ulong InvalidPackageCount
{
get => Common.invalidPackageCount;
set => Common.invalidPackageCount = value;
}
internal static ulong InputEventCount
{
get => Common.inputEventCount;
set => Common.inputEventCount = value;
}
internal static ulong RealInputEventCount
{
get;
set;
}
private static Point actualLastPos;
private static int myLastX;
private static int myLastY;
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Dotnet port with style preservation")]
internal static void MouseEvent(MOUSEDATA e, int dx, int dy)
{
try
{
PaintCount = 0;
bool switchByMouseEnabled = IsSwitchingByMouseEnabled();
if (switchByMouseEnabled && Sk != null && (DesMachineID == MachineID || !Setting.Values.MoveMouseRelatively) && e.dwFlags == WM_MOUSEMOVE)
{
Point p = MoveToMyNeighbourIfNeeded(e.X, e.Y, desMachineID);
if (!p.IsEmpty)
{
HasSwitchedMachineSinceLastCopy = true;
Common.LogDebug(string.Format(
CultureInfo.CurrentCulture,
"***** Host Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})",
newDesMachineIdEx,
e.X,
e.Y));
myLastX = e.X;
myLastY = e.Y;
PrepareToSwitchToMachine(newDesMachineIdEx, p);
}
}
if (desMachineID != MachineID && SwitchLocation.Count <= 0)
{
MousePackage.Des = desMachineID;
MousePackage.Type = PackageType.Mouse;
MousePackage.Md.dwFlags = e.dwFlags;
MousePackage.Md.WheelDelta = e.WheelDelta;
// Relative move
if (Setting.Values.MoveMouseRelatively && Math.Abs(dx) >= MOVE_MOUSE_RELATIVE && Math.Abs(dy) >= MOVE_MOUSE_RELATIVE)
{
MousePackage.Md.X = dx;
MousePackage.Md.Y = dy;
}
else
{
MousePackage.Md.X = (e.X - primaryScreenBounds.Left) * 65535 / screenWidth;
MousePackage.Md.Y = (e.Y - primaryScreenBounds.Top) * 65535 / screenHeight;
}
SkSend(MousePackage, null, false);
if (MousePackage.Md.dwFlags is WM_LBUTTONUP or WM_RBUTTONUP)
{
Thread.Sleep(10);
}
NativeMethods.GetCursorPos(ref actualLastPos);
if (actualLastPos != Common.LastPos)
{
Common.LogDebug($"Mouse cursor has moved unexpectedly: Expected: {Common.LastPos}, actual: {actualLastPos}.");
Common.LastPos = actualLastPos;
}
}
#if SHOW_ON_WINLOGON_EX
if (RunOnLogonDesktop && e.dwFlags == WM_RBUTTONUP &&
desMachineID == machineID &&
e.x > 2 && e.x < 100 && e.y > 2 && e.y < 20)
{
DoSomethingInUIThread(delegate()
{
MainForm.HideMenuWhenRunOnLogonDesktop();
MainForm.MainMenu.Hide();
MainForm.MainMenu.Show(e.x - 5, e.y - 3);
});
}
#endif
}
catch (Exception ex)
{
Log(ex);
}
}
private static bool IsSwitchingByMouseEnabled()
{
return (EasyMouseOption)Setting.Values.EasyMouse == EasyMouseOption.Enable || InputHook.EasyMouseKeyDown;
}
internal static void PrepareToSwitchToMachine(ID newDesMachineID, Point desMachineXY)
{
LogDebug($"PrepareToSwitchToMachine: newDesMachineID = {newDesMachineID}, desMachineXY = {desMachineXY}");
if (((GetTick() - lastJump < 100) && (GetTick() - lastJump > 0)) || desMachineID == ID.ALL)
{
LogDebug("PrepareToSwitchToMachine: lastJump");
return;
}
lastJump = GetTick();
string newDesMachineName = NameFromID(newDesMachineID);
if (!IsConnectedTo(newDesMachineID))
{// Connection lost, cancel switching
LogDebug("No active connection found for " + newDesMachineName);
// ShowToolTip("No active connection found for [" + newDesMachineName + "]!", 500);
}
else
{
Common.newDesMachineID = newDesMachineID;
SwitchLocation.X = desMachineXY.X;
SwitchLocation.Y = desMachineXY.Y;
SwitchLocation.ResetCount();
_ = EvSwitch.Set();
// PostMessage(mainForm.Handle, WM_SWITCH, IntPtr.Zero, IntPtr.Zero);
if (newDesMachineID != DragMachine)
{
if (!IsDragging && !IsDropping)
{
if (MouseDown && !RunOnLogonDesktop && !RunOnScrSaverDesktop)
{
DragDropStep02();
}
}
else if (DragMachine != (ID)1)
{
ChangeDropMachine();
}
}
else
{
DragDropStep11();
}
// Change des machine
if (desMachineID != newDesMachineID)
{
LogDebug("MouseEvent: Switching to new machine:" + newDesMachineName);
// Ask current machine to hide the Mouse cursor
if (newDesMachineID != ID.ALL && desMachineID != MachineID)
{
SendPackage(desMachineID, PackageType.HideMouse);
}
DesMachineID = newDesMachineID;
if (desMachineID == MachineID)
{
if (GetTick() - clipboardCopiedTime < BIG_CLIPBOARD_DATA_TIMEOUT)
{
clipboardCopiedTime = 0;
Common.GetRemoteClipboard("PrepareToSwitchToMachine");
}
}
else
{
// Ask the new active machine to get clipboard data (if the data is too big)
SendPackage(desMachineID, PackageType.MachineSwitched);
}
_ = Interlocked.Increment(ref switchCount);
}
}
}
internal static void SaveSwitchCount()
{
if (SwitchCount > 0)
{
_ = Task.Run(() =>
{
Setting.Values.SwitchCount += SwitchCount;
_ = Interlocked.Exchange(ref switchCount, 0);
});
}
}
internal static void KeybdEvent(KEYBDDATA e)
{
try
{
PaintCount = 0;
if (desMachineID != newDesMachineID)
{
LogDebug("KeybdEvent: Switching to new machine...");
DesMachineID = newDesMachineID;
}
if (desMachineID != MachineID)
{
KeybdPackage.Des = desMachineID;
KeybdPackage.Type = PackageType.Keyboard;
KeybdPackage.Kd = e;
KeybdPackage.DateTime = GetTick();
SkSend(KeybdPackage, null, false);
if (KeybdPackage.Kd.dwFlags is WM_KEYUP or WM_SYSKEYUP)
{
Thread.Sleep(10);
}
}
}
catch (Exception ex)
{
Log(ex);
}
}
}
}

View File

@@ -0,0 +1,494 @@
// 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.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Windows.Forms;
// <summary>
// Some other helper methods.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using Microsoft.Win32;
using MouseWithoutBorders.Class;
using static System.Windows.Forms.Control;
namespace MouseWithoutBorders
{
internal partial class Common
{
internal const string HELPER_FORM_TEXT = "Mouse without Borders Helper";
internal const string HelperProcessName = "PowerToys.MouseWithoutBordersHelper";
private static bool signalHelperToExit;
private static bool signalWatchDogToExit;
internal static long WndProcCounter;
private static void WatchDogThread()
{
long oldCounter = WndProcCounter;
do
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
if (signalWatchDogToExit)
{
break;
}
}
while (BlockingUI)
{
Thread.Sleep(1000);
}
if (WndProcCounter == oldCounter)
{
Process p = Process.GetCurrentProcess();
string procInfo = $"{p.PrivateMemorySize64 / 1024 / 1024}MB, {p.TotalProcessorTime}, {Environment.ProcessorCount}.";
string threadStacks = $"{procInfo} {Thread.DumpThreadsStack()}";
Common.TelemetryLogTrace(threadStacks, SeverityLevel.Error);
break;
}
oldCounter = WndProcCounter;
}
while (true);
}
private static void HelperThread()
{
try
{
while (true)
{
_ = EvSwitch.WaitOne(); // Switching to another machine?
if (signalHelperToExit)
{
break;
}
if (Common.NewDesMachineID != Common.MachineID && Common.NewDesMachineID != ID.ALL)
{
HideMouseCursor(false);
Common.MainFormDotEx(true);
}
else
{
if (Common.SwitchLocation.Count > 0)
{
Common.SwitchLocation.Count--;
// When we want to move mouse by pixels, we add 300k to x and y (search for XY_BY_PIXEL for other related code).
Common.LogDebug($"+++++ Moving mouse to {Common.SwitchLocation.X}, {Common.SwitchLocation.Y}");
// MaxXY = 65535 so 100k is safe.
if (Common.SwitchLocation.X > XY_BY_PIXEL - 100000 || Common.SwitchLocation.Y > XY_BY_PIXEL - 100000)
{
InputSimulation.MoveMouse(Common.SwitchLocation.X - XY_BY_PIXEL, Common.SwitchLocation.Y - XY_BY_PIXEL);
}
else
{
InputSimulation.MoveMouseEx(Common.SwitchLocation.X, Common.SwitchLocation.Y);
}
Common.MainFormDot();
}
}
if (Common.NewDesMachineID == Common.MachineID)
{
ReleaseAllKeys();
}
}
}
catch (Exception e)
{
Log(e);
}
signalHelperToExit = false;
LogDebug("^^^Helper Thread exiting...^^^");
}
internal static void MainFormDotEx(bool bCheckTS)
{
LogDebug("***** MainFormDotEx:");
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
int left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 1;
int top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2);
Common.MainFormVisible = true;
if (Setting.Values.HideMouse && Setting.Values.StealFocusWhenSwitchingMachine && Common.SendMessageToHelper(0x407, new IntPtr(left), new IntPtr(top), true) == 0)
{
try
{
/* When user just switches to the Logon desktop, user is actually on the "Windows Default Lock Screen" (LockApp).
* If a click is sent to this during switch, it actually triggers a desktop switch on the local machine causing a reconnection affecting the machine switch.
* We can detect and skip in this case.
* */
IntPtr foreGroundWindow = NativeMethods.GetForegroundWindow();
string foreGroundWindowText = GetText(foreGroundWindow);
bool mouseClick = true;
if (foreGroundWindowText.Equals("Windows Default Lock Screen", StringComparison.OrdinalIgnoreCase))
{
mouseClick = false;
}
// Window title may be localized, check process name:
if (mouseClick)
{
_ = NativeMethods.GetWindowThreadProcessId(foreGroundWindow, out uint pid);
if (pid > 0)
{
string foreGroundWindowProcess = Process.GetProcessById((int)pid)?.ProcessName;
if (foreGroundWindowProcess.Equals("LockApp", StringComparison.OrdinalIgnoreCase))
{
mouseClick = false;
}
}
}
if (mouseClick)
{
InputSimulation.MouseClickDotForm(left + 1, top + 1);
}
}
catch (Exception e)
{
Log(e);
}
}
}
CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue);
}
internal static void MainForm3Pixels()
{
LogDebug("***** MainFormDotLarge:");
DoSomethingInUIThread(
() =>
{
MainForm.Left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 2;
MainForm.Top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2) - 1;
MainForm.Width = 3;
MainForm.Height = 3;
MainForm.Opacity = 0.11D;
MainForm.TopMost = true;
if (Setting.Values.HideMouse)
{
MainForm.BackColor = Color.Black;
MainForm.Show();
Common.MainFormVisible = true;
}
else
{
MainForm.BackColor = Color.White;
MainForm.Hide();
Common.MainFormVisible = false;
}
},
true);
CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue);
}
internal static void MainFormDot()
{
DoSomethingInUIThread(
() =>
{
_ = Common.SendMessageToHelper(0x408, IntPtr.Zero, IntPtr.Zero, false);
MainForm.Left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 1;
MainForm.Top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2);
MainForm.Width = 1;
MainForm.Height = 1;
MainForm.Opacity = 0.15;
MainForm.Hide();
Common.MainFormVisible = false;
},
true);
CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue);
}
internal static void ToggleIcon()
{
try
{
if (toggleIconsIndex < TOGGLE_ICONS_SIZE)
{
Common.DoSomethingInUIThread(() => Common.MainForm.ChangeIcon(toggleIcons[toggleIconsIndex++]));
}
else
{
toggleIconsIndex = 0;
toggleIcons = null;
}
}
catch (Exception e)
{
Log(e);
}
}
internal static void RunDDHelper(bool cleanUp = false)
{
if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop)
{
return;
}
if (cleanUp)
{
try
{
Process[] ps = Process.GetProcessesByName(HelperProcessName);
foreach (Process p in ps)
{
p.KillProcess();
}
}
catch (Exception e)
{
Log(e);
_ = Common.SendMessageToHelper(SharedConst.QUIT_CMD, IntPtr.Zero, IntPtr.Zero);
}
return;
}
if (!Common.IsMyDesktopActive())
{
return;
}
if (!Common.IpcChannelCreated)
{
TelemetryLogTrace($"{nameof(Common.IpcChannelCreated)} = {Common.IpcChannelCreated}. {GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
return;
}
if (!MainForm.IsDisposed)
{
MainForm.NotifyIcon.Visible = false;
MainForm.NotifyIcon.Visible = Setting.Values.ShowOriginalUI;
}
IntPtr h = (IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT);
if (h.ToInt32() <= 0)
{
_ = Common.CreateProcessInInputDesktopSession(
$"\"{Path.GetDirectoryName(Application.ExecutablePath)}\\{HelperProcessName}.exe\"",
string.Empty,
Common.GetInputDesktop(),
0);
HasSwitchedMachineSinceLastCopy = true;
// Common.CreateLowIntegrityProcess("\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", string.Empty, 0, false, 0);
if (Process.GetProcessesByName(HelperProcessName)?.Any() != true)
{
Log("Unable to start helper process.");
Common.ShowToolTip("Error starting Mouse Without Borders Helper, clipboard sharing will not work!", 5000, ToolTipIcon.Error);
}
else
{
Log("Helper process started.");
}
}
else
{
if (Process.GetProcessesByName(HelperProcessName)?.Any() == true)
{
Log("Helper process found running.");
}
else
{
Log("Invalid helper process found running.");
Common.ShowToolTip("Error finding Mouse Without Borders Helper, clipboard sharing will not work!", 5000, ToolTipIcon.Error);
}
}
}
internal static int SendMessageToHelper(int msg, IntPtr wparam, IntPtr lparam, bool wait = true, bool log = true)
{
int h = NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT);
int rv = -1;
if (h > 0)
{
rv = wait
? (int)NativeMethods.SendMessage((IntPtr)h, msg, wparam, lparam)
: NativeMethods.PostMessage((IntPtr)h, msg, wparam, lparam) ? 1 : 0;
}
if (log)
{
Common.LogDebug($"SendMessageToHelper: HelperWindow={h}, Return={rv}, msg={msg}, w={wparam.ToInt32()}, l={lparam.ToInt32()}, Post={!wait}");
}
return rv;
}
internal static bool IsWindows8AndUp()
{
return (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2)
|| Environment.OSVersion.Version.Major > 6;
}
internal static string GetMiniLog(IEnumerable<ControlCollection> optionControls)
{
string log = string.Empty;
log += "=============================================================================================================================\r\n";
log += $"{Application.ProductName} version {Application.ProductVersion}\r\n";
log += $"{Setting.Values.Username}/{GetDebugInfo(MyKey)}\r\n";
log += $"{MachineName}/{MachineID}/{DesMachineID}\r\n";
log += $"Id: {Setting.Values.DeviceId}\r\n";
log += $"Matrix: {string.Join(",", MachineMatrix)}\r\n";
log += $"McPool: {Setting.Values.MachinePoolString}\r\n";
log += "\r\nOPTIONS:\r\n";
foreach (ControlCollection controlCollection in optionControls)
{
foreach (object c in controlCollection)
{
if (c is CheckBox checkBox)
{
log += $"({(checkBox.Checked ? 1 : 0)}) {checkBox.Text}\r\n";
continue;
}
if (c is RadioButton radioButton)
{
log += $"({(radioButton.Checked ? 1 : 0)}) {radioButton.Name}.[{radioButton.Text}]\r\n";
continue;
}
if (c is ComboBox comboBox)
{
log += $"{comboBox.Name} = {comboBox.Text}\r\n";
continue;
}
}
}
log += "\r\n";
SocketStuff sk = Sk;
if (sk?.TcpSockets != null)
{
foreach (TcpSk tcp in sk.TcpSockets)
{
log += $"{Common.MachineName}{(tcp.IsClient ? "=>" : "<=")}{tcp.MachineName}({tcp.MachineId}):{tcp.Status}\r\n";
}
}
log += string.Format(CultureInfo.CurrentCulture, "Helper:{0}\r\n", SendMessageToHelper(0x400, IntPtr.Zero, IntPtr.Zero));
log += Setting.Values.LastPersonalizeLogonScr + "\r\n";
log += "Name2IP =\r\n" + Setting.Values.Name2IP + "\r\n";
log += "Last 10 trace messages:\r\n";
log += string.Join(Environment.NewLine, LogCounter.Select(item => $"({item.Value}): {item.Key}").Take(10));
log += "\r\n=============================================================================================================================";
return log;
}
internal static bool GetUserName()
{
if (string.IsNullOrEmpty(Setting.Values.Username) && !Common.RunOnLogonDesktop)
{
if (Program.User.ToLower(CultureInfo.CurrentCulture).Contains("system"))
{
_ = Common.ImpersonateLoggedOnUserAndDoSomething(() =>
{
Setting.Values.Username = WindowsIdentity.GetCurrent(true).Name;
});
}
else
{
Setting.Values.Username = Program.User;
}
Common.LogDebug("[Username] = " + Setting.Values.Username);
}
return !string.IsNullOrEmpty(Setting.Values.Username);
}
internal static void ShowOneWayModeMessage()
{
ToggleShowTopMostMessage(
@"
Due to Security Controls, a remote device cannot control a SAW device.
Please use the keyboard and Mouse from the SAW device.
(Press Esc to hide this message)
",
string.Empty,
10);
}
internal static void ApplyCADSetting()
{
try
{
if (Setting.Values.DisableCAD)
{
RegistryKey k = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System");
if (k != null)
{
k.SetValue("DisableCAD", 1, RegistryValueKind.DWord);
k.Close();
}
}
else
{
RegistryKey k = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System");
if (k != null)
{
k.SetValue("DisableCAD", 0, RegistryValueKind.DWord);
k.Close();
}
}
}
catch (Exception e)
{
Log(e);
}
}
}
}

View File

@@ -0,0 +1,265 @@
// 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;
// <summary>
// Initialization and clean up.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using Microsoft.Win32;
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Form;
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();
_ = Common.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();
}
Common.MachinePool.Initialize(info);
Common.MachinePool.ResetIPAddressesForDeadMachines(true);
}
catch (Exception ex)
{
Common.Log(ex);
Common.MachinePool.Clear();
}
}
internal static void SetupMachineNameAndID()
{
try
{
GetMachineName();
DesMachineID = NewDesMachineID = MachineID;
// MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test
InitializeMachinePoolFromSettings();
Common.MachineName = Common.MachineName.Trim();
_ = Common.MachinePool.LearnMachine(Common.MachineName);
_ = Common.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
Common.UpdateMachinePoolStringSetting();
}
catch (Exception e)
{
Log(e);
}
}
internal static void Init()
{
_ = Common.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();
Common.Log(e.Message);
}
catch (CryptographicException e)
{
Common.KeyCorrupted = true;
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Common.Log(e.Message);
}
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)
{
Common.WndProcCounter++;
if (e.Mode is PowerModes.Resume or PowerModes.Suspend)
{
Common.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information);
LastResumeSuspendTime = DateTime.UtcNow;
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(HelperThread), "Helper Thread");
helper.SetApartmentState(ApartmentState.STA);
helper.Start();
}
private static void AskHelperThreadsToExit(int waitTime)
{
signalHelperToExit = true;
signalWatchDogToExit = true;
_ = EvSwitch.Set();
int c = 0;
if (helper != null && c < waitTime)
{
while (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)
{
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,
};
LogDebug("***** ReleaseAllKeys has been called! *****:");
foreach (VK vk in keys)
{
if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0)
{
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)
{
LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture));
Common.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;
}
}
}
}

View File

@@ -0,0 +1,307 @@
// 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.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Principal;
// <summary>
// Impersonation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
namespace MouseWithoutBorders
{
internal partial class Common
{
internal static bool RunElevated()
{
return WindowsIdentity.GetCurrent().Owner.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
}
internal static bool ImpersonateLoggedOnUserAndDoSomething(Action targetFunc)
{
if (Common.RunWithNoAdminRight)
{
targetFunc();
return true;
}
else
{
uint dwSessionId;
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero;
try
{
dwSessionId = (uint)Process.GetCurrentProcess().SessionId;
uint rv = NativeMethods.WTSQueryUserToken(dwSessionId, ref hUserToken);
LogDebug("WTSQueryUserToken returned " + rv.ToString(CultureInfo.CurrentCulture));
if (rv == 0)
{
LogDebug($"WTSQueryUserToken failed with: {Marshal.GetLastWin32Error()}.");
return false;
}
if (!NativeMethods.DuplicateToken(hUserToken, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref hUserTokenDup))
{
TelemetryLogTrace($"DuplicateToken Failed! {GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return false;
}
if (NativeMethods.ImpersonateLoggedOnUser(hUserTokenDup))
{
targetFunc();
_ = NativeMethods.RevertToSelf();
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return true;
}
else
{
Log("ImpersonateLoggedOnUser Failed!");
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return false;
}
}
catch (Exception e)
{
Common.Log(e);
return false;
}
}
}
[SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")]
internal static int CreateProcessInInputDesktopSession(string commandLine, string arg, string desktop, short wShowWindow, bool lowIntegrity = false)
// As user who runs explorer.exe
{
if (!Program.User.ToLower(CultureInfo.InvariantCulture).Contains("system"))
{
ProcessStartInfo s = new(commandLine, arg);
s.WindowStyle = wShowWindow != 0 ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden;
Process p = Process.Start(s);
return p == null ? 0 : p.Id;
}
string commandLineWithArg = commandLine + " " + arg;
int lastError;
int dwSessionId;
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero;
Common.LogDebug("CreateProcessInInputDesktopSession called, launching " + commandLineWithArg + " on " + desktop);
try
{
dwSessionId = Process.GetCurrentProcess().SessionId;
// Get the user token used by DuplicateTokenEx
lastError = (int)NativeMethods.WTSQueryUserToken((uint)dwSessionId, ref hUserToken);
NativeMethods.STARTUPINFO si = default;
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\" + desktop;
si.wShowWindow = wShowWindow;
NativeMethods.SECURITY_ATTRIBUTES sa = default;
sa.Length = Marshal.SizeOf(sa);
if (!NativeMethods.DuplicateTokenEx(hUserToken, NativeMethods.MAXIMUM_ALLOWED, ref sa, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)NativeMethods.TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
{
lastError = Marshal.GetLastWin32Error();
Common.Log(string.Format(CultureInfo.CurrentCulture, "DuplicateTokenEx error: {0} Token does not have the privilege.", lastError));
_ = NativeMethods.CloseHandle(hUserToken);
return 0;
}
if (lowIntegrity)
{
NativeMethods.TOKEN_MANDATORY_LABEL tIL;
// Low
string sIntegritySid = "S-1-16-4096";
bool rv = NativeMethods.ConvertStringSidToSid(sIntegritySid, out IntPtr pIntegritySid);
if (!rv)
{
Log("ConvertStringSidToSid failed");
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return 0;
}
tIL.Label.Attributes = NativeMethods.SE_GROUP_INTEGRITY;
tIL.Label.Sid = pIntegritySid;
rv = NativeMethods.SetTokenInformation(hUserTokenDup, NativeMethods.TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, ref tIL, (uint)Marshal.SizeOf(tIL) + (uint)IntPtr.Size);
if (!rv)
{
Log("SetTokenInformation failed");
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return 0;
}
}
uint dwCreationFlags = NativeMethods.NORMAL_PRIORITY_CLASS | NativeMethods.CREATE_NEW_CONSOLE;
IntPtr pEnv = IntPtr.Zero;
if (NativeMethods.CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
{
dwCreationFlags |= NativeMethods.CREATE_UNICODE_ENVIRONMENT;
}
else
{
pEnv = IntPtr.Zero;
}
_ = NativeMethods.CreateProcessAsUser(
hUserTokenDup, // client's access token
null, // file to execute
commandLineWithArg, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
(int)dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
null, // name of current directory
ref si, // pointer to STARTUPINFO structure
out NativeMethods.PROCESS_INFORMATION pi); // receives information about new process
// GetLastError should be 0
int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
LogDebug("CreateProcessAsUser returned " + iResultOfCreateProcessAsUser.ToString(CultureInfo.CurrentCulture));
// Close handles task
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return (iResultOfCreateProcessAsUser == 0) ? (int)pi.dwProcessId : 0;
}
catch (Exception e)
{
Common.Log(e);
return 0;
}
}
#if CUSTOMIZE_LOGON_SCREEN
internal static bool CreateLowIntegrityProcess(string commandLine, string args, int wait, bool killIfTimedOut, long limitedMem, short wShowWindow = 0)
{
int processId = CreateProcessInInputDesktopSession(commandLine, args, "default", wShowWindow, true);
if (processId <= 0)
{
return false;
}
if (wait > 0)
{
if (limitedMem > 0)
{
int sec = 0;
while (true)
{
Process p;
try
{
if ((p = Process.GetProcessById(processId)) == null)
{
Log("Process exited!");
break;
}
}
catch (ArgumentException)
{
Log("GetProcessById.ArgumentException");
break;
}
if ((!p.HasExited && p.PrivateMemorySize64 > limitedMem) || (++sec > (wait / 1000)))
{
Log(string.Format(CultureInfo.CurrentCulture, "Process log (mem): {0}, {1}", sec, p.PrivateMemorySize64));
return false;
}
Thread.Sleep(1000);
}
}
else
{
Process p;
if ((p = Process.GetProcessById(processId)) == null)
{
Log("Process exited!");
}
else if (NativeMethods.WaitForSingleObject(p.Handle, wait) != NativeMethods.WAIT_OBJECT_0 && killIfTimedOut)
{
Log("Process log (time).");
TerminateProcessTree(p.Handle, (uint)processId, -1);
return false;
}
}
}
return true;
}
internal static void TerminateProcessTree(IntPtr hProcess, uint processID, int exitCode)
{
if (processID > 0 && hProcess.ToInt32() > 0)
{
Process[] processes = Process.GetProcesses();
int dwSessionId = Process.GetCurrentProcess().SessionId;
foreach (Process p in processes)
{
if (p.SessionId == dwSessionId)
{
NativeMethods.PROCESS_BASIC_INFORMATION processBasicInformation = default;
try
{
if (NativeMethods.NtQueryInformationProcess(p.Handle, 0, ref processBasicInformation, (uint)Marshal.SizeOf(processBasicInformation), out uint bytesWritten) >= 0)
{// NT_SUCCESS(...)
if (processBasicInformation.InheritedFromUniqueProcessId == processID)
{
TerminateProcessTree(p.Handle, processBasicInformation.UniqueProcessId, exitCode);
}
}
}
catch (InvalidOperationException e)
{
Log(e);
continue;
}
catch (Win32Exception e)
{
Log(e);
continue;
}
}
}
_ = NativeMethods.TerminateProcess(hProcess, (IntPtr)exitCode);
}
}
#endif
}
}

View File

@@ -0,0 +1,509 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
// <summary>
// Logging.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Exceptions;
namespace MouseWithoutBorders
{
public class Thread
{
private static readonly object ThreadsLock = new();
private static List<System.Threading.Thread> threads;
private readonly System.Threading.Thread thread;
internal Thread(ThreadStart callback, string name)
{
UpdateThreads(thread = new System.Threading.Thread(callback) { Name = name });
}
internal Thread(ParameterizedThreadStart callback, string name)
{
UpdateThreads(thread = new System.Threading.Thread(callback) { Name = name });
}
internal static void UpdateThreads(System.Threading.Thread thread)
{
lock (ThreadsLock)
{
bool found = false;
List<System.Threading.Thread> toBeRemovedThreads = new();
threads ??= new List<System.Threading.Thread>();
foreach (System.Threading.Thread t in threads)
{
if (!t.IsAlive)
{
toBeRemovedThreads.Add(t);
}
else if (t.ManagedThreadId == thread.ManagedThreadId)
{
found = true;
}
}
foreach (System.Threading.Thread t in toBeRemovedThreads)
{
_ = threads.Remove(t);
}
if (!found)
{
threads.Add(thread);
}
}
}
internal static string DumpThreadsStack()
{
string stack = "\r\nMANAGED THREADS: " + threads.Count.ToString(CultureInfo.InvariantCulture) + "\r\n";
stack += Common.GetStackTrace(new StackTrace());
return stack;
}
internal static void SuspendAllThreadsBut(int threadId)
{
lock (ThreadsLock)
{
#pragma warning disable 618 // Temporary
threads.Where(t => t.IsAlive && t.ManagedThreadId != threadId).ToList().ForEach(t => t.Suspend());
#pragma warning restore 618
}
}
internal void SetApartmentState(ApartmentState apartmentState)
{
thread.SetApartmentState(apartmentState);
}
internal void Start()
{
thread.Start();
}
internal void Start(object parameter)
{
thread.Start(parameter);
}
internal static void Sleep(int millisecondsTimeout)
{
System.Threading.Thread.Sleep(millisecondsTimeout);
}
internal static System.Threading.Thread CurrentThread => System.Threading.Thread.CurrentThread;
internal ThreadPriority Priority
{
get => thread.Priority;
set => thread.Priority = value;
}
internal System.Threading.ThreadState ThreadState => thread.ThreadState;
}
internal partial class Common
{
private static readonly string[] AllLogs = new string[MAX_LOG];
private static readonly object AllLogsLock = new();
private static readonly ConcurrentDictionary<string, int> LogCounter = new();
private static readonly int[] RepeatedLogIndexSelection = new[] { 1, 3, 10, 50, 100 };
private const int MAX_LOG = 10000;
private static int allLogsIndex;
private const int MaxLogExceptionPerHour = 1000;
private static int lastHour;
private static int exceptionCount;
internal static void TelemetryLogTrace(string log, SeverityLevel severityLevel, bool flush = false)
{
int logCount = LogCounter.AddOrUpdate(log, 1, (key, value) => value + 1);
Common.Log(log);
}
internal static void Log(Exception e)
{
if (e is not KnownException)
{
string exText = e.ToString();
Log($"!Exception!: {exText}");
if (DateTime.UtcNow.Hour != lastHour)
{
lastHour = DateTime.UtcNow.Hour;
exceptionCount = 0;
}
if (exceptionCount < MaxLogExceptionPerHour)
{
exceptionCount++;
}
else if (exceptionCount != short.MaxValue)
{
exceptionCount = short.MaxValue;
}
}
}
private const string HeaderSENT =
"Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13}";
private const string HeaderRECEIVED =
"Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15}";
internal static void LogDebug(string log, bool clearLog = false)
{
#if DEBUG
Log(log, clearLog);
#endif
}
internal static void Log(string log, bool clearLog = false)
{
log = DateTime.Now.ToString("MM/dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + $"({Thread.CurrentThread.ManagedThreadId})" + log;
ManagedCommon.Logger.LogInfo(log);
lock (AllLogsLock)
{
if (clearLog)
{
allLogsIndex = 0;
}
AllLogs[allLogsIndex] = log;
allLogsIndex = (allLogsIndex + 1) % MAX_LOG;
}
}
internal static void LogDebug(string format, params object[] args)
{
#if DEBUG
Log(format, args);
#endif
}
internal static void Log(string format, params object[] args)
{
Common.Log(string.Format(CultureInfo.InvariantCulture, format, args));
}
private static PackageMonitor p1;
private static PackageMonitor p2;
[Conditional("DEBUG")]
internal static void LogAll()
{
string log;
if (!p1.Equals(PackageSent))
{
log = string.Format(
CultureInfo.CurrentCulture,
"SENT:" + HeaderSENT,
PackageSent.Heartbeat,
PackageSent.Keyboard,
PackageSent.Mouse,
PackageSent.Hello,
PackageSent.Matrix,
PackageSent.ClipboardText,
PackageSent.ClipboardImage,
PackageSent.ByeBye,
PackageSent.Clipboard,
PackageSent.ClipboardDragDrop,
PackageSent.ClipboardDragDropEnd,
PackageSent.ExplorerDragDrop,
inputEventCount,
PackageSent.Nil);
Log(log);
p1 = PackageSent; // Copy data
}
if (!p2.Equals(PackageReceived))
{
log = string.Format(
CultureInfo.CurrentCulture,
"RECEIVED:" + HeaderRECEIVED,
PackageReceived.Heartbeat,
PackageReceived.Keyboard,
PackageReceived.Mouse,
PackageReceived.Hello,
PackageReceived.Matrix,
PackageReceived.ClipboardText,
PackageReceived.ClipboardImage,
PackageReceived.ByeBye,
PackageReceived.Clipboard,
PackageReceived.ClipboardDragDrop,
PackageReceived.ClipboardDragDropEnd,
PackageReceived.ExplorerDragDrop,
invalidPackageCount,
PackageReceived.Nil,
processedPackageCount,
skippedPackageCount);
Log(log);
p2 = PackageReceived;
}
}
internal static void GenerateLog()
{
int l = Setting.Values.DumpObjectsLevel;
if (l is > 0 and < 10)
{
Common.DumpObjects(l);
}
}
private static List<ProcessThread> myThreads;
internal static void DumpObjects(int level)
{
try
{
string logFile = Path.Combine(Common.RunWithNoAdminRight ? Path.GetTempPath() : Path.GetDirectoryName(Application.ExecutablePath), "MagicMouse.log");
StringBuilder sb = new(1000000);
string log;
myThreads = new List<ProcessThread>();
foreach (ProcessThread t in Process.GetCurrentProcess().Threads)
{
myThreads.Add(t);
}
_ = PrivateDump(sb, AllLogs, "[Program logs]\r\n===============\r\n", 0, level, false);
_ = PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, level, false);
log = string.Format(
CultureInfo.CurrentCulture,
"{0} {1}\r\n{2}\r\n\r\n{3}",
Application.ProductName,
Application.ProductVersion,
"Private Mem: " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024).ToString(CultureInfo.CurrentCulture) + "KB",
sb.ToString());
if (!string.IsNullOrEmpty(myKey))
{
log = log.Replace(MyKey, GetDebugInfo(MyKey));
}
log += Thread.DumpThreadsStack();
log += $"\r\nCurrent process session: {Process.GetCurrentProcess().SessionId}, active console session: {NativeMethods.WTSGetActiveConsoleSessionId()}.";
File.WriteAllText(logFile, log);
if (RunOnLogonDesktop || RunOnScrSaverDesktop)
{
_ = MessageBox.Show("Dump file created: " + logFile, Application.ProductName);
}
else
{
ShowToolTip("Dump file created: " + logFile + " and placed in the Clipboard.", 10000);
Clipboard.SetText(logFile);
}
}
catch (Exception e)
{
_ = MessageBox.Show(e.Message + "\r\n" + e.StackTrace, Application.ProductName);
}
}
private static object GetFieldValue(object obj, string fieldName)
{
FieldInfo fi;
Type t;
t = obj.GetType();
fi = t.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
return fi?.GetValue(obj);
}
private static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool stop)
{
Type t;
string padStr = string.Empty;
string[] strArr;
string objString;
if (obj == null || (maxLevel >= 0 && level >= maxLevel) || obj is Cursor)
{
return false;
}
for (int i = 0; i < level; i++)
{
padStr += i < level - 1 ? "-" : padStr += string.Empty;
}
objString = obj.ToString();
t = obj.GetType();
strArr = new string[7];
strArr[0] = padStr;
strArr[1] = objName;
// strArr[2] = " ";
// strArr[3] = t.FullName;
strArr[4] = " = ";
strArr[5] = objName.Equals("myKey", StringComparison.OrdinalIgnoreCase) ? GetDebugInfo(objString)
: objName.Equals("lastClipboardObject", StringComparison.OrdinalIgnoreCase) ? string.Empty
: objString.Replace("System.Windows.Forms.", string.Empty).Replace("System.Net.Sockets.", string.Empty).Replace("System.Security.Cryptography.", string.Empty).Replace("System.Threading.", string.Empty)
.Replace("System.ComponentModel.", string.Empty).Replace("System.Runtime.", string.Empty).Replace("System.Drawing.", string.Empty).Replace("System.Object", "O").Replace("System.Diagnostics.", string.Empty)
.Replace("System.Collections.", string.Empty).Replace("System.Drawing.", string.Empty).Replace("System.Int", string.Empty).Replace("System.EventHandler.", string.Empty);
strArr[6] = "\r\n";
_ = sb.Append(string.Concat(strArr).Replace(Common.BinaryName, "MM"));
if (stop || t.IsPrimitive)
{
return false;
}
DumpType(padStr, sb, obj, level, t, maxLevel);
return true;
}
private static void DumpType(string initialStr, StringBuilder sb, object obj, int level, System.Type t, int maxLevel)
{
int i;
bool stop;
if (t == typeof(System.Delegate))
{
return;
}
FieldInfo[] fi;
string type;
if (obj is MouseWithoutBorders.PackageType or string or AddressFamily or ID or IPAddress
)
{
return;
}
type = obj.GetType().ToString();
if (type.EndsWith("type", StringComparison.CurrentCultureIgnoreCase) || type.Contains("Cryptography")
|| type.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase))
{
return;
}
stop = obj == null || obj is MouseWithoutBorders.DATA || obj.GetType().BaseType == typeof(ValueType)
|| obj.GetType().Namespace.Contains("System.Windows");
fi = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (FieldInfo f in fi)
{
if (f.GetValue(obj) != AllLogs)
{
_ = PrivateDump(sb, f.GetValue(obj), f.Name, level + 1, maxLevel, stop);
}
}
if (obj is Dictionary<string, List<IPAddress>>)
{
Dictionary<string, List<IPAddress>> d = obj as Dictionary<string, List<IPAddress>>;
foreach (string k in d.Keys)
{
if (d.TryGetValue(k, out List<IPAddress> l))
{
foreach (IPAddress ip in l)
{
_ = PrivateDump(sb, ip, "[" + k + "]", level + 1, maxLevel, false);
}
}
}
}
if (obj is System.Array)
{
try
{
if (obj is MachineInf[])
{
MachineInf[] os = (MachineInf[])obj;
for (i = 0; i < os.GetLength(0); i++)
{
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
}
}
else if (obj is int[] || obj is uint[])
{
int[] os = (int[])obj;
for (i = 0; i < os.GetLength(0); i++)
{
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
}
}
else if (obj is short[] || obj is ushort[])
{
short[] os = (short[])obj;
for (i = 0; i < os.GetLength(0); i++)
{
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
}
}
else if (obj is TcpClient[] || obj is IPAddress[] || obj is TcpSk[] || obj is string[]
|| obj is TcpServer[]
|| obj is ProcessThread[] || obj is Thread[])
{
object[] os = (object[])obj;
for (i = 0; i < os.GetLength(0); i++)
{
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
}
}
else
{
_ = PrivateDump(sb, obj.GetType().ToString() + ": N/A", obj.GetType().ToString(), level + 1, maxLevel, false);
}
}
catch (Exception)
{
}
}
}
internal static string GetStackTrace(StackTrace st)
{
string rv = string.Empty;
for (int i = 0; i < st.FrameCount; i++)
{
StackFrame sf = st.GetFrame(i);
rv += sf.GetMethod() + " <= ";
}
return rv;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,262 @@
// 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.
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
// In X64, we are WOW
[module: SuppressMessage("Microsoft.Portability", "CA1900:ValueTypeFieldsShouldBePortable", Scope = "type", Target = "MouseWithoutBorders.DATA", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders
{
internal enum PackageType// : int
{
// Search for PACKAGE_TYPE_RELATED before changing these!
Invalid = 0xFF,
Error = 0xFE,
Hi = 2,
Hello = 3,
ByeBye = 4,
Heartbeat = 20,
Awake = 21,
HideMouse = 50,
Heartbeat_ex = 51,
Heartbeat_ex_l2 = 52,
Heartbeat_ex_l3 = 53,
Clipboard = 69,
ClipboardDragDrop = 70,
ClipboardDragDropEnd = 71,
ExplorerDragDrop = 72,
ClipboardCapture = 73,
CaptureScreenCommand = 74,
ClipboardDragDropOperation = 75,
ClipboardDataEnd = 76,
MachineSwitched = 77,
ClipboardAsk = 78,
ClipboardPush = 79,
NextMachine = 121,
Keyboard = 122,
Mouse = 123,
ClipboardText = 124,
ClipboardImage = 125,
Handshake = 126,
HandshakeAck = 127,
Matrix = 128,
MatrixSwapFlag = 2,
MatrixTwoRowFlag = 4,
}
internal struct PackageMonitor
{
internal ulong Keyboard;
internal ulong Mouse;
internal ulong Heartbeat;
internal ulong ByeBye;
internal ulong Hello;
internal ulong Matrix;
internal ulong ClipboardText;
internal ulong ClipboardImage;
internal ulong Clipboard;
internal ulong ClipboardDragDrop;
internal ulong ClipboardDragDropEnd;
internal ulong ClipboardAsk;
internal ulong ExplorerDragDrop;
internal ulong Nil;
internal PackageMonitor(ulong value)
{
ClipboardDragDrop = ClipboardDragDropEnd = ExplorerDragDrop =
Keyboard = Mouse = Heartbeat = ByeBye = Hello = Clipboard =
Matrix = ClipboardImage = ClipboardText = Nil = ClipboardAsk = value;
}
}
internal enum ID : uint
{
NONE = 0,
ALL = 255,
}
internal enum ClipboardPostAction : uint
{
Other = 0,
Desktop = 1,
Mspaint = 2,
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDDATA
{
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int wVk;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEDATA
{
internal int X;
internal int Y;
internal int WheelDelta;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int dwFlags;
}
// The beauty of "union" in C#
[StructLayout(LayoutKind.Explicit)]
internal class DATA
{
[FieldOffset(0)]
internal PackageType Type; // 4 (first byte = package type, 1 = checksum, 2+3 = magic no.)
[FieldOffset(sizeof(PackageType))]
internal int Id; // 4
[FieldOffset(sizeof(PackageType) + sizeof(uint))]
internal ID Src; // 4
[FieldOffset(sizeof(PackageType) + (2 * sizeof(uint)))]
internal ID Des; // 4
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal long DateTime;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)) + sizeof(long))]
internal KEYBDDATA Kd;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal MOUSEDATA Md;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal ID Machine1;
[FieldOffset(sizeof(PackageType) + (4 * sizeof(uint)))]
internal ID Machine2;
[FieldOffset(sizeof(PackageType) + (5 * sizeof(uint)))]
internal ID Machine3;
[FieldOffset(sizeof(PackageType) + (6 * sizeof(uint)))]
internal ID Machine4;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal ClipboardPostAction PostAction;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)))]
private long machineNameP1;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + sizeof(long))]
private long machineNameP2;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + (2 * sizeof(long)))]
private long machineNameP3;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + (3 * sizeof(long)))]
private long machineNameP4;
internal string MachineName
{
get
{
string name = Common.GetString(BitConverter.GetBytes(machineNameP1))
+ Common.GetString(BitConverter.GetBytes(machineNameP2))
+ Common.GetString(BitConverter.GetBytes(machineNameP3))
+ Common.GetString(BitConverter.GetBytes(machineNameP4));
return name.Trim();
}
set
{
byte[] machineName = Common.GetBytes(value.PadRight(32, ' '));
machineNameP1 = BitConverter.ToInt64(machineName, 0);
machineNameP2 = BitConverter.ToInt64(machineName, 8);
machineNameP3 = BitConverter.ToInt64(machineName, 16);
machineNameP4 = BitConverter.ToInt64(machineName, 24);
}
}
public DATA()
{
}
public DATA(byte[] initialData)
{
Bytes = initialData;
}
internal byte[] Bytes
{
get
{
byte[] buf = new byte[IsBigPackage ? Common.PACKAGE_SIZE_EX : Common.PACKAGE_SIZE];
Array.Copy(StructToBytes(this), buf, IsBigPackage ? Common.PACKAGE_SIZE_EX : Common.PACKAGE_SIZE);
return buf;
}
set
{
Debug.Assert(value.Length <= Common.PACKAGE_SIZE_EX, "Length > package size");
byte[] buf = new byte[Common.PACKAGE_SIZE_EX];
Array.Copy(value, buf, value.Length);
BytesToStruct(buf, this);
}
}
internal bool IsBigPackage
{
get => Type == 0
? throw new InvalidOperationException("Package type not set.")
: Type switch
{
PackageType.Hello or PackageType.Awake or PackageType.Heartbeat or PackageType.Heartbeat_ex or PackageType.Handshake or PackageType.HandshakeAck or PackageType.ClipboardPush or PackageType.Clipboard or PackageType.ClipboardAsk or PackageType.ClipboardImage or PackageType.ClipboardText or PackageType.ClipboardDataEnd => true,
_ => (Type & PackageType.Matrix) == PackageType.Matrix,
};
}
private byte[] StructToBytes(object structObject)
{
byte[] bytes = new byte[Common.PACKAGE_SIZE_EX];
GCHandle bHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
Marshal.StructureToPtr(structObject, Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0), false);
bHandle.Free();
return bytes;
}
private void BytesToStruct(byte[] value, object structObject)
{
GCHandle bHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(value, 0), structObject);
bHandle.Free();
}
}
internal partial class Common
{
internal const byte PACKAGE_SIZE = 32;
internal const byte PACKAGE_SIZE_EX = 64;
internal const byte WP_PACKAGE_SIZE = 6;
internal static PackageMonitor PackageSent;
internal static PackageMonitor PackageReceived;
internal static int PackageID;
}
}

View File

@@ -0,0 +1,428 @@
// 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.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
// <summary>
// Back-end thread for the socket.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
[module: SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity", Scope = "member", Target = "MouseWithoutBorders.Common.#PreProcess(MouseWithoutBorders.DATA)", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders
{
internal partial class Common
{
private static readonly uint QUEUE_SIZE = 50;
private static readonly int[] RecentProcessedPackageIDs = new int[QUEUE_SIZE];
private static int recentProcessedPackageIndex;
private static long processedPackageCount;
private static long skippedPackageCount;
internal static long JustGotAKey { get; set; }
private static bool PreProcess(DATA package)
{
if (package.Type == PackageType.Invalid)
{
if ((Common.InvalidPackageCount % 100) == 0)
{
ShowToolTip("Invalid packages received!", 1000, ToolTipIcon.Warning, false);
}
Common.InvalidPackageCount++;
Common.Log("Invalid packages received!");
return false;
}
else if (package.Type == 0)
{
Common.Log("Got an unknown package!");
return false;
}
else if (package.Type is not PackageType.ClipboardText and not PackageType.ClipboardImage
// BEGIN: These package types are sent by TcpSend which is single direction.
and not PackageType.Handshake and not PackageType.HandshakeAck)
{
// END
lock (RecentProcessedPackageIDs)
{
for (int i = 0; i < QUEUE_SIZE; i++)
{
if (RecentProcessedPackageIDs[i] == package.Id)
{
skippedPackageCount++;
return false;
}
}
processedPackageCount++;
recentProcessedPackageIndex = (int)((recentProcessedPackageIndex + 1) % QUEUE_SIZE);
RecentProcessedPackageIDs[recentProcessedPackageIndex] = package.Id;
}
}
return true;
}
private static System.Drawing.Point lastXY;
internal static void ProcessPackage(DATA package, TcpSk tcp)
{
if (!PreProcess(package))
{
return;
}
switch (package.Type)
{
case PackageType.Keyboard:
PackageReceived.Keyboard++;
if (package.Des == MachineID || package.Des == ID.ALL)
{
JustGotAKey = GetTick();
// NOTE(@yuyoyuppe): disabled to drop elevation requirement
bool nonElevated = Common.RunWithNoAdminRight && false;
if (nonElevated && Setting.Values.OneWayControlMode)
{
if ((package.Kd.dwFlags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
{
Common.ShowOneWayModeMessage();
}
return;
}
InputSimulation.SendKey(package.Kd);
}
break;
case PackageType.Mouse:
PackageReceived.Mouse++;
if (package.Des == MachineID || package.Des == ID.ALL)
{
if (desMachineID != MachineID)
{
NewDesMachineID = DesMachineID = MachineID;
}
// NOTE(@yuyoyuppe): disabled to drop elevation requirement
bool nonElevated = Common.RunWithNoAdminRight && false;
if (nonElevated && Setting.Values.OneWayControlMode && package.Md.dwFlags != Common.WM_MOUSEMOVE)
{
if (!IsDropping)
{
if (package.Md.dwFlags is WM_LBUTTONDOWN or WM_RBUTTONDOWN)
{
Common.ShowOneWayModeMessage();
}
}
else if (package.Md.dwFlags is WM_LBUTTONUP or WM_RBUTTONUP)
{
IsDropping = false;
}
return;
}
if (Math.Abs(package.Md.X) >= MOVE_MOUSE_RELATIVE && Math.Abs(package.Md.Y) >= MOVE_MOUSE_RELATIVE)
{
if (package.Md.dwFlags == Common.WM_MOUSEMOVE)
{
InputSimulation.MoveMouseRelative(
package.Md.X < 0 ? package.Md.X + MOVE_MOUSE_RELATIVE : package.Md.X - MOVE_MOUSE_RELATIVE,
package.Md.Y < 0 ? package.Md.Y + MOVE_MOUSE_RELATIVE : package.Md.Y - MOVE_MOUSE_RELATIVE);
_ = NativeMethods.GetCursorPos(ref lastXY);
Point p = MoveToMyNeighbourIfNeeded(lastXY.X, lastXY.Y, MachineID);
if (!p.IsEmpty)
{
HasSwitchedMachineSinceLastCopy = true;
Common.LogDebug(string.Format(
CultureInfo.CurrentCulture,
"***** Controlled Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})",
newDesMachineIdEx,
lastXY.X,
lastXY.Y));
SendNextMachine(package.Src, newDesMachineIdEx, p);
}
}
else
{
_ = NativeMethods.GetCursorPos(ref lastXY);
package.Md.X = lastXY.X * 65535 / screenWidth;
package.Md.Y = lastXY.Y * 65535 / screenHeight;
_ = InputSimulation.SendMouse(package.Md);
}
}
else
{
_ = InputSimulation.SendMouse(package.Md);
_ = NativeMethods.GetCursorPos(ref lastXY);
}
LastX = lastXY.X;
LastY = lastXY.Y;
CustomCursor.ShowFakeMouseCursor(LastX, LastY);
}
DragDropStep01(package.Md.dwFlags);
DragDropStep09(package.Md.dwFlags);
break;
case PackageType.NextMachine:
LogDebug("PackageType.NextMachine received!");
if (IsSwitchingByMouseEnabled())
{
PrepareToSwitchToMachine((ID)package.Md.WheelDelta, new Point(package.Md.X, package.Md.Y));
}
break;
case PackageType.ExplorerDragDrop:
PackageReceived.ExplorerDragDrop++;
DragDropStep03(package);
break;
case PackageType.Heartbeat:
case PackageType.Heartbeat_ex:
PackageReceived.Heartbeat++;
Common.GeneratedKey = Common.GeneratedKey || package.Type == PackageType.Heartbeat_ex;
if (Common.GeneratedKey)
{
Setting.Values.MyKey = Common.MyKey;
SendPackage(ID.ALL, PackageType.Heartbeat_ex_l2);
}
string desMachine = Common.AddToMachinePool(package);
if (Setting.Values.FirstRun && !string.IsNullOrEmpty(desMachine))
{
Common.UpdateSetupMachineMatrix(desMachine);
Common.UpdateClientSockets("UpdateSetupMachineMatrix");
}
break;
case PackageType.Heartbeat_ex_l2:
Common.GeneratedKey = true;
Setting.Values.MyKey = Common.MyKey;
SendPackage(ID.ALL, PackageType.Heartbeat_ex_l3);
break;
case PackageType.Heartbeat_ex_l3:
Common.GeneratedKey = true;
Setting.Values.MyKey = Common.MyKey;
break;
case PackageType.Awake:
PackageReceived.Heartbeat++;
_ = Common.AddToMachinePool(package);
Common.HumanBeingDetected();
break;
case PackageType.Hello:
PackageReceived.Hello++;
SendHeartBeat();
string newMachine = Common.AddToMachinePool(package);
if (Setting.Values.MachineMatrixString == null)
{
string tip = newMachine + " saying Hello!";
tip += "\r\n Right Click to setup your machine Matrix";
ShowToolTip(tip);
}
break;
case PackageType.Hi:
PackageReceived.Hello++;
break;
case PackageType.ByeBye:
PackageReceived.ByeBye++;
ProcessByeByeMessage(package);
break;
case PackageType.Clipboard:
PackageReceived.Clipboard++;
if (!RunOnLogonDesktop && !RunOnScrSaverDesktop)
{
clipboardCopiedTime = GetTick();
GetNameOfMachineWithClipboardData(package);
SignalBigClipboardData();
}
break;
case PackageType.MachineSwitched:
if (GetTick() - clipboardCopiedTime < BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == MachineID))
{
clipboardCopiedTime = 0;
Common.GetRemoteClipboard("PackageType.MachineSwitched");
}
break;
case PackageType.ClipboardCapture:
PackageReceived.Clipboard++;
if (!RunOnLogonDesktop && !RunOnScrSaverDesktop)
{
if (package.Des == MachineID || package.Des == ID.ALL)
{
GetNameOfMachineWithClipboardData(package);
GetRemoteClipboard("mspaint," + LastMachineWithClipboardData);
}
}
break;
case PackageType.CaptureScreenCommand:
PackageReceived.Clipboard++;
if (package.Des == MachineID || package.Des == ID.ALL)
{
Common.SendImage(package.Src, Common.CaptureScreen());
}
break;
case PackageType.ClipboardAsk:
PackageReceived.ClipboardAsk++;
if (package.Des == MachineID)
{
_ = Task.Run(() =>
{
try
{
System.Threading.Thread thread = Thread.CurrentThread;
thread.Name = $"{nameof(PackageType.ClipboardAsk)}.{thread.ManagedThreadId}";
Thread.UpdateThreads(thread);
string remoteMachine = package.MachineName;
System.Net.Sockets.TcpClient client = ConnectToRemoteClipboardSocket(remoteMachine);
bool clientPushData = true;
if (ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction))
{
SocketStuff.SendClipboardData(client.Client, enStream);
}
}
catch (Exception e)
{
Log(e);
}
});
}
break;
case PackageType.ClipboardDragDrop:
PackageReceived.ClipboardDragDrop++;
DragDropStep08(package);
break;
case PackageType.ClipboardDragDropOperation:
PackageReceived.ClipboardDragDrop++;
DragDropStep08_2(package);
break;
case PackageType.ClipboardDragDropEnd:
PackageReceived.ClipboardDragDropEnd++;
DragDropStep12();
break;
case PackageType.ClipboardText:
case PackageType.ClipboardImage:
clipboardCopiedTime = 0;
if (package.Type == PackageType.ClipboardImage)
{
PackageReceived.ClipboardImage++;
}
else
{
PackageReceived.ClipboardText++;
}
if (tcp != null)
{
Common.ReceiveClipboardDataUsingTCP(
package,
package.Type == PackageType.ClipboardImage,
tcp);
}
break;
case PackageType.HideMouse:
HasSwitchedMachineSinceLastCopy = true;
HideMouseCursor(true);
MainFormDotEx(false);
ReleaseAllKeys();
break;
default:
if ((package.Type & PackageType.Matrix) == PackageType.Matrix)
{
PackageReceived.Matrix++;
UpdateMachineMatrix(package);
break;
}
else
{
// We should never get to this point!
Common.Log("Invalid package received!");
return;
}
}
}
private static void GetNameOfMachineWithClipboardData(DATA package)
{
LastIDWithClipboardData = package.Src;
List<MachineInf> matchingMachines = Common.MachinePool.TryFindMachineByID(LastIDWithClipboardData);
if (matchingMachines.Count >= 1)
{
LastMachineWithClipboardData = matchingMachines[0].Name.Trim();
}
/*
lastMachineWithClipboardData =
Common.GetString(BitConverter.GetBytes(package.machineNameHead));
lastMachineWithClipboardData +=
Common.GetString(BitConverter.GetBytes(package.machineNameTail));
lastMachineWithClipboardData = lastMachineWithClipboardData.Trim();
* */
}
private static void SignalBigClipboardData()
{
LogDebug("SignalBigClipboardData");
SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_BIG_CLIPBOARD, -1, ICON_BIG_CLIPBOARD, -1 });
}
}
}

View File

@@ -0,0 +1,160 @@
// 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.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.ServiceProcess;
using System.Threading.Tasks;
using System.Windows.Forms;
// <summary>
// Service control code.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
[module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.Common.#StartMouseWithoutBordersService()", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders
{
internal partial class Common
{
private static bool shownErrMessage;
private static DateTime lastStartServiceTime = DateTime.UtcNow;
internal static void StartMouseWithoutBordersService(string desktopToRunMouseWithoutBordersOn = null, string startTag1 = "byapp", string startTag2 = null)
{
// NOTE(@yuyoyuppe): the new flow assumes we run both mwb processes directly from the svc.
if (Common.RunWithNoAdminRight || true)
{
return;
}
Log($"{nameof(StartMouseWithoutBordersService)}: {GetStackTrace(new StackTrace())}.");
Task task = Task.Run(() =>
{
Process[] ps = Process.GetProcessesByName("MouseWithoutBordersSvc");
if (ps.Any())
{
if (DateTime.UtcNow - lastStartServiceTime < TimeSpan.FromSeconds(5))
{
Log($"{nameof(StartMouseWithoutBordersService)}: Aborted.");
return;
}
foreach (Process pp in ps)
{
Common.Log(string.Format(CultureInfo.InvariantCulture, "Killing process MouseWithoutBordersSvc {0}.", pp.Id));
pp.KillProcess();
}
}
lastStartServiceTime = DateTime.UtcNow;
ServiceController service = new("MouseWithoutBordersSvc");
try
{
Log("Starting " + service.ServiceName);
}
catch (Exception)
{
if (!shownErrMessage)
{
shownErrMessage = true;
_ = MessageBox.Show(
Application.ProductName + " is not installed yet, please run Setup.exe first!",
Application.ProductName,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
return;
}
try
{
int c = 0;
while (service.Status != ServiceControllerStatus.Stopped && c++ < 5)
{
Thread.Sleep(1000);
service = new ServiceController("MouseWithoutBordersSvc");
}
if (string.IsNullOrWhiteSpace(desktopToRunMouseWithoutBordersOn))
{
startTag2 ??= Process.GetCurrentProcess().SessionId.ToString(CultureInfo.InvariantCulture);
service.Start(new string[] { startTag1, startTag2 });
}
else
{
service.Start(new string[] { desktopToRunMouseWithoutBordersOn });
}
}
catch (Exception e)
{
Log(e);
// ERROR_SERVICE_ALREADY_RUNNING
if (!(shownErrMessage || ((e?.InnerException as Win32Exception)?.NativeErrorCode == 1056)))
{
shownErrMessage = true;
_ = MessageBox.Show(
"Cannot start service " + service.ServiceName + ": " + e.Message,
Common.BinaryName,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
return;
}
});
// Wait for the task while not blocking the UI thread.
do
{
MMSleep(1);
if (task.IsCanceled || task.IsCompleted || task.IsFaulted)
{
break;
}
}
while (true);
}
internal static void StartServiceAndSendLogoffSignal()
{
try
{
Process[] p = Process.GetProcessesByName("winlogon");
Process me = Process.GetCurrentProcess();
string myWinlogon = p?.FirstOrDefault(item => item.SessionId == me.SessionId)?.Id.ToString(CultureInfo.InvariantCulture) ?? null;
if (string.IsNullOrWhiteSpace(myWinlogon))
{
StartMouseWithoutBordersService(null, "logoff");
}
else
{
StartMouseWithoutBordersService(null, "logoff", myWinlogon);
}
}
catch (Exception e)
{
Common.Log($"{nameof(StartServiceAndSendLogoffSignal)}: {e.Message}");
}
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using ManagedCommon;
namespace MouseWithoutBorders
{
internal class ShutdownWithPowerToys
{
public static void WaitForPowerToysRunner()
{
try
{
RunnerHelper.WaitForPowerToysRunnerExitFallback(() =>
{
Common.MainForm.Quit(true, false);
});
}
catch (Exception e)
{
Common.Log(e);
}
}
}
}

View File

@@ -0,0 +1,130 @@
// 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.
// <summary>
// Virtual key constants.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
namespace MouseWithoutBorders
{
internal enum VK : ushort
{
CAPITAL = 0x14,
NUMLOCK = 0x90,
SHIFT = 0x10,
CONTROL = 0x11,
MENU = 0x12,
ESCAPE = 0x1B,
BACK = 0x08,
TAB = 0x09,
RETURN = 0x0D,
PRIOR = 0x21,
NEXT = 0x22,
END = 0x23,
HOME = 0x24,
LEFT = 0x25,
UP = 0x26,
RIGHT = 0x27,
DOWN = 0x28,
SELECT = 0x29,
PRINT = 0x2A,
EXECUTE = 0x2B,
SNAPSHOT = 0x2C,
INSERT = 0x2D,
DELETE = 0x2E,
HELP = 0x2F,
NUMPAD0 = 0x60,
NUMPAD1 = 0x61,
NUMPAD2 = 0x62,
NUMPAD3 = 0x63,
NUMPAD4 = 0x64,
NUMPAD5 = 0x65,
NUMPAD6 = 0x66,
NUMPAD7 = 0x67,
NUMPAD8 = 0x68,
NUMPAD9 = 0x69,
MULTIPLY = 0x6A,
ADD = 0x6B,
SEPARATOR = 0x6C,
SUBTRACT = 0x6D,
DECIMAL = 0x6E,
DIVIDE = 0x6F,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7A,
F12 = 0x7B,
OEM_1 = 0xBA,
OEM_PLUS = 0xBB,
OEM_COMMA = 0xBC,
OEM_MINUS = 0xBD,
OEM_PERIOD = 0xBE,
OEM_2 = 0xBF,
OEM_3 = 0xC0,
MEDIA_NEXT_TRACK = 0xB0,
MEDIA_PREV_TRACK = 0xB1,
MEDIA_STOP = 0xB2,
MEDIA_PLAY_PAUSE = 0xB3,
LWIN = 0x5B,
RWIN = 0x5C,
LSHIFT = 0xA0,
RSHIFT = 0xA1,
LCONTROL = 0xA2,
RCONTROL = 0xA3,
LMENU = 0xA4,
RMENU = 0xA5,
}
internal partial class Common
{
internal const ushort KEYEVENTF_KEYDOWN = 0x0001;
internal const ushort KEYEVENTF_KEYUP = 0x0002;
internal const int WH_MOUSE = 7;
internal const int WH_KEYBOARD = 2;
internal const int WH_MOUSE_LL = 14;
internal const int WH_KEYBOARD_LL = 13;
internal const int WM_MOUSEMOVE = 0x200;
internal const int WM_LBUTTONDOWN = 0x201;
internal const int WM_RBUTTONDOWN = 0x204;
internal const int WM_MBUTTONDOWN = 0x207;
internal const int WM_XBUTTONDOWN = 0x20B;
internal const int WM_LBUTTONUP = 0x202;
internal const int WM_RBUTTONUP = 0x205;
internal const int WM_MBUTTONUP = 0x208;
internal const int WM_XBUTTONUP = 0x20C;
internal const int WM_LBUTTONDBLCLK = 0x203;
internal const int WM_RBUTTONDBLCLK = 0x206;
internal const int WM_MBUTTONDBLCLK = 0x209;
internal const int WM_MOUSEWHEEL = 0x020A;
internal const int WM_KEYDOWN = 0x100;
internal const int WM_KEYUP = 0x101;
internal const int WM_SYSKEYDOWN = 0x104;
internal const int WM_SYSKEYUP = 0x105;
[Flags]
internal enum LLKHF
{
EXTENDED = 0x01,
INJECTED = 0x10,
ALTDOWN = 0x20,
UP = 0x80,
}
}
}

View File

@@ -0,0 +1,360 @@
// 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.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
// <summary>
// Screen/Desktop helper functions.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
namespace MouseWithoutBorders
{
// Desktops, and GetScreenConfig routines
internal partial class Common
{
private static MyRectangle newDesktopBounds;
private static MyRectangle newPrimaryScreenBounds;
private static string activeDesktop;
internal static string ActiveDesktop => Common.activeDesktop;
private static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
GetScreenConfig();
}
private static readonly List<Point> SensitivePoints = new();
private static bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeMethods.RECT lprcMonitor, IntPtr dwData)
{
// lprcMonitor is wrong!!! => using GetMonitorInfo(...)
// Log(String.Format( CultureInfo.CurrentCulture,"MONITOR: l{0}, t{1}, r{2}, b{3}", lprcMonitor.Left, lprcMonitor.Top, lprcMonitor.Right, lprcMonitor.Bottom));
NativeMethods.MonitorInfoEx mi = default;
mi.cbSize = Marshal.SizeOf(mi);
_ = NativeMethods.GetMonitorInfo(hMonitor, ref mi);
try
{
// For logging only
_ = NativeMethods.GetDpiForMonitor(hMonitor, 0, out uint dpiX, out uint dpiY);
Log(string.Format(CultureInfo.CurrentCulture, "MONITOR: ({0}, {1}, {2}, {3}). DPI: ({4}, {5})", mi.rcMonitor.Left, mi.rcMonitor.Top, mi.rcMonitor.Right, mi.rcMonitor.Bottom, dpiX, dpiY));
}
catch (DllNotFoundException)
{
Common.Log("GetDpiForMonitor is unsupported in Windows 7 and lower.");
}
catch (EntryPointNotFoundException)
{
Common.Log("GetDpiForMonitor is unsupported in Windows 7 and lower.");
}
catch (Exception e)
{
Common.Log(e);
}
if (mi.rcMonitor.Left == 0 && mi.rcMonitor.Top == 0 && mi.rcMonitor.Right != 0 && mi.rcMonitor.Bottom != 0)
{
// Primary screen
_ = Interlocked.Exchange(ref screenWidth, mi.rcMonitor.Right - mi.rcMonitor.Left);
_ = Interlocked.Exchange(ref screenHeight, mi.rcMonitor.Bottom - mi.rcMonitor.Top);
newPrimaryScreenBounds.Left = mi.rcMonitor.Left;
newPrimaryScreenBounds.Top = mi.rcMonitor.Top;
newPrimaryScreenBounds.Right = mi.rcMonitor.Right;
newPrimaryScreenBounds.Bottom = mi.rcMonitor.Bottom;
}
else
{
if (mi.rcMonitor.Left < newDesktopBounds.Left)
{
newDesktopBounds.Left = mi.rcMonitor.Left;
}
if (mi.rcMonitor.Top < newDesktopBounds.Top)
{
newDesktopBounds.Top = mi.rcMonitor.Top;
}
if (mi.rcMonitor.Right > newDesktopBounds.Right)
{
newDesktopBounds.Right = mi.rcMonitor.Right;
}
if (mi.rcMonitor.Bottom > newDesktopBounds.Bottom)
{
newDesktopBounds.Bottom = mi.rcMonitor.Bottom;
}
}
lock (SensitivePoints)
{
SensitivePoints.Add(new Point(mi.rcMonitor.Left, mi.rcMonitor.Top));
SensitivePoints.Add(new Point(mi.rcMonitor.Right, mi.rcMonitor.Top));
SensitivePoints.Add(new Point(mi.rcMonitor.Right, mi.rcMonitor.Bottom));
SensitivePoints.Add(new Point(mi.rcMonitor.Left, mi.rcMonitor.Bottom));
}
return true;
}
internal static void GetScreenConfig()
{
try
{
Common.LogDebug("==================== GetScreenConfig started");
newDesktopBounds = new MyRectangle();
newPrimaryScreenBounds = new MyRectangle();
newDesktopBounds.Left = newPrimaryScreenBounds.Left = Screen.PrimaryScreen.Bounds.Left;
newDesktopBounds.Top = newPrimaryScreenBounds.Top = Screen.PrimaryScreen.Bounds.Top;
newDesktopBounds.Right = newPrimaryScreenBounds.Right = Screen.PrimaryScreen.Bounds.Right;
newDesktopBounds.Bottom = newPrimaryScreenBounds.Bottom = Screen.PrimaryScreen.Bounds.Bottom;
Common.Log(string.Format(
CultureInfo.CurrentCulture,
"logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}",
Common.RunOnLogonDesktop,
Common.newPrimaryScreenBounds.Left,
Common.newPrimaryScreenBounds.Top,
Common.newPrimaryScreenBounds.Right,
Common.newPrimaryScreenBounds.Bottom,
Common.newDesktopBounds.Left,
Common.newDesktopBounds.Top,
Common.newDesktopBounds.Right,
Common.newDesktopBounds.Bottom));
#if USE_MANAGED_ROUTINES
// Managed routines do not work well when running on secure desktop:(
screenWidth = Screen.PrimaryScreen.Bounds.Width;
screenHeight = Screen.PrimaryScreen.Bounds.Height;
screenCount = Screen.AllScreens.Length;
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
if (Screen.AllScreens[i].Bounds.Left < desktopBounds.Left) desktopBounds.Left = Screen.AllScreens[i].Bounds.Left;
if (Screen.AllScreens[i].Bounds.Top < desktopBounds.Top) desktopBounds.Top = Screen.AllScreens[i].Bounds.Top;
if (Screen.AllScreens[i].Bounds.Right > desktopBounds.Right) desktopBounds.Right = Screen.AllScreens[i].Bounds.Right;
if (Screen.AllScreens[i].Bounds.Bottom > desktopBounds.Bottom) desktopBounds.Bottom = Screen.AllScreens[i].Bounds.Bottom;
}
#else
lock (SensitivePoints)
{
SensitivePoints.Clear();
}
NativeMethods.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumProc, IntPtr.Zero);
// 1000 calls to EnumDisplayMonitors cost a dozen of milliseconds
#endif
Interlocked.Exchange(ref desktopBounds, newDesktopBounds);
Interlocked.Exchange(ref primaryScreenBounds, newPrimaryScreenBounds);
Common.Log(string.Format(
CultureInfo.CurrentCulture,
"logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}",
Common.RunOnLogonDesktop,
Common.PrimaryScreenBounds.Left,
Common.PrimaryScreenBounds.Top,
Common.PrimaryScreenBounds.Right,
Common.PrimaryScreenBounds.Bottom,
Common.DesktopBounds.Left,
Common.DesktopBounds.Top,
Common.DesktopBounds.Right,
Common.DesktopBounds.Bottom));
Common.Log("==================== GetScreenConfig ended");
}
catch (Exception e)
{
Log(e);
}
}
#if USING_SCREEN_SAVER_ROUTINES
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int PostMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr OpenDesktop(string hDesktop, int Flags, bool Inherit, UInt32 DesiredAccess);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseDesktop(IntPtr hDesktop);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool EnumDesktopWindows( IntPtr hDesktop, EnumDesktopWindowsProc callback, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int uAction, int uParam, ref int pvParam, int flags);
private delegate bool EnumDesktopWindowsProc(IntPtr hDesktop, IntPtr lParam);
private const int WM_CLOSE = 16;
private const int SPI_GETSCREENSAVERRUNNING = 114;
internal static bool IsScreenSaverRunning()
{
int isRunning = 0;
SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0,ref isRunning, 0);
return (isRunning != 0);
}
internal static void CloseScreenSaver()
{
IntPtr hDesktop = OpenDesktop("Screen-saver", 0, false, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hDesktop != IntPtr.Zero)
{
LogDebug("Closing screen saver...");
EnumDesktopWindows(hDesktop, new EnumDesktopWindowsProc(CloseScreenSaverFunc), IntPtr.Zero);
CloseDesktop(hDesktop);
}
}
private static bool CloseScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
{
if (IsWindowVisible(hWnd))
{
LogDebug("Posting WM_CLOSE to " + hWnd.ToString(CultureInfo.InvariantCulture));
PostMessage(hWnd, WM_CLOSE, 0, 0);
}
return true;
}
#endif
internal static string GetMyDesktop()
{
byte[] arThreadDesktop = new byte[256];
IntPtr hD = NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId());
if (hD != IntPtr.Zero)
{
_ = NativeMethods.GetUserObjectInformation(hD, NativeMethods.UOI_NAME, arThreadDesktop, arThreadDesktop.Length, out _);
return GetString(arThreadDesktop).Replace("\0", string.Empty);
}
return string.Empty;
}
internal static string GetInputDesktop()
{
byte[] arInputDesktop = new byte[256];
IntPtr hD = NativeMethods.OpenInputDesktop(0, false, NativeMethods.DESKTOP_READOBJECTS);
if (hD != IntPtr.Zero)
{
_ = NativeMethods.GetUserObjectInformation(hD, NativeMethods.UOI_NAME, arInputDesktop, arInputDesktop.Length, out _);
return GetString(arInputDesktop).Replace("\0", string.Empty);
}
return string.Empty;
}
internal static void StartMMService(string desktopToRunMouseWithoutBordersOn)
{
if (!Common.RunWithNoAdminRight)
{
LogDebug("*** Starting on active Desktop: " + desktopToRunMouseWithoutBordersOn);
StartMouseWithoutBordersService(desktopToRunMouseWithoutBordersOn);
}
}
internal static void CheckForDesktopSwitchEvent(bool cleanupIfExit)
{
try
{
if (!IsMyDesktopActive() || Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
{
Common.RunDDHelper(true);
int waitCount = 20;
while (NativeMethods.WTSGetActiveConsoleSessionId() == 0xFFFFFFFF && waitCount > 0)
{
waitCount--;
LogDebug("The session is detached/attached.");
Thread.Sleep(500);
}
string myDesktop = GetMyDesktop();
activeDesktop = GetInputDesktop();
LogDebug("*** Active Desktop = " + activeDesktop);
LogDebug("*** My Desktop = " + myDesktop);
if (myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase))
{
LogDebug("*** Active Desktop == My Desktop (TS session)");
}
if (!activeDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) &&
!activeDesktop.Equals("default", StringComparison.OrdinalIgnoreCase) &&
!activeDesktop.Equals("disconnect", StringComparison.OrdinalIgnoreCase))
{
try
{
StartMMService(activeDesktop);
}
catch (Exception e)
{
Common.Log($"{nameof(CheckForDesktopSwitchEvent)}: {e}");
}
}
else
{
if (!myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase))
{
Log("*** Active Desktop <> My Desktop");
}
uint sid = NativeMethods.WTSGetActiveConsoleSessionId();
if (Process.GetProcessesByName(Common.BinaryName).Any(p => (uint)p.SessionId == sid))
{
Log("Found MouseWithoutBorders on the active session!");
}
else
{
Log("MouseWithoutBorders not found on the active session!");
StartMMService(null);
}
}
if (!myDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) &&
!myDesktop.Equals("default", StringComparison.OrdinalIgnoreCase))
{
LogDebug("*** Desktop inactive, exiting: " + myDesktop);
Setting.Values.LastX = JUST_GOT_BACK_FROM_SCREEN_SAVER;
if (cleanupIfExit)
{
Common.Cleanup();
}
Process.GetCurrentProcess().KillProcess();
}
}
}
catch (Exception e)
{
Log(e);
}
}
private static Point p;
internal static bool IsMyDesktopActive()
{
return NativeMethods.GetCursorPos(ref p);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
// 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.
// <summary>
// Create a customed cursor.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
// Disable the warning to preserve original code
#pragma warning disable CA1716
namespace MouseWithoutBorders.Class
#pragma warning restore CA1716
{
internal struct IconInfo
{
// Suppress warning to match COM names
#pragma warning disable SA1307
internal bool fIcon;
internal int xHotspot;
internal int yHotspot;
internal IntPtr hbmMask;
internal IntPtr hbmColor;
#pragma warning restore SA1307
}
internal class CustomCursor
{
private CustomCursor()
{
}
internal static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
IconInfo tmp = default;
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
// GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.hbmColor = bmp.GetHbitmap();
tmp.hbmMask = bmp.GetHbitmap();
return new Cursor(NativeMethods.CreateIconIndirect(ref tmp));
}
internal static Cursor CreateDotCursor()
{
Bitmap bitmap = new(32, 32, PixelFormat.Format24bppRgb);
bitmap.MakeTransparent(Color.Black);
Graphics g = Graphics.FromImage(bitmap);
g.DrawLine(Pens.Gray, 0, 0, 1, 1);
Cursor c = CreateCursor(bitmap, 0, 0);
bitmap.Dispose();
return c;
}
private static int hiding;
private static NativeMethods.CURSORINFO ci;
internal static void ShowFakeMouseCursor(int x, int y)
{
if (Setting.Values.DrawMouse)
{
ci.cbSize = Marshal.SizeOf(typeof(NativeMethods.CURSORINFO));
_ = NativeMethods.GetCursorInfo(out ci);
// The cursor is hidden or suppressed.q
if (ci.flags != 1)
{
Common.DoSomethingInTheInputCallbackThread(
() =>
{
Common.MouseCursorForm ??= new FrmMouseCursor();
try
{
Common.MouseCursorForm.Text = string.Empty;
if (x == int.MinValue || y == int.MinValue)
{
hiding = 3;
}
if (hiding > 0)
{
hiding--;
Common.MouseCursorForm.Hide();
}
else
{
Common.MouseCursorForm.Left = x + 1;
Common.MouseCursorForm.Top = y + 1;
Common.MouseCursorForm.Width = Common.MouseCursorForm.Height = 32;
Common.MouseCursorForm.TopMost = true;
Common.MouseCursorForm.Show();
}
}
catch (NullReferenceException)
{
Common.Log($"{nameof(Common.MouseCursorForm)} has been set to null by another thread.");
Common.MouseCursorForm = new FrmMouseCursor();
}
catch (ObjectDisposedException)
{
Common.Log($"{nameof(Common.MouseCursorForm)} has been disposed.");
Common.MouseCursorForm = new FrmMouseCursor();
}
},
false);
return;
}
}
Common.DoSomethingInTheInputCallbackThread(
() =>
{
if (Common.MouseCursorForm != null)
{
try
{
Common.MouseCursorForm.Close();
Common.MouseCursorForm.Dispose();
}
catch (NullReferenceException)
{
Common.Log($"{nameof(Common.MouseCursorForm)} has already been set to null by another thread!");
}
catch (ObjectDisposedException)
{
Common.Log($"{nameof(Common.MouseCursorForm)} has already been disposed!");
}
Common.MouseCursorForm = null;
}
},
false);
}
}
}

View File

@@ -0,0 +1,14 @@
// 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.
namespace MouseWithoutBorders.Class
{
internal enum EasyMouseOption : int
{
Disable = 0,
Enable = 1,
Ctrl = 2,
Shift = 3,
}
}

View File

@@ -0,0 +1,55 @@
// 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.ComponentModel;
using System.Diagnostics;
using System.IO;
namespace MouseWithoutBorders.Class
{
internal static class Extensions
{
internal static int ReadEx(this Stream st, byte[] buf, int bufIndex, int length)
{
int bytesReceived = st.Read(buf, bufIndex, length);
int receivedCount = bytesReceived;
while (receivedCount != 0 && bytesReceived < length)
{
bytesReceived += receivedCount = st.Read(buf, bufIndex + bytesReceived, length - bytesReceived);
}
return bytesReceived;
}
internal static void KillProcess(this Process process, bool keepTrying = false)
{
string processName = process.ProcessName;
int processId = process.Id;
do
{
try
{
process.Kill();
break;
}
catch (Win32Exception e)
{
string log = $"The process {processName} (PID={processId}) could not be terminated, error: {e.Message}";
Common.TelemetryLogTrace(log, SeverityLevel.Error);
Common.ShowToolTip(log, 5000);
if (!keepTrying)
{
Thread.Sleep(1000);
break;
}
}
}
while (true);
}
}
}

View File

@@ -0,0 +1,636 @@
// 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;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Pipes;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.VisualStudio.Threading;
using Newtonsoft.Json;
using StreamJsonRpc;
#if !MM_HELPER
using MouseWithoutBorders.Class;
#endif
using SystemClipboard = System.Windows.Forms.Clipboard;
namespace MouseWithoutBorders
{
[JsonObject(MemberSerialization.OptIn)]
public struct ByteArrayOrString
{
private enum ValueType
{
ByteArray,
String,
}
[JsonProperty]
private ValueType _type;
private byte[] _byteArrayValue;
private string _stringValue;
public ByteArrayOrString(byte[] byteArray)
{
_type = ValueType.ByteArray;
_byteArrayValue = byteArray;
_stringValue = null;
}
public ByteArrayOrString(string str)
{
_type = ValueType.String;
_byteArrayValue = null;
_stringValue = str;
}
public bool IsByteArray => _type == ValueType.ByteArray;
public bool IsString => _type == ValueType.String;
[JsonProperty("byteArray")]
public byte[] ByteArray
{
get => _byteArrayValue;
set
{
_byteArrayValue = value;
_type = ValueType.ByteArray;
}
}
[JsonProperty("string")]
public string TheString
{
get => _stringValue;
set
{
_stringValue = value;
_type = ValueType.String;
}
}
public bool ShouldSerializeByteArray() => IsByteArray;
public bool ShouldSerializeString() => IsString;
public byte[] GetByteArray()
{
if (IsByteArray)
{
return _byteArrayValue;
}
throw new InvalidOperationException("The value is not a byte array.");
}
public string GetString()
{
if (IsString)
{
return _stringValue;
}
throw new InvalidOperationException("The value is not a string.");
}
public static implicit operator ByteArrayOrString(byte[] byteArray) => new ByteArrayOrString(byteArray);
public static implicit operator ByteArrayOrString(string str) => new ByteArrayOrString(str);
}
public interface IClipboardHelper
{
void SendLog(string log);
void SendDragFile(string fileName);
void SendClipboardData(ByteArrayOrString data, bool isFilePath);
}
#if !MM_HELPER
public class ClipboardHelper : IClipboardHelper
{
public void SendLog(string log)
{
Common.LogDebug("FROM HELPER: " + log);
if (!string.IsNullOrEmpty(log))
{
if (log.StartsWith("Screen capture ended", StringComparison.InvariantCulture))
{
/* TODO: Telemetry for screen capture. */
if (Setting.Values.FirstCtrlShiftS)
{
Setting.Values.FirstCtrlShiftS = false;
Common.ShowToolTip("Selective screen capture has been triggered, you can change the hotkey on the Settings form.", 10000);
}
}
else if (log.StartsWith("Trace:", StringComparison.InvariantCulture))
{
Common.TelemetryLogTrace(log, SeverityLevel.Information);
}
}
}
public void SendDragFile(string fileName)
{
Common.DragDropStep05Ex(fileName);
}
public void SendClipboardData(ByteArrayOrString data, bool isFilePath)
{
_ = Common.CheckClipboardEx(data, isFilePath);
}
}
#endif
internal sealed class IpcChannel<T>
where T : new()
{
public static T StartIpcServer(string pipeName, CancellationToken cancellationToken)
{
SecurityIdentifier securityIdentifier = new SecurityIdentifier(
WellKnownSidType.AuthenticatedUserSid, null);
PipeSecurity pipeSecurity = new PipeSecurity();
pipeSecurity.AddAccessRule(new PipeAccessRule(
securityIdentifier,
PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance,
AccessControlType.Allow));
_ = Task.Factory.StartNew(
async () =>
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
using (var serverChannel = NamedPipeServerStreamAcl.Create(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 0, 0, pipeSecurity))
{
await serverChannel.WaitForConnectionAsync();
var taskRpc = JsonRpc.Attach(serverChannel, new T());
await taskRpc.Completion;
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception e)
{
#if MM_HELPER
_ = e;
#else
Common.Log(e);
#endif
}
},
cancellationToken,
TaskCreationOptions.None,
TaskScheduler.Default);
return default(T);
}
}
internal sealed class IpcHelper
{
private const string ChannelName = "MouseWithoutBorders";
private const string RemoteObjectName = "ClipboardHelper";
#if !MM_HELPER
private static void CleanupStream()
{
if (_serverTaskCancellationSource != null)
{
_serverTaskCancellationSource.Cancel();
_serverTaskCancellationSource.Dispose();
_serverTaskCancellationSource = null;
}
}
private static CancellationTokenSource _serverTaskCancellationSource;
internal static void CreateIpcServer(bool cleanup)
{
try
{
if (cleanup)
{
CleanupStream();
return;
}
_serverTaskCancellationSource = new CancellationTokenSource();
CancellationToken cancellationToken = _serverTaskCancellationSource.Token;
IpcChannel<ClipboardHelper>.StartIpcServer(ChannelName + "/" + RemoteObjectName, cancellationToken);
Common.IpcChannelCreated = true;
}
catch (Exception e)
{
Common.IpcChannelCreated = false;
Common.ShowToolTip("Error setting up clipboard sharing, clipboard sharing will not work!", 5000, ToolTipIcon.Error);
Common.Log(e);
}
}
#else
internal static IClipboardHelper CreateIpcClient()
{
try
{
var stream = new NamedPipeClientStream(".", ChannelName + "/" + RemoteObjectName, PipeDirection.InOut, PipeOptions.Asynchronous);
stream.Connect();
return JsonRpc.Attach<IClipboardHelper>(stream);
}
catch (Exception e)
{
Logger.LogEvent(e.Message, EventLogEntryType.Error);
}
return null;
}
#endif
}
internal static class Logger
{
#if MM_HELPER
private const string EventSourceName = "MouseWithoutBordersHelper";
#else
private const string EventSourceName = "MouseWithoutBorders";
#endif
internal static void LogEvent(string message, EventLogEntryType logType = EventLogEntryType.Information)
{
try
{
if (!EventLog.SourceExists(EventSourceName))
{
EventLog.CreateEventSource(EventSourceName, "Application");
}
EventLog.WriteEntry(EventSourceName, message, logType);
}
catch (Exception e)
{
Debug.WriteLine(message + ": " + e.Message);
}
}
}
#if MM_HELPER
internal static class ClipboardMMHelper
{
internal static IntPtr NextClipboardViewer = IntPtr.Zero;
private static FormHelper helperForm;
private static bool addClipboardFormatListenerResult;
private static void Log(string log)
{
helperForm.SendLog(log);
}
private static void Log(Exception e)
{
Log($"Trace: {e}");
}
internal static void HookClipboard(FormHelper f)
{
helperForm = f;
try
{
addClipboardFormatListenerResult = NativeMethods.AddClipboardFormatListener(f.Handle);
int err = addClipboardFormatListenerResult ? 0 : Marshal.GetLastWin32Error();
if (err != 0)
{
Log($"Trace: {nameof(NativeMethods.AddClipboardFormatListener)}: GetLastError = {err}");
}
}
catch (EntryPointNotFoundException e)
{
Log($"{nameof(NativeMethods.AddClipboardFormatListener)} is unavailable in this version of Windows.");
Log(e);
}
catch (Exception e)
{
Log(e);
}
// Fallback
if (!addClipboardFormatListenerResult)
{
NextClipboardViewer = NativeMethods.SetClipboardViewer(f.Handle);
int err = NextClipboardViewer == IntPtr.Zero ? Marshal.GetLastWin32Error() : 0;
if (err != 0)
{
Log($"Trace: {nameof(NativeMethods.SetClipboardViewer)}: GetLastError = {err}");
}
}
Log($"Trace: Clipboard monitor method {(addClipboardFormatListenerResult ? nameof(NativeMethods.AddClipboardFormatListener) : NextClipboardViewer != IntPtr.Zero ? nameof(NativeMethods.SetClipboardViewer) : "(none)")} is used.");
}
internal static void UnhookClipboard()
{
if (addClipboardFormatListenerResult)
{
addClipboardFormatListenerResult = false;
_ = NativeMethods.RemoveClipboardFormatListener(helperForm.Handle);
}
else
{
_ = NativeMethods.ChangeClipboardChain(helperForm.Handle, NextClipboardViewer);
NextClipboardViewer = IntPtr.Zero;
}
}
private static void ReHookClipboard()
{
UnhookClipboard();
HookClipboard(helperForm);
}
internal static bool UpdateNextClipboardViewer(Message m)
{
if (m.WParam == NextClipboardViewer)
{
NextClipboardViewer = m.LParam;
return true;
}
return false;
}
internal static void PassMessageToTheNextViewer(Message m)
{
if (NextClipboardViewer != IntPtr.Zero && NextClipboardViewer != helperForm.Handle)
{
_ = NativeMethods.SendMessage(NextClipboardViewer, m.Msg, m.WParam, m.LParam);
}
}
public static bool ContainsFileDropList()
{
bool rv = false;
try
{
rv = Common.Retry(nameof(SystemClipboard.ContainsFileDropList), () => { return SystemClipboard.ContainsFileDropList(); }, (log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
return rv;
}
public static bool ContainsImage()
{
bool rv = false;
try
{
rv = Common.Retry(nameof(SystemClipboard.ContainsImage), () => { return SystemClipboard.ContainsImage(); }, (log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
return rv;
}
public static bool ContainsText()
{
bool rv = false;
try
{
rv = Common.Retry(nameof(SystemClipboard.ContainsText), () => { return SystemClipboard.ContainsText(); }, (log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
return rv;
}
public static StringCollection GetFileDropList()
{
StringCollection rv = null;
try
{
rv = Common.Retry(nameof(SystemClipboard.GetFileDropList), () => { return SystemClipboard.GetFileDropList(); }, (log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
return rv;
}
public static Image GetImage()
{
Image rv = null;
try
{
rv = Common.Retry(nameof(SystemClipboard.GetImage), () => { return SystemClipboard.GetImage(); }, (log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
return rv;
}
public static string GetText(TextDataFormat format)
{
string rv = null;
try
{
rv = Common.Retry(nameof(SystemClipboard.GetText), () => { return SystemClipboard.GetText(format); }, (log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
catch (System.ComponentModel.InvalidEnumArgumentException e)
{
Log(e);
}
return rv;
}
public static void SetImage(Image image)
{
try
{
_ = Common.Retry(
nameof(SystemClipboard.SetImage),
() =>
{
SystemClipboard.SetImage(image);
return true;
},
(log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
catch (ArgumentNullException e)
{
Log(e);
}
}
public static void SetText(string text)
{
try
{
_ = Common.Retry(
nameof(SystemClipboard.SetText),
() =>
{
SystemClipboard.SetText(text);
return true;
},
(log) => Log(log));
}
catch (ExternalException e)
{
Log(e);
ReHookClipboard();
}
catch (ThreadStateException e)
{
Log(e);
ReHookClipboard();
}
catch (ArgumentNullException e)
{
Log(e);
}
}
}
#endif
internal sealed class SharedConst
{
internal const int QUIT_CMD = 0x409;
}
internal sealed partial class Common
{
internal static bool IpcChannelCreated { get; set; }
internal static T Retry<T>(string name, Func<T> func, Action<string> log, Action preRetry = null)
{
int count = 0;
do
{
try
{
T rv = func();
if (count > 0)
{
log($"Trace: {name} has been successful after {count} retry.");
}
return rv;
}
catch (Exception)
{
count++;
preRetry?.Invoke();
if (count > 10)
{
throw;
}
Application.DoEvents();
Thread.Sleep(200);
}
}
while (true);
}
}
}

View File

@@ -0,0 +1,705 @@
// 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.
// <summary>
// Keyboard/Mouse hook callbacks, pre-process before calling to routines in Common.Event.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.InputHook.#MouseHookProc(System.Int32,System.Int32,System.IntPtr)", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.InputHook.#ProcessKeyEx(System.Int32,System.Int32)", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.InputHook.#Start()", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders.Class
{
internal class InputHook
{
internal delegate void MouseEvHandler(MOUSEDATA e, int dx, int dy);
internal delegate void KeybdEvHandler(KEYBDDATA e);
[StructLayout(LayoutKind.Sequential)]
private struct MouseHookStruct
{
internal NativeMethods.POINT Pt;
internal int Hwnd;
internal int WHitTestCode;
internal int DwExtraInfo;
}
// http://msdn.microsoft.com/en-us/library/ms644970(VS.85).aspx
[StructLayout(LayoutKind.Sequential)]
private struct MouseLLHookStruct
{
internal NativeMethods.POINT Pt;
internal int MouseData;
internal int Flags;
internal int Time;
internal int DwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
private struct KeyboardHookStruct
{
internal int VkCode;
internal int ScanCode;
internal int Flags;
internal int Time;
internal int DwExtraInfo;
}
internal event MouseEvHandler MouseEvent;
internal event KeybdEvHandler KeyboardEvent;
private int hMouseHook;
private int hKeyboardHook;
private static NativeMethods.HookProc mouseHookProcedure;
private static NativeMethods.HookProc keyboardHookProcedure;
private static MouseLLHookStruct mouseHookStruct;
private static KeyboardHookStruct keyboardHookStruct;
private static MOUSEDATA hookCallbackMouseData;
private static KEYBDDATA hookCallbackKeybdData;
private static bool winDown;
private static bool altDown;
private static bool shiftDown;
internal static bool RealData { get; set; } = true;
internal static int SkipMouseUpCount { get; set; }
internal static bool SkipMouseUpDown { get; set; }
internal static bool CtrlDown { get; private set; }
internal static bool EasyMouseKeyDown { get; set; }
internal InputHook()
{
Start();
}
~InputHook()
{
Stop();
}
internal void Start()
{
int le;
bool er = false;
// Install Mouse Hook
mouseHookProcedure = new NativeMethods.HookProc(MouseHookProc);
hMouseHook = NativeMethods.SetWindowsHookEx(
Common.WH_MOUSE_LL,
mouseHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
if (hMouseHook == 0)
{
le = Marshal.GetLastWin32Error();
Common.Log("Error installing Mouse hook: " + le.ToString(CultureInfo.CurrentCulture));
er = true;
Stop();
}
// Install Keyboard Hook
keyboardHookProcedure = new NativeMethods.HookProc(KeyboardHookProc);
hKeyboardHook = NativeMethods.SetWindowsHookEx(
Common.WH_KEYBOARD_LL,
keyboardHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
if (hKeyboardHook == 0)
{
le = Marshal.GetLastWin32Error();
Common.Log("Error installing keyboard hook: " + le.ToString(CultureInfo.CurrentCulture));
er = true;
Stop();
}
if (er)
{
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
_ = MessageBox.Show(
"Error installing keyboard/Mouse hook!",
Application.ProductName,
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
else
{
Common.InitLastInputEventCount();
}
}
internal void Stop()
{
if (hMouseHook != 0)
{
int retMouse = NativeMethods.UnhookWindowsHookEx(hMouseHook);
hMouseHook = 0;
if (retMouse == 0)
{
int errorCode = Marshal.GetLastWin32Error();
// throw new Win32Exception(errorCode);
Common.Log("Exception uninstalling Mouse hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture));
}
}
if (hKeyboardHook != 0)
{
int retKeyboard = NativeMethods.UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
if (retKeyboard == 0)
{
int errorCode = Marshal.GetLastWin32Error();
// throw new Win32Exception(errorCode);
Common.Log("Exception uninstalling keyboard hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture));
}
}
}
// Better performance, compared to Marshal.PtrToStructure.
private static MouseLLHookStruct LParamToMouseLLHookStruct(IntPtr lParam)
{
unsafe
{
return *(MouseLLHookStruct*)lParam;
}
}
private static KeyboardHookStruct LParamToKeyboardHookStruct(IntPtr lParam)
{
unsafe
{
return *(KeyboardHookStruct*)lParam;
}
}
private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
int rv = 1, dx = 0, dy = 0;
bool local = false;
Common.InputEventCount++;
try
{
if (!RealData)
{
RealData = true;
// Common.Log("MouseHookProc: Not real data!");
// return rv;
rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
else
{
Common.RealInputEventCount++;
if (Common.NewDesMachineID == Common.MachineID || Common.NewDesMachineID == ID.ALL)
{
local = true;
if (Common.MainFormVisible && !Common.IsDropping)
{
Common.MainFormDot();
}
}
if (nCode >= 0 && MouseEvent != null)
{
if (wParam == Common.WM_LBUTTONUP && SkipMouseUpCount > 0)
{
Common.LogDebug($"{nameof(SkipMouseUpCount)}: {SkipMouseUpCount}.");
SkipMouseUpCount--;
rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam);
return rv;
}
if ((wParam == Common.WM_LBUTTONUP || wParam == Common.WM_LBUTTONDOWN) && SkipMouseUpDown)
{
rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam);
return rv;
}
mouseHookStruct = LParamToMouseLLHookStruct(lParam);
hookCallbackMouseData.dwFlags = wParam;
// Use WheelDelta to store XBUTTON1/XBUTTON2 data.
hookCallbackMouseData.WheelDelta = (short)((mouseHookStruct.MouseData >> 16) & 0xffff);
if (local)
{
hookCallbackMouseData.X = mouseHookStruct.Pt.x;
hookCallbackMouseData.Y = mouseHookStruct.Pt.y;
if (Setting.Values.DrawMouse && Common.MouseCursorForm != null)
{
CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue);
}
}
else
{
if (Common.SwitchLocation.Count > 0 && Common.NewDesMachineID != Common.MachineID && Common.NewDesMachineID != ID.ALL)
{
Common.SwitchLocation.Count--;
if (Common.SwitchLocation.X > Common.XY_BY_PIXEL - 100000 || Common.SwitchLocation.Y > Common.XY_BY_PIXEL - 100000)
{
hookCallbackMouseData.X = Common.SwitchLocation.X - Common.XY_BY_PIXEL;
hookCallbackMouseData.Y = Common.SwitchLocation.Y - Common.XY_BY_PIXEL;
}
else
{
hookCallbackMouseData.X = (Common.SwitchLocation.X * Common.ScreenWidth / 65535) + Common.PrimaryScreenBounds.Left;
hookCallbackMouseData.Y = (Common.SwitchLocation.Y * Common.ScreenHeight / 65535) + Common.PrimaryScreenBounds.Top;
}
Common.HideMouseCursor(false);
}
else
{
dx = mouseHookStruct.Pt.x - Common.LastPos.X;
dy = mouseHookStruct.Pt.y - Common.LastPos.Y;
hookCallbackMouseData.X += dx;
hookCallbackMouseData.Y += dy;
if (hookCallbackMouseData.X < Common.PrimaryScreenBounds.Left)
{
hookCallbackMouseData.X = Common.PrimaryScreenBounds.Left - 1;
}
else if (hookCallbackMouseData.X > Common.PrimaryScreenBounds.Right)
{
hookCallbackMouseData.X = Common.PrimaryScreenBounds.Right + 1;
}
if (hookCallbackMouseData.Y < Common.PrimaryScreenBounds.Top)
{
hookCallbackMouseData.Y = Common.PrimaryScreenBounds.Top - 1;
}
else if (hookCallbackMouseData.Y > Common.PrimaryScreenBounds.Bottom)
{
hookCallbackMouseData.Y = Common.PrimaryScreenBounds.Bottom + 1;
}
dx += dx < 0 ? -Common.MOVE_MOUSE_RELATIVE : Common.MOVE_MOUSE_RELATIVE;
dy += dy < 0 ? -Common.MOVE_MOUSE_RELATIVE : Common.MOVE_MOUSE_RELATIVE;
}
}
MouseEvent(hookCallbackMouseData, dx, dy);
Common.DragDropStep01(wParam);
Common.DragDropStep09(wParam);
}
if (local)
{
rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
}
}
catch (Exception e)
{
Common.Log(e);
rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
return rv;
}
private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
{
Common.InputEventCount++;
if (!RealData)
{
return NativeMethods.CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
Common.RealInputEventCount++;
keyboardHookStruct = LParamToKeyboardHookStruct(lParam);
hookCallbackKeybdData.dwFlags = keyboardHookStruct.Flags;
hookCallbackKeybdData.wVk = (short)keyboardHookStruct.VkCode;
if (nCode >= 0 && KeyboardEvent != null)
{
if (!ProcessKeyEx(keyboardHookStruct.VkCode, keyboardHookStruct.Flags, hookCallbackKeybdData))
{
return 1;
}
KeyboardEvent(hookCallbackKeybdData);
}
if (Common.DesMachineID == ID.NONE || Common.DesMachineID == ID.ALL || Common.DesMachineID == Common.MachineID)
{
if (nCode >= 0 && Setting.Values.UseVKMap && Setting.Values.VKMap != null && Setting.Values.VKMap.ContainsKey(hookCallbackKeybdData.wVk) && !CtrlDown)
{
InputSimulation.SendKey(hookCallbackKeybdData);
return 1;
}
else
{
return NativeMethods.CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
}
else
{
return 1;
}
}
// Returns false if we do not want to redirect the keystrokes to other machine(s).
private int ctrlTouchesDnIndex;
private int ctrlTouchesUpIndex = 1;
private const int CONTROL_TOUCH = 4;
private readonly long[] ctrlTouches = new long[CONTROL_TOUCH];
private bool ProcessKeyEx(int vkCode, int flags, KEYBDDATA hookCallbackKeybdData)
{
bool allTouched;
if ((flags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
{
EasyMouseKeyDown = false;
switch ((VK)vkCode)
{
case VK.LWIN:
case VK.RWIN:
winDown = false;
break;
case VK.LCONTROL:
case VK.RCONTROL:
CtrlDown = false;
if (Setting.Values.HotKeySwitch2AllPC == 1)
{
ctrlTouches[ctrlTouchesUpIndex] = Common.GetTick();
ctrlTouchesUpIndex = ((ctrlTouchesUpIndex % CONTROL_TOUCH) + 2) % CONTROL_TOUCH;
}
break;
case VK.LMENU:
case VK.RMENU:
altDown = false;
break;
case VK.LSHIFT:
shiftDown = false;
break;
default:
break;
}
}
else
{
UpdateEasyMouseKeyDown((VK)vkCode);
switch ((VK)vkCode)
{
case VK.LWIN:
case VK.RWIN:
winDown = true;
break;
case VK.LCONTROL:
case VK.RCONTROL:
Common.LogDebug("VK.RCONTROL");
CtrlDown = true;
if (Setting.Values.HotKeySwitch2AllPC == 1)
{
ctrlTouches[ctrlTouchesDnIndex] = Common.GetTick();
ctrlTouchesDnIndex = (ctrlTouchesDnIndex + 2) % CONTROL_TOUCH;
}
allTouched = true;
for (int i = 0; i < CONTROL_TOUCH; i++)
{
if (ctrlTouches[i] == 0 || Common.GetTick() - ctrlTouches[i] > 400)
{
allTouched = false;
break;
}
}
if (allTouched && Common.GetTick() - Common.JustGotAKey > 1000)
{
ResetLastSwitchKeys();
Common.SwitchToMultipleMode(Common.DesMachineID != ID.ALL, true);
}
break;
case VK.LMENU:
case VK.RMENU:
altDown = true;
break;
case VK.LSHIFT:
shiftDown = true;
break;
case VK.DELETE:
if (CtrlDown && altDown)
{
CtrlDown = altDown = false;
KeyboardEvent(hookCallbackKeybdData);
if (Common.DesMachineID != ID.ALL)
{
Common.SwitchToMachine(Common.MachineName.Trim());
}
/*
#if CUSTOMIZE_LOGON_SCREEN
Common.DoSomethingInUIThread(delegate()
{
Common.MainForm.LoadNewLogonBackground();
});
#endif
* */
}
break;
case (VK)'L':
if (winDown)
{
winDown = false;
if (Common.DesMachineID != ID.ALL)
{
KeyboardEvent(hookCallbackKeybdData);
Common.SwitchToMachine(Common.MachineName.Trim());
}
}
else
{
return ProcessHotKeys(vkCode, hookCallbackKeybdData);
}
break;
case VK.ESCAPE:
if (Common.IsTopMostMessageNotNull())
{
Common.HideTopMostMessage();
}
break;
default:
Common.LogDebug("X");
return ProcessHotKeys(vkCode, hookCallbackKeybdData);
}
}
return true;
}
private void UpdateEasyMouseKeyDown(VK vkCode)
{
EasyMouseOption easyMouseOption = (EasyMouseOption)Setting.Values.EasyMouse;
EasyMouseKeyDown = (easyMouseOption == EasyMouseOption.Ctrl && (vkCode == VK.LCONTROL || vkCode == VK.RCONTROL))
|| (easyMouseOption == EasyMouseOption.Shift && (vkCode == VK.LSHIFT || vkCode == VK.RSHIFT));
}
private static long lastHotKeyLockMachine;
private bool ProcessHotKeys(int vkCode, KEYBDDATA hookCallbackKeybdData)
{
if (vkCode == Setting.Values.HotKeyCaptureScreen && CtrlDown && shiftDown && !altDown &&
(Common.DesMachineID == Common.MachineID || Common.DesMachineID == ID.ALL))
{
CtrlDown = shiftDown = false;
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Common.PrepareScreenCapture();
return false;
}
}
if (CtrlDown && altDown)
{
if (shiftDown && vkCode == Setting.Values.HotKeyExitMM &&
(Common.DesMachineID == Common.MachineID || Common.DesMachineID == ID.ALL))
{
Common.DoSomethingInUIThread(() =>
{
Common.MainForm.NotifyIcon.Visible = false;
for (int i = 1; i < 10; i++)
{
Application.DoEvents();
Thread.Sleep(20);
}
Common.MainForm.Quit(false, false);
});
}
else if (vkCode == Setting.Values.HotKeySwitchMachine ||
vkCode == Setting.Values.HotKeySwitchMachine + 1 ||
vkCode == Setting.Values.HotKeySwitchMachine + 2 ||
vkCode == Setting.Values.HotKeySwitchMachine + 3)
{
if (Switch2(vkCode - Setting.Values.HotKeySwitchMachine))
{
return false;
}
}
else if (vkCode == Setting.Values.HotKeyLockMachine)
{
if (!Common.RunOnLogonDesktop
&& !Common.RunOnScrSaverDesktop)
{
if (Common.GetTick() - lastHotKeyLockMachine < 500)
{
Common.SwitchToMultipleMode(true, true);
hookCallbackKeybdData.wVk = (short)VK.LCONTROL;
KeyboardEvent(hookCallbackKeybdData);
hookCallbackKeybdData.wVk = (short)VK.LMENU;
KeyboardEvent(hookCallbackKeybdData);
hookCallbackKeybdData.wVk = vkCode;
KeyboardEvent(hookCallbackKeybdData);
hookCallbackKeybdData.dwFlags |= (int)Common.LLKHF.UP;
hookCallbackKeybdData.wVk = (short)VK.LCONTROL;
KeyboardEvent(hookCallbackKeybdData);
hookCallbackKeybdData.wVk = (short)VK.LMENU;
KeyboardEvent(hookCallbackKeybdData);
hookCallbackKeybdData.wVk = vkCode;
KeyboardEvent(hookCallbackKeybdData);
Common.SwitchToMultipleMode(false, true);
_ = NativeMethods.LockWorkStation();
}
else
{
KeyboardEvent(hookCallbackKeybdData);
}
lastHotKeyLockMachine = Common.GetTick();
return false;
}
}
else if (vkCode == Setting.Values.HotKeyReconnect)
{
Common.ShowToolTip("Reconnecting...", 2000);
Common.LastReconnectByHotKeyTime = Common.GetTick();
Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY;
return false;
}
else if (vkCode == Setting.Values.HotKeySwitch2AllPC)
{
Common.SwitchToMultipleMode(Common.DesMachineID != ID.ALL, true);
return false;
}
else if (vkCode == Setting.Values.HotKeyToggleEasyMouse)
{
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
EasyMouseOption easyMouseOption = (EasyMouseOption)Setting.Values.EasyMouse;
if (easyMouseOption is EasyMouseOption.Disable or EasyMouseOption.Enable)
{
Setting.Values.EasyMouse = (int)(easyMouseOption == EasyMouseOption.Disable ? EasyMouseOption.Enable : EasyMouseOption.Disable);
Common.ShowToolTip($"Easy Mouse has been toggled to [{(EasyMouseOption)Setting.Values.EasyMouse}] by a hotkey. You can change the hotkey in the Settings form.", 5000);
return false;
}
}
}
else if (shiftDown && Setting.Values.VKMap != null && vkCode == (Setting.Values.VKMap.ContainsKey(0) ? (int)Setting.Values.VKMap[0] : 'K'))
{
if (Common.DesMachineID == Common.MachineID || Common.DesMachineID == ID.ALL)
{
Setting.Values.UseVKMap = !Setting.Values.UseVKMap;
return Common.DesMachineID == ID.ALL;
}
}
}
return true;
}
private static bool Switch2(int index)
{
if (Common.MachineMatrix != null && Common.MachineMatrix.Length > index)
{
string mcName = Common.MachineMatrix[index].Trim();
if (!string.IsNullOrEmpty(mcName))
{
// Common.DoSomethingInUIThread(delegate()
{
Common.ReleaseAllKeys();
}
// );
Common.SwitchToMachine(mcName);
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Common.ShowToolTip(
string.Format(
CultureInfo.CurrentCulture,
"Control has been switched to {0} by the hotkey Ctrl+Alt+{1}{2}",
mcName,
Setting.Values.HotKeySwitchMachine == (int)VK.F1 ? "F" : string.Empty,
index + 1),
3000);
}
return true;
}
}
return false;
}
internal void ResetLastSwitchKeys()
{
for (int i = 0; i < CONTROL_TOUCH; i++)
{
ctrlTouches[i] = 0;
}
CtrlDown = winDown = altDown = false;
}
}
}

View File

@@ -0,0 +1,467 @@
// 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.
// <summary>
// Keyboard/Mouse simulation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading.Tasks;
[module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.InputSimulation.#keybd_event(System.Byte,System.Byte,System.UInt32,System.Int32)", MessageId = "3", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.InputSimulation.#InputProcessKeyEx(System.Int32,System.Int32,System.Boolean&)", MessageId = "MouseWithoutBorders.NativeMethods.LockWorkStation", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders.Class
{
internal class InputSimulation
{
private InputSimulation()
{
}
private static uint SendInputEx(NativeMethods.INPUT input)
{
Common.PaintCount = 0;
uint rv;
if (Common.Is64bitOS)
{
NativeMethods.INPUT64 input64 = default;
input64.type = input.type;
// Keyboard
if (input.type == 1)
{
input64.ki.wVk = input.ki.wVk;
input64.ki.wScan = input.ki.wScan;
input64.ki.dwFlags = input.ki.dwFlags;
input64.ki.time = input.ki.time;
input64.ki.dwExtraInfo = input.ki.dwExtraInfo;
}
// Mouse
else
{
input64.mi.dx = input.mi.dx;
input64.mi.dy = input.mi.dy;
input64.mi.dwFlags = input.mi.dwFlags;
input64.mi.mouseData = input.mi.mouseData;
input64.mi.time = input.mi.time;
input64.mi.dwExtraInfo = input.mi.dwExtraInfo;
}
NativeMethods.INPUT64[] inputs = { input64 };
// mouse click simulation
// TODO: Find alternative API that simulates mouse input more directly
rv = NativeMethods.SendInput64(1, inputs, Marshal.SizeOf(input64));
}
else
{
NativeMethods.INPUT[] inputs = { input };
// TODO: Find alternative API that simulates mouse input more directly
rv = NativeMethods.SendInput(1, inputs, Marshal.SizeOf(input));
}
return rv;
}
internal static void SendKey(KEYBDDATA kd)
{
string log = string.Empty;
NativeMethods.KEYEVENTF dwFlags = NativeMethods.KEYEVENTF.KEYDOWN;
uint scanCode = 0;
// http://msdn.microsoft.com/en-us/library/ms644967(VS.85).aspx
if ((kd.dwFlags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
{
dwFlags = NativeMethods.KEYEVENTF.KEYUP;
}
if ((kd.dwFlags & (int)Common.LLKHF.EXTENDED) == (int)Common.LLKHF.EXTENDED)
{
dwFlags |= NativeMethods.KEYEVENTF.EXTENDEDKEY;
}
scanCode = NativeMethods.MapVirtualKey((uint)kd.wVk, 0);
InputProcessKeyEx(kd.wVk, kd.dwFlags, out bool eatKey);
if (Setting.Values.UseVKMap && Setting.Values.VKMap != null && Setting.Values.VKMap.ContainsKey(kd.wVk) && !ctrlDown)
{
kd.wVk = (int)Setting.Values.VKMap[kd.wVk];
}
if (!eatKey)
{
InputHook.RealData = false;
#if !USING_keybd_event
// #if USING_SendInput
NativeMethods.INPUT structInput;
structInput = default;
structInput.type = 1;
structInput.ki.wScan = (short)scanCode;
structInput.ki.time = 0;
structInput.ki.wVk = (short)kd.wVk;
structInput.ki.dwFlags = (int)dwFlags;
structInput.ki.dwExtraInfo = NativeMethods.GetMessageExtraInfo();
Common.DoSomethingInTheInputSimulationThread(() =>
{
// key press simulation
SendInputEx(structInput);
});
#else
keybd_event((byte)kd.wVk, (byte)scanCode, (UInt32)dwFlags, 0);
#endif
InputHook.RealData = true;
}
log += "*"; // ((Keys)kd.wVk).ToString(CultureInfo.InvariantCulture);
Common.LogDebug(log);
}
// Md.X, Md.Y is from 0 to 65535
internal static uint SendMouse(MOUSEDATA md)
{
uint rv = 0;
NativeMethods.INPUT mouse_input = default;
long w65535 = (Common.DesktopBounds.Right - Common.DesktopBounds.Left) * 65535 / Common.ScreenWidth;
long h65535 = (Common.DesktopBounds.Bottom - Common.DesktopBounds.Top) * 65535 / Common.ScreenHeight;
long l65535 = Common.DesktopBounds.Left * 65535 / Common.ScreenWidth;
long t65535 = Common.DesktopBounds.Top * 65535 / Common.ScreenHeight;
mouse_input.type = 0;
long dx = (md.X * w65535 / 65535) + l65535;
long dy = (md.Y * h65535 / 65535) + t65535;
mouse_input.mi.dx = (int)dx;
mouse_input.mi.dy = (int)dy;
mouse_input.mi.mouseData = md.WheelDelta;
if (md.dwFlags != Common.WM_MOUSEMOVE)
{
Common.LogDebug($"InputSimulation.SendMouse: x = {md.X}, y = {md.Y}, WheelDelta = {md.WheelDelta}, dwFlags = {md.dwFlags}.");
}
switch (md.dwFlags)
{
case Common.WM_MOUSEMOVE:
mouse_input.mi.dwFlags |= (int)(NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE);
break;
case Common.WM_LBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.LEFTDOWN;
break;
case Common.WM_LBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.LEFTUP;
break;
case Common.WM_RBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.RIGHTDOWN;
break;
case Common.WM_RBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.RIGHTUP;
break;
case Common.WM_MBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.MIDDLEDOWN;
break;
case Common.WM_MBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.MIDDLEUP;
break;
case Common.WM_MOUSEWHEEL:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.WHEEL;
break;
case Common.WM_XBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.XUP;
break;
case Common.WM_XBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.XDOWN;
break;
default:
break;
}
Common.DoSomethingInTheInputSimulationThread(() =>
{
InputHook.RealData = false;
rv = SendInputEx(mouse_input);
});
if (Common.MainFormVisible && !Common.IsDropping)
{
Common.MainFormDot();
}
return rv;
}
internal static void MoveMouseEx(int x, int y)
{
NativeMethods.INPUT mouse_input = default;
long w65535 = (Common.DesktopBounds.Right - Common.DesktopBounds.Left) * 65535 / Common.ScreenWidth;
long h65535 = (Common.DesktopBounds.Bottom - Common.DesktopBounds.Top) * 65535 / Common.ScreenHeight;
long l65535 = Common.DesktopBounds.Left * 65535 / Common.ScreenWidth;
long t65535 = Common.DesktopBounds.Top * 65535 / Common.ScreenHeight;
mouse_input.type = 0;
long dx = (x * w65535 / 65535) + l65535;
long dy = (y * h65535 / 65535) + t65535;
mouse_input.mi.dx = (int)dx;
mouse_input.mi.dy = (int)dy;
Common.LogDebug($"InputSimulation.MoveMouseEx: x = {x}, y = {y}.");
mouse_input.mi.dwFlags |= (int)(NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE);
Common.DoSomethingInTheInputSimulationThread(() =>
{
InputHook.RealData = false;
SendInputEx(mouse_input);
});
}
// x, y is in pixel
internal static void MoveMouse(int x, int y)
{
// Common.Log("Mouse move: " + x.ToString(CultureInfo.CurrentCulture) + "," + y.ToString(CultureInfo.CurrentCulture));
NativeMethods.INPUT mouse_input = default;
mouse_input.type = 0;
mouse_input.mi.dx = x * 65535 / Common.ScreenWidth;
mouse_input.mi.dy = y * 65535 / Common.ScreenHeight;
mouse_input.mi.mouseData = 0;
mouse_input.mi.dwFlags = (int)(NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE);
Common.LogDebug($"InputSimulation.MoveMouse: x = {x}, y = {y}.");
Common.DoSomethingInTheInputSimulationThread(() =>
{
InputHook.RealData = false;
SendInputEx(mouse_input);
// NativeMethods.SetCursorPos(x, y);
});
}
// dx, dy is in pixel, relative
internal static void MoveMouseRelative(int dx, int dy)
{
NativeMethods.INPUT mouse_input = default;
mouse_input.type = 0;
mouse_input.mi.dx = dx; // *65535 / Common.ScreenWidth;
mouse_input.mi.dy = dy; // *65535 / Common.ScreenHeight;
mouse_input.mi.mouseData = 0;
mouse_input.mi.dwFlags = (int)NativeMethods.MOUSEEVENTF.MOVE;
Common.LogDebug($"InputSimulation.MoveMouseRelative: x = {dx}, y = {dy}.");
Common.DoSomethingInTheInputSimulationThread(() =>
{
InputHook.RealData = false;
SendInputEx(mouse_input);
// NativeMethods.SetCursorPos(x, y);
});
}
internal static void MouseUp()
{
Common.DoSomethingInTheInputSimulationThread(() =>
{
NativeMethods.INPUT input = default;
input.type = 0;
input.mi.dx = 0;
input.mi.dy = 0;
input.mi.mouseData = 0;
input.mi.dwFlags = (int)NativeMethods.MOUSEEVENTF.LEFTUP;
InputHook.SkipMouseUpCount++;
_ = SendInputEx(input);
Common.LogDebug("MouseUp() called");
});
}
internal static void MouseClickDotForm(int x, int y)
{
_ = Task.Factory.StartNew(
() =>
{
NativeMethods.INPUT input = default;
input.type = 0;
input.mi.dx = 0;
input.mi.dy = 0;
input.mi.mouseData = 0;
InputHook.SkipMouseUpDown = true;
try
{
MoveMouse(x, y);
InputHook.RealData = false;
input.mi.dwFlags = (int)NativeMethods.MOUSEEVENTF.LEFTDOWN;
_ = SendInputEx(input);
InputHook.RealData = false;
input.mi.dwFlags = (int)NativeMethods.MOUSEEVENTF.LEFTUP;
_ = SendInputEx(input);
Common.LogDebug("MouseClick() called");
Thread.Sleep(200);
}
finally
{
InputHook.SkipMouseUpDown = false;
}
},
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
}
private static bool winDown;
private static bool ctrlDown;
private static bool altDown;
private static bool shiftDown;
private static void InputProcessKeyEx(int vkCode, int flags, out bool eatKey)
{
eatKey = false;
if ((flags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
{
switch ((VK)vkCode)
{
case VK.LWIN:
case VK.RWIN:
winDown = false;
break;
case VK.LCONTROL:
case VK.RCONTROL:
ctrlDown = false;
break;
case VK.LMENU:
case VK.RMENU:
altDown = false;
break;
case VK.LSHIFT:
case VK.RSHIFT:
shiftDown = false;
break;
default:
break;
}
}
else
{
if (vkCode == Setting.Values.HotKeyLockMachine)
{
if (!Common.RunOnLogonDesktop
&& !Common.RunOnScrSaverDesktop)
{
if (ctrlDown && altDown)
{
ctrlDown = altDown = false;
eatKey = true;
Common.ReleaseAllKeys();
_ = NativeMethods.LockWorkStation();
}
}
}
else if (Setting.Values.VKMap != null && vkCode == (Setting.Values.VKMap.ContainsKey(0) ? (int)Setting.Values.VKMap[0] : 'K'))
{
if (ctrlDown && altDown && shiftDown)
{
ctrlDown = altDown = shiftDown = false;
Setting.Values.UseVKMap = !Setting.Values.UseVKMap;
eatKey = true;
}
}
else if (vkCode == Setting.Values.HotKeyCaptureScreen && ctrlDown && shiftDown && !altDown)
{
ctrlDown = shiftDown = false;
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Common.PrepareScreenCapture();
eatKey = true;
}
}
switch ((VK)vkCode)
{
case VK.LWIN:
case VK.RWIN:
winDown = true;
break;
case VK.LCONTROL:
case VK.RCONTROL:
ctrlDown = true;
break;
case VK.LMENU:
case VK.RMENU:
altDown = true;
break;
case VK.LSHIFT:
case VK.RSHIFT:
shiftDown = true;
break;
case VK.DELETE:
if (ctrlDown && altDown)
{
ctrlDown = altDown = false;
eatKey = true;
Common.ReleaseAllKeys();
}
break;
case (VK)'L':
if (winDown)
{
winDown = false;
eatKey = true;
Common.ReleaseAllKeys();
uint rv = NativeMethods.LockWorkStation();
Common.LogDebug("LockWorkStation returned " + rv.ToString(CultureInfo.CurrentCulture));
}
break;
case VK.END:
if (ctrlDown && altDown)
{
ctrlDown = altDown = false;
new ServiceController("MouseWithoutBordersSvc").Start(new string[] { "CAD" });
}
break;
default:
break;
}
}
}
internal static void ResetSystemKeyFlags()
{
ctrlDown = winDown = altDown = false;
}
}
}

View File

@@ -0,0 +1,342 @@
// 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.
// <history>
// Class created as refactoring by Tim Lovell-Smith (tilovell)
// 2023- Included in PowerToys.
// </history>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace MouseWithoutBorders.Class
{
/// <summary>
/// Holds our current 'last known state of the world' information about Machines the user wants to connect to.
/// Keeps track of whether machines are 'alive' or 'disconnected' implicitly via timestamps.
/// Only ever knows about Common.MAX_MACHINE machines.
/// </summary>
/// <remarks>
/// Operations in the class are designed to be easily thread-safe.
/// There are two factors helping this:
///
/// 1) Minimal calls outside of itself.
/// This class doesn't call other classes to do any 'interesting' or *potentially locking* work. This helps us avoid deadlock. (Common.GetTick() is safe)
///
/// 2) Best-effort semantics. Callers will find that sometimes the thing they wanted to do just won't work,
/// due to unfortunate timing - machine not found, too many machines, or whatever.
/// e.g.
/// - Every update is a 'try' to update.
/// - Every find is a 'try' to find.
/// etc.
///
/// Please write regression tests (unit tests) for bugs before fixing them.
/// </remarks>
internal class MachinePool
{
private readonly object @lock;
private readonly List<MachineInf> list;
public MachinePool()
{
@lock = new object();
list = new List<MachineInf>();
}
// This will set the timestamp to current time, making the machine 'alive'.
internal bool TryUpdateMachineID(string machineName, ID id, bool updateTimeStamp)
{
bool rv = false;
lock (@lock)
{
CheckName(machineName);
for (int i = 0; i < list.Count; i++)
{
if (NamesAreEqual(machineName, list[i].Name))
{
list[i] =
// This looks funny - but you have to rebuild the actual struct because List<T> doesn't let you make updates to struct fields.
new MachineInf
{
Name = list[i].Name,
Id = id,
Time = updateTimeStamp ? Common.GetTick() : list[i].Time,
};
rv = true;
}
else if (list[i].Id == id)
{
// Duplicate ID? Reset the old machine's.
list[i] = new MachineInf
{
Name = list[i].Name,
Id = ID.NONE,
Time = updateTimeStamp ? Common.GetTick() : list[i].Time,
};
}
}
}
return rv;
}
/// <summary>
/// Set a machine to 'disconnected' state due to Socket/Channel exception (overriding timestamp).
/// Returns true if a matching machine name existed (regardless of whether the machine was already connected).
/// </summary>
/// <remarks>
/// Explanation: When two machines are connected and one of them hibernates (or sleeps),
/// the socket state of the other machine is still Connected and there would be an
/// exception when we try to send the data using the socket.
/// In this case we would want to remove the machine and move the Mouse control back to the host machine…
/// </remarks>
internal bool SetMachineDisconnected(string machineName)
{
lock (@lock)
{
bool foundAndTimedOut = false;
CheckName(machineName);
for (int i = 0; i < list.Count; i++)
{
if (NamesAreEqual(machineName, list[i].Name))
{
list[i] =
// This looks funny - but you have to rebuild the actual struct because List<T> doesn't let you make updates to struct fields.
new MachineInf
{
Name = list[i].Name,
Id = list[i].Id,
Time = list[i].Time > Common.GetTick() - Common.HEARTBEAT_TIMEOUT + 10000 ? Common.GetTick() - Common.HEARTBEAT_TIMEOUT + 10000 : list[i].Time,
};
foundAndTimedOut = list[i].Time < Common.GetTick() - Common.HEARTBEAT_TIMEOUT + 10000 - 5000;
}
}
return foundAndTimedOut;
}
}
// TODO: would probably be cleaner interface as IEnumerable
internal List<MachineInf> ListAllMachines()
{
return Where((inf) => true);
}
internal List<MachineInf> TryFindMachineByID(ID id)
{
return id == ID.NONE ? new List<MachineInf>() : Where((inf) => inf.Id == id);
}
internal void Clear()
{
lock (@lock)
{
list.Clear();
}
}
public void Initialize(IEnumerable<string> machineNames)
{
lock (@lock)
{
list.Clear();
foreach (string name in machineNames)
{
if (string.IsNullOrEmpty(name.Trim()))
{
continue; // next
}
else if (list.Count >= 4)
{
throw new ArgumentException("machineNames.Length > Common.MAX_MACHINE");
}
_ = LearnMachine(name);
}
}
}
public void Initialize(IEnumerable<MachineInf> infos)
{
lock (@lock)
{
list.Clear();
foreach (MachineInf inf in infos)
{
if (string.IsNullOrEmpty(inf.Name.Trim()))
{
continue; // next
}
else if (list.Count >= 4)
{
throw new ArgumentException("infos.Length > Common.MAX_MACHINE");
}
_ = LearnMachine(inf.Name);
_ = TryUpdateMachineID(inf.Name, inf.Id, false);
}
}
}
// Add a new machine to our set of known machines. Initially it is 'disconnected'.
// Fails and return false if the machine pool is already full.
public bool LearnMachine(string machineName)
{
if (machineName == null)
{
throw new ArgumentNullException(machineName);
}
else if (string.IsNullOrEmpty(machineName.Trim()))
{
throw new ArgumentException(machineName);
}
lock (@lock)
{
CheckName(machineName);
for (int i = 0; i < list.Count; i++)
{
if (NamesAreEqual(list[i].Name, machineName))
{
return false; // already in list
}
}
if (list.Count >= Common.MAX_MACHINE)
{
int slotFound = -1;
for (int i = 0; i < list.Count; i++)
{
if (!Common.InMachineMatrix(list[i].Name))
{
slotFound = i;
break;
}
}
if (slotFound >= 0)
{
list.RemoveAt(slotFound);
}
else
{
return false;
}
}
list.Add(new MachineInf { Name = machineName });
return true;
}
}
internal bool TryFindMachineByName(string machineName, out MachineInf result)
{
lock (@lock)
{
CheckName(machineName);
for (int i = 0; i < list.Count; i++)
{
if (NamesAreEqual(list[i].Name, machineName))
{
result = list[i];
return true;
}
}
result = default;
return false;
}
}
internal ID ResolveID(string machineName)
{
return TryFindMachineByName(machineName, out MachineInf inf) ? inf.Id : ID.NONE;
}
private List<MachineInf> Where(Predicate<MachineInf> test)
{
lock (@lock)
{
List<MachineInf> ret = new();
for (int i = 0; i < list.Count; i++)
{
if (test(list[i]))
{
ret.Add(list[i]);
}
}
return ret;
}
}
internal string SerializedAsString()
{
lock (@lock)
{
List<MachineInf> machinePool = ListAllMachines();
string rv = string.Join(",", machinePool.Select(m => $"{m.Name}:{m.Id}"));
for (int j = machinePool.Count; j < Common.MAX_MACHINE; j++)
{
rv += ",:";
}
return rv;
}
}
/// <param name="clockSkewInMS_forTesting">When doing unit tests it's nice to be able to fudge with the clock time, adding milliseconds, instead of sleeping.</param>
internal static bool IsAlive(MachineInf inf, int clockSkewInMS_forTesting = 0)
{
return inf.Id != ID.NONE && (Common.GetTick() + clockSkewInMS_forTesting - inf.Time <= Common.HEARTBEAT_TIMEOUT || Common.IsConnectedTo(inf.Id));
}
private static bool NamesAreEqual(string name1, string name2)
{
return string.Equals(name1, name2, StringComparison.OrdinalIgnoreCase);
}
private static void CheckName(string machineName)
{
Debug.Assert(machineName != null, "machineName is null");
Debug.Assert(machineName.Trim().Length == machineName.Length, "machineName contains spaces");
}
internal void ResetIPAddressesForDeadMachines(bool firstLoaded = false)
{
lock (@lock)
{
for (int i = 0; i < list.Count; i++)
{
if (!string.IsNullOrEmpty(list[i].Name) && list[i].Name.Equals(Common.MachineName, StringComparison.Ordinal))
{
continue;
}
if ((firstLoaded && !Common.InMachineMatrix(list[i].Name)) || (!firstLoaded && (!Common.InMachineMatrix(list[i].Name) || !IsAlive(list[i]))))
{
list[i] =
new MachineInf
{
Name = list[i].Name,
Id = ID.NONE,
Time = list[i].Time,
};
}
}
}
}
}
}

View File

@@ -0,0 +1,40 @@
// 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;
namespace MouseWithoutBorders.Class
{
internal static class MachinePoolHelpers
{
internal static MachineInf[] LoadMachineInfoFromMachinePoolStringSetting(string s)
{
if (s == null)
{
throw new ArgumentNullException(s);
}
string[] st = s.Split(new char[] { ',' });
if (st.Length < Common.MAX_MACHINE)
{
throw new ArgumentException("Not enough elements in encoded MachinePool string");
}
MachineInf[] rv = new MachineInf[Common.MAX_MACHINE];
for (int i = 0; i < Common.MAX_MACHINE; i++)
{
string[] mc = st[i].Split(new char[] { ':' });
if (mc.Length == 2)
{
rv[i].Name = mc[0];
rv[i].Id = uint.TryParse(mc[1], out uint ip) ? (ID)ip : ID.NONE;
rv[i].Time = rv[i].Id == ID.NONE ? Common.GetTick() - Common.HEARTBEAT_TIMEOUT : Common.GetTick();
}
}
return rv;
}
}
}

View File

@@ -0,0 +1,20 @@
// 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.
namespace MouseWithoutBorders.Class
{
internal class MouseLocation
{
internal int X { get; set; }
internal int Y { get; set; }
internal int Count { get; set; }
internal void ResetCount()
{
Count = 1;
}
}
}

View File

@@ -0,0 +1,159 @@
// 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.
// <summary>
// Customed bitmap parser.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.MyKnownBitmap.#FromFile(System.string)", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders.Class
{
#if CUSTOMIZE_LOGON_SCREEN
internal class MyKnownBitmap
{
private const int BITMAP_FILE_HEADER_SIZE = 14;
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = BITMAP_FILE_HEADER_SIZE)]
private struct BITMAPFILEHEADER
{
public ushort BfType;
public uint BfSize;
public ushort BfReserved1;
public ushort BfReserved2;
public uint BfOffBits;
}
private const int BITMAP_INFO_HEADER_SIZE = 40;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct BITMAPINFOHEADER
{
public uint BiSize;
public int BiWidth;
public int BiHeight;
public ushort BiPlanes;
public ushort BiBitCount;
public uint BiCompression;
public uint BiSizeImage;
public int BiXPelsPerMeter;
public int BiYPelsPerMeter;
public uint BiClrUsed;
public uint BiClrImportant;
}
private const long MAX_FILE_SIZE = 10 * 1024 * 1024;
internal static object FromFile(string bitmapFile)
{
IntPtr p;
byte[] buf;
try
{
FileStream f = File.OpenRead(bitmapFile);
long fs = f.Length;
if (fs is < 1024 or > MAX_FILE_SIZE)
{
f.Close();
return string.Format(CultureInfo.CurrentCulture, "File Size exception: {0}", fs);
}
BITMAPFILEHEADER bitmapFileHeader;
buf = new byte[BITMAP_FILE_HEADER_SIZE];
p = Marshal.AllocHGlobal(BITMAP_FILE_HEADER_SIZE);
if (buf == null)
{
f.Close();
return "p or buf is null! (1)";
}
_ = f.Read(buf, 0, buf.Length);
Marshal.Copy(buf, 0, p, buf.Length);
bitmapFileHeader = (BITMAPFILEHEADER)Marshal.PtrToStructure(p, typeof(BITMAPFILEHEADER));
Marshal.FreeHGlobal(p);
BITMAPINFOHEADER bitmapInfoHeader;
buf = new byte[BITMAP_INFO_HEADER_SIZE];
p = Marshal.AllocHGlobal(BITMAP_INFO_HEADER_SIZE);
if (buf == null)
{
f.Close();
return "p or buf is null! (2)";
}
_ = f.Read(buf, 0, buf.Length);
Marshal.Copy(buf, 0, p, buf.Length);
bitmapInfoHeader = (BITMAPINFOHEADER)Marshal.PtrToStructure(p, typeof(BITMAPINFOHEADER));
Marshal.FreeHGlobal(p);
if (bitmapFileHeader.BfType != 0x4D42 || bitmapFileHeader.BfSize != fs || bitmapFileHeader.BfOffBits != BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_SIZE ||
bitmapInfoHeader.BiBitCount != 24 || bitmapInfoHeader.BiCompression != 0 || bitmapInfoHeader.BiSize != BITMAP_INFO_HEADER_SIZE || bitmapInfoHeader.BiPlanes != 1 ||
bitmapInfoHeader.BiWidth <= 0 || bitmapInfoHeader.BiWidth > 4096 ||
bitmapInfoHeader.BiHeight <= 0 || bitmapInfoHeader.BiHeight > 4096)
{
f.Close();
return string.Format(
CultureInfo.CurrentCulture,
"Bitmap Format Exception: {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}",
bitmapFileHeader.BfType,
bitmapFileHeader.BfSize,
bitmapFileHeader.BfOffBits,
bitmapInfoHeader.BiBitCount,
bitmapInfoHeader.BiCompression,
bitmapInfoHeader.BiSize,
bitmapInfoHeader.BiPlanes,
bitmapInfoHeader.BiWidth,
bitmapInfoHeader.BiWidth,
bitmapInfoHeader.BiHeight,
bitmapInfoHeader.BiHeight);
}
Bitmap bm = new(bitmapInfoHeader.BiWidth, bitmapInfoHeader.BiHeight, PixelFormat.Format24bppRgb);
BitmapData bd = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
buf = new byte[bm.Width * bm.Height * 3];
_ = f.Read(buf, 0, buf.Length);
f.Close();
int ws = buf.Length < (bd.Width * bd.Stride) ? buf.Length : bd.Width * bd.Stride;
// Should never happen
if (ws <= 0)
{
bm.UnlockBits(bd);
bm.Dispose();
return string.Format(CultureInfo.CurrentCulture, "Something wrong: {0} {1} {2}", buf.Length, bd.Width, bd.Stride);
}
Marshal.Copy(buf, 0, bd.Scan0, ws);
bm.UnlockBits(bd);
bm.RotateFlip(RotateFlipType.Rotate180FlipX);
return bm;
}
catch (Exception e)
{
return e.Message + e.StackTrace;
}
}
private MyKnownBitmap()
{
}
}
#endif
}

View File

@@ -0,0 +1,945 @@
// 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.
// <summary>
// Windows APIs.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
// We are sure we dont have managed resource in KEYBDINPUT, IntPtr just holds a value
[module: SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable", Scope = "type", Target = "MouseWithoutBorders.NativeMethods+KEYBDINPUT", Justification = "Dotnet port with style preservation")]
// Some other minor issues
[module: SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#ConvertStringSidToSid(System.String,System.IntPtr&)", MessageId = "0", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#DrawText(System.IntPtr,System.String,System.Int32,MouseWithoutBorders.NativeMethods+RECT&,System.UInt32)", MessageId = "1", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#SetWindowText(System.IntPtr,System.String)", MessageId = "1", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#FindWindow(System.String,System.String)", MessageId = "0", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#FindWindow(System.String,System.String)", MessageId = "1", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#GetWindowText(System.IntPtr,System.Text.StringBuilder,System.Int32)", MessageId = "1", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#keybd_event(System.Byte,System.Byte,System.UInt32,System.Int32)", MessageId = "3", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", MessageId = "return", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Scope = "member", Target = "MouseWithoutBorders.NativeMethods+KEYBDINPUT.#dwExtraInfo", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Scope = "member", Target = "MouseWithoutBorders.NativeMethods+INPUT64.#type", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#TerminateProcess(System.IntPtr,System.IntPtr)", MessageId = "1", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#GetClassName(System.IntPtr,System.Text.StringBuilder,System.Int32)", MessageId = "1", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#GetClassName(System.IntPtr,System.Text.StringBuilder,System.Int32)", MessageId = "return", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#GetAsyncKeyState(System.IntPtr)", MessageId = "0", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#GetAsyncKeyState(System.IntPtr)", MessageId = "return", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders.Class
{
internal partial class NativeMethods
{
#if !MM_HELPER
[DllImport("user32.dll")]
internal static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
internal static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetProcessDPIAware();
[DllImport("Shcore.dll", SetLastError = true)]
internal static extern int SetProcessDpiAwareness(uint type); // Win 8.1 and up, DPI can be per monitor.
[DllImport("kernel32.dll")]
internal static extern uint WTSGetActiveConsoleSessionId();
#endif
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint msg, int action, IntPtr changeInfo);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool PostMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = false)]
internal static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
internal static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int DrawText(IntPtr hDC, string lpString, int nCount, ref RECT lpRect, uint uFormat);
[DllImport("gdi32.dll")]
internal static extern uint SetTextColor(IntPtr hdc, int crColor);
[DllImport("gdi32.dll")]
internal static extern uint SetBkColor(IntPtr hdc, int crColor);
[DllImport("user32.dll")]
internal static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
/*
internal const int SW_MAXIMIZE = 3;
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
public static WINDOWPLACEMENT Default
{
get
{
WINDOWPLACEMENT result = new WINDOWPLACEMENT();
result.length = Marshal.SizeOf(result);
return result;
}
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
* */
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetWindowText(IntPtr hWnd, string lpString);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DestroyIcon(IntPtr handle);
// [DllImport("user32.dll")]
// [return: MarshalAs(UnmanagedType.Bool)]
// internal static extern bool IsWindowVisible(IntPtr hWnd);
// [DllImport("user32")]
// internal static extern int GetKeyboardState(byte[] pbKeyState);
// [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
// internal static extern short GetKeyState(int vKey);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetCursorPos(int X, int Y);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Point p);
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DuplicateTokenEx(
IntPtr ExistingTokenHandle,
uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
int TokenType,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ConvertStringSidToSid(string StringSid, out IntPtr ptrSid);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, ref TOKEN_MANDATORY_LABEL TokenInformation, uint TokenInformationLength);
// [DllImport("advapi32.dll", SetLastError = true)]
// [return: MarshalAs(UnmanagedType.Bool)]
// internal static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength);
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", Justification = "Dotnet port with style preservation")]
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
int dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EnumDisplayMonitors(
IntPtr hdc,
IntPtr lprcClip,
EnumMonitorsDelegate lpfnEnum,
IntPtr dwData);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfoEx lpmi);
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
internal static extern int FindWindow(string ClassName, string WindowName);
[DllImport("kernel32.dll")]
internal static extern uint GetCurrentThreadId();
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetThreadDesktop(uint dwThreadId);
[DllImport("user32.dll")]
internal static extern short GetAsyncKeyState(IntPtr vKey); // Keys vKey
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[StructLayout(LayoutKind.Sequential)]
internal struct POINT
{
internal int x;
internal int y;
}
[StructLayout(LayoutKind.Sequential)]
internal struct CURSORINFO
{
public int cbSize;
public int flags;
public IntPtr hCursor;
public POINT ptScreenPos;
}
[DllImport("user32.dll")]
internal static extern bool GetCursorInfo(out CURSORINFO ci);
#if CUSTOMIZE_LOGON_SCREEN
[DllImport("kernel32", SetLastError = true)]
internal static extern uint WaitForSingleObject(IntPtr handle, int milliseconds);
internal const uint WAIT_OBJECT_0 = 0x00000000;
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
public int ExitStatus;
public int PebBaseAddress;
public int AffinityMask;
public int BasePriority;
public uint UniqueProcessId;
public uint InheritedFromUniqueProcessId;
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool TerminateProcess(IntPtr hProcess, IntPtr exitCode);
[DllImport("ntdll.dll")]
internal static extern int NtQueryInformationProcess(
IntPtr hProcess,
int processInformationClass /* 0 */,
ref PROCESS_BASIC_INFORMATION processBasicInformation,
uint processInformationLength,
out uint returnLength);
#endif
#if USE_GetSecurityDescriptorSacl
internal enum SE_OBJECT_TYPE
{
SE_UNKNOWN_OBJECT_TYPE = 0,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT,
SE_REGISTRY_WOW64_32KEY
}
[Flags]
internal enum SECURITY_INFORMATION : uint
{
LABEL_SECURITY_INFORMATION = 0x00000010
}
[StructLayoutAttribute(LayoutKind.Explicit)]
internal struct SECURITY_DESCRIPTOR
{
[FieldOffset(0)]
public byte revision;
[FieldOffset(1)]
public byte size;
[FieldOffset(2)]
public short control;
[FieldOffset(4)]
public IntPtr owner;
[FieldOffset(8)]
public IntPtr group;
[FieldOffset(12)]
public IntPtr sacl;
[FieldOffset(16)]
public IntPtr dacl;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ACL { public byte AclRevision; public byte Sbz1; public int AclSize; public int AceCount; public int Sbz2; }
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor,
UInt32 StringSDRevision, out SECURITY_DESCRIPTOR SecurityDescriptor, out UInt64 SecurityDescriptorSize);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int GetSecurityDescriptorSacl([MarshalAs(UnmanagedType.Struct)] ref SECURITY_DESCRIPTOR pSecurityDescriptor, int lpbSaclPresent, [MarshalAs(UnmanagedType.Struct)] ref ACL pSacl, int lpbSaclDefaulted);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
internal static extern uint SetNamedSecurityInfo(
string pObjectName,
SE_OBJECT_TYPE ObjectType,
SECURITY_INFORMATION SecurityInfo,
IntPtr psidOwner,
IntPtr psidGroup,
IntPtr pDacl,
IntPtr pSacl);
#endif
#if SINGLE_PROCESS
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetThreadDesktop(IntPtr hDesktop);
#endif
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr OpenInputDesktop(uint dwFlags, [MarshalAs(UnmanagedType.Bool)] bool fInherit, uint dwDesiredAccess);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, [Out] byte[] pvInfo, int nLength, out uint lpnLengthNeeded);
// [DllImport("user32.dll")]
// [return: MarshalAs(UnmanagedType.Bool)]
// internal static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
// [DllImport("gdi32.dll")]
// internal static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);
[DllImport("gdi32.dll")]
internal static extern uint SetPixel(IntPtr hdc, int X, int Y, uint crColor);
// internal const int WM_CLOSE = 16;
internal const int WM_SHOW_DRAG_DROP = 0x400;
internal const int WM_HIDE_DRAG_DROP = 0x401;
internal const int WM_CHECK_EXPLORER_DRAG_DROP = 0x402;
internal const int WM_QUIT = 0x403;
internal const int WM_SWITCH = 0x404;
internal const int WM_HIDE_DD_HELPER = 0x405;
internal const int WM_SHOW_SETTINGS_FORM = 0x406;
internal static readonly IntPtr HWND_TOPMOST = new(-1);
// internal static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
// internal static readonly IntPtr HWND_TOP = new IntPtr(0);
// internal static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
internal const uint SWP_NOSIZE = 0x0001;
internal const uint SWP_NOMOVE = 0x0002;
internal const uint SWP_NOZORDER = 0x0004;
internal const uint SWP_NOREDRAW = 0x0008;
internal const uint SWP_SHOWWINDOW = 0x0040;
internal const uint SWP_HIDEWINDOW = 0x0080;
internal const int UOI_FLAGS = 1;
internal const int UOI_NAME = 2;
internal const int UOI_TYPE = 3;
internal const int UOI_USER_SID = 4;
internal const uint DESKTOP_WRITEOBJECTS = 0x0080;
internal const uint DESKTOP_READOBJECTS = 0x0001;
internal const uint DF_ALLOWOTHERACCOUNTHOOK = 0x0001;
// internal const UInt32 GENERIC_READ = 0x80000000;
internal const uint GENERIC_WRITE = 0x40000000;
// internal const UInt32 GENERIC_EXECUTE = 0x20000000;
internal const uint GENERIC_ALL = 0x10000000;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct RECT
{
internal int Left;
internal int Top;
internal int Right;
internal int Bottom;
}
// size of a device name string
internal const int CCHDEVICENAME = 32;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct MonitorInfoEx
{
internal int cbSize;
internal RECT rcMonitor;
internal RECT rcWork;
internal uint dwFlags;
// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
// internal string szDeviceName;
}
// We are WOW
[DllImport(
"user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
internal static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport(
"user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
internal static extern int UnhookWindowsHookEx(int idHook);
// In X64, we are running WOW
[DllImport(
"user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
internal static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
// [DllImport("user32")]
// internal static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);
private enum InputType
{
INPUT_MOUSE = 0,
INPUT_KEYBOARD = 1,
INPUT_HARDWARE = 2,
}
[Flags]
internal enum MOUSEEVENTF
{
MOVE = 0x0001,
LEFTDOWN = 0x0002,
LEFTUP = 0x0004,
RIGHTDOWN = 0x0008,
RIGHTUP = 0x0010,
MIDDLEDOWN = 0x0020,
MIDDLEUP = 0x0040,
XDOWN = 0x0080,
XUP = 0x0100,
WHEEL = 0x0800,
VIRTUALDESK = 0x4000,
ABSOLUTE = 0x8000,
}
[Flags]
internal enum KEYEVENTF
{
KEYDOWN = 0x0000,
EXTENDEDKEY = 0x0001,
KEYUP = 0x0002,
UNICODE = 0x0004,
SCANCODE = 0x0008,
}
// http://msdn.microsoft.com/en-us/library/ms646273(VS.85).aspx
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct HARDWAREINPUT
{
internal int uMsg;
internal short wParamL;
internal short wParamH;
}
[StructLayout(LayoutKind.Explicit)]
internal struct INPUT
{
[FieldOffset(0)]
internal int type;
[FieldOffset(4)]
internal MOUSEINPUT mi;
[FieldOffset(4)]
internal KEYBDINPUT ki;
}
[StructLayout(LayoutKind.Explicit)]
internal struct INPUT64
{
[FieldOffset(0)]
internal int type;
[FieldOffset(8)]
internal MOUSEINPUT mi;
[FieldOffset(8)]
internal KEYBDINPUT ki;
}
[DllImport("user32.dll", EntryPoint = "SendInput", SetLastError = true)]
internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
[DllImport("user32.dll", EntryPoint = "SendInput", SetLastError = true)]
internal static extern uint SendInput64(uint nInputs, INPUT64[] pInputs, int cbSize);
[DllImport("user32.dll", EntryPoint = "GetMessageExtraInfo", SetLastError = true)]
internal static extern IntPtr GetMessageExtraInfo();
[DllImport("user32.dll", EntryPoint = "LockWorkStation", SetLastError = true)]
internal static extern uint LockWorkStation();
// [DllImport("user32.dll")]
// internal static extern void keybd_event(byte bVk, byte bScan, UInt32 dwFlags, int dwExtraInfo);
[DllImport("user32.dll")]
internal static extern uint MapVirtualKey(uint uCode, uint uMapType);
[StructLayout(LayoutKind.Sequential)]
internal struct LUID
{
internal int LowPart;
internal int HighPart;
}// end struct
[StructLayout(LayoutKind.Sequential)]
internal struct LUID_AND_ATTRIBUTES
{
internal LUID Luid;
internal int Attributes;
}// end struct
[StructLayout(LayoutKind.Sequential)]
internal struct TOKEN_PRIVILEGES
{
internal int PrivilegeCount;
// LUID_AND_ATTRIBUTES
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
internal int[] Privileges;
}
internal const int READ_CONTROL = 0x00020000;
internal const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
internal const int STANDARD_RIGHTS_READ = READ_CONTROL;
internal const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
internal const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
internal const int STANDARD_RIGHTS_ALL = 0x001F0000;
internal const int SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
internal const int TOKEN_IMPERSONATE = 0x0004;
internal const int TOKEN_QUERY_SOURCE = 0x0010;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
internal const int TOKEN_ADJUST_GROUPS = 0x0040;
internal const int TOKEN_ADJUST_SESSIONID = 0x0100;
internal const int TOKEN_ALL_ACCESS_P = STANDARD_RIGHTS_REQUIRED |
TOKEN_ASSIGN_PRIMARY |
TOKEN_DUPLICATE |
TOKEN_IMPERSONATE |
TOKEN_QUERY |
TOKEN_QUERY_SOURCE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT;
internal const int TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
internal const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
internal const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
TOKEN_ADJUST_PRIVILEGES |
TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT;
internal const int TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
internal const int CREATE_NEW_PROCESS_GROUP = 0x00000200;
internal const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
internal const int IDLE_PRIORITY_CLASS = 0x40;
internal const int NORMAL_PRIORITY_CLASS = 0x20;
internal const int HIGH_PRIORITY_CLASS = 0x80;
internal const int REALTIME_PRIORITY_CLASS = 0x100;
internal const int CREATE_NEW_CONSOLE = 0x00000010;
internal const string SE_DEBUG_NAME = "SeDebugPrivilege";
internal const string SE_RESTORE_NAME = "SeRestorePrivilege";
internal const string SE_BACKUP_NAME = "SeBackupPrivilege";
internal const int SE_PRIVILEGE_ENABLED = 0x0002;
internal const int ERROR_NOT_ALL_ASSIGNED = 1300;
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESSENTRY32
{
internal uint dwSize;
internal uint cntUsage;
internal uint th32ProcessID;
internal IntPtr th32DefaultHeapID;
internal uint th32ModuleID;
internal uint cntThreads;
internal uint th32ParentProcessID;
internal int pcPriClassBase;
internal uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
internal string szExeFile;
}
internal const uint TH32CS_SNAPPROCESS = 0x00000002;
// internal static int INVALID_HANDLE_VALUE = -1;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr hSnapshot);
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_ATTRIBUTES
{
internal int Length;
internal IntPtr lpSecurityDescriptor;
internal bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
internal IntPtr hProcess;
internal IntPtr hThread;
internal uint dwProcessId;
internal uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct STARTUPINFO
{
internal int cb;
internal string lpReserved;
internal string lpDesktop;
internal string lpTitle;
internal uint dwX;
internal uint dwY;
internal uint dwXSize;
internal uint dwYSize;
internal uint dwXCountChars;
internal uint dwYCountChars;
internal uint dwFillAttribute;
internal uint dwFlags;
internal short wShowWindow;
internal short cbReserved2;
internal IntPtr lpReserved2;
internal IntPtr hStdInput;
internal IntPtr hStdOutput;
internal IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SID_AND_ATTRIBUTES
{
internal IntPtr Sid;
internal int Attributes;
}
[StructLayout(LayoutKind.Sequential)]
internal struct TOKEN_MANDATORY_LABEL
{
internal SID_AND_ATTRIBUTES Label;
}
internal const int TOKEN_DUPLICATE = 0x0002;
internal const int TOKEN_QUERY = 0x0008;
internal const int TOKEN_ADJUST_DEFAULT = 0x0080;
internal const int TOKEN_ASSIGN_PRIMARY = 0x0001;
internal const uint MAXIMUM_ALLOWED = 0x2000000;
internal const int SE_GROUP_INTEGRITY = 0x00000020;
internal enum SECURITY_IMPERSONATION_LEVEL : int
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
internal enum TOKEN_TYPE : int
{
TokenPrimary = 1,
TokenImpersonation = 2,
}
internal enum TOKEN_INFORMATION_CLASS : int
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass,
}
// [DllImport("kernel32.dll")]
// internal static extern int Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
// [DllImport("kernel32.dll")]
// internal static extern int Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
// [DllImport("kernel32.dll", SetLastError = true)]
// internal static extern IntPtr CreateToolhelp32Snapshot(UInt32 dwFlags, UInt32 th32ProcessID);
[DllImport("Wtsapi32.dll")]
internal static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "1", Justification = "Dotnet port with style preservation")]
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
// [DllImport("kernel32.dll")]
// [return: MarshalAs(UnmanagedType.Bool)]
// static extern bool ProcessIdToSessionId(UInt32 dwProcessId, ref UInt32 pSessionId);
[DllImport("kernel32.dll")]
internal static extern IntPtr OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, [MarshalAs(UnmanagedType.Bool)] bool bInherit);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool RevertToSelf();
internal delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
internal delegate int HookProc(int nCode, int wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal class MEMORYSTATUSEX
{
public uint dwLength;
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
public MEMORYSTATUSEX()
{
dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
}
}
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer);
/*
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
internal extern static int NetUserGetInfo([MarshalAs(UnmanagedType.LPWStr)] string ServerName,
[MarshalAs(UnmanagedType.LPWStr)] string UserName, int level,out IntPtr BufPtr);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct USER_INFO_10
{
[MarshalAs(UnmanagedType.LPWStr)]
public string usri10_name;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri10_comment;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri10_usr_comment;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri10_full_name;
}
[DllImport("Netapi32.dll", SetLastError = true)]
internal static extern int NetApiBufferFree(IntPtr Buffer);
*/
internal enum EXTENDED_NAME_FORMAT
{
NameUnknown = 0,
NameFullyQualifiedDN = 1,
NameSamCompatible = 2,
NameDisplay = 3,
NameUniqueId = 6,
NameCanonical = 7,
NameUserPrincipal = 8,
NameCanonicalEx = 9,
NameServicePrincipal = 10,
NameDnsDomain = 12,
}
[DllImport("secur32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
internal static extern bool GetUserNameEx(int nameFormat, StringBuilder userName, ref uint userNameSize);
[DllImport("Shcore.dll", SetLastError = true)]
internal static extern int GetDpiForMonitor(IntPtr hMonitor, uint dpiType, out uint dpiX, out uint dpiY);
private static string GetDNSDomain()
{
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
{
return null;
}
StringBuilder userName = new(1024);
uint userNameSize = (uint)userName.Capacity;
if (GetUserNameEx((int)EXTENDED_NAME_FORMAT.NameDnsDomain, userName, ref userNameSize))
{
string[] nameParts = userName.ToString()
.Split('\\');
return nameParts.Length != 2 ? null : nameParts[0];
}
return null;
}
/// <summary>
/// Use this method to figure out if your code is running on a Microsoft computer.
/// </summary>
/// <returns></returns>
internal static bool IsRunningAtMicrosoft()
{
string domain = GetDNSDomain();
return !string.IsNullOrEmpty(domain) && domain.EndsWith("microsoft.com", true, System.Globalization.CultureInfo.CurrentCulture);
}
private NativeMethods()
{
}
}
}

View File

@@ -0,0 +1,402 @@
// 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.
// <summary>
// Application entry and pre-process/initialization.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Printing;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Security.Authentication.ExtendedProtection;
using System.Security.Principal;
using System.ServiceModel.Channels;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Telemetry;
using Newtonsoft.Json;
using StreamJsonRpc;
[module: SuppressMessage("Microsoft.MSInternal", "CA904:DeclareTypesInMicrosoftOrSystemNamespace", Scope = "namespace", Target = "MouseWithoutBorders", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", Scope = "member", Target = "MouseWithoutBorders.Program.#Main()", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.Program.#Main()", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders.Class
{
internal static class Program
{
private static readonly string ServiceName = "PowerToys.MWB.Service";
private static readonly string ServiceModeArg = "UseService";
[STAThread]
private static void Main()
{
ManagedCommon.Logger.InitializeLogger("\\MouseWithoutBorders\\Logs");
Common.Log(Application.ProductName + " Started!");
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
Common.Log("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
return;
}
Thread.CurrentThread.Name = Application.ProductName + " main thread";
Common.BinaryName = Path.GetFileNameWithoutExtension(Application.ExecutablePath);
WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
SecurityIdentifier currentUserSID = currentUser.User;
SecurityIdentifier localSystemSID = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
bool runningAsSystem = currentUserSID.Equals(localSystemSID);
Common.RunWithNoAdminRight = !runningAsSystem;
try
{
string[] args = Environment.GetCommandLineArgs();
string firstArg = string.Empty;
if (args.Length > 1 && args[1] != null)
{
firstArg = args[1].Trim();
}
User = WindowsIdentity.GetCurrent().Name;
Common.LogDebug("*** Started as " + User);
Common.Log(Environment.CommandLine);
bool serviceMode = firstArg == ServiceModeArg;
// If we're started from the .dll module or from the service process, we should
// assume the service mode.
if (serviceMode || runningAsSystem)
{
if (!runningAsSystem)
{
var sc = new ServiceController(ServiceName);
sc.Start();
return;
}
if (args.Length > 2)
{
Helper.UserLocalAppDataPath = args[2].Trim();
}
}
ShutdownWithPowerToys.WaitForPowerToysRunner();
if (firstArg != string.Empty)
{
if (Common.CheckSecondInstance(Common.RunWithNoAdminRight))
{
Common.Log("*** Second instance, exiting...");
return;
}
string myDesktop = Common.GetMyDesktop();
if (firstArg.Equals("winlogon", StringComparison.OrdinalIgnoreCase))
{
// Executed by service, running on logon desktop
Common.Log("*** Running on " + firstArg + " desktop");
Common.RunOnLogonDesktop = true;
}
else if (args[1].Trim().Equals("default", StringComparison.OrdinalIgnoreCase))
{
Common.Log("*** Running on " + firstArg + " desktop");
}
else if (args[1].Equals(myDesktop, StringComparison.OrdinalIgnoreCase))
{
Common.Log("*** Running on " + myDesktop);
if (myDesktop.Equals("Screen-saver", StringComparison.OrdinalIgnoreCase))
{
Common.RunOnScrSaverDesktop = true;
Setting.Values.LastX = Common.JUST_GOT_BACK_FROM_SCREEN_SAVER;
}
}
}
else
{
if (Common.CheckSecondInstance(true))
{
Common.Log("*** Second instance, exiting...");
return;
}
}
PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersStartedEvent());
try
{
Common.CurrentProcess = Process.GetCurrentProcess();
Common.CurrentProcess.PriorityClass = ProcessPriorityClass.RealTime;
}
catch (Exception e)
{
Common.Log(e);
}
Common.Log(Environment.OSVersion.ToString());
// Environment.OSVersion is unreliable from 6.2 and up, so just forcefully call the APIs and log the exception unsupported by Windows:
int setProcessDpiAwarenessResult = -1;
try
{
setProcessDpiAwarenessResult = NativeMethods.SetProcessDpiAwareness(2);
Common.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDpiAwareness: {0}.", setProcessDpiAwarenessResult));
}
catch (DllNotFoundException)
{
Common.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower.");
}
catch (EntryPointNotFoundException)
{
Common.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower.");
}
catch (Exception e)
{
Common.Log(e);
}
try
{
if (setProcessDpiAwarenessResult != 0)
{
Common.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDPIAware: {0}.", NativeMethods.SetProcessDPIAware()));
}
}
catch (Exception e)
{
Common.Log(e);
}
System.Threading.Thread mainUIThread = Thread.CurrentThread;
Common.UIThreadID = mainUIThread.ManagedThreadId;
Thread.UpdateThreads(mainUIThread);
StartInputCallbackThread();
if (!Common.RunOnLogonDesktop)
{
StartSettingSyncThread();
}
Application.EnableVisualStyles();
_ = Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
Application.SetCompatibleTextRenderingDefault(false);
Common.Init();
Common.WndProcCounter++;
var formScreen = new FrmScreen();
Application.Run(formScreen);
}
catch (Exception e)
{
Common.Log(e);
}
}
private interface ISettingsSyncHelper
{
[JsonObject(MemberSerialization.OptIn)]
public struct MachineSocketState
{
// Disable false-positive warning due to IPC
#pragma warning disable CS0649
[JsonProperty]
public string Name;
[JsonProperty]
public SocketStatus Status;
#pragma warning restore CS0649
}
void Shutdown();
void Reconnect();
void GenerateNewKey();
void ConnectToMachine(string machineName, string securityKey);
Task<MachineSocketState[]> RequestMachineSocketStateAsync();
}
private sealed class SettingsSyncHelper : ISettingsSyncHelper
{
public Task<ISettingsSyncHelper.MachineSocketState[]> RequestMachineSocketStateAsync()
{
var machineStates = new Dictionary<string, SocketStatus>();
if (Common.Sk == null || Common.Sk.TcpSockets == null)
{
return Task.FromResult(Array.Empty<ISettingsSyncHelper.MachineSocketState>());
}
foreach (var client in Common.Sk.TcpSockets
.Where(t => t != null && t.IsClient && !string.IsNullOrEmpty(t.MachineName)))
{
var exists = machineStates.TryGetValue(client.MachineName, out var existingStatus);
if (!exists || existingStatus == SocketStatus.NA)
{
machineStates[client.MachineName] = client.Status;
}
}
return Task.FromResult(machineStates.Select((state) => new ISettingsSyncHelper.MachineSocketState { Name = state.Key, Status = state.Value }).ToArray());
}
public void ConnectToMachine(string pcName, string securityKey)
{
Setting.Values.PauseInstantSaving = true;
Common.ClearComputerMatrix();
Setting.Values.MyKey = securityKey;
Common.MyKey = securityKey;
Common.MagicNumber = Common.Get24BitHash(Common.MyKey);
Common.MachineMatrix = new string[Common.MAX_MACHINE] { pcName.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty };
string[] machines = Common.MachineMatrix;
Common.MachinePool.Initialize(machines);
Common.UpdateMachinePoolStringSetting();
SocketStuff.InvalidKeyFound = false;
Common.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
Common.SendMachineMatrix();
Setting.Values.PauseInstantSaving = false;
Setting.Values.SaveSettings();
}
public void GenerateNewKey()
{
Setting.Values.PauseInstantSaving = true;
Setting.Values.EasyMouse = (int)EasyMouseOption.Enable;
Common.ClearComputerMatrix();
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Common.GeneratedKey = true;
Setting.Values.PauseInstantSaving = false;
Setting.Values.SaveSettings();
Reconnect();
}
public void Reconnect()
{
SocketStuff.InvalidKeyFound = false;
Common.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
for (int i = 0; i < 10; i++)
{
if (Common.AtLeastOneSocketConnected())
{
Common.MMSleep(0.5);
break;
}
Common.MMSleep(0.2);
}
Common.SendMachineMatrix();
}
public void Shutdown()
{
Process[] ps = Process.GetProcessesByName("PowerToys.MouseWithoutBorders");
Process me = Process.GetCurrentProcess();
foreach (Process p in ps)
{
if (p.Id != me.Id)
{
p.Kill();
}
}
Common.MainForm.Quit(true, false);
}
}
internal static void StartSettingSyncThread()
{
var serverTaskCancellationSource = new CancellationTokenSource();
CancellationToken cancellationToken = serverTaskCancellationSource.Token;
IpcChannel<SettingsSyncHelper>.StartIpcServer("MouseWithoutBorders/SettingsSync", cancellationToken);
}
internal static void StartInputCallbackThread()
{
System.Collections.Hashtable dummy = Setting.Values.VKMap; // Reading from registry to memory.
Thread inputCallback = new(new ThreadStart(InputCallbackThread), "InputCallback Thread");
inputCallback.SetApartmentState(ApartmentState.STA);
inputCallback.Priority = ThreadPriority.Highest;
inputCallback.Start();
}
private static void InputCallbackThread()
{
Common.InputCallbackThreadID = Thread.CurrentThread.ManagedThreadId;
while (!Common.InitDone)
{
Thread.Sleep(100);
}
Application.Run(new FrmInputCallback());
}
internal static void StartService()
{
if (Common.RunWithNoAdminRight)
{
return;
}
try
{
// Kill all but me
Process me = Process.GetCurrentProcess();
Process[] ps = Process.GetProcessesByName(Common.BinaryName);
foreach (Process pp in ps)
{
if (pp.Id != me.Id)
{
Common.Log(string.Format(CultureInfo.InvariantCulture, "Killing process {0}.", pp.Id));
pp.KillProcess();
}
}
}
catch (Exception e)
{
Common.Log(e);
}
Common.StartMouseWithoutBordersService();
}
internal static string User { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
// 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.
// <summary>
// Logging.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Class
{
internal class SeverityLevel
{
internal static readonly SeverityLevel Information = new SeverityLevel();
internal static readonly SeverityLevel Error = new SeverityLevel();
internal static readonly SeverityLevel Warning = new SeverityLevel();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,176 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
// <summary>
// TCP Server implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Exceptions;
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.TcpServer.#Close()", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders.Class
{
internal class TcpServer
{
private readonly TcpListener server;
internal string Name { get; private set; }
internal TcpServer(int port, ParameterizedThreadStart job)
{
Common.Log($"TCP listening on port: {port}");
Name = port.ToString(CultureInfo.CurrentCulture);
server = TcpListener.Create(port);
StartServer(job);
}
private void StartServer(ParameterizedThreadStart job)
{
int tryCount = 6;
do
{
try
{
server.Start();
break;
}
catch (SocketException e)
{
// DHCP error, etc.
if (server.LocalEndpoint.ToString().StartsWith("169.254", StringComparison.InvariantCulture) || server.LocalEndpoint.ToString().StartsWith("0.0", StringComparison.InvariantCulture))
{
throw new ExpectedSocketException($"Error: The machine has limited connectivity on [{server.LocalEndpoint}]!");
}
if (e.ErrorCode == 10048 /*WSAEADDRINUSE*/)
{
if (--tryCount >= 0)
{
Thread.Sleep(500);
continue;
}
if (!Common.IsMyDesktopActive())
{
// We can just throw the SocketException but to avoid a redundant log entry:
throw new ExpectedSocketException($"{nameof(StartServer)}: The desktop is no longer active.");
}
else
{
LogError($"WSAEADDRINUSE: {server.LocalEndpoint}: {e.Message}");
throw;
}
}
else
{
Common.TelemetryLogTrace($"Error listening on: {server.LocalEndpoint}: {e.ErrorCode}/{e.Message}", SeverityLevel.Error);
throw;
}
}
}
while (true);
Thread t = new(job, Name = "Tcp Server: " + job.Method.Name + " " + server.LocalEndpoint.ToString());
t.SetApartmentState(ApartmentState.STA);
t.Start(server);
}
internal void Close()
{
try
{
server?.Stop();
}
catch (Exception e)
{
Common.Log(e);
}
}
private static bool logged;
private void LogError(string log)
{
if (!logged)
{
logged = true;
_ = Task.Factory.StartNew(
() =>
{
try
{
using Process proc = new();
ProcessStartInfo startInfo = new()
{
FileName = Environment.ExpandEnvironmentVariables(@"%windir%\System32\netstat.exe"),
Arguments = "-nao",
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
proc.StartInfo = startInfo;
_ = proc.Start();
string status = proc.StandardOutput.ReadToEnd() + Environment.NewLine;
if (proc.ExitCode == 0)
{
System.Collections.Generic.IEnumerable<string> portLog = status.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Where(line => line.Contains("LISTENING") && (line.Contains(":15100 ") || line.Contains(":15101 ")));
foreach (string portLogLine in portLog)
{
int pid = 0;
Process process = null;
try
{
// Assuming the format of netstat's output is fixed.
pid = int.Parse(portLogLine.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).Last(), CultureInfo.CurrentCulture);
process = Process.GetProcessById(pid);
}
catch (Exception)
{
/* TODO: There was some telemetry here. Log instead? */
}
/* TODO: There was some telemetry here. Log instead? */
}
}
else
{
/* TODO: There was some telemetry here. Log instead? */
}
}
catch (Exception)
{
/* TODO: There was some telemetry here. Log instead? */
}
},
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
}
}
}
}