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); /// /// Signals that TabTip was closed after it was opened /// with a call to StartPoolingForTabTipClosedEvent method /// internal static event Action Closed; private static IntPtr GetTabTipWindowHandle() => FindWindow(TabTipWindowClassName, null); internal static void OpenUndockedAndStartPoolingForClosedEvent() { OpenUndocked(); StartPoolingForTabTipClosedEvent(); } /// /// Open TabTip /// 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); } /// /// Open TabTip in undocked state /// 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(); } /// /// Close TabTip /// 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; /// /// Gets TabTip Window Rectangle /// /// [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")] public static Rectangle GetTabTipRectangle() { if (TabTipClosed()) return new Rectangle(); return GetWouldBeTabTipRectangle(); } private static Rectangle previousTabTipRectangle; /// /// Gets Window Rectangle which would be occupied by TabTip if TabTip was opened. /// /// [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); } } }