Updated the NVIDIALibrary and WinLibrary to v1.7.0

This is the version used within NVIDIAInfo v1.7.0
This commit is contained in:
Terry MacDonald 2022-03-24 22:36:42 +13:00
parent 1f6af1f0c7
commit 67c1015730
12 changed files with 3518 additions and 1419 deletions

View File

@ -601,6 +601,12 @@ namespace DisplayMagician.Processes
{
if (ex.ErrorCode == -2147467259)
{
if (runAsAdministrator)
{
logger.Error(ex, $"ProcessUtils/TryExecute: Exception while trying to start {executable} for a second time with administrative rights. Giving up.");
return false;
}
logger.Error(ex, $"ProcessUtils/TryExecute: Exception while trying to start {executable}. The process requires elevation. Attempting again with admin rights.");
if (TryExecute(executable, arguments, out processCreated, true, priorityClass, maxWaitMs))
{

View File

@ -26,8 +26,8 @@ using System.Resources;
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
// Version information
[assembly: AssemblyVersion("2.2.0.250")]
[assembly: AssemblyFileVersion("2.2.0.250")]
[assembly: AssemblyVersion("2.2.0.251")]
[assembly: AssemblyFileVersion("2.2.0.251")]
[assembly: NeutralResourcesLanguageAttribute( "en" )]
[assembly: CLSCompliant(true)]

View File

@ -82,7 +82,7 @@
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="Utils.cs" />
<Compile Include="Windows\TaskBarSettings.cs" />
<Compile Include="Windows\TaskBarLayout.cs" />
<Compile Include="Windows\TaskBarStuckRectangle.cs" />
<Compile Include="UserControls\DisplayView.cs">
<SubType>UserControl</SubType>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -839,43 +839,7 @@ namespace DisplayMagicianShared
// We do the actual change we were trying to do
try
{
// Now we try to patch in a Windows Taskbar Stuck Rects list into the json if there isnt one
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing Windows Taskbar layout.");
// Create a default object (an empty list)
List<TaskBarStuckRectangle> taskBarStuckRectangles = new List<TaskBarStuckRectangle>();
for (int i = 0; i < root.Count; i++)
{
JObject profile = (JObject)root[i];
JArray WindowsTaskBarLayout = (JArray)profile.SelectToken("WindowsDisplayConfig.TaskBarLayout");
if (WindowsTaskBarLayout == null)
{
JObject WindowsDisplayConfig = (JObject)profile.SelectToken("WindowsDisplayConfig");
JArray newTaskBarLayout = JArray.FromObject(taskBarStuckRectangles);
WindowsDisplayConfig.Add("TaskBarLayout",newTaskBarLayout);
changedJson = true;
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Patched missing Windows TaskBarLayout in profile {profile.SelectToken("Name")} (index {i}).");
}
}
// Now we try to patch in a Windows Taskbar Settings list into the json if there isnt one
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing Windows Taskbar settings.");
// Create a default object using whatever the taskbar settings are right now
// (We're assuming the user keeps these settings standard)
TaskBarSettings taskBarSettings = TaskBarSettings.GetCurrent();
for (int i = 0; i < root.Count; i++)
{
JObject profile = (JObject)root[i];
JObject WindowsTaskBarSettings = (JObject)profile.SelectToken("WindowsDisplayConfig.TaskBarSettings");
if (WindowsTaskBarSettings == null)
{
JObject WindowsDisplayConfig = (JObject)profile.SelectToken("WindowsDisplayConfig");
JObject newTaskBarSettings = JObject.FromObject(taskBarSettings);
WindowsDisplayConfig.Add("TaskBarSettings", newTaskBarSettings);
changedJson = true;
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Patched missing Windows TaskBarSettings in profile {profile.SelectToken("Name")} (index {i}).");
}
}
// Nothing to patch at the moment!
}
catch (JsonReaderException ex)
{

View File

@ -1,6 +1,7 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@ -8,26 +9,259 @@ using System.Threading.Tasks;
namespace DisplayMagicianShared
{
class Utils
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
public struct WINDOWPOS
{
[StructLayout(LayoutKind.Sequential)]
public struct RECT
public IntPtr hWnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public SET_WINDOW_POSITION_FLAGS flags;
// Returns the WINDOWPOS structure pointed to by the lParam parameter
// of a WM_WINDOWPOSCHANGING or WM_WINDOWPOSCHANGED message.
public static WINDOWPOS FromMessage(IntPtr lParam)
{
public int left;
public int top;
public int right;
public int bottom;
// Marshal the lParam parameter to an WINDOWPOS structure,
// and return the new structure
return (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
}
[Flags]
public enum SendMessageTimeoutFlag : uint
// Replaces the original WINDOWPOS structure pointed to by the lParam
// parameter of a WM_WINDOWPOSCHANGING or WM_WINDOWPSCHANGING message
// with this one, so that the native window will be able to see any
// changes that we have made to its values.
public void UpdateMessage(IntPtr lParam)
{
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8,
SMTO_ERRORONEXIT = 0x20
// Marshal this updated structure back to lParam so the native
// window can respond to our changes.
// The old structure that it points to should be deleted, too.
Marshal.StructureToPtr(this, lParam, true);
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
public struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 8)]
public struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public uint uCallbackMessage;
public ABEDGE uEdge;
public RECT rc;
public int lParam;
}
/// <summary>
/// The MONITORINFOEX structure contains information about a display monitor.
/// The GetMonitorInfo function stores information into a MONITORINFOEX structure or a MONITORINFO structure.
/// The MONITORINFOEX structure is a superset of the MONITORINFO structure. The MONITORINFOEX structure adds a string member to contain a name
/// for the display monitor.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
public struct MONITORINFOEX
{
/// <summary>
/// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFOEX) (72) before calling the GetMonitorInfo function.
/// Doing so lets the function determine the type of structure you are passing to it.
/// </summary>
public UInt32 cbSize;
/// <summary>
/// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public RECT rcMonitor;
/// <summary>
/// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications,
/// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor.
/// The rest of the area in rcMonitor contains system windows such as the task bar and side bars.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public RECT rcWork;
/// <summary>
/// The attributes of the display monitor.
///
/// This member can be the following value:
/// 1 : MONITORINFOF_PRIMARY
/// </summary>
public UInt32 dwFlags;
/// <summary>
/// A string that specifies the device name of the monitor being used. Most applications have no use for a display monitor name,
/// and so can save some bytes by using a MONITORINFO structure.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Utils.CCHDEVICENAME)]
public string szDevice;
/*public void Init()
{
this.cbSize = 40 + 2 * Utils.CCHDEVICENAME;
this.DeviceName = string.Empty;
}*/
}
/// <summary>
/// The MONITORINFO structure contains information about a display monitor.
/// The GetMonitorInfo function stores information into a MONITORINFOEX structure or a MONITORINFO structure.
/// The MONITORINFOEX structure is a superset of the MONITORINFO structure. The MONITORINFOEX structure adds a string member to contain a name
/// for the display monitor.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
public struct MONITORINFO
{
/// <summary>
/// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFOEX) (72) before calling the GetMonitorInfo function.
/// Doing so lets the function determine the type of structure you are passing to it.
/// </summary>
public UInt32 cbSize;
/// <summary>
/// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public RECT rcMonitor;
/// <summary>
/// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications,
/// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor.
/// The rest of the area in rcMonitor contains system windows such as the task bar and side bars.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public RECT rcWork;
/// <summary>
/// The attributes of the display monitor.
///
/// This member can be the following value:
/// 1 : MONITORINFOF_PRIMARY
/// </summary>
public UInt32 dwFlags;
/*public void Init()
{
this.cbSize = 40 + 2 * Utils.CCHDEVICENAME;
this.DeviceName = string.Empty;
}*/
}
[Flags]
public enum SET_WINDOW_POSITION_FLAGS : UInt32
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
public enum SET_WINDOW_POSITION_ZORDER : Int32
{
HWND_TOP = 0,
HWND_BOTTOM = 1,
HWND_TOPMOST = -1,
HWND_NOTOPMOST = -2,
}
[Flags]
public enum SendMessageTimeoutFlag : uint
{
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8,
SMTO_ERRORONEXIT = 0x20
}
public enum ABEDGE : UInt32
{
ABE_LEFT = 0x0,
ABE_TOP = 0x1,
ABE_RIGHT = 0x2,
ABE_BOTTOM = 0x3,
}
[Flags]
public enum MOUSEKEYS : UInt32
{
MK_LBUTTON = 0x1,
MK_RBUTTON = 0x2,
MK_SHIFT = 0x4,
MK_CONTROL = 0x8,
MK_MBUTTON = 0x10,
MK_XBUTTON1 = 0x20,
MK_XBUTTON2 = 0x40,
}
enum SYSCOMMAND : int
{
SC_SIZE = 0xF000,
SC_MOVE = 0xF010,
SC_MINIMIZE = 0xF020,
SC_MAXIMIZE = 0xF030,
SC_NEXTWINDOW = 0xF040,
SC_PREVWINDOW = 0xF050,
SC_CLOSE = 0xF060,
SC_VSCROLL = 0xF070,
SC_HSCROLL = 0xF080,
SC_MOUSEMENU = 0xF090,
SC_KEYMENU = 0xF100,
SC_ARRANGE = 0xF110,
SC_RESTORE = 0xF120,
SC_TASKLIST = 0xF130,
SC_SCREENSAVE = 0xF140,
SC_HOTKEY = 0xF150,
//#if(WINVER >= 0x0400) //Win95
SC_DEFAULT = 0xF160,
SC_MONITORPOWER = 0xF170,
SC_CONTEXTHELP = 0xF180,
SC_SEPARATOR = 0xF00F,
//#endif /* WINVER >= 0x0400 */
//#if(WINVER >= 0x0600) //Vista
SCF_ISSECURE = 0x00000001,
//#endif /* WINVER >= 0x0600 */
/*
* Obsolete names
*/
SC_ICON = SC_MINIMIZE,
SC_ZOOM = SC_MAXIMIZE,
}
class Utils
{
#region enum HChangeNotifyEventID
/// <summary>
@ -298,19 +532,43 @@ namespace DisplayMagicianShared
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, Int16 wParam, Int16 lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern void SHChangeNotify(HChangeNotifyEventID wEventId,
HChangeNotifyFlags uFlags,
IntPtr dwItem1,
IntPtr dwItem2);
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int SHAppBarMessage(uint dwMessage, ref APPBARDATA pData);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
@ -318,6 +576,44 @@ namespace DisplayMagicianShared
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, SET_WINDOW_POSITION_ZORDER hWndInsertAfter, int x, int y, int cx, int cy, SET_WINDOW_POSITION_FLAGS uFlags);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int ShowWindow(IntPtr hwnd, int command);
[DllImport("user32.dll", SetLastError = true)]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
/// <summary>
/// The MoveWindow function changes the position and dimensions of the specified window. For a top-level window, the
/// position and dimensions are relative to the upper-left corner of the screen. For a child window, they are relative
/// to the upper-left corner of the parent window's client area.
/// <para>
/// Go to https://msdn.microsoft.com/en-us/library/windows/desktop/ms633534%28v=vs.85%29.aspx for more
/// information
/// </para>
/// </summary>
/// <param name="hWnd">C++ ( hWnd [in]. Type: HWND )<br /> Handle to the window.</param>
/// <param name="X">C++ ( X [in]. Type: int )<br />Specifies the new position of the left side of the window.</param>
/// <param name="Y">C++ ( Y [in]. Type: int )<br /> Specifies the new position of the top of the window.</param>
/// <param name="nWidth">C++ ( nWidth [in]. Type: int )<br />Specifies the new width of the window.</param>
/// <param name="nHeight">C++ ( nHeight [in]. Type: int )<br />Specifies the new height of the window.</param>
/// <param name="bRepaint">
/// C++ ( bRepaint [in]. Type: bool )<br />Specifies whether the window is to be repainted. If this
/// parameter is TRUE, the window receives a message. If the parameter is FALSE, no repainting of any kind occurs. This
/// applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the
/// parent window uncovered as a result of moving a child window.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.<br /> If the function fails, the return value is zero.
/// <br />To get extended error information, call GetLastError.
/// </returns>
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
public static bool IsWindows11()
{
var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
@ -328,11 +624,41 @@ namespace DisplayMagicianShared
return currentBuild >= 22000;
}
public static int MakeLParam(int p, int p_2)
public static int MakeLParam(int p, int p_2)
{
return ((p_2 << 16) | (p & 0xFFFF));
}
internal delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
public static List<MONITORINFOEX> EnumMonitors()
{
List<MONITORINFOEX> monitors = new List<MONITORINFOEX>();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
{
MONITORINFOEX mi = new MONITORINFOEX();
mi.cbSize = (uint)Marshal.SizeOf(mi);
bool success = GetMonitorInfo(hMonitor, ref mi);
if (success)
{
monitors.Add(mi);
}
return true;
}, IntPtr.Zero);
return monitors;
}
private static bool MonitorEnumCallBack(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
{
MONITORINFOEX mon_info = new MONITORINFOEX();
mon_info.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX));
//mon_info.szDevice = new char[Utils.CCHDEVICENAME];
GetMonitorInfo(hMonitor, ref mon_info);
///Monitor info is stored in 'mon_info'
return true;
}
/*public static bool RefreshNotificationTray()
{
Utils.SHChangeNotify(Utils.HChangeNotifyEventID.SHCNE_ASSOCCHANGED, Utils.HChangeNotifyFlags.SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
@ -344,13 +670,84 @@ namespace DisplayMagicianShared
return true;
}*/
public static Point PointFromLParam(IntPtr lParam)
{
return new Point((int)(lParam) & 0xFFFF, ((int)(lParam) >> 16) & 0xFFFF);
}
public static IntPtr LParamFromPoint(Point point)
{
return (IntPtr)((point.Y << 16) | (point.X & 0xFFFF));
}
public static IntPtr LParamFromPoint(int x, int y)
{
return (IntPtr)((y << 16) | (x & 0xFFFF));
}
public const int NULL = 0;
public const int HWND_BROADCAST = 0xffff;
public const int WM_ENTERSIZEMOVE = 0x0231;
public const int WM_EXITSIZEMOVE = 0x0232;
public const int WM_WINDOWPOSCHANGING = 0x0046;
public const int WM_WINDOWPOSCHANGED = 0x0047;
public const int WM_SYSCOMMAND = 0x112;
public const int WM_NOTIFY = 0xA005;
public const int WM_SETTINGCHANGE = 0x001a;
public const int WM_THEMECHANGED = 0x031a;
public const int WM_MOUSEMOVE = 0x0200;
public const int SPI_SETWORKAREA = 0x002F;
public const int SPI_SETWORKAREA = 0x002F;
public const int SHELLHOOK = 0xC028;
public const int WM_USER_REFRESHTASKBAR = 0x05CA;
public const int WM_USER_451 = 0x05C3;
public const int WM_USER_440 = 0x05B8;
public const int WM_USER_336 = 0x0550;
public const int WM_USER_92 = 0x045C;
public const int WM_USER_7 = 0x0407;
public const int WM_USER_1 = 0x0401;
public const int WM_USER_100 = 0x0464;
public const int WM_USER_13 = 0x040D;
public const int wParam_SHELLTRAY = 0x00000006;
public const int ABM_NEW = 0x00000000;
public const int ABM_REMOVE = 0x00000001;
public const int ABM_QUERYPOS = 0x00000002;
public const int ABM_SETPOS = 0x00000003;
public const int ABM_GETSTATE = 0x00000004;
public const int ABM_GETTASKBARPOS = 0x00000005;
public const int ABM_ACTIVATE = 0x00000006; // lParam == TRUE/FALSE means activate/deactivate
public const int ABM_GETAUTOHIDEBAR = 0x00000007;
public const int ABM_SETAUTOHIDEBAR = 0x00000008; // this can fail at any time. MUST check the result
// lParam = TRUE/FALSE Set/Unset
// uEdge = what edge
public const int ABM_WINDOWPOSCHANGED = 0x0000009;
public const int ABM_SETSTATE = 0x0000000a;
// these are put in the wparam of callback messages
public const int ABN_STATECHANGE = 0x0000000;
public const int ABN_POSCHANGED = 0x0000001;
public const int ABN_FULLSCREENAPP = 0x0000002;
public const int ABN_WINDOWARRANGE = 0x0000003; // lParam == TRUE means hide
// flags for get state
public const int ABS_AUTOHIDE = 0x0000001;
public const int ABS_ALWAYSONTOP = 0x0000002;
public const int ABE_LEFT = 0;
public const int ABE_TOP = 1;
public const int ABE_RIGHT = 2;
public const int ABE_BOTTOM = 3;
public const int MONITOR_DEFAULTTONULL = 0;
public const int MONITOR_DEFAULTTOPRIMARY = 1;
public const int MONITOR_DEFAULTTONEAREST = 2;
// size of a device name string
public const int CCHDEVICENAME = 32;
public const uint MONITORINFOF_PRIMARY = 1;
}

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace DisplayMagicianShared.Windows
{
public enum WIN32STATUS : uint
public enum WIN32STATUS : UInt32
{
ERROR_SUCCESS = 0,
ERROR_ACCESS_DENIED = 5,
@ -19,24 +19,29 @@ namespace DisplayMagicianShared.Windows
ERROR_BAD_CONFIGURATION = 1610,
}
public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : uint
public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : UInt32
{
Zero = 0,
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3,
DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4,
DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5,
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6,
DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, // Specifies the source name of the display device. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns the source name in the DISPLAYCONFIG_SOURCE_DEVICE_NAME structure.
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, // Specifies information about the monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns info about the monitor in the DISPLAYCONFIG_TARGET_DEVICE_NAME structure.
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, // Specifies information about the preferred mode of a monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns info about the preferred mode of a monitor in the DISPLAYCONFIG_TARGET_PREFERRED_MODE structure.
DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, // Specifies the graphics adapter name. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns the adapter name in the DISPLAYCONFIG_ADAPTER_NAME structure.
DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, // Specifies how to set the monitor. If the DisplayConfigSetDeviceInfo function is successful, DisplayConfigSetDeviceInfo uses info in the DISPLAYCONFIG_SET_TARGET_PERSISTENCE structure to force the output in a boot-persistent manner.
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, // Specifies how to set the base output technology for a given target ID. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns base output technology info in the DISPLAYCONFIG_TARGET_BASE_TYPE structure.
// Supported by WDDM 1.3 and later user-mode display drivers running on Windows 8.1 and later.
DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7, // Specifies the state of virtual mode support. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns virtual mode support information in the DISPLAYCONFIG_SUPPORT_VIRTUAL_RESOLUTION structure. Supported starting in Windows 10.
DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8, // Specifies how to set the state of virtual mode support. If the DisplayConfigSetDeviceInfo function is successful, DisplayConfigSetDeviceInfo uses info in the DISPLAYCONFIG_SUPPORT_VIRTUAL_RESOLUTION structure to change the state of virtual mode support. Supported starting in Windows 10.
DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9, // Specifies information about the state of the HDR Color for a display
DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10, // Enables or disables the HDR Color for a display
DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11, // Specifies the current SDR white level for an HDR monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo return SDR white level info in the DISPLAYCONFIG_SDR_WHITE_LEVEL structure.
// Supported starting in Windows<77>10 Fall Creators Update (Version 1709).
DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_SPECIALIZATION = 12,
DISPLAYCONFIG_DEVICE_INFO_SET_MONITOR_SPECIALIZATION = 13,
DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF // Only here to
}
[Flags]
public enum DISPLAYCONFIG_COLOR_ENCODING : uint
public enum DISPLAYCONFIG_COLOR_ENCODING : UInt32
{
DISPLAYCONFIG_COLOR_ENCODING_RGB = 0,
DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1,
@ -46,7 +51,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_SCALING : uint
public enum DISPLAYCONFIG_SCALING : UInt32
{
Zero = 0,
DISPLAYCONFIG_SCALING_IDENTITY = 1,
@ -59,7 +64,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_ROTATION : uint
public enum DISPLAYCONFIG_ROTATION : UInt32
{
Zero = 0,
DISPLAYCONFIG_ROTATION_IDENTITY = 1,
@ -70,7 +75,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : uint
public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : UInt32
{
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = 4294967295, // - 1
DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = 0,
@ -95,7 +100,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_TOPOLOGY_ID : uint
public enum DISPLAYCONFIG_TOPOLOGY_ID : UInt32
{
Zero = 0x0,
DISPLAYCONFIG_TOPOLOGY_INTERNAL = 0x00000001,
@ -106,7 +111,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_PATH_FLAGS : uint
public enum DISPLAYCONFIG_PATH_FLAGS : UInt32
{
Zero = 0x0,
DISPLAYCONFIG_PATH_ACTIVE = 0x00000001,
@ -115,14 +120,14 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_SOURCE_FLAGS : uint
public enum DISPLAYCONFIG_SOURCE_FLAGS : UInt32
{
Zero = 0x0,
DISPLAYCONFIG_SOURCE_IN_USE = 0x00000001,
}
[Flags]
public enum DISPLAYCONFIG_TARGET_FLAGS : uint
public enum DISPLAYCONFIG_TARGET_FLAGS : UInt32
{
Zero = 0x0,
DISPLAYCONFIG_TARGET_IN_USE = 0x00000001,
@ -134,7 +139,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum QDC : uint
public enum QDC : UInt32
{
Zero = 0x0,
// Get all paths
@ -154,7 +159,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum SDC : uint
public enum SDC : UInt32
{
Zero = 0x0,
SDC_TOPOLOGY_public = 0x00000001,
@ -194,7 +199,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_SCANLINE_ORDERING : uint
public enum DISPLAYCONFIG_SCANLINE_ORDERING : UInt32
{
DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED = 0,
DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE = 1,
@ -205,7 +210,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_PIXELFORMAT : uint
public enum DISPLAYCONFIG_PIXELFORMAT : UInt32
{
Zero = 0x0,
DISPLAYCONFIG_PIXELFORMAT_8BPP = 1,
@ -217,7 +222,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum DISPLAYCONFIG_MODE_INFO_TYPE : uint
public enum DISPLAYCONFIG_MODE_INFO_TYPE : UInt32
{
Zero = 0x0,
DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1,
@ -227,7 +232,7 @@ namespace DisplayMagicianShared.Windows
}
[Flags]
public enum D3D_VIDEO_SIGNAL_STANDARD : uint
public enum D3D_VIDEO_SIGNAL_STANDARD : UInt32
{
Uninitialized = 0,
VesaDmt = 1,

View File

@ -0,0 +1,861 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DisplayMagicianShared;
using Microsoft.Win32;
using Newtonsoft.Json;
// This file is based on Soroush Falahati's amazing HeliosDisplayManagement software
// available at https://github.com/falahati/HeliosDisplayManagement
// Substantial modifications made by Terry MacDonald 2022 onwards
namespace DisplayMagicianShared.Windows
{
public class TaskBarLayout
{
public enum TaskBarEdge : UInt32
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
[Flags]
public enum TaskBarOptions : UInt32
{
None = 0,
AutoHide = 1 << 0,
KeepOnTop = 1 << 1,
UseSmallIcons = 1 << 2,
HideClock = 1 << 3,
HideVolume = 1 << 4,
HideNetwork = 1 << 5,
HidePower = 1 << 6,
WindowPreview = 1 << 7,
Unknown1 = 1 << 8,
Unknown2 = 1 << 9,
HideActionCenter = 1 << 10,
Unknown3 = 1 << 11,
HideLocation = 1 << 12,
HideLanguageBar = 1 << 13
}
private const string MainDisplayAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects{0:D}";
private const string MultiDisplayAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MMStuckRects{0:D}";
/*private static readonly Dictionary<int, byte[]> Headers = new Dictionary<int, byte[]>
{
{2, new byte[] {0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}},
{3, new byte[] {0x30, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF}}
};
*/
public bool ReadFromRegistry(string regKeyValue)
{
bool MMStuckRectVerFound = false;
// Check if key exists
int version = 3;
string address = string.Format(MultiDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
MMStuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect3 registry key! {address}");
}
else
{
// If it's not version 3, then try version 2
version = 2;
address = string.Format(MultiDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
MMStuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect2 registry key! {address}");
}
else
{
// It's not v2 or v3, so it must be a single display
MMStuckRectVerFound = false;
SharedLogger.logger.Warn($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an MMStuckRect2 or MMStuckRect3 registry key! Going to test if it is a single display only.");
}
}
if (MMStuckRectVerFound)
{
// Check if value exists
if (version >= 2 && version <= 3)
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = false;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
// If we get here then we're done and don't need to continue with the rest of the code.
return true;
}
else
{
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen.");
}
}
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A MMStuckRect entry was found, but the version of the field is wrong.");
}
}
else
{
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: A MMStuckRect entry was NOT found. We will try to find the object in the StuckRect registry key instead");
}
bool StuckRectVerFound = false;
// Check if string exists
version = 3;
address = string.Format(MainDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
StuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect3 single display registry key! {address}");
}
else
{
// If it's not version 3, then try version 2
version = 2;
address = string.Format(MainDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
StuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect2 single display registry key! {address}");
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an single display StuckRect2 or StuckRect3 registry key! So we have to just return after doing nothing as there is nothing we can do.");
return false;
}
}
if (StuckRectVerFound)
{
// Check if value exists
if (version >= 2 && version <= 3)
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = true;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
return true;
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen.");
return false;
}
}
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was found, but the version of the field is wrong.");
return false;
}
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was NOT found. This means we're unable to get the taskbar location, an unable to return a sensible TaskBarStuckRectangle object.");
return false;
}
}
public TaskBarLayout()
{
}
public byte[] Binary { get; set; }
public string RegKeyValue { get; set; }
public bool MainScreen { get; set; }
public UInt32 DPI { get; set; }
public TaskBarEdge Edge { get; set; }
public Rectangle TaskBarLocation { get; set; }
public Rectangle StartMenuLocation { get; set; }
public Rectangle MonitorLocation { get; set; }
public Size MinSize { get; set; }
public TaskBarOptions Options { get; set; }
public uint Rows { get; set; }
public int Version { get; set; }
public override bool Equals(object obj) => obj is TaskBarLayout other && this.Equals(other);
public bool Equals(TaskBarLayout other)
{
return Version == other.Version &&
RegKeyValue == other.RegKeyValue &&
MainScreen == other.MainScreen &&
DPI == other.DPI &&
Edge == other.Edge &&
TaskBarLocation == other.TaskBarLocation &&
MinSize == other.MinSize &&
Options == other.Options &&
Rows == other.Rows;
}
public override int GetHashCode()
{
return (Version, MainScreen, RegKeyValue, DPI, Edge, TaskBarLocation, MinSize, Options, Rows).GetHashCode();
}
public static bool operator ==(TaskBarLayout lhs, TaskBarLayout rhs) => lhs.Equals(rhs);
public static bool operator !=(TaskBarLayout lhs, TaskBarLayout rhs) => !(lhs == rhs);
static bool Xor(byte[] a, byte[] b)
{
int x = a.Length ^ b.Length;
for (int i = 0; i < a.Length && i < b.Length; ++i)
{
x |= a[i] ^ b[i];
}
return x == 0;
}
private bool PopulateFieldsFromBinary()
{
// Now we decipher the binary properties features to populate the stuckrectangle
// DPI
if (Binary.Length < 44)
{
DPI = 0;
}
else
{
DPI = BitConverter.ToUInt32(Binary, 40);
}
// Edge
if (Binary.Length < 16)
{
Edge = TaskBarEdge.Bottom;
}
else
{
Edge = (TaskBarEdge)BitConverter.ToUInt32(Binary, 12);
}
// Location
if (Binary.Length < 40)
{
TaskBarLocation = Rectangle.Empty;
}
else
{
var left = BitConverter.ToInt32(Binary, 24);
var top = BitConverter.ToInt32(Binary, 28);
var right = BitConverter.ToInt32(Binary, 32);
var bottom = BitConverter.ToInt32(Binary, 36);
TaskBarLocation = Rectangle.FromLTRB(left, top, right, bottom);
}
// MinSize
if (Binary.Length < 24)
{
MinSize = Size.Empty;
}
else
{
var width = BitConverter.ToInt32(Binary, 16);
var height = BitConverter.ToInt32(Binary, 20);
MinSize = new Size(width, height);
}
// Options
if (Binary.Length < 12)
{
Options = 0;
}
else
{
Options = (TaskBarOptions)BitConverter.ToUInt32(Binary, 8);
}
// Rows
if (Binary.Length < 48)
{
Rows = 1;
}
else
{
Rows = BitConverter.ToUInt32(Binary, 44);
}
SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateFieldsFromBinary: Grabbed the following settings for {RegKeyValue} from the registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}.");
return true;
}
public bool PopulateBinaryFromFields()
{
// Set the DPI
if (Binary.Length < 44)
{
DPI = 0;
var bytes = BitConverter.GetBytes(DPI);
Array.Copy(bytes, 0, Binary, 40, 4);
}
else
{
var bytes = BitConverter.GetBytes(DPI);
Array.Copy(bytes, 0, Binary, 40, 4);
}
// Edge
if (Binary.Length < 16)
{
Edge = TaskBarEdge.Bottom;
var bytes = BitConverter.GetBytes((uint)Edge);
Array.Copy(bytes, 0, Binary, 12, 4);
}
else
{
var bytes = BitConverter.GetBytes((uint)Edge);
Array.Copy(bytes, 0, Binary, 12, 4);
}
// Location
if (Binary.Length < 40)
{
var bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 24, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 28, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 32, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 36, 4);
}
else
{
var bytes = BitConverter.GetBytes(TaskBarLocation.Left);
Array.Copy(bytes, 0, Binary, 24, 4);
bytes = BitConverter.GetBytes(TaskBarLocation.Top);
Array.Copy(bytes, 0, Binary, 28, 4);
bytes = BitConverter.GetBytes(TaskBarLocation.Right);
Array.Copy(bytes, 0, Binary, 32, 4);
bytes = BitConverter.GetBytes(TaskBarLocation.Bottom);
Array.Copy(bytes, 0, Binary, 36, 4);
}
// MinSize
if (Binary.Length < 24)
{
var bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 16, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 20, 4);
}
else
{
var bytes = BitConverter.GetBytes(MinSize.Width);
Array.Copy(bytes, 0, Binary, 16, 4);
bytes = BitConverter.GetBytes(MinSize.Height);
Array.Copy(bytes, 0, Binary, 20, 4);
}
// Options
if (Binary.Length < 12)
{
var bytes = BitConverter.GetBytes((uint)0);
Array.Copy(bytes, 0, Binary, 8, 4);
}
else
{
var bytes = BitConverter.GetBytes((uint)Options);
Array.Copy(bytes, 0, Binary, 8, 4);
}
// Rows
if (Binary.Length < 48)
{
var bytes = BitConverter.GetBytes(1);
Array.Copy(bytes, 0, Binary, 44, 4);
}
else
{
var bytes = BitConverter.GetBytes(Rows);
Array.Copy(bytes, 0, Binary, 44, 4);
}
SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateBinaryFromFields: Set the following settings for {RegKeyValue} into registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}.");
return true;
}
public bool WriteToRegistry()
{
// Update the binary with the current settings from the object
//PopulateBinaryFromFields();
// Write the binary field to registry
string address;
if (MainScreen && RegKeyValue.Equals("Settings"))
{
address = string.Format(MainDisplayAddress, Version);
// Set the Main Screen
try
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(RegKeyValue, Binary);
SharedLogger.logger.Trace($"TaskBarStuckRectangle/Apply: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!");
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/GetCurrent: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!");
}
}
else
{
address = string.Format(MultiDisplayAddress, Version);
// Grab the main screen taskbar placement
try
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(RegKeyValue, Binary);
SharedLogger.logger.Trace($"TaskBarStuckRectangle/WriteToRegistry: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!");
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/WriteToRegistry: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!");
}
}
return true;
}
public static Dictionary<string, TaskBarLayout> GetAllCurrentTaskBarLayouts(Dictionary<string, List<DISPLAY_SOURCE>> displaySources)
{
Dictionary<string, TaskBarLayout> taskBarStuckRectangles = new Dictionary<string, TaskBarLayout>();
int state;
APPBARDATA abd = new APPBARDATA();
// Sleep delay just for testing so I can get the position of the Start Menu
// Note for future me, the Start menu window is moved around the desktop to be next to the start button that is pressed by the user.
// e.g. if you have two screens, and you click the right most start button, the Start menu window is moved to be the same as the WorkRect of the
// monitor that the start button is on.
//System.Threading.Thread.Sleep(5000);
// Firstly try to get the position of the main screen and main start menu
try
{
// Figure out which monitor this taskbar is on
IntPtr mainTaskbarHwnd = Utils.FindWindow("Shell_TrayWnd", "");
IntPtr mainMonitorHwnd = Utils.MonitorFromWindow(mainTaskbarHwnd, Utils.MONITOR_DEFAULTTOPRIMARY);
//IntPtr startMenuHwnd = Utils.FindWindow("Windows.UI.Core.CoreWindow", "Start");
//Utils.GetWindowRect(startMenuHwnd, out RECT lpRect);
// Figure out the monitor coordinates
MONITORINFOEX monitorInfo = new MONITORINFOEX();
monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX));
//monitorInfo.szDevice = new char[Utils.CCHDEVICENAME];
Utils.GetMonitorInfo(mainMonitorHwnd, ref monitorInfo);
abd.hWnd = mainTaskbarHwnd;
abd.uEdge = ABEDGE.ABE_BOTTOM;
abd.lParam = 0x1;
abd.cbSize = Marshal.SizeOf(typeof(APPBARDATA));
state = Utils.SHAppBarMessage(Utils.ABM_GETTASKBARPOS, ref abd);
if (state == 1)
{
int tbWidth = Math.Abs(abd.rc.left - abd.rc.right);
int tbHeight = Math.Abs(abd.rc.top - abd.rc.bottom);
int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right);
int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom);
TaskBarLayout tbsr = new TaskBarLayout();
// Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created
tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
tbsr.Edge = (TaskBarEdge)abd.uEdge;
tbsr.MonitorLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight);
switch (tbsr.Edge)
{
case TaskBarEdge.Left:
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
break;
case TaskBarEdge.Top:
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
break;
case TaskBarEdge.Right:
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.right, monitorInfo.rcWork.top, tbWidth, tbHeight);
break;
case TaskBarEdge.Bottom:
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight);
break;
default:
// Default is bottom taskbar
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight);
break;
}
tbsr.MainScreen = true;
// Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored
tbsr.PopulateBinaryFromFields();
taskBarStuckRectangles.Add(monitorInfo.szDevice, tbsr);
// If it's a main screen, also add a duplicate so we track the main StuckRects settings separately too
TaskBarLayout tbsrMain = new TaskBarLayout();
tbsrMain.ReadFromRegistry("Settings");
tbsrMain.Edge = tbsr.Edge;
tbsrMain.MonitorLocation = tbsr.MonitorLocation;
tbsrMain.TaskBarLocation = tbsr.TaskBarLocation;
tbsrMain.MainScreen = tbsr.MainScreen;
// Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored
tbsrMain.PopulateBinaryFromFields();
taskBarStuckRectangles.Add("Settings", tbsrMain);
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"WinLibrary/GetAllCurrentTaskBarPositions: Exception while trying to get the maintaskbar position");
}
// Then go through the secondary windows and get the position of them
// Tell Windows to refresh the Other Windows Taskbars if needed
IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL;
for (int i = 0; i < 100; i++)
{
// Find the next "Shell_SecondaryTrayWnd" window
IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null);
if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL)
{
// No more windows taskbars to notify
break;
}
IntPtr secMonitorHwnd = Utils.MonitorFromWindow(nextTaskBarWindowHwnd, Utils.MONITOR_DEFAULTTONEAREST);
// Figure out the monitor coordinates
MONITORINFOEX monitorInfo = new MONITORINFOEX();
monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX));
//monitorInfo.szDevice = new char[Utils.CCHDEVICENAME];
Utils.GetMonitorInfo(secMonitorHwnd, ref monitorInfo);
// Figure out the position of the taskbar ourselves
int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right);
int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom);
int wrkWidth = Math.Abs(monitorInfo.rcWork.left - monitorInfo.rcWork.right);
int wrkHeight = Math.Abs(monitorInfo.rcWork.top - monitorInfo.rcWork.bottom);
int tbWidth;
int tbHeight;
TaskBarLayout tbsr = new TaskBarLayout();
// Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created
tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
if (monWidth == wrkWidth)
{
// Taskbar on top or bottom
if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top)
{
// Taskbar on bottom
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Bottom;
}
else if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom)
{
// Taskbar on top
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Top;
}
else
{
// Invalid state
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not on a horizontal edge of a monitor!");
}
}
else if (monHeight == wrkHeight)
{
// Taskbar on the sides
if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom)
{
// Taskbar on left
tbWidth = monWidth - wrkWidth;
tbHeight = monHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Left;
}
else if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top)
{
// Taskbar on right
tbWidth = monWidth - wrkWidth;
tbHeight = monHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.right, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Right;
}
else
{
// Invalid state
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not on a vertical edge of a monitor!");
}
}
else
{
// Invalid state
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not fully along one of the monitor edges!");
}
tbsr.MonitorLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight);
tbsr.MainScreen = false;
// Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored
tbsr.PopulateBinaryFromFields();
if (!taskBarStuckRectangles.ContainsKey(monitorInfo.szDevice))
{
taskBarStuckRectangles.Add(monitorInfo.szDevice, tbsr);
}
else
{
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Skipping grabbing Taskbar position from a cloned display {monitorInfo.szDevice}");
}
// Prep the next taskbar window so we continue through them
lastTaskBarWindowHwnd = nextTaskBarWindowHwnd;
}
return taskBarStuckRectangles;
}
public bool MoveTaskBar()
{
if (RegKeyValue.Equals("Settings") && MainScreen)
{
// We only want to set the position for the main screen if it has a "Settings" entry and is a main screen
// Find the window to move
IntPtr mainTaskbarHwnd = Utils.FindWindow("Shell_TrayWnd", "");
//IntPtr startButtonHandle = Utils.FindWindowEx(mainTaskbarHwnd, IntPtr.Zero, "Start", null);
IntPtr systemTrayNotifyHandle = Utils.FindWindowEx(mainTaskbarHwnd, IntPtr.Zero, "TrayNotifyWnd", null);
//IntPtr rebarWindowHandle = Utils.FindWindowEx(mainTaskbarHwnd, IntPtr.Zero, "ReBarWindow32", null);
//IntPtr trayDesktopShowButtonHandle = Utils.FindWindowEx(systemTrayNotifyHandle, IntPtr.Zero, "TrayShowDesktopButtonWClass", null);
IntPtr result;
// ===== MOVE THE MAIN TASKBAR WINDOW =====
// Prepare the taskbar for moving
Utils.SendMessageTimeout(mainTaskbarHwnd, Utils.WM_ENTERSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result);
// Move the taskbar window
Utils.MoveWindow(mainTaskbarHwnd, TaskBarLocation.X, TaskBarLocation.Y, TaskBarLocation.Width, TaskBarLocation.Height, false);
// ===== LOCK THE MAIN TASKBAR WINDOW BACK DOWN =====
// Tell the taskbar we've stopped moving it now
Utils.SendMessageTimeout(mainTaskbarHwnd, Utils.WM_EXITSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result);
// Tell the taskbar it needs to update it's theme
Utils.PostMessage(mainTaskbarHwnd, Utils.WM_THEMECHANGED, IntPtr.Zero, IntPtr.Zero);
// Tell the taskbar it needs to recalculate it's work area
Utils.SendMessageTimeout(systemTrayNotifyHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result);
// We also save the taskbar position for the monitor in registry, so that Windows will actually properly update the position
// after 5 seconds (and this one will stick between reboots too!).
WriteToRegistry();
}
else if (MainScreen && !RegKeyValue.Equals("Settings"))
{
// If it's a main screen, but not "settings", then its the registry key only taskbar setting we need to change
// This is because hte only screen settings that matter are the StuckRect3 registry key (for the main screen) and
// all of the secondary windows
WriteToRegistry();
}
else
{
// This is a secondary screen, so we need to set it's position
// Then go through the secondary windows and get the position of them
// Tell Windows to refresh the Other Windows Taskbars if needed
//WriteToRegistry();
IntPtr mainTaskbarHwnd = Utils.FindWindow("Shell_TrayWnd", "");
IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL;
for (int i = 0; i < 100; i++)
{
// Find the next "Shell_SecondaryTrayWnd" window
IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null);
if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL)
{
// No more windows taskbars to notify
break;
}
IntPtr secMonitorHwnd = Utils.MonitorFromWindow(nextTaskBarWindowHwnd, Utils.MONITOR_DEFAULTTONEAREST);
// Figure out this monitor coordinates
MONITORINFOEX monitorInfo = new MONITORINFOEX();
monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX));
//monitorInfo.szDevice = new char[Utils.CCHDEVICENAME];
Utils.GetMonitorInfo(secMonitorHwnd, ref monitorInfo);
// Figure out the position of the taskbar ourselves
int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right);
int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom);
Rectangle thisMonitorLocation = new Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight);
if (MonitorLocation.Equals(thisMonitorLocation))
{
// This is the right monitor, so we should move the taskbar on it.
IntPtr result;
// ===== MOVE THE MAIN TASKBAR WINDOW =====
// Prepare the taskbar for moving
Utils.SendMessageTimeout(nextTaskBarWindowHwnd, Utils.WM_ENTERSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result);
// Move the taskbar window
Utils.MoveWindow(nextTaskBarWindowHwnd, TaskBarLocation.X, TaskBarLocation.Y, TaskBarLocation.Width, TaskBarLocation.Height, true);
// ===== LOCK THE MAIN TASKBAR WINDOW BACK DOWN =====
// Tell the taskbar we've stopped moving it now
//Utils.SendMessageTimeout(nextTaskBarWindowHwnd, Utils.WM_EXITSIZEMOVE, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result);
// Tell the taskbar it needs to update it's theme
//Utils.PostMessage(nextTaskBarWindowHwnd, Utils.WM_THEMECHANGED, IntPtr.Zero, IntPtr.Zero);
// Tell the taskbar it needs to recalculate it's work area
//Utils.SendMessageTimeout(nextTaskBarWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, IntPtr.Zero, SendMessageTimeoutFlag.SMTO_NORMAL, 10, out result);
// We also save the taskbar position for the monitor in registry, so that Windows will actually properly update the position
// after 5 seconds (and this one will stick between reboots too!).
WriteToRegistry();
// We then want to stop as we've found the correct taskbar to move!
break;
}
// Prep the next taskbar window so we continue through them
lastTaskBarWindowHwnd = nextTaskBarWindowHwnd;
}
}
return true;
}
public static string GetRegKeyValueFromDevicePath(string devicePath)
{
string regKeyValue = "";
// e.g. "\\\\?\\DISPLAY#NVS10DE#5&2b46c695&0&UID185344#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}"
string pattern = @"DISPLAY\#(.*)\#\{";
Match match = Regex.Match(devicePath, pattern);
if (match.Success)
{
regKeyValue = match.Groups[1].Value;
SharedLogger.logger.Trace($"TaskBarLayout/GetRegKeyValueFromDevicePath: Found regKeyValue {regKeyValue } in the devicePath {devicePath }.");
}
else
{
SharedLogger.logger.Warn($"TaskBarLayout/GetRegKeyValueFromDevicePath: We were unable to figure out the regKeyValue {regKeyValue } in the devicePath {devicePath }..");
}
return regKeyValue;
}
public static bool ForceTaskBarRedraw(IntPtr mainToolBarHWnd)
{
// Tell Windows to refresh the Main Screen Windows Taskbar registry settings by telling Explorer to update.
Utils.SendMessage(mainToolBarHWnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL);
// Tell Windows to refresh the child Windows in the taskbar
IntPtr lastChildWindowHwnd = (IntPtr)Utils.NULL;
for (int i = 0; i < 100; i++)
{
// Find the next "Shell_SecondaryTrayWnd" window
IntPtr nextChildWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, mainToolBarHWnd, "", null);
if (nextChildWindowHwnd == (IntPtr)Utils.NULL)
{
// No more windows taskbars to notify
break;
}
// Send the "Shell_TrayWnd" window a WM_SETTINGCHANGE with a wParameter of SPI_SETWORKAREA
Utils.SendMessage(lastChildWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL);
lastChildWindowHwnd = nextChildWindowHwnd;
}
//IntPtr explorerToolBarHWnd = Utils.FindWindow("Shell_TrayWnd", null);
//Utils.PostMessage((IntPtr)Utils.HWND_BROADCAST, Utils.SHELLHOOK, 0x13, (int) mainToolBarHWnd);
//Utils.PostMessage((IntPtr)Utils.HWND_BROADCAST, Utils.WM_SETTINGCHANGE, (int)Utils.SPI_SETWORKAREA, (int)Utils.NULL);
/*IntPtr result;
Utils.SendMessageTimeout((IntPtr)Utils.HWND_BROADCAST, Utils.WM_USER_1, (IntPtr)Utils.NULL, (IntPtr)Utils.NULL, Utils.SendMessageTimeoutFlag.SMTO_ABORTIFHUNG, 15, out result);*/
return true;
}
}
[global::System.Serializable]
public class TaskBarStuckRectangleException : Exception
{
public TaskBarStuckRectangleException() { }
public TaskBarStuckRectangleException(string message) : base(message) { }
public TaskBarStuckRectangleException(string message, Exception inner) : base(message, inner) { }
protected TaskBarStuckRectangleException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}

