Guest User

Untitled

a guest
Feb 7th, 2025
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 22.64 KB | Source Code | 0 0
  1. using NAudio.CoreAudioApi;
  2. using OpenQA.Selenium.Firefox;
  3. using OpenQA.Selenium.Remote;
  4. using System;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Runtime.InteropServices;
  9.  
  10. namespace AvaloniaTray.Services.GlobalKeyboardHook
  11. {
  12.     public class GlobalKeyboardHookService : IDisposable
  13.     {
  14.         private static IntPtr keyboardHookId = IntPtr.Zero;
  15.         private static IntPtr mouseHookId = IntPtr.Zero;
  16.         private static IntPtr swallowEvent = (IntPtr)1;
  17.         private static LowLevelKeyboardProc keyboardProc = KeyboardHookCallback;
  18.         private static LowLevelMouseProc mouseProc = MouseHookCallback;
  19.         private static MMDeviceEnumerator deviceEnumerator = new MMDeviceEnumerator();
  20.         private static MMDevice device = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
  21.  
  22.         private static bool isXButton1Down = false;
  23.         private static bool isXButton2Down = false;
  24.         private static DateTime lastCapsLockPress = DateTime.MinValue;
  25.         private static DateTime lastXButton1Press = DateTime.MinValue;
  26.         private static DateTime lastXButton2Press = DateTime.MinValue;
  27.         private static readonly TimeSpan DoubleTapThreshold = TimeSpan.FromMilliseconds(300);
  28.  
  29.         private static string processName_Firefox = "firefox";
  30.  
  31.         public GlobalKeyboardHookService()
  32.         {
  33.             keyboardHookId = SetHook(keyboardProc, WH_KEYBOARD_LL);
  34.             mouseHookId = SetHook(mouseProc, WH_MOUSE_LL);
  35.         }
  36.  
  37.         public void Dispose()
  38.         {
  39.             UnhookWindowsHookEx(keyboardHookId);
  40.             UnhookWindowsHookEx(mouseHookId);
  41.         }
  42.  
  43.         private static IntPtr SetHook(Delegate proc, int hookType)
  44.         {
  45.             using (Process curProcess = Process.GetCurrentProcess())
  46.             using (ProcessModule curModule = curProcess.MainModule)
  47.             {
  48.                 return SetWindowsHookEx(hookType, proc, GetModuleHandle(curModule.ModuleName), 0);
  49.             }
  50.         }
  51.  
  52.         private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
  53.         private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
  54.  
  55.         private static IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
  56.         {
  57.             // Check if the hook code is valid and the key event is a key down or system key down event
  58.             if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
  59.             {
  60.                 // Read the virtual key code from the lParam
  61.                 int vkCode = Marshal.ReadInt32(lParam);
  62.  
  63.                 // Check if the key pressed is Caps Lock
  64.                 if (vkCode == VK_CAPITAL)
  65.                 {
  66.                     // Get the current time
  67.                     DateTime now = DateTime.Now;
  68.  
  69.                     // Check if the time difference between the current press and the last press is within the double tap threshold
  70.                     if (IsDoubleTap(now) && IsCapsLockHeld() == false)
  71.                     {
  72.                         WindowMaximizeOrMakeActive("firefox");
  73.                     }
  74.                     // Update the last Caps Lock press time
  75.                     lastCapsLockPress = now;
  76.                 }
  77.  
  78.                 // Check if the key pressed is 'B' and Caps Lock is held
  79.                 if (vkCode == VK_B && IsCapsLockHeld())
  80.                 {
  81.                     // Open the Bluetooth devices window
  82.                     OpenBluetoothDevices();
  83.                 }
  84.             }
  85.             // Call the next hook in the chain
  86.             return CallNextHookEx(keyboardHookId, nCode, wParam, lParam);
  87.         }
  88.  
  89.         private static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
  90.         {
  91.             if (nCode >= 0)
  92.             {
  93.                 MSLLHOOKSTRUCT hookStruct = Marshal.PtrToStructure<MSLLHOOKSTRUCT>(lParam);
  94.  
  95.                 if (IsXButtonEvent(wParam))
  96.                 {
  97.                     HandleXButtonEvent(wParam, hookStruct);
  98.                 }
  99.                 else if (IsMouseWheelEvent(wParam) && isXButton1Down)
  100.                 {
  101.                     HandleMouseWheelEvent_WhenXButton1Down(hookStruct);
  102.                     return swallowEvent;
  103.                 }
  104.                 else if (IsMouseWheelEvent(wParam) && isXButton2Down)
  105.                 {
  106.                     HandleMouseWheelEvent_WhenXButton2Down(hookStruct);
  107.                     return swallowEvent;
  108.                 }
  109.                 else if (IsScrollWheelButtonEvent(wParam) && isXButton1Down)
  110.                 {
  111.                     ToggleMuteAllWindowInstances(processName_Firefox);
  112.                     // desired function here
  113.                 }
  114.                 else if (IsScrollWheelButtonEvent(wParam) && isXButton2Down)
  115.                 {
  116.                     ToggleSystemMute();
  117.                     return swallowEvent;
  118.  
  119.                     // this needs to be adjusted and thought through
  120.                     //HandleScrollWheelButtonEvent(hookStruct);
  121.                     //return swallowEvent;
  122.                 }
  123.             }
  124.  
  125.             return CallNextHookEx(mouseHookId, nCode, wParam, lParam);
  126.         }
  127.  
  128.         private static bool IsXButtonEvent(IntPtr wParam)
  129.         {
  130.             return wParam == (IntPtr)WM_XBUTTONDOWN || wParam == (IntPtr)WM_XBUTTONUP;
  131.         }
  132.  
  133.         private static bool IsMouseWheelEvent(IntPtr wParam)
  134.         {
  135.             return wParam == (IntPtr)WM_MOUSEWHEEL;
  136.         }
  137.  
  138.         private static bool IsScrollWheelButtonEvent(IntPtr wParam)
  139.         {
  140.             return wParam == (IntPtr)WM_MBUTTONDOWN;
  141.         }
  142.  
  143.         private static bool IsDoubleTap(DateTime now)
  144.         {
  145.             return now - lastCapsLockPress < DoubleTapThreshold;
  146.         }
  147.  
  148.         private static void HandleXButtonEvent(IntPtr wParam, MSLLHOOKSTRUCT hookStruct)
  149.         {
  150.             int mouseButton = HIWORD((int)hookStruct.mouseData);
  151.  
  152.             if (mouseButton == XBUTTON1)
  153.             {
  154.                 if (wParam == (IntPtr)WM_XBUTTONDOWN)
  155.                 {
  156.                     DateTime now = DateTime.Now;
  157.                     if (now - lastXButton1Press < DoubleTapThreshold)
  158.                     {
  159.                         WindowMaximizeOrMakeActive(processName_Firefox);
  160.                     }
  161.                     lastXButton1Press = now;
  162.                     isXButton1Down = true;
  163.                 }
  164.                 else if (wParam == (IntPtr)WM_XBUTTONUP)
  165.                 {
  166.                     isXButton1Down = false;
  167.                 }
  168.             }
  169.             else if (mouseButton == XBUTTON2)
  170.             {
  171.                 if (wParam == (IntPtr)WM_XBUTTONDOWN)
  172.                 {
  173.                     DateTime now = DateTime.Now;
  174.                     if (now - lastXButton2Press < DoubleTapThreshold)
  175.                     {
  176.                         // Add any double-tap logic for XBUTTON2 here if needed
  177.                     }
  178.                     lastXButton2Press = now;
  179.                     isXButton2Down = true;
  180.                 }
  181.                 else if (wParam == (IntPtr)WM_XBUTTONUP)
  182.                 {
  183.                     isXButton2Down = false;
  184.                 }
  185.                 if (isXButton1Down)
  186.                 {
  187.                     //desired function here
  188.                     isXButton1Down = false;
  189.                 }
  190.             }
  191.  
  192.             isXButton1Down = IsX1ButtonHeld();
  193.             isXButton2Down = IsX2ButtonHeld();
  194.         }
  195.  
  196.         private static void HandleMouseWheelEvent_WhenXButton1Down(MSLLHOOKSTRUCT hookStruct)
  197.         {
  198.             int delta = (short)HIWORD((int)hookStruct.mouseData);
  199.  
  200.             // Adjust process volume
  201.             if (delta > 0)
  202.             {
  203.                 AdjustProcessVolume(4, processName_Firefox);
  204.             }
  205.             else if (delta < 0)
  206.             {
  207.                 AdjustProcessVolume(-4, processName_Firefox);
  208.             }
  209.         }
  210.  
  211.         private static void HandleMouseWheelEvent_WhenXButton2Down(MSLLHOOKSTRUCT hookStruct)
  212.         {
  213.             int delta = (short)HIWORD((int)hookStruct.mouseData);
  214.  
  215.             // Show the small volume control by simulating a left-click on the speaker icon
  216.             const int WM_APPCOMMAND = 0x319;
  217.             const int APPCOMMAND_VOLUME_UP = 0xA0000;
  218.             const int APPCOMMAND_VOLUME_DOWN = 0x90000;
  219.             IntPtr hWnd = FindWindow("Shell_TrayWnd", null);
  220.  
  221.             if (hWnd != IntPtr.Zero)
  222.             {
  223.                 SendMessage(hWnd, WM_APPCOMMAND, IntPtr.Zero, (IntPtr)(delta > 0 ? APPCOMMAND_VOLUME_UP : APPCOMMAND_VOLUME_DOWN));
  224.             }
  225.  
  226.             // Adjust the system volume
  227.             if (delta > 0)
  228.             {
  229.                 AdjustSystemVolume(1);
  230.             }
  231.             else if (delta < 0)
  232.             {
  233.                 AdjustSystemVolume(-2);
  234.             }
  235.         }
  236.  
  237.         private static bool IsCapsLockHeld()
  238.         {
  239.             return (GetKeyState(VK_CAPITAL) & 0x8000) != 0;
  240.         }
  241.  
  242.         private static bool IsX1ButtonHeld()
  243.         {
  244.             return isXButton1Down;
  245.         }
  246.  
  247.         private static bool IsX2ButtonHeld()
  248.         {
  249.             return isXButton2Down;
  250.         }
  251.  
  252.         private static void OpenBluetoothDevices()
  253.         {
  254.             Process.Start(new ProcessStartInfo("ms-settings:bluetooth") { UseShellExecute = true });
  255.         }
  256.  
  257.         private static void AdjustSystemVolume(int volumeChange)
  258.         {
  259.             float newVolume = device.AudioEndpointVolume.MasterVolumeLevelScalar + (volumeChange / 100.0f);
  260.             device.AudioEndpointVolume.MasterVolumeLevelScalar = Math.Clamp(newVolume, 0.0f, 1.0f);
  261.         }
  262.  
  263.         private static void AdjustProcessVolume(int volumeChange, string processName)
  264.         {
  265.             var deviceEnumerator = new MMDeviceEnumerator();
  266.             var defaultDevice = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
  267.             var sessions = defaultDevice.AudioSessionManager.Sessions;
  268.  
  269.             for (int i = 0; i < sessions.Count; i++)
  270.             {
  271.                 var session = sessions[i];
  272.                 if (session.GetProcessID == Process.GetProcessesByName(processName).FirstOrDefault()?.Id)
  273.                 {
  274.                     float newVolume = session.SimpleAudioVolume.Volume + (volumeChange / 100.0f);
  275.                     newVolume = Math.Max(0, Math.Min(1, newVolume)); // Ensure volume is between 0 and 1
  276.                     session.SimpleAudioVolume.Volume = newVolume;
  277.                 }
  278.             }
  279.         }
  280.  
  281.         private static void ToggleSystemMute()
  282.         {
  283.             device.AudioEndpointVolume.Mute = !device.AudioEndpointVolume.Mute;
  284.         }
  285.  
  286.         #region Projection mode
  287.  
  288.         public static void SetProjectionMode(string mode)
  289.         {
  290.             string argument = mode == "extend" ? "/extend" : "/clone";
  291.             Process.Start(new ProcessStartInfo("DisplaySwitch.exe", argument) { UseShellExecute = true });
  292.         }
  293.  
  294.         #endregion
  295.  
  296.         private static void WindowMaximizeOrMakeActive(string processName)
  297.         {
  298.             var firefoxWindow = Process.GetProcessesByName(processName).FirstOrDefault();
  299.             if (firefoxWindow != null)
  300.             {
  301.                 IntPtr handle = firefoxWindow.MainWindowHandle;
  302.                 if (handle != IntPtr.Zero)
  303.                 {
  304.                     // Check if the window is already maximized
  305.                     WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
  306.                     GetWindowPlacement(handle, ref placement);
  307.  
  308.                     if (placement.showCmd == SW_SHOWMAXIMIZED)
  309.                     {
  310.                         // If the window is already maximized, bring it to the foreground
  311.                         SetForegroundWindow(handle);
  312.                     }
  313.                     else
  314.                     {
  315.                         // Otherwise, maximize the window and bring it to the foreground
  316.                         ShowWindow(handle, SW_SHOWMAXIMIZED);
  317.                         SetForegroundWindow(handle);
  318.                     }
  319.                 }
  320.             }
  321.             else
  322.             {
  323.                 // If the Firefox window doesn't exist, create a new one
  324.                 StartFirefox();
  325.             }
  326.         }
  327.  
  328.         private static void ToggleMuteAllWindowInstances(string processName)
  329.         {
  330.             var firefoxProcesses = Process.GetProcessesByName(processName);
  331.             foreach (var firefoxProcess in firefoxProcesses)
  332.             {
  333.                 IntPtr handle = firefoxProcess.MainWindowHandle;
  334.                 if (handle != IntPtr.Zero)
  335.                 {
  336.                     ToggleMuteForProcess(firefoxProcess);
  337.                 }
  338.             }
  339.         }
  340.  
  341.         private static void ToggleMuteForProcess(Process process)
  342.         {
  343.             var deviceEnumerator = new MMDeviceEnumerator();
  344.             var defaultDevice = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
  345.             var sessions = defaultDevice.AudioSessionManager.Sessions;
  346.  
  347.             for (int i = 0; i < sessions.Count; i++)
  348.             {
  349.                 var session = sessions[i];
  350.                 if (session.GetProcessID == process.Id)
  351.                 {
  352.                     session.SimpleAudioVolume.Mute = !session.SimpleAudioVolume.Mute;
  353.                 }
  354.             }
  355.         }
  356.        
  357.         private static void StartFirefox()
  358.         {
  359.             string firefoxPath = @"C:\Program Files\Mozilla Firefox\firefox.exe";
  360.             if (File.Exists(firefoxPath))
  361.             {
  362.                 Process.Start(firefoxPath);
  363.             }
  364.         }
  365.  
  366.         public static void ShutdownSystem(int delay)
  367.         {
  368.             Process.Start("shutdown", $"/s /t {delay}");
  369.         }
  370.  
  371.         public static void ShutdownSystemCancel()
  372.         {
  373.             Process.Start("shutdown", "/a");
  374.         }
  375.  
  376.         public static int ShutdownSystemGetTimer()
  377.         {
  378.             //doesn't work
  379.             int remainingTime = (int)GetSystemMetrics(SM_SHUTDOWNTIMEOUT);
  380.             return remainingTime;
  381.         }
  382.  
  383.         #region Selenium firefox control
  384.  
  385.         private static void HandleScrollWheelButtonEvent(MSLLHOOKSTRUCT hookStruct)
  386.         {
  387.             IntPtr hWnd = WindowFromPoint(hookStruct.pt);
  388.             if (hWnd != IntPtr.Zero)
  389.             {
  390.                 uint processId;
  391.                 GetWindowThreadProcessId(hWnd, out processId);
  392.                 var process = Process.GetProcessById((int)processId);
  393.                 if (process.ProcessName.ToLower() == "firefox")
  394.                 {
  395.                     // Inject JavaScript to mute/unmute the video or active tab
  396.                     InjectMuteUnmuteScript(process);
  397.                 }
  398.             }
  399.         }
  400.  
  401.         private static void InjectMuteUnmuteScript(Process process)
  402.         {
  403.             // Connect to the existing Firefox instance with remote debugging enabled
  404.             var options = new FirefoxOptions();
  405.             options.BrowserExecutableLocation = @"C:\Program Files\Mozilla Firefox\firefox.exe";
  406.             options.AddArgument("--remote-debugging-port=9222");
  407.  
  408.             var driverService = FirefoxDriverService.CreateDefaultService();
  409.             driverService.FirefoxBinaryPath = options.BrowserExecutableLocation;
  410.             driverService.HideCommandPromptWindow = true;
  411.  
  412.             using (var driver = new RemoteWebDriver(new Uri("http://localhost:9222"), options))
  413.             {
  414.                 driver.Navigate().GoToUrl("about:blank"); // Ensure the driver is connected to the Firefox instance
  415.  
  416.                 string script = @"
  417.                    var video = document.querySelector('video:hover');
  418.                    if (video) {
  419.                        if (video.paused) {
  420.                            video.play();
  421.                            video.muted = false;
  422.                        } else {
  423.                            video.muted = !video.muted;
  424.                        }
  425.                    } else {
  426.                        var activeTab = document.querySelector('video');
  427.                        if (activeTab) {
  428.                            if (activeTab.paused) {
  429.                                activeTab.play();
  430.                                activeTab.muted = false;
  431.                            } else {
  432.                                activeTab.muted = !activeTab.muted;
  433.                            }
  434.                        }
  435.                    }
  436.                ";
  437.  
  438.                 driver.ExecuteScript(script);
  439.             }
  440.         }
  441.  
  442.         #endregion
  443.  
  444.         #region WinAPI
  445.  
  446.         // Hook types
  447.         private const int WH_KEYBOARD_LL = 13; // Low-level keyboard input events
  448.         private const int WH_MOUSE_LL = 14; // Low-level mouse input events
  449.  
  450.         // Window messages
  451.         private const int WM_KEYDOWN = 0x0100; // Key down
  452.         private const int WM_SYSKEYDOWN = 0x0104; // System key down
  453.         private const int WM_XBUTTONDOWN = 0x020B; // X button down
  454.         private const int WM_XBUTTONUP = 0x020C; // X button up
  455.         private const int WM_MOUSEWHEEL = 0x020A; // Mouse wheel
  456.         private const int WM_MBUTTONDOWN = 0x0207; // Middle mouse button down
  457.  
  458.         // X button constants
  459.         private const int XBUTTON1 = 0x0001; // X button 1 (typically the "Back" button on a mouse)
  460.         private const int XBUTTON2 = 0x0002; // X button 2 (typically the "Forward" button on a mouse)
  461.  
  462.         // Virtual key codes
  463.         private const int VK_CAPITAL = 0x14; // Caps Lock key
  464.         private const int VK_B = 0x42; // 'B' key
  465.  
  466.         // Show window commands
  467.         private const int SW_HIDE = 0; // Hides the window and activates another window.
  468.         private const int SW_SHOWNORMAL = 1; // Activates and displays a window. Restores it to its original size and position if minimized or maximized.
  469.         private const int SW_SHOWMINIMIZED = 2; // Activates the window and displays it as a minimized window.
  470.         private const int SW_SHOWMAXIMIZED = 3; // Activates the window and displays it as a maximized window.
  471.         private const int SW_SHOWNOACTIVATE = 4; // Displays a window in its most recent size and position without activating it.
  472.         private const int SW_SHOW = 5; // Activates the window and displays it in its current size and position.
  473.         private const int SW_MINIMIZE = 6; // Minimizes the specified window and activates the next top-level window in the Z order.
  474.         private const int SW_SHOWMINNOACTIVE = 7; // Displays the window as a minimized window without activating it.
  475.         private const int SW_SHOWNA = 8; // Displays the window in its current size and position without activating it.
  476.         private const int SW_RESTORE = 9; // Activates and displays the window. Restores it to its original size and position if minimized or maximized.
  477.         private const int SW_SHOWDEFAULT = 10; // Sets the show state based on the SW_ value specified in the STARTUPINFO structure passed to CreateProcess.
  478.         private const int SW_FORCEMINIMIZE = 11; // Minimizes a window, even if the thread that owns the window is not responding. Used only when minimizing windows from a different thread.
  479.  
  480.         // shutdown timer
  481.         const int SM_SHUTTINGDOWN = 0x2000;
  482.         const int SM_SHUTDOWNTIMEOUT = 0x2001;
  483.  
  484.         // Helper method to extract high-order word
  485.         private static int HIWORD(int n)
  486.         {
  487.             return (n >> 16) & 0xffff;
  488.         }
  489.  
  490.         [DllImport("user32.dll", CharSet = CharSet.Auto)]
  491.         private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
  492.  
  493.         [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  494.         private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
  495.  
  496.         [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  497.         private static extern IntPtr SetWindowsHookEx(int idHook, Delegate lpfn, IntPtr hMod, uint dwThreadId);
  498.  
  499.         [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  500.         [return: MarshalAs(UnmanagedType.Bool)]
  501.         private static extern bool UnhookWindowsHookEx(IntPtr hhk);
  502.  
  503.         [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  504.         private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
  505.  
  506.         [DllImport("user32.dll", CharSet = CharSet.Auto)]
  507.         private static extern short GetKeyState(int nVirtKey);
  508.  
  509.         [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  510.         private static extern IntPtr GetModuleHandle(string lpModuleName);
  511.  
  512.         [DllImport("user32.dll")]
  513.         [return: MarshalAs(UnmanagedType.Bool)]
  514.         private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
  515.  
  516.         [DllImport("user32.dll")]
  517.         [return: MarshalAs(UnmanagedType.Bool)]
  518.         private static extern bool SetForegroundWindow(IntPtr hWnd);
  519.  
  520.         [DllImport("user32.dll")]
  521.         [return: MarshalAs(UnmanagedType.Bool)]
  522.         private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
  523.  
  524.         [DllImport("user32.dll")]
  525.         private static extern IntPtr WindowFromPoint(POINT Point);
  526.  
  527.         [DllImport("user32.dll")]
  528.         private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
  529.  
  530.         [DllImport("user32")]
  531.         static extern int GetSystemMetrics(int nIndex);
  532.  
  533.         // Struct for mouse hook
  534.         [StructLayout(LayoutKind.Sequential)]
  535.         private struct MSLLHOOKSTRUCT
  536.         {
  537.             public POINT pt; // Mouse coordinates
  538.             public uint mouseData; // Mouse data
  539.             public uint flags; // Event-injected flags
  540.             public uint time; // Timestamp
  541.             public IntPtr dwExtraInfo; // Extra information
  542.         }
  543.  
  544.         // Struct for point coordinates
  545.         [StructLayout(LayoutKind.Sequential)]
  546.         private struct POINT
  547.         {
  548.             public int x; // X coordinate
  549.             public int y; // Y coordinate
  550.         }
  551.  
  552.         [StructLayout(LayoutKind.Sequential)]
  553.         private struct WINDOWPLACEMENT
  554.         {
  555.             public int length;
  556.             public int flags;
  557.             public int showCmd;
  558.             public POINT ptMinPosition;
  559.             public POINT ptMaxPosition;
  560.             public RECT rcNormalPosition;
  561.         }
  562.  
  563.         [StructLayout(LayoutKind.Sequential)]
  564.         private struct RECT
  565.         {
  566.             public int left;
  567.             public int top;
  568.             public int right;
  569.             public int bottom;
  570.         }
  571.  
  572.         #endregion
  573.     }
  574. }
  575.  
Advertisement
Add Comment
Please, Sign In to add comment