167 lines
6.3 KiB
C#
167 lines
6.3 KiB
C#
using System;
|
||
using System.Diagnostics;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Drawing;
|
||
using System.Runtime.InteropServices;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.Win32;
|
||
|
||
namespace DM_Weight.util.TabTip
|
||
{
|
||
public static class TabTip
|
||
{
|
||
private const string TabTipWindowClassName = "IPTip_Main_Window";
|
||
private const string TabTipExecPath = @"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe";
|
||
private const string TabTipRegistryKeyName = @"HKEY_CURRENT_USER\Software\Microsoft\TabletTip\1.7";
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern int SendMessage(int hWnd, uint msg, int wParam, int lParam);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern IntPtr FindWindow(String sClassName, String sAppName);
|
||
|
||
[DllImport("user32.dll")]
|
||
[return: MarshalAs(UnmanagedType.Bool)]
|
||
private static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);
|
||
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
private struct RECT
|
||
{
|
||
public readonly int Left; // x position of upper-left corner
|
||
public readonly int Top; // y position of upper-left corner
|
||
public readonly int Right; // x position of lower-right corner
|
||
public readonly int Bottom; // y position of lower-right corner
|
||
}
|
||
|
||
[DllImport("user32.dll", SetLastError = true)]
|
||
private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
|
||
|
||
/// <summary>
|
||
/// Signals that TabTip was closed after it was opened
|
||
/// with a call to StartPoolingForTabTipClosedEvent method
|
||
/// </summary>
|
||
internal static event Action Closed;
|
||
|
||
private static IntPtr GetTabTipWindowHandle() => FindWindow(TabTipWindowClassName, null);
|
||
|
||
internal static void OpenUndockedAndStartPoolingForClosedEvent()
|
||
{
|
||
OpenUndocked();
|
||
StartPoolingForTabTipClosedEvent();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Open TabTip
|
||
/// </summary>
|
||
public static void Open()
|
||
{
|
||
if (EnvironmentEx.GetOSVersion() == OSVersion.Win10)
|
||
EnableTabTipOpenInDesctopModeOnWin10();
|
||
|
||
Process.Start(new ProcessStartInfo(TabTipExecPath) { UseShellExecute = true});
|
||
}
|
||
|
||
private static void EnableTabTipOpenInDesctopModeOnWin10()
|
||
{
|
||
const string TabTipAutoInvokeKey = "EnableDesktopModeAutoInvoke";
|
||
|
||
int EnableDesktopModeAutoInvoke = (int) (Registry.GetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, -1) ?? -1);
|
||
if (EnableDesktopModeAutoInvoke != 1)
|
||
Registry.SetValue(TabTipRegistryKeyName, TabTipAutoInvokeKey, 1);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Open TabTip in undocked state
|
||
/// </summary>
|
||
public static void OpenUndocked()
|
||
{
|
||
const string TabTipDockedKey = "EdgeTargetDockedState";
|
||
const string TabTipProcessName = "TabTip";
|
||
|
||
int docked = (int) (Registry.GetValue(TabTipRegistryKeyName, TabTipDockedKey, 1) ?? 1);
|
||
if (docked == 1)
|
||
{
|
||
Registry.SetValue(TabTipRegistryKeyName, TabTipDockedKey, 0);
|
||
foreach (Process tabTipProcess in Process.GetProcessesByName(TabTipProcessName))
|
||
tabTipProcess.Kill();
|
||
}
|
||
Open();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Close TabTip
|
||
/// </summary>
|
||
public static void Close()
|
||
{
|
||
const int WM_SYSCOMMAND = 274;
|
||
const int SC_CLOSE = 61536;
|
||
SendMessage(GetTabTipWindowHandle().ToInt32(), WM_SYSCOMMAND, SC_CLOSE, 0);
|
||
}
|
||
|
||
private static void StartPoolingForTabTipClosedEvent()
|
||
{
|
||
PoolingTimer.PoolUntilTrue(
|
||
PoolingFunc: TabTipClosed,
|
||
Callback: () => Closed?.Invoke(),
|
||
dueTime: TimeSpan.FromMilliseconds(700),
|
||
period: TimeSpan.FromMilliseconds(50));
|
||
}
|
||
|
||
private static bool TabTipClosed()
|
||
{
|
||
const int GWL_STYLE = -16; // Specifies we wish to retrieve window styles.
|
||
const uint KeyboardClosedStyle = 2617245696;
|
||
IntPtr KeyboardWnd = GetTabTipWindowHandle();
|
||
return (KeyboardWnd.ToInt32() == 0 || GetWindowLong(KeyboardWnd, GWL_STYLE) == KeyboardClosedStyle);
|
||
}
|
||
|
||
// ReSharper disable once UnusedMember.Local
|
||
private static bool IsTabTipProcessRunning => GetTabTipWindowHandle() != IntPtr.Zero;
|
||
|
||
/// <summary>
|
||
/// Gets TabTip Window Rectangle
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
|
||
public static Rectangle GetTabTipRectangle()
|
||
{
|
||
if (TabTipClosed())
|
||
return new Rectangle();
|
||
|
||
return GetWouldBeTabTipRectangle();
|
||
}
|
||
|
||
private static Rectangle previousTabTipRectangle;
|
||
|
||
/// <summary>
|
||
/// Gets Window Rectangle which would be occupied by TabTip if TabTip was opened.
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
|
||
internal static Rectangle GetWouldBeTabTipRectangle()
|
||
{
|
||
RECT rect;
|
||
|
||
if (!GetWindowRect(new HandleRef(null, GetTabTipWindowHandle()), out rect))
|
||
{
|
||
if (previousTabTipRectangle.Equals(new Rectangle())) //in case TabTip was closed and previousTabTipRectangle was not set
|
||
Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(TryGetTabTipRectangleToСache);
|
||
|
||
return previousTabTipRectangle;
|
||
}
|
||
|
||
Rectangle wouldBeTabTipRectangle = new Rectangle(x: rect.Left, y: rect.Top, width: rect.Right - rect.Left + 1, height: rect.Bottom - rect.Top + 1);
|
||
previousTabTipRectangle = wouldBeTabTipRectangle;
|
||
|
||
return wouldBeTabTipRectangle;
|
||
}
|
||
|
||
private static void TryGetTabTipRectangleToСache(Task task)
|
||
{
|
||
RECT rect;
|
||
if (GetWindowRect(new HandleRef(null, GetTabTipWindowHandle()), out rect))
|
||
previousTabTipRectangle = new Rectangle(x: rect.Left, y: rect.Top, width: rect.Right - rect.Left + 1, height: rect.Bottom - rect.Top + 1);
|
||
}
|
||
}
|
||
}
|