View File

@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;
// This file is taken from Soroush Falahati's amazing HeliosDisplayManagement software
// available at https://github.com/falahati/HeliosDisplayManagement
// Modifications made by Terry MacDonald
namespace DisplayMagicianShared.Windows
{
public class TaskBarSettings
{
private const string AdvancedSettingsAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
private static List<string> WantedAdvancedSettingValues = new List<string>
{
// Win10/11 registry keys (not all will be populated, only those that the user modified from default at least once)
"MMTaskbarEnabled", // Multiple Taskbars: 0 for show taskbar on main screen only, 1 for show taskbar on all screens
"MMTaskbarMode", // Show taskbar buttons on: 0 = all taskbars, 1 = main taskbar and where windows is open, 2 = taskbar where window is open
"MMTaskbarGlomLevel", // Buttons on other taskbars: 0 = always combine, combine when the taskbar is full, 2 = never combine
"NoTaskGrouping", // Disable all Task Grouping (overrides "TaskbarGroupSize"): 0 = enable task grouping, 1 = disable task grouping
"SearchboxTaskbarMode", // Show Search Button in Taskbar: 0 = remove search button, 1 = show search button
"ShowTaskViewButton", // Show Taskview Button in Taskbar: 0 = remove taskview button, 1 = show taskview button
"TaskbarAl", // Start Button Alignment: 0 for left, 1 for center,
"TaskbarDa", // Show Widgets button in Taskbar: 0 = remove widgets button, 1 = Show widgets button
"TaskbarGlomLevel", // Buttons on main screen: 0 = always combine, combine when the taskbar is full, 2 = never combine
"TaskbarGroupSize", // TaskBar left/right grouping by age: 0 = oldest first (default), 1 = roup by size largest first, 2 = group all with 2 or more, 3 = group all with 3 or more (see NoTaskGrouping to prevent Grouping )
"TaskbarMn", // Show Chat Button in Taskbar: 0 = remove chat button, 1 = show chat button
"TaskbarSi", // Taskbar Size: 0 = small, 1 = medium, 2 = Large
"TaskbarSizeMove", // Lock the Taskbar (prevent resizing): 0 = taskbar size is locked, 1 = taskbar size is unlocked
"TaskbarSmallIcons", // Small Taskbar Icons: 0 = normal sized icons, 1 = small icons
"TaskbarSd", // Show Desktop Button in Taskbar: 0 for hid the show desktop button, 1 for show the Show desktop button
};
public Tuple<string, int>[] Options { get; set; }
public override bool Equals(object obj) => obj is TaskBarSettings other && this.Equals(other);
public bool Equals(TaskBarSettings other)
=> Options.All(a => other.Options.Any(x => x.Item1 == a.Item1 && x.Item2 == a.Item2));
public override int GetHashCode()
{
return (Options).GetHashCode();
}
public static bool operator ==(TaskBarSettings lhs, TaskBarSettings rhs) => lhs.Equals(rhs);
public static bool operator !=(TaskBarSettings lhs, TaskBarSettings rhs) => !(lhs == rhs);
public static TaskBarSettings GetCurrent()
{
var taskBarOptions = new List<Tuple<string, int>>();
// Get modified and stored Taskbar options from the User Registry
// Note: Only the taskbar options changed from default at least once in the past will be listed in Registry
try
{
using (var key = Registry.CurrentUser.OpenSubKey(
AdvancedSettingsAddress,
RegistryKeyPermissionCheck.ReadSubTree))
{
if (key != null)
{
foreach (var valueName in WantedAdvancedSettingValues)
{
try
{
var value = key.GetValue(valueName, null,
RegistryValueOptions.DoNotExpandEnvironmentNames);
if (value != null && value is int intValue)
{
taskBarOptions.Add(new Tuple<string, int>(valueName, intValue));
}
}
catch (Exception)
{
// ignored, as this will happen
}
}
}
}
}
catch (Exception)
{
// ignored
}
if (taskBarOptions.Count == 0)
{
return null;
}
return new TaskBarSettings
{
Options = taskBarOptions.ToArray()
};
}
public bool Apply()
{
if (Options.Length == 0)
{
throw new InvalidOperationException();
}
using (var optionsKey = Registry.CurrentUser.OpenSubKey(
AdvancedSettingsAddress,
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
if (optionsKey == null)
{
return false;
}
// Write
foreach (var option in Options)
{
try
{
optionsKey.SetValue(option.Item1, option.Item2);
}
catch (Exception)
{
// ignored
}
}
}
return true;
}
}
}

View File

@ -1,561 +1,137 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DisplayMagicianShared;
using System.Linq;
using Microsoft.Win32;
using Newtonsoft.Json;
// This file is taken from Soroush Falahati's amazing HeliosDisplayManagement software
// available at https://github.com/falahati/HeliosDisplayManagement
// Substantial modifications made by Terry MacDonald 2022 onwards
// Modifications made by Terry MacDonald
namespace DisplayMagicianShared.Windows
{
public class TaskBarStuckRectangle
{
public class TaskBarSettings
{
private const string AdvancedSettingsAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
public enum TaskBarEdge : UInt32
private static List<string> WantedAdvancedSettingValues = new List<string>
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
[Flags]
public enum TaskBarOptions : UInt32
{
None = 0,
AutoHide = 1 << 0,
KeepOnTop = 1 << 1,
UseSmallIcons = 1 << 2,
HideClock = 1 << 3,
HideVolume = 1 << 4,
HideNetwork = 1 << 5,
HidePower = 1 << 6,
WindowPreview = 1 << 7,
Unknown1 = 1 << 8,
Unknown2 = 1 << 9,
HideActionCenter = 1 << 10,
Unknown3 = 1 << 11,
HideLocation = 1 << 12,
HideLanguageBar = 1 << 13
}
private const string MainDisplayAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StuckRects{0:D}";
private const string MultiDisplayAddress =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MMStuckRects{0:D}";
/*private static readonly Dictionary<int, byte[]> Headers = new Dictionary<int, byte[]>
{
{2, new byte[] {0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}},
{3, new byte[] {0x30, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF}}
// Win10/11 registry keys (not all will be populated, only those that the user modified from default at least once)
"MMTaskbarEnabled", // Multiple Taskbars: 0 for show taskbar on main screen only, 1 for show taskbar on all screens
"MMTaskbarMode", // Show taskbar buttons on: 0 = all taskbars, 1 = main taskbar and where windows is open, 2 = taskbar where window is open
"MMTaskbarGlomLevel", // Buttons on other taskbars: 0 = always combine, combine when the taskbar is full, 2 = never combine
"NoTaskGrouping", // Disable all Task Grouping (overrides "TaskbarGroupSize"): 0 = enable task grouping, 1 = disable task grouping
"SearchboxTaskbarMode", // Show Search Button in Taskbar: 0 = remove search button, 1 = show search button
"ShowTaskViewButton", // Show Taskview Button in Taskbar: 0 = remove taskview button, 1 = show taskview button
"TaskbarAl", // Start Button Alignment: 0 for left, 1 for center,
"TaskbarDa", // Show Widgets button in Taskbar: 0 = remove widgets button, 1 = Show widgets button
"TaskbarGlomLevel", // Buttons on main screen: 0 = always combine, combine when the taskbar is full, 2 = never combine
"TaskbarGroupSize", // TaskBar left/right grouping by age: 0 = oldest first (default), 1 = roup by size largest first, 2 = group all with 2 or more, 3 = group all with 3 or more (see NoTaskGrouping to prevent Grouping )
"TaskbarMn", // Show Chat Button in Taskbar: 0 = remove chat button, 1 = show chat button
"TaskbarSi", // Taskbar Size: 0 = small, 1 = medium, 2 = Large
"TaskbarSizeMove", // Lock the Taskbar (prevent resizing): 0 = taskbar size is locked, 1 = taskbar size is unlocked
"TaskbarSmallIcons", // Small Taskbar Icons: 0 = normal sized icons, 1 = small icons
"TaskbarSd", // Show Desktop Button in Taskbar: 0 for hid the show desktop button, 1 for show the Show desktop button
};
*/
public TaskBarStuckRectangle(string devicePath)
{
bool MMStuckRectVerFound = false;
// Check if key exists
int version = 3;
string address = string.Format(MultiDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
MMStuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect3 registry key! {address}");
}
else
{
// If it's not version 3, then try version 2
version = 2;
address = string.Format(MultiDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
MMStuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect2 registry key! {address}");
}
else
{
// It's not v2 or v3, so it must be a single display
MMStuckRectVerFound = false;
SharedLogger.logger.Warn($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an MMStuckRect2 or MMStuckRect3 registry key! Going to test if it is a single display only.");
}
}
bool foundDevicePath = false;
if (MMStuckRectVerFound)
{
// Check if value exists
if (version >= 2 && version <= 3)
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(devicePath) as byte[];
if (binary?.Length > 0)
{
foundDevicePath = true;
MainScreen = false;
DevicePath = devicePath;
Binary = binary;
OriginalBinary = new byte[binary.Length];
binary.CopyTo(OriginalBinary, 0);
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
public Tuple<string, int>[] Options { get; set; }
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {DevicePath} is against the {Edge} edge, is positioned at ({Location.X},{Location.Y}) and is {Location.Width}x{Location.Height} in size.");
}
else
{
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {devicePath} screen.");
}
}
}
}
if (!foundDevicePath)
{
bool StuckRectVerFound = false;
// Check if string exists
version = 3;
address = string.Format(MainDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
StuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect3 single display registry key! {address}");
}
else
{
// If it's not version 3, then try version 2
version = 2;
address = string.Format(MainDisplayAddress, version);
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
StuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect2 single display registry key! {address}");
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an single display StuckRect2 or StuckRect3 registry key! So we have to just return after doing nothing as there is nothing we can do.");
return;
}
}
if (StuckRectVerFound)
{
// Check if value exists
if (version >= 2 && version <= 3)
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(devicePath) as byte[];
if (binary?.Length > 0)
{
foundDevicePath = true;
MainScreen = true;
DevicePath = devicePath;
Binary = binary;
OriginalBinary = new byte[binary.Length];
binary.CopyTo(OriginalBinary, 0);
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {DevicePath} is against the {Edge} edge, is positioned at ({Location.X},{Location.Y}) and is {Location.Width}x{Location.Height} in size.");
}
else
{
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {devicePath} screen.");
}
}
}
}
}
}
public TaskBarStuckRectangle()
{
}
public byte[] Binary { get; set; }
public byte[] OriginalBinary { get; set; }
public string DevicePath { get; set; }
public bool MainScreen { get; set; }
public UInt32 DPI { get; set; }
public TaskBarEdge Edge { get; set; }
public Rectangle Location { get; set; }
public Size MinSize { get; set; }
public TaskBarOptions Options { get; set; }
public uint Rows { get; set; }
public int Version { get; set; }
public override bool Equals(object obj) => obj is TaskBarStuckRectangle other && this.Equals(other);
public bool Equals(TaskBarStuckRectangle other)
{
return Version == other.Version &&
DevicePath == other.DevicePath &&
MainScreen == other.MainScreen &&
DPI == other.DPI &&
Edge == other.Edge &&
Location == other.Location &&
MinSize == other.MinSize &&
Options == other.Options &&
Rows == other.Rows;
}
public override bool Equals(object obj) => obj is TaskBarSettings other && this.Equals(other);
public bool Equals(TaskBarSettings other)
=> Options.All(a => other.Options.Any(x => x.Item1 == a.Item1 && x.Item2 == a.Item2));
public override int GetHashCode()
{
return (Version, MainScreen, DevicePath, DPI, Edge, Location, MinSize, Options, Rows).GetHashCode();
return (Options).GetHashCode();
}
public static bool operator ==(TaskBarStuckRectangle lhs, TaskBarStuckRectangle rhs) => lhs.Equals(rhs);
public static bool operator ==(TaskBarSettings lhs, TaskBarSettings rhs) => lhs.Equals(rhs);
public static bool operator !=(TaskBarStuckRectangle lhs, TaskBarStuckRectangle rhs) => !(lhs == rhs);
static bool Xor(byte[] a, byte[] b)
public static bool operator !=(TaskBarSettings lhs, TaskBarSettings rhs) => !(lhs == rhs);
public static TaskBarSettings GetCurrent()
{
var taskBarOptions = new List<Tuple<string, int>>();
int x = a.Length ^ b.Length;
for (int i = 0; i < a.Length && i < b.Length; ++i)
// Get modified and stored Taskbar options from the User Registry
// Note: Only the taskbar options changed from default at least once in the past will be listed in Registry
try
{
x |= a[i] ^ b[i];
}
return x == 0;
}
private bool PopulateFieldsFromBinary()
{
// Now we decipher the binary properties features to populate the stuckrectangle
// DPI
if (Binary.Length < 44)
{
DPI = 0;
}
else
{
DPI = BitConverter.ToUInt32(Binary, 40);
}
// Edge
if (Binary.Length < 16)
{
Edge = TaskBarEdge.Bottom;
}
else
{
Edge = (TaskBarEdge)BitConverter.ToUInt32(Binary, 12);
}
// Location
if (Binary.Length < 40)
{
Location = Rectangle.Empty;
}
else
{
var left = BitConverter.ToInt32(Binary, 24);
var top = BitConverter.ToInt32(Binary, 28);
var right = BitConverter.ToInt32(Binary, 32);
var bottom = BitConverter.ToInt32(Binary, 36);
Location = Rectangle.FromLTRB(left, top, right, bottom);
}
// MinSize
if (Binary.Length < 24)
{
MinSize = Size.Empty;
}
else
{
var width = BitConverter.ToInt32(Binary, 16);
var height = BitConverter.ToInt32(Binary, 20);
MinSize = new Size(width, height);
}
// Options
if (Binary.Length < 12)
{
Options = 0;
}
else
{
Options = (TaskBarOptions)BitConverter.ToUInt32(Binary, 8);
}
// Rows
if (Binary.Length < 48)
{
Rows = 1;
}
else
{
Rows = BitConverter.ToUInt32(Binary, 44);
}
SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateFieldsFromBinary: Grabbed the following settings for {DevicePath} from the registry: DPI = {DPI}, Edge = {Edge}, Location = ({Location.X},{Location.Y}), MinSize = {Location.Width}x{Location.Height}, Options = {Options}, Rows = {Rows}.");
return true;
}
public bool PopulateBinaryFromFields()
{
// Set the DPI
if (Binary.Length < 44)
{
DPI = 0;
var bytes = BitConverter.GetBytes(DPI);
Array.Copy(bytes, 0, Binary, 40, 4);
}
else
{
var bytes = BitConverter.GetBytes(DPI);
Array.Copy(bytes, 0, Binary, 40, 4);
}
// Edge
if (Binary.Length < 16)
{
Edge = TaskBarEdge.Bottom;
var bytes = BitConverter.GetBytes((uint)Edge);
Array.Copy(bytes, 0, Binary, 12, 1);
}
else
{
var bytes = BitConverter.GetBytes((uint)Edge);
Array.Copy(bytes, 0, Binary, 12, 1);
}
// Location
if (Binary.Length < 40)
{
var bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 24, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 28, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 32, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 36, 4);
}
else
{
var bytes = BitConverter.GetBytes(Location.Left);
Array.Copy(bytes, 0, Binary, 24, 4);
bytes = BitConverter.GetBytes(Location.Top);
Array.Copy(bytes, 0, Binary, 28, 4);
bytes = BitConverter.GetBytes(Location.Right);
Array.Copy(bytes, 0, Binary, 32, 4);
bytes = BitConverter.GetBytes(Location.Bottom);
Array.Copy(bytes, 0, Binary, 36, 4);
}
// MinSize
if (Binary.Length < 24)
{
var bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 16, 4);
bytes = BitConverter.GetBytes(0);
Array.Copy(bytes, 0, Binary, 20, 4);
}
else
{
var bytes = BitConverter.GetBytes(MinSize.Width);
Array.Copy(bytes, 0, Binary, 16, 4);
bytes = BitConverter.GetBytes(MinSize.Height);
Array.Copy(bytes, 0, Binary, 20, 4);
}
// Options
if (Binary.Length < 12)
{
var bytes = BitConverter.GetBytes((uint)0);
Array.Copy(bytes, 0, Binary, 8, 4);
}
else
{
var bytes = BitConverter.GetBytes((uint)Options);
Array.Copy(bytes, 0, Binary, 8, 4);
}
// Rows
if (Binary.Length < 48)
{
var bytes = BitConverter.GetBytes(1);
Array.Copy(bytes, 0, Binary, 44, 4);
}
else
{
var bytes = BitConverter.GetBytes(Rows);
Array.Copy(bytes, 0, Binary, 44, 4);
}
SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateBinaryFromFields: Set the following settings for {DevicePath} into registry: DPI = {DPI}, Edge = {Edge}, Location = ({Location.X},{Location.Y}), MinSize = {Location.Width}x{Location.Height}, Options = {Options}, Rows = {Rows}.");
return true;
}
public bool WriteToRegistry()
{
// Update the binary with the current settings from the object
PopulateBinaryFromFields();
// Write the binary field to registry
string address;
if (MainScreen)
{
address = string.Format(MainDisplayAddress, Version);
// Set the Main Screen
try
using (var key = Registry.CurrentUser.OpenSubKey(
AdvancedSettingsAddress,
RegistryKeyPermissionCheck.ReadSubTree))
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadWriteSubTree))
if (key != null)
{
key.SetValue(DevicePath, Binary);
SharedLogger.logger.Trace($"TaskBarStuckRectangle/Apply: Successfully applied TaskBarStuckRectangle registry settings for the {DevicePath} Screen in {address}!");
foreach (var valueName in WantedAdvancedSettingValues)
{
try
{
var value = key.GetValue(valueName, null,
RegistryValueOptions.DoNotExpandEnvironmentNames);
if (value != null && value is int intValue)
{
taskBarOptions.Add(new Tuple<string, int>(valueName, intValue));
}
}
catch (Exception)
{
// ignored, as this will happen
}
}
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/GetCurrent: Unable to set the {DevicePath} TaskBarStuckRectangle registry settings in {address} due to an exception!");
}
}
else
catch (Exception)
{
address = string.Format(MultiDisplayAddress, Version);
// Grab the main screen taskbar placement
try
// ignored
}
if (taskBarOptions.Count == 0)
{
return null;
}
return new TaskBarSettings
{
Options = taskBarOptions.ToArray()
};
}
public bool Apply()
{
if (Options.Length == 0)
{
throw new InvalidOperationException();
}
using (var optionsKey = Registry.CurrentUser.OpenSubKey(
AdvancedSettingsAddress,
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
if (optionsKey == null)
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadWriteSubTree))
return false;
}
// Write
foreach (var option in Options)
{
try
{
key.SetValue(DevicePath, Binary);
SharedLogger.logger.Trace($"TaskBarStuckRectangle/WriteToRegistry: Successfully applied TaskBarStuckRectangle registry settings for the {DevicePath} Screen in {address}!");
optionsKey.SetValue(option.Item1, option.Item2);
}
catch (Exception)
{
// ignored
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/WriteToRegistry: Unable to set the {DevicePath} TaskBarStuckRectangle registry settings in {address} due to an exception!");
}
}
return true;
}
public static bool RepositionMainTaskBar(TaskBarEdge edge)
{
// Tell Windows to refresh the Main Screen Windows Taskbar
// Find the "Shell_TrayWnd" window
IntPtr mainToolBarHWnd = Utils.FindWindow("Shell_TrayWnd", null);
// Send the "Shell_TrayWnd" window a WM_USER_REFRESHTASKBAR with a wParameter of 0006 and a lParamater of the position (e.g. 0000 for left, 0001 for top, 0002 for right and 0003 for bottom)
IntPtr taskBarPositionBuffer = new IntPtr((Int32)edge);
Utils.SendMessage(mainToolBarHWnd, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer);
return true;
}
public static bool RepositionSecondaryTaskBars()
{
// Tell Windows to refresh the Other Windows Taskbars if needed
IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL;
for (int i = 0; i < 100; i++)
{
// Find the next "Shell_SecondaryTrayWnd" window
IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null);
if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL)
{
// No more windows taskbars to notify
break;
}
// Send the "Shell_TrayWnd" window a WM_SETTINGCHANGE with a wParameter of SPI_SETWORKAREA
Utils.SendMessage(lastTaskBarWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL);
lastTaskBarWindowHwnd = nextTaskBarWindowHwnd;
}
return true;
}
public static void RefreshTrayArea()
{
// Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "Notification Area" containing the visible notification area icons (windows 7 version)
IntPtr systemTrayContainerHandle = Utils.FindWindow("Shell_TrayWnd", null);
IntPtr systemTrayHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
IntPtr notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
// If the visible notification area icons (Windows 7 aren't found, then we're on a later version of windows, and we need to look for different window names
if (notificationAreaHandle == IntPtr.Zero)
{
// Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "User Promoted Notification Area" containing the visible notification area icons (windows 10+ version)
notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
// Also attempt to find the NotifyIconOverflowWindow -> "Overflow Notification Area' window which is the hidden windoww that notification icons live when they are
// too numberous or are hidden by the user.
IntPtr notifyIconOverflowWindowHandle = Utils.FindWindow("NotifyIconOverflowWindow", null);
IntPtr overflowNotificationAreaHandle = Utils.FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
// Fool the "Overflow Notification Area' window into thinking the mouse is moving over it
// which will force windows to refresh the "Overflow Notification Area' window and remove old icons.
RefreshTrayArea(overflowNotificationAreaHandle);
notifyIconOverflowWindowHandle = IntPtr.Zero;
overflowNotificationAreaHandle = IntPtr.Zero;
}
// Fool the "Notification Area" or "User Promoted Notification Area" window (depends on the version of windows) into thinking the mouse is moving over it
// which will force windows to refresh the "Notification Area" or "User Promoted Notification Area" window and remove old icons.
RefreshTrayArea(notificationAreaHandle);
systemTrayContainerHandle = IntPtr.Zero;
systemTrayHandle = IntPtr.Zero;
sysPagerHandle = IntPtr.Zero;
notificationAreaHandle = IntPtr.Zero;
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
// Moves the mouse around within the window area of the supplied window
Utils.RECT rect;
Utils.GetClientRect(windowHandle, out rect);
for (var x = 0; x < rect.right; x += 5)
for (var y = 0; y < rect.bottom; y += 5)
Utils.SendMessage(windowHandle, Utils.WM_MOUSEMOVE, 0, (y << 16) + x);
}
/*public void DoMouseLeftClick(IntPtr handle, Point x)
{
Utils.SendMessage(handle, (int)Utils.WM_MOUSEMOVE, 0, Utils.MakeLParam(x.X, x.Y));
//SendMessage(handle, (int)Utils.WM_LBUTTONUP, 0, Utils.MakeLParam(x.X, x.Y));
return;
//I have tried PostMessage, and SendMessage, and both of them at the same time, and neither works.
Utils.PostMessage(handle, Utils.WM_MOUSEMOVE, 0, Utils.MakeLParam(x.X, x.Y));
//PostMessage(handle, (uint)Utils.WM_LBUTTONUP, 0, Utils.MakeLParam(x.X, x.Y));
}
*/
}
}

