Files
PowerToys/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs
Andrey Nekrasov 29eebe16a4 [New Utility]Mouse Without Borders
* Integrate Mouse Without Borders into PowerToys

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2023-05-18 21:48:03 +01:00

495 lines
18 KiB
C#

// 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);
}
}
}
}