diff --git a/DisplayMagician/Properties/AssemblyInfo.cs b/DisplayMagician/Properties/AssemblyInfo.cs index e32be6a..4943fcc 100644 --- a/DisplayMagician/Properties/AssemblyInfo.cs +++ b/DisplayMagician/Properties/AssemblyInfo.cs @@ -26,8 +26,8 @@ using System.Resources; [assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")] // Version information -[assembly: AssemblyVersion("2.2.0.252")] -[assembly: AssemblyFileVersion("2.2.0.252")] +[assembly: AssemblyVersion("2.2.0.280")] +[assembly: AssemblyFileVersion("2.2.0.280")] [assembly: NeutralResourcesLanguageAttribute( "en" )] [assembly: CLSCompliant(true)] diff --git a/DisplayMagician/ShortcutRepository.cs b/DisplayMagician/ShortcutRepository.cs index 9a4c23a..11a91d1 100644 --- a/DisplayMagician/ShortcutRepository.cs +++ b/DisplayMagician/ShortcutRepository.cs @@ -1922,11 +1922,12 @@ namespace DisplayMagician // Shutdown the processes ProcessUtils.StopProcess(startProgramsToStop); - // Refresh the system tray / notification tray area to clean out any applications we stopped - DisplayMagicianShared.Windows.TaskBarStuckRectangle.RefreshTrayArea(); + // Refresh the system tray / notification tray area to clean out any applications we stopped + DisplayMagicianShared.Windows.WinLibrary.RefreshTrayArea(); + } - + // Change Audio Device back (if one specified) if (activeAudioDevices.Count > 0) { diff --git a/DisplayMagician/UIForms/DisplayProfileForm.Designer.cs b/DisplayMagician/UIForms/DisplayProfileForm.Designer.cs index 50ebe61..eaa381a 100644 --- a/DisplayMagician/UIForms/DisplayProfileForm.Designer.cs +++ b/DisplayMagician/UIForms/DisplayProfileForm.Designer.cs @@ -289,6 +289,7 @@ namespace DisplayMagician.UIForms this.lbl_save_profile.Anchor = System.Windows.Forms.AnchorStyles.Top; this.lbl_save_profile.BackColor = System.Drawing.Color.Brown; this.lbl_save_profile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lbl_save_profile.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.lbl_save_profile.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F); this.lbl_save_profile.ForeColor = System.Drawing.Color.White; this.lbl_save_profile.ImeMode = System.Windows.Forms.ImeMode.NoControl; diff --git a/DisplayMagician/UIForms/DisplayProfileForm.cs b/DisplayMagician/UIForms/DisplayProfileForm.cs index 35afc39..62076e4 100644 --- a/DisplayMagician/UIForms/DisplayProfileForm.cs +++ b/DisplayMagician/UIForms/DisplayProfileForm.cs @@ -518,9 +518,13 @@ namespace DisplayMagician.UIForms protected override void WndProc(ref Message m) { const int WM_DISPLAYCHANGE = 0x007E; + const int WM_SETTINGCHANGE = 0x001A; + const int WM_DEVICECHANGE = 0x0219; switch (m.Msg) { + case WM_DEVICECHANGE: + case WM_SETTINGCHANGE: case WM_DISPLAYCHANGE: btn_view_current.PerformClick(); break; diff --git a/DisplayMagician/UIForms/DisplayProfileForm.resx b/DisplayMagician/UIForms/DisplayProfileForm.resx index 500413f..4d8e98d 100644 --- a/DisplayMagician/UIForms/DisplayProfileForm.resx +++ b/DisplayMagician/UIForms/DisplayProfileForm.resx @@ -160,7 +160,7 @@ 248, 17 - Setup your display layout in NVIDIA Control Panel, AMD Radeon Adrenaline or Windows Display Settings, then return to DisplayMagician and click 'Save' to store this Display Profile for later. Click the Help button for step-by-step instructions. + Setup your display layout in NVIDIA Control Panel, AMD Radeon Adrenaline or Windows Display Settings, then return to DisplayMagician and click 'Save' to store this Display Profile for later use. Taskbar changes will show up after a few seconds. 358, 17 diff --git a/DisplayMagician/UIForms/ProfileToolsForm.cs b/DisplayMagician/UIForms/ProfileToolsForm.cs index a89e273..24572ba 100644 --- a/DisplayMagician/UIForms/ProfileToolsForm.cs +++ b/DisplayMagician/UIForms/ProfileToolsForm.cs @@ -9,7 +9,6 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Windows.Forms; -using DisplayMagicianShared.Windows; namespace DisplayMagician.UIForms { @@ -117,7 +116,7 @@ namespace DisplayMagician.UIForms if (tbsr.MainScreen) { - TaskBarStuckRectangle.RepositionMainTaskBar(taskbarForcedEdge); + WinLibrary.RepositionMainTaskBar(taskbarForcedEdge); } } @@ -132,7 +131,7 @@ namespace DisplayMagician.UIForms { SharedLogger.logger.Trace($"ProfileToolsForm/btn_apply_Click: No taskbar layout in display profile so skipping setting it!"); } - TaskBarStuckRectangle.RepositionSecondaryTaskBars(); + WinLibrary.RepositionSecondaryTaskBars(); // Now set the option to completed. DialogResult = DialogResult.OK; diff --git a/DisplayMagicianShared/AMD/AMDLibrary.cs b/DisplayMagicianShared/AMD/AMDLibrary.cs index 13a6f68..08dfcf9 100644 --- a/DisplayMagicianShared/AMD/AMDLibrary.cs +++ b/DisplayMagicianShared/AMD/AMDLibrary.cs @@ -318,6 +318,7 @@ namespace DisplayMagicianShared.AMD // so that we won't break json.net when we save a default config myDefaultConfig.AdapterConfigs = new List(); + myDefaultConfig.SlsConfig.IsSlsEnabled = false; myDefaultConfig.SlsConfig.SLSMapConfigs = new List(); myDefaultConfig.SlsConfig.SLSEnabledDisplayTargets = new List(); myDefaultConfig.DisplayMaps = new List(); @@ -353,13 +354,7 @@ namespace DisplayMagicianShared.AMD private AMD_DISPLAY_CONFIG GetAMDDisplayConfig(bool allDisplays = false) { - AMD_DISPLAY_CONFIG myDisplayConfig = new AMD_DISPLAY_CONFIG(); - myDisplayConfig.AdapterConfigs = new List(); - - // We set up the default for this display config as SLS disabled - // (We will change this later if it turns out we're using SLS) - myDisplayConfig.SlsConfig.IsSlsEnabled = false; - myDisplayConfig.SlsConfig.SLSEnabledDisplayTargets = new List(); + AMD_DISPLAY_CONFIG myDisplayConfig = CreateDefaultConfig(); if (_initialised) { diff --git a/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs b/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs index af875f9..3063246 100644 --- a/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs +++ b/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs @@ -59,8 +59,9 @@ namespace DisplayMagicianShared.NVIDIA public override bool Equals(object obj) => obj is NVIDIA_HDR_CONFIG other && this.Equals(other); public bool Equals(NVIDIA_HDR_CONFIG other) => HdrCapabilities.SequenceEqual(other.HdrCapabilities) && - HdrColorData.SequenceEqual(other.HdrColorData) && - IsNvHdrEnabled == other.IsNvHdrEnabled; + HdrColorData.SequenceEqual(other.HdrColorData) && + IsNvHdrEnabled == other.IsNvHdrEnabled; + public override int GetHashCode() { @@ -124,12 +125,13 @@ namespace DisplayMagicianShared.NVIDIA public override bool Equals(object obj) => obj is NVIDIA_DISPLAY_CONFIG other && this.Equals(other); public bool Equals(NVIDIA_DISPLAY_CONFIG other) - => MosaicConfig.Equals(other.MosaicConfig) && - HdrConfig.Equals(other.HdrConfig) && + => HdrConfig.Equals(other.HdrConfig) && + MosaicConfig.Equals(other.MosaicConfig) && ColorConfig.Equals(other.ColorConfig) && CustomDisplays.SequenceEqual(other.CustomDisplays) && DisplayConfigs.SequenceEqual(other.DisplayConfigs) && DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers); + public override int GetHashCode() { @@ -282,6 +284,7 @@ namespace DisplayMagicianShared.NVIDIA // Fill in the minimal amount we need to avoid null references // so that we won't break json.net when we save a default config + myDefaultConfig.MosaicConfig.IsMosaicEnabled = false; myDefaultConfig.MosaicConfig.MosaicGridTopos = new NV_MOSAIC_GRID_TOPO_V2[0]; myDefaultConfig.MosaicConfig.MosaicViewports = new List(); myDefaultConfig.HdrConfig.HdrCapabilities = new Dictionary(); @@ -320,7 +323,7 @@ namespace DisplayMagicianShared.NVIDIA private NVIDIA_DISPLAY_CONFIG GetNVIDIADisplayConfig(bool allDisplays = false) { - NVIDIA_DISPLAY_CONFIG myDisplayConfig = new NVIDIA_DISPLAY_CONFIG(); + NVIDIA_DISPLAY_CONFIG myDisplayConfig = CreateDefaultConfig(); if (_initialised) { diff --git a/DisplayMagicianShared/ProfileItem.cs b/DisplayMagicianShared/ProfileItem.cs index 2e7c4df..1d2c613 100644 --- a/DisplayMagicianShared/ProfileItem.cs +++ b/DisplayMagicianShared/ProfileItem.cs @@ -350,12 +350,19 @@ namespace DisplayMagicianShared { get { - if (_profileShortcutBitmap != null) - return _profileShortcutBitmap; + if (ProfileRepository.ProfilesLoaded) + { + if (_profileShortcutBitmap != null) + return _profileShortcutBitmap; + else + { + _profileShortcutBitmap = this.ProfileIcon.ToTightestBitmap(); + return _profileShortcutBitmap; + } + } else { - _profileShortcutBitmap = this.ProfileIcon.ToTightestBitmap(); - return _profileShortcutBitmap; + return null; } } set @@ -1280,18 +1287,7 @@ namespace DisplayMagicianShared screen.IsSpanned = false; screen.Colour = normalScreenColor; // this is the default unless overridden by the primary screen screen.IsClone = false; - screen.ClonedCopies = 0; - try - { - screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.DevicePath.Contains($"UID{targetId}")).Edge; - SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Position of the taskbar on display {targetId} is on the {screen.TaskBarEdge } of the screen."); - } - catch (Exception ex) - { - // Guess that it is at the bottom (90% correct) - SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetId}"); - screen.TaskBarEdge = TaskBarStuckRectangle.TaskBarEdge.Bottom; - } + screen.ClonedCopies = 0; foreach (var displaySource in _windowsDisplayConfig.DisplaySources) { if (displaySource.Value.Contains(sourceId)) @@ -1357,6 +1353,39 @@ namespace DisplayMagicianShared } } + // Now we try to set the taskbar positions + if (screen.IsPrimary) + { + // If the screen is the primary screen, then we check if we need to use the StuckRect 'Settings' reg keys + // rather than the MMStuckRect reg keys + try + { + screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tb => tb.DevicePath.Contains("Settings")).Edge; + SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Position of the taskbar on the primary display {targetId} is on the {screen.TaskBarEdge } of the screen."); + } + catch (Exception ex) + { + // Guess that it is at the bottom (90% correct) + SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on primary display {targetId}"); + screen.TaskBarEdge = TaskBarStuckRectangle.TaskBarEdge.Bottom; + } + + } + else + { + try + { + screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.DevicePath.Contains($"UID{targetId}")).Edge; + SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Position of the taskbar on display {targetId} is on the {screen.TaskBarEdge } of the screen."); + } + catch (Exception ex) + { + // Guess that it is at the bottom (90% correct) + SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetId}"); + screen.TaskBarEdge = TaskBarStuckRectangle.TaskBarEdge.Bottom; + } + } + SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Added a new Screen {screen.Name} ({screen.ScreenWidth}x{screen.ScreenHeight}) at position {screen.ScreenX},{screen.ScreenY}."); _screens.Add(screen); diff --git a/DisplayMagicianShared/ProfileRepository.cs b/DisplayMagicianShared/ProfileRepository.cs index 1289e1c..9694645 100644 --- a/DisplayMagicianShared/ProfileRepository.cs +++ b/DisplayMagicianShared/ProfileRepository.cs @@ -639,6 +639,10 @@ namespace DisplayMagicianShared SharedLogger.logger.Trace($"ProfileRepository/UpdateActiveProfile: Paused updating display settings for {totalDelay} milliseconds."); } + // Force explorer to update the TaskBar settings just in case they were moved + //ShellHelper.TellShellToWriteSettings(); + //WinLibrary.RefreshTaskBars(); + profile.CreateProfileFromCurrentDisplaySettings(); if (_profilesLoaded && _allProfiles.Count > 0) diff --git a/DisplayMagicianShared/ShellHelper.cs b/DisplayMagicianShared/ShellHelper.cs index 8d9c509..48b4272 100644 --- a/DisplayMagicianShared/ShellHelper.cs +++ b/DisplayMagicianShared/ShellHelper.cs @@ -42,7 +42,7 @@ namespace DisplayMagicianShared return null; } - public static async Task IntrigueShellToWriteSettings() + public static async Task TellShellToWriteSettings() { try { diff --git a/DisplayMagicianShared/Utils.cs b/DisplayMagicianShared/Utils.cs index d382088..9f7089f 100644 --- a/DisplayMagicianShared/Utils.cs +++ b/DisplayMagicianShared/Utils.cs @@ -349,7 +349,12 @@ namespace DisplayMagicianShared public const int WM_SETTINGCHANGE = 0x001a; public const int WM_MOUSEMOVE = 0x0200; public const int SPI_SETWORKAREA = 0x002F; + public const int SHELLHOOK = 0xC028; public const int WM_USER_REFRESHTASKBAR = 0x05CA; + public const int WM_USER_440 = 0x05B8; + public const int WM_USER_92 = 0x045C; + public const int WM_USER_1 = 0x0401; + public const int WM_USER_100 = 0x0464; public const int wParam_SHELLTRAY = 0x00000006; } diff --git a/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs b/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs index a166816..47e862a 100644 --- a/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs +++ b/DisplayMagicianShared/Windows/TaskBarStuckRectangle.cs @@ -112,6 +112,9 @@ namespace DisplayMagicianShared.Windows 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."); + + // If we get here then we're done and don't need to continue with the rest of the code. + return; } else { @@ -119,6 +122,14 @@ namespace DisplayMagicianShared.Windows } } } + 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"); } if (!foundDevicePath) @@ -180,6 +191,23 @@ namespace DisplayMagicianShared.Windows } } } + else + { + SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was found, but the version of the field is wrong."); + } + } + 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."); + throw new TaskBarStuckRectangleException("A StuckRect entry was NOT found. This means we're unable to get the taskbar location, an unable to return a sensible TaskBarStuckRectangle object."); + //SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was NOT found. This means we're unable to get the taskbar location, so we'll return a default object instead."); + /*Version = 3; + DPI = 0; + Edge = TaskBarEdge.Bottom; + Location = Rectangle.Empty; + MinSize = new Size(48,48); + Options = 0; + Rows = 1;*/ } } } @@ -470,79 +498,7 @@ namespace DisplayMagicianShared.Windows 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) { @@ -558,4 +514,15 @@ namespace DisplayMagicianShared.Windows } */ } + + [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) { } + } } \ No newline at end of file diff --git a/DisplayMagicianShared/Windows/WinLibrary.cs b/DisplayMagicianShared/Windows/WinLibrary.cs index b1a1dec..ee1436a 100644 --- a/DisplayMagicianShared/Windows/WinLibrary.cs +++ b/DisplayMagicianShared/Windows/WinLibrary.cs @@ -9,6 +9,7 @@ using System.IO; using System.ComponentModel; using Microsoft.Win32; using System.Threading.Tasks; +using static DisplayMagicianShared.Windows.TaskBarStuckRectangle; namespace DisplayMagicianShared.Windows { @@ -320,6 +321,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,13 +373,7 @@ 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(); - windowsDisplayConfig.DisplayHDRStates = new List(); - windowsDisplayConfig.DisplaySources = new Dictionary>(); - windowsDisplayConfig.IsCloned = false; - + // First of all generate the current displayIdentifiers windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers(); @@ -633,6 +632,7 @@ namespace DisplayMagicianShared.Windows if (match.Success) { string devicePath = match.Groups[1].Value; + SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found devicePath {devicePath} from the display identifier {displayId}."); TaskBarStuckRectangle taskBarStuckRectangle = new TaskBarStuckRectangle(devicePath); taskBarStuckRectangles.Add(taskBarStuckRectangle); } @@ -1393,7 +1393,7 @@ namespace DisplayMagicianShared.Windows if (tbsr.MainScreen) { - TaskBarStuckRectangle.RepositionMainTaskBar(tbsr.Edge); + RepositionMainTaskBar(tbsr.Edge); } } @@ -1408,7 +1408,7 @@ namespace DisplayMagicianShared.Windows IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL; if (displayConfig.TaskBarLayout.Count > 1) { - TaskBarStuckRectangle.RepositionSecondaryTaskBars(); + RepositionSecondaryTaskBars(); } } @@ -1958,7 +1958,111 @@ 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 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 bool RefreshTaskBars() + { + // Tell Windows to refresh the Main Screen Windows Taskbar registry settings by telling Explorer to update. + // Find the "Shell_TrayWnd" window + IntPtr mainToolBarHWnd = Utils.FindWindow("Shell_TrayWnd", null); + Utils.SendMessage(mainToolBarHWnd, Utils.WM_SETTINGCHANGE, (IntPtr)Utils.SPI_SETWORKAREA, (IntPtr)Utils.NULL); + // 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; + } + + //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; + } + + 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); + } }