View File

@ -9,6 +9,8 @@ using System.IO;
using System.ComponentModel;
using Microsoft.Win32;
using System.Threading.Tasks;
using static DisplayMagicianShared.Windows.TaskBarLayout;
using System.Diagnostics;
namespace DisplayMagicianShared.Windows
{
@ -37,6 +39,27 @@ namespace DisplayMagicianShared.Windows
public static bool operator !=(ADVANCED_HDR_INFO_PER_PATH lhs, ADVANCED_HDR_INFO_PER_PATH rhs) => !(lhs == rhs);
}
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAY_SOURCE : IEquatable<DISPLAY_SOURCE>
{
public LUID AdapterId;
public UInt32 SourceId;
public UInt32 TargetId;
public string DevicePath;
public override bool Equals(object obj) => obj is DISPLAY_SOURCE other && this.Equals(other);
public bool Equals(DISPLAY_SOURCE other)
=> true;
public override int GetHashCode()
{
return 300;
}
public static bool operator ==(DISPLAY_SOURCE lhs, DISPLAY_SOURCE rhs) => lhs.Equals(rhs);
public static bool operator !=(DISPLAY_SOURCE lhs, DISPLAY_SOURCE rhs) => !(lhs == rhs);
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWS_DISPLAY_CONFIG : IEquatable<WINDOWS_DISPLAY_CONFIG>
{
@ -45,13 +68,13 @@ namespace DisplayMagicianShared.Windows
public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes;
public List<ADVANCED_HDR_INFO_PER_PATH> DisplayHDRStates;
public Dictionary<string, GDI_DISPLAY_SETTING> GdiDisplaySettings;
public List<TaskBarStuckRectangle> TaskBarLayout;
public Dictionary<string, TaskBarLayout> TaskBarLayout;
public TaskBarSettings TaskBarSettings;
public bool IsCloned;
// Note: We purposely have left out the DisplaySources from the Equals as it's order keeps changing after each reboot and after each profile swap
// and it is informational only and doesn't contribute to the configuration (it's used for generating the Screens structure, and therefore for
// generating the profile icon.
public Dictionary<string, List<uint>> DisplaySources;
public Dictionary<string, List<DISPLAY_SOURCE>> DisplaySources;
public List<string> DisplayIdentifiers;
public override bool Equals(object obj) => obj is WINDOWS_DISPLAY_CONFIG other && this.Equals(other);
@ -64,9 +87,12 @@ namespace DisplayMagicianShared.Windows
// Additionally, we had to disable the DEviceKey from the equality testing within the GDI library itself as that waould also change after changing back from NVIDIA surround
// This still allows us to detect when refresh rates change, which will allow DisplayMagician to detect profile differences.
GdiDisplaySettings.Values.SequenceEqual(other.GdiDisplaySettings.Values) &&
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers) &&
TaskBarLayout.SequenceEqual(other.TaskBarLayout) &&
TaskBarSettings.Equals(other.TaskBarSettings);
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers);
// NOTE: I have disabled the TaskBar specific matching for now due to errors I cannot fix
// WinLibrary will still track the location of the taskbars, but won't actually set them as the setting of the taskbars doesnt work at the moment.
/*&&
TaskBarLayout.SequenceEqual(other.TaskBarLayout) &&
TaskBarSettings.Equals(other.TaskBarSettings);*/
public override int GetHashCode()
{
@ -180,9 +206,9 @@ namespace DisplayMagicianShared.Windows
myDefaultConfig.DisplayConfigPaths = new DISPLAYCONFIG_PATH_INFO[0];
myDefaultConfig.DisplayHDRStates = new List<ADVANCED_HDR_INFO_PER_PATH>();
myDefaultConfig.DisplayIdentifiers = new List<string>();
myDefaultConfig.DisplaySources = new Dictionary<string, List<uint>>();
myDefaultConfig.DisplaySources = new Dictionary<string, List<DISPLAY_SOURCE>>();
myDefaultConfig.GdiDisplaySettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
myDefaultConfig.TaskBarLayout = new List<TaskBarStuckRectangle>();
myDefaultConfig.TaskBarLayout = new Dictionary<string, TaskBarLayout>();
myDefaultConfig.TaskBarSettings = new TaskBarSettings();
myDefaultConfig.IsCloned = false;
@ -292,6 +318,34 @@ namespace DisplayMagicianShared.Windows
}
}
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display sources list info to update the adapter id");
// Update the DisplaySources with the current adapter id
for (int i = 0; i < savedDisplayConfig.DisplaySources.Count; i++)
{
List<DISPLAY_SOURCE> dsList = savedDisplayConfig.DisplaySources.ElementAt(i).Value;
if (dsList.Count > 0)
{
for (int j = 0; j < dsList.Count; j++)
{
DISPLAY_SOURCE ds = dsList[j];
// Change the Display Source AdapterID
if (adapterOldToNewMap.ContainsKey(ds.AdapterId.Value))
{
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[ds.AdapterId.Value];
ds.AdapterId = AdapterValueToLUID(newAdapterValue);
}
else
{
// if there isn't a matching adapter, then we just pick the first current one and hope that works!
// (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break)
newAdapterValue = currentAdapterMap.First().Key;
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value} didn't have a current match in Display Sources! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
ds.AdapterId = AdapterValueToLUID(newAdapterValue);
}
}
}
}
}
public bool UpdateActiveConfig()
@ -320,6 +374,10 @@ namespace DisplayMagicianShared.Windows
private WINDOWS_DISPLAY_CONFIG GetWindowsDisplayConfig(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS | QDC.QDC_INCLUDE_HMD)
{
// Prepare the empty windows display config
WINDOWS_DISPLAY_CONFIG windowsDisplayConfig = CreateDefaultConfig();
// Get the size of the largest Active Paths and Modes arrays
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the size of the largest Active Paths and Modes arrays");
int pathCount = 0;
@ -368,12 +426,6 @@ namespace DisplayMagicianShared.Windows
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays.");
}
// Prepare the empty windows display config
WINDOWS_DISPLAY_CONFIG windowsDisplayConfig = new WINDOWS_DISPLAY_CONFIG();
windowsDisplayConfig.DisplayAdapters = new Dictionary<ulong, string>();
windowsDisplayConfig.DisplayHDRStates = new List<ADVANCED_HDR_INFO_PER_PATH>();
windowsDisplayConfig.DisplaySources = new Dictionary<string, List<uint>>();
windowsDisplayConfig.IsCloned = false;
// First of all generate the current displayIdentifiers
windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers();
@ -393,6 +445,7 @@ namespace DisplayMagicianShared.Windows
// Now cycle through the paths and grab the HDR state information
// and map the adapter name to adapter id
// and populate the display source information
List<uint> targetPathIdsToChange = new List<uint>();
List<uint> targetModeIdsToChange = new List<uint>();
List<uint> targetIdsFound = new List<uint>();
@ -432,7 +485,11 @@ namespace DisplayMagicianShared.Windows
if (windowsDisplayConfig.DisplaySources.ContainsKey(sourceInfo.ViewGdiDeviceName))
{
// We already have at least one display using this source, so we need to add the other cloned display to the existing list
windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(paths[i].SourceInfo.Id);
DISPLAY_SOURCE ds = new DISPLAY_SOURCE();
ds.AdapterId = paths[i].SourceInfo.AdapterId;
ds.SourceId = paths[i].SourceInfo.Id;
ds.TargetId = paths[i].TargetInfo.Id;
windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(ds);
isClonedPath = true;
isClonedProfile = true;
windowsDisplayConfig.IsCloned = true;
@ -440,9 +497,13 @@ namespace DisplayMagicianShared.Windows
else
{
// This is the first display to use this source
List<uint> sourceIds = new List<uint>();
sourceIds.Add(paths[i].SourceInfo.Id);
windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, sourceIds);
List<DISPLAY_SOURCE> sources = new List<DISPLAY_SOURCE>();
DISPLAY_SOURCE ds = new DISPLAY_SOURCE();
ds.AdapterId = paths[i].SourceInfo.AdapterId;
ds.SourceId = paths[i].SourceInfo.Id;
ds.TargetId = paths[i].TargetInfo.Id;
sources.Add(ds);
windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, sources);
}
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {paths[i].SourceInfo.Id}.");
@ -618,33 +679,58 @@ namespace DisplayMagicianShared.Windows
modes[i].Id = targetIdMap[modes[i].Id];
}
}
// And then we need to go through the list of display sources and patch the 'cloned' displays with a real display ID so the display layout is right in cloned displays
for (int i = 0; i < windowsDisplayConfig.DisplaySources.Count; i++)
{
string key = windowsDisplayConfig.DisplaySources.ElementAt(i).Key;
DISPLAY_SOURCE[] dsList = windowsDisplayConfig.DisplaySources.ElementAt(i).Value.ToArray();
for (int j = 0; j < dsList.Length; j++)
{
// We only change the ids that match in InfoType for target displays
if (targetIdMap.ContainsKey(dsList[j].TargetId))
{
// Patch the cloned ids with a real working one!
dsList[j].TargetId = targetIdMap[dsList[j].TargetId];
}
}
windowsDisplayConfig.DisplaySources[key] = dsList.ToList();
}
}
// Now we need to find the DevicePaths for the DisplaySources (as at this point the cloned display sources have been corrected)
for (int i = 0; i < windowsDisplayConfig.DisplaySources.Count; i++)
{
string key = windowsDisplayConfig.DisplaySources.ElementAt(i).Key;
DISPLAY_SOURCE[] dsList = windowsDisplayConfig.DisplaySources.ElementAt(i).Value.ToArray();
for (int j = 0; j < dsList.Length; j++)
{
// get display target name
var targetInfo = new DISPLAYCONFIG_TARGET_DEVICE_NAME();
targetInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_TARGET_DEVICE_NAME>();
targetInfo.Header.AdapterId = dsList[j].AdapterId;
targetInfo.Header.Id = dsList[j].TargetId;
err = CCDImport.DisplayConfigGetDeviceInfo(ref targetInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Successfully got the target info from {dsList[j].TargetId}.");
dsList[j].DevicePath = targetInfo.MonitorDevicePath;
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetSomeDisplayIdentifiers: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{dsList[j].TargetId}");
}
}
windowsDisplayConfig.DisplaySources[key] = dsList.ToList();
}
Dictionary<string, TaskBarLayout> taskBarStuckRectangles = new Dictionary<string, TaskBarLayout>();
// Now attempt to get the windows taskbar location for each display
// We use the information we already got from the display identifiers
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get the Windows Taskbar layout.");
List<TaskBarStuckRectangle> taskBarStuckRectangles = new List<TaskBarStuckRectangle>();
foreach (var displayId in windowsDisplayConfig.DisplayIdentifiers)
{
// e.g. "WINAPI|\\\\?\\PCI#VEN_10DE&DEV_2482&SUBSYS_408E1458&REV_A1#4&2283f625&0&0019#{5b45201d-f2f2-4f3b-85bb-30ff1f953599}|DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI|54074|4318|\\\\?\\DISPLAY#NVS10DE#5&2b46c695&0&UID185344#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}|NV Surround"
string[] winapiLine = displayId.Split('|');
string pattern = @"DISPLAY\#(.*)\#\{";
Match match = Regex.Match(winapiLine[5], pattern);
if (match.Success)
{
string devicePath = match.Groups[1].Value;
TaskBarStuckRectangle taskBarStuckRectangle = new TaskBarStuckRectangle(devicePath);
taskBarStuckRectangles.Add(taskBarStuckRectangle);
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: We were unable to figure out the DevicePath for the '{displayId}' display identifier.");
}
}
// And we get the Main Screen taskbar too
TaskBarStuckRectangle mainTaskBarStuckRectangle = new TaskBarStuckRectangle("Settings");
taskBarStuckRectangles.Add(mainTaskBarStuckRectangle);
taskBarStuckRectangles = TaskBarLayout.GetAllCurrentTaskBarLayouts(windowsDisplayConfig.DisplaySources);
// Now we try to get the taskbar settings too
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get the Windows Taskbar settings.");
@ -655,7 +741,6 @@ namespace DisplayMagicianShared.Windows
windowsDisplayConfig.DisplayConfigModes = modes;
windowsDisplayConfig.GdiDisplaySettings = GetGdiDisplaySettings();
windowsDisplayConfig.TaskBarLayout = taskBarStuckRectangles;
//windowsDisplayConfig.OriginalTaskBarLayout = new List<TaskBarStuckRectangle>(taskBarStuckRectangles);
windowsDisplayConfig.TaskBarSettings = taskBarSettings;
return windowsDisplayConfig;
@ -1380,36 +1465,65 @@ namespace DisplayMagicianShared.Windows
}
// NOTE: I have disabled the TaskBar setting logic for now due to errors I cannot fix in this code.
// WinLibrary will still track the location of the taskbars, but won't actually set them as the setting of the taskbars doesnt work at the moment.
// I hope to eventually fix this code, but I don't want to hold up a new DisplayMagician release while troubleshooting this.
/*
// Now set the taskbar position for each screen
if (displayConfig.TaskBarLayout.Count > 0)
if (displayConfig.TaskBarLayout.Count > 0 && allWindowsDisplayConfig.TaskBarLayout.Count > 0)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Setting the taskbar layout.");
foreach (TaskBarStuckRectangle tbsr in displayConfig.TaskBarLayout)
foreach (var tbrDictEntry in displayConfig.TaskBarLayout)
{
if (tbsr.Version >= 2 && tbsr.Version <= 3)
// Look up the monitor location of the current monitor and find the matching taskbar location in the taskbar settings
if (allWindowsDisplayConfig.TaskBarLayout.ContainsKey(tbrDictEntry.Key))
{
// Write the settings to registry
tbsr.WriteToRegistry();
if (tbsr.MainScreen)
// check the current monitor taskbar location
// if the current monitor location is the same as the monitor we want to set then
TaskBarLayout currentLayout = displayConfig.TaskBarLayout[tbrDictEntry.Key];
TaskBarLayout wantedLayout = allWindowsDisplayConfig.TaskBarLayout[tbrDictEntry.Key];
if (currentLayout.Equals(wantedLayout))
{
TaskBarStuckRectangle.RepositionMainTaskBar(tbsr.Edge);
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Display {tbrDictEntry.Key} ({tbrDictEntry.Value.RegKeyValue}) has the taskbar with the correct position, size and settings, so no need to move it");
}
else
{
// if the current monitor taskbar location is not where we want it then
// move the taskbar manually
TaskBarLayout tbr = tbrDictEntry.Value;
tbr.MoveTaskBar();
}
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Unable to set the {tbsr.DevicePath} TaskBarStuckRectangle registry settings as the version isn't v2 or v3!");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Display {tbrDictEntry.Key} ({tbrDictEntry.Value.RegKeyValue}) is not currently in use, so cannot set any taskbars on it!");
}
}
// Tell Windows to refresh the Other Windows Taskbars if needed
IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL;
if (displayConfig.TaskBarLayout.Count > 1)
// This will actually move the taskbars by forcing Explorer to read from registry key
/*RestartManagerSession.RestartExplorer();
Process[] explorers = Process.GetProcessesByName("Explorer");
for (int i = 0; i < explorers.Length; i++)
{
TaskBarStuckRectangle.RepositionSecondaryTaskBars();
kill
}
// Enum all the monitors
//List<MONITORINFOEX> currentMonitors = Utils.EnumMonitors();
// Go through each monitor
//foreach (MONITORINFOEX mi in currentMonitors)
//{
// Look up the monitor location of the current monitor and find the matching taskbar location in the taskbar settings
//if (current)
// check the current monitor taskbar location
// if the current monitor location is the same as the monitor we want to set then
// if the current monitor taskbar location where we want it then
// move the taskbar manually
// Find the registry key for the monitor we are modifying
// save the taskbar position for the monitor in registry
// else
// log the fact that the monitor is in the right place so skipping moving it
// if we didn't find a taskbar location for this monitor
// log the fact that the taskbar location wasnt foound for this monitor
//}
}
else
@ -1437,7 +1551,7 @@ namespace DisplayMagicianShared.Windows
{
// The settings are the same, so we should skip applying them
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: The current taskbar settings are the same as the one's we want, so skipping setting them!");
}
}*/
return true;
}
@ -1958,7 +2072,118 @@ namespace DisplayMagicianShared.Windows
{
return false;
}
}
}
public static bool RepositionMainTaskBar(TaskBarEdge edge)
{
// Tell Windows to refresh the Main Screen Windows Taskbar
// Find the "Shell_TrayWnd" window
IntPtr systemTrayContainerHandle = Utils.FindWindow("Shell_TrayWnd", null);
IntPtr startButtonHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "Start", null);
IntPtr systemTrayHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr rebarWindowHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "ReBarWindow32", null);
IntPtr taskBarPositionBuffer = new IntPtr((Int32)edge);
IntPtr trayDesktopShowButtonHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "TrayShowDesktopButtonWClass", null);
IntPtr trayInputIndicatorHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "TrayInputIndicatorWClass", null);
// Send messages
// Send the "TrayNotifyWnd" window a WM_USER+13 (0x040D) message with a wParameter of 0x0 and a lParameter of the position (e.g. 0x0000 for left, 0x0001 for top, 0x0002 for right and 0x0003 for bottom)
Utils.SendMessage(systemTrayHandle, Utils.WM_USER_13, IntPtr.Zero, taskBarPositionBuffer);
Utils.SendMessage(systemTrayHandle, Utils.WM_USER_100, (IntPtr)0x3e, (IntPtr)0x21c);
Utils.SendMessage(systemTrayHandle, Utils.WM_THEMECHANGED, (IntPtr)0xffffffffffffffff, (IntPtr)0x000000008000001);
// Next, send the "TrayShowDesktopButtonWClass" window a WM_USER+13 (0x040D) message with a wParameter of 0x0 and a lParameter of the position (e.g. 0x0000 for left, 0x0001 for top, 0x0002 for right and 0x0003 for bottom)
Utils.SendMessage(trayDesktopShowButtonHandle, Utils.WM_USER_13, IntPtr.Zero, taskBarPositionBuffer);
Utils.SendMessage(startButtonHandle, Utils.WM_USER_440, (IntPtr)0x0, (IntPtr)0x0);
Utils.SendMessage(systemTrayHandle, Utils.WM_USER_1, (IntPtr)0x0, (IntPtr)0x0);
Utils.SendMessage(systemTrayContainerHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL);
Utils.PostMessage(systemTrayHandle, Utils.WM_SETTINGCHANGE, Utils.SPI_SETWORKAREA, Utils.NULL);
Utils.SendMessage(systemTrayContainerHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL);
Utils.SendMessage(rebarWindowHandle, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL);
//Utils.SendMessage(trayInputIndicatorHandle, Utils.WM_USER_100, (IntPtr)0x3e, (IntPtr)0x21c);
//Utils.SendMessage(systemTrayHandle, Utils.WM_USER_1, (IntPtr)0x0, (IntPtr)0x0);
// Move all the taskbars to this location
//Utils.SendMessage(systemTrayContainerHandle, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer);
//Utils.SendMessage(systemTrayContainerHandle, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer);
return true;
}
public static bool RepositionAllTaskBars(TaskBarEdge edge)
{
// Tell Windows to refresh the Main Screen Windows Taskbar
// Find the "Shell_TrayWnd" window
IntPtr mainToolBarHWnd = Utils.FindWindow("Shell_TrayWnd", null);
// Send the "Shell_TrayWnd" window a WM_USER_REFRESHTASKBAR with a wParameter of 0006 and a lParameter of the position (e.g. 0000 for left, 0001 for top, 0002 for right and 0003 for bottom)
IntPtr taskBarPositionBuffer = new IntPtr((Int32)edge);
Utils.SendMessage(mainToolBarHWnd, Utils.WM_USER_REFRESHTASKBAR, (IntPtr)Utils.wParam_SHELLTRAY, taskBarPositionBuffer);
return true;
}
public static bool RepositionSecondaryTaskBars()
{
// Tell Windows to refresh the Other Windows Taskbars if needed
IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL;
for (int i = 0; i < 100; i++)
{
// Find the next "Shell_SecondaryTrayWnd" window
IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null);
if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL)
{
// No more windows taskbars to notify
break;
}
// Send the "Shell_TrayWnd" window a WM_SETTINGCHANGE with a wParameter of SPI_SETWORKAREA
Utils.SendMessage(lastTaskBarWindowHwnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL);
lastTaskBarWindowHwnd = nextTaskBarWindowHwnd;
}
return true;
}
public static void RefreshTrayArea()
{
// Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "Notification Area" containing the visible notification area icons (windows 7 version)
IntPtr systemTrayContainerHandle = Utils.FindWindow("Shell_TrayWnd", null);
IntPtr systemTrayHandle = Utils.FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = Utils.FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
IntPtr notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
// If the visible notification area icons (Windows 7 aren't found, then we're on a later version of windows, and we need to look for different window names
if (notificationAreaHandle == IntPtr.Zero)
{
// Finds the Shell_TrayWnd -> TrayNotifyWnd -> SysPager -> "User Promoted Notification Area" containing the visible notification area icons (windows 10+ version)
notificationAreaHandle = Utils.FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
// Also attempt to find the NotifyIconOverflowWindow -> "Overflow Notification Area' window which is the hidden windoww that notification icons live when they are
// too numberous or are hidden by the user.
IntPtr notifyIconOverflowWindowHandle = Utils.FindWindow("NotifyIconOverflowWindow", null);
IntPtr overflowNotificationAreaHandle = Utils.FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
// Fool the "Overflow Notification Area' window into thinking the mouse is moving over it
// which will force windows to refresh the "Overflow Notification Area' window and remove old icons.
RefreshTrayArea(overflowNotificationAreaHandle);
notifyIconOverflowWindowHandle = IntPtr.Zero;
overflowNotificationAreaHandle = IntPtr.Zero;
}
// Fool the "Notification Area" or "User Promoted Notification Area" window (depends on the version of windows) into thinking the mouse is moving over it
// which will force windows to refresh the "Notification Area" or "User Promoted Notification Area" window and remove old icons.
RefreshTrayArea(notificationAreaHandle);
systemTrayContainerHandle = IntPtr.Zero;
systemTrayHandle = IntPtr.Zero;
sysPagerHandle = IntPtr.Zero;
notificationAreaHandle = IntPtr.Zero;
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
// Moves the mouse around within the window area of the supplied window
RECT rect;
Utils.GetClientRect(windowHandle, out rect);
for (var x = 0; x < rect.right; x += 5)
for (var y = 0; y < rect.bottom; y += 5)
Utils.SendMessage(windowHandle, Utils.WM_MOUSEMOVE, 0, (y << 16) + x);
}
}