Last active
February 24, 2021 00:29
-
-
Save emoacht/aa8e20497623d6491d636979d3809eb9 to your computer and use it in GitHub Desktop.
Enumerate sets of information on taskbar especially Device Instance ID and taskbar alignment.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public enum TaskbarAlignment { Unknown, Left, Top, Right, Bottom } | |
public static class TaskbarHelper | |
{ | |
#region Win32 | |
[DllImport("User32.dll", SetLastError = true)] | |
private static extern IntPtr FindWindowEx( | |
IntPtr hwndParent, | |
IntPtr hwndChildAfter, | |
string lpszClass, | |
string lpszWindow); | |
[DllImport("User32.dll", EntryPoint = "EnumDisplayDevicesA")] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool EnumDisplayDevices( | |
string lpDevice, | |
uint iDevNum, | |
ref DISPLAY_DEVICE lpDisplayDevice, | |
uint dwFlags); | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] | |
private struct DISPLAY_DEVICE | |
{ | |
public uint cb; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] | |
public string DeviceName; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] | |
public string DeviceString; | |
public DISPLAY_DEVICE_FLAG StateFlags; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] | |
public string DeviceID; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] | |
public string DeviceKey; | |
} | |
[Flags] | |
private enum DISPLAY_DEVICE_FLAG : uint | |
{ | |
DISPLAY_DEVICE_ATTACHED_TO_DESKTOP = 0x00000001, | |
DISPLAY_DEVICE_MULTI_DRIVER = 0x00000002, | |
DISPLAY_DEVICE_PRIMARY_DEVICE = 0x00000004, | |
DISPLAY_DEVICE_MIRRORING_DRIVER = 0x00000008, | |
DISPLAY_DEVICE_VGA_COMPATIBLE = 0x00000010, | |
DISPLAY_DEVICE_REMOVABLE = 0x00000020, | |
DISPLAY_DEVICE_ACC_DRIVER = 0x00000040, | |
DISPLAY_DEVICE_RDPUDD = 0x01000000, | |
DISPLAY_DEVICE_DISCONNECT = 0x02000000, | |
DISPLAY_DEVICE_REMOTE = 0x04000000, | |
DISPLAY_DEVICE_MODESPRUNED = 0x08000000, | |
DISPLAY_DEVICE_ACTIVE = 0x00000001, | |
DISPLAY_DEVICE_ATTACHED = 0x00000002, | |
} | |
private const uint EDD_GET_DEVICE_INTERFACE_NAME = 0x00000001; | |
[DllImport("User32.dll", SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private extern static bool EnumWindows( | |
EnumWindowsProc lpEnumFun, | |
IntPtr lparam); | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private delegate bool EnumWindowsProc( | |
IntPtr hWnd, | |
IntPtr lParam); | |
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)] | |
private static extern int GetClassName( | |
IntPtr hWnd, | |
StringBuilder lpClassName, | |
int nMaxCount); | |
[DllImport("User32.dll", SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool GetWindowRect( | |
IntPtr hWnd, | |
out RECT lpRect); | |
[StructLayout(LayoutKind.Sequential)] | |
public struct RECT | |
{ | |
public int left; | |
public int top; | |
public int right; | |
public int bottom; | |
public static implicit operator Rect(RECT rect) | |
{ | |
if ((rect.right - rect.left < 0) || (rect.bottom - rect.top < 0)) | |
return Rect.Empty; | |
return new Rect( | |
rect.left, | |
rect.top, | |
rect.right - rect.left, | |
rect.bottom - rect.top); | |
} | |
public static implicit operator RECT(Rect rect) | |
{ | |
return new RECT | |
{ | |
left = (int)rect.X, | |
top = (int)rect.Y, | |
right = (int)rect.Right, | |
bottom = (int)rect.Bottom | |
}; | |
} | |
} | |
[DllImport("User32.dll", SetLastError = true)] | |
private static extern IntPtr MonitorFromWindow( | |
IntPtr hWnd, | |
MONITOR_DEFAULTTO dwFlags); | |
private enum MONITOR_DEFAULTTO : uint | |
{ | |
/// <summary> | |
/// If no display monitor intersects, returns null. | |
/// </summary> | |
MONITOR_DEFAULTTONULL = 0x00000000, | |
/// <summary> | |
/// If no display monitor intersects, returns a handle to the primary display monitor. | |
/// </summary> | |
MONITOR_DEFAULTTOPRIMARY = 0x00000001, | |
/// <summary> | |
/// If no display monitor intersects, returns a handle to the display monitor that is nearest to the rectangle. | |
/// </summary> | |
MONITOR_DEFAULTTONEAREST = 0x00000002, | |
} | |
[DllImport("User32.dll", EntryPoint = "GetMonitorInfoW")] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool GetMonitorInfo( | |
IntPtr hMonitor, | |
ref MONITORINFOEX lpmi); | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |
public struct MONITORINFOEX | |
{ | |
public uint cbSize; | |
public RECT rcMonitor; | |
public RECT rcWork; | |
public uint dwFlags; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] | |
public string szDevice; | |
} | |
#endregion | |
private const string PrimaryTaskbarWindowClassName = "Shell_TrayWnd"; | |
private const string SecondaryTaskbarWindowClassName = "Shell_SecondaryTrayWnd"; | |
public static IntPtr GetPrimaryTaskbarHandle() | |
{ | |
return FindWindowEx( | |
IntPtr.Zero, | |
IntPtr.Zero, | |
PrimaryTaskbarWindowClassName, | |
string.Empty); | |
} | |
public class TaskbarItem | |
{ | |
public string DeviceInstanceId { get; } | |
public IntPtr Handle { get; } | |
public bool IsPrimary { get; } | |
public TaskbarAlignment Alignment { get; } | |
public Rect TaskbarRect { get; } | |
public Rect MonitorRect { get; } | |
public Rect WorkRect { get; } | |
public TaskbarItem( | |
string deviceInstanceId, | |
IntPtr handle, | |
bool isPrimary, | |
TaskbarAlignment alignment, | |
Rect taskbarRect, | |
Rect monitorRect, | |
Rect workRect) | |
{ | |
this.DeviceInstanceId = deviceInstanceId; | |
this.Handle = handle; | |
this.IsPrimary = isPrimary; | |
this.Alignment = alignment; | |
this.TaskbarRect = taskbarRect; | |
this.MonitorRect = monitorRect; | |
this.WorkRect = workRect; | |
} | |
} | |
public static IEnumerable<TaskbarItem> Enumerate() | |
{ | |
var monitors = EnumerateMonitors().ToArray(); | |
var taskbars = GetWindows(new[] { PrimaryTaskbarWindowClassName, SecondaryTaskbarWindowClassName }, monitors.Length); | |
foreach (var (deviceName, deviceInstanceId) in monitors) | |
{ | |
var taskbar = taskbars.FirstOrDefault(x => string.Equals(x.deviceName, deviceName, StringComparison.OrdinalIgnoreCase)); | |
if (taskbar == default) | |
continue; | |
var isPrimary = (taskbar.className == PrimaryTaskbarWindowClassName); | |
var alignment = GetTaskbarAlignment(taskbar.monitorRect, taskbar.windowRect); | |
if (alignment == default) | |
continue; | |
yield return new TaskbarItem( | |
deviceInstanceId, | |
taskbar.handle, | |
isPrimary, | |
alignment, | |
taskbarRect: taskbar.windowRect, | |
monitorRect: taskbar.monitorRect, | |
workRect: taskbar.workRect); | |
} | |
} | |
private static IEnumerable<(string deviceName, string deviceInstanceId)> EnumerateMonitors() | |
{ | |
var size = (uint)Marshal.SizeOf<DISPLAY_DEVICE>(); | |
var display = new DISPLAY_DEVICE { cb = size }; | |
var monitor = new DISPLAY_DEVICE { cb = size }; | |
for (uint i = 0; EnumDisplayDevices(null, i, ref display, EDD_GET_DEVICE_INTERFACE_NAME); i++) | |
{ | |
if (display.StateFlags.HasFlag(DISPLAY_DEVICE_FLAG.DISPLAY_DEVICE_MIRRORING_DRIVER)) | |
continue; | |
for (uint j = 0; EnumDisplayDevices(display.DeviceName, j, ref monitor, EDD_GET_DEVICE_INTERFACE_NAME); j++) | |
{ | |
if (!monitor.StateFlags.HasFlag(DISPLAY_DEVICE_FLAG.DISPLAY_DEVICE_ACTIVE)) | |
continue; | |
yield return (display.DeviceName, ConvertDeviceInstanceId(monitor.DeviceID)); | |
} | |
} | |
} | |
private static (IntPtr handle, string className, Rect windowRect, string deviceName, Rect monitorRect, Rect workRect)[] GetWindows(string[] classNames, int count) | |
{ | |
var windows = new List<(IntPtr, string, Rect, string, Rect, Rect)>(); | |
if (EnumWindows( | |
Proc, | |
IntPtr.Zero)) | |
{ | |
return windows.ToArray(); | |
} | |
return Array.Empty<(IntPtr, string, Rect, string, Rect, Rect)>(); | |
bool Proc(IntPtr windowHandle, IntPtr _lParam) | |
{ | |
var buffer = new StringBuilder(256); | |
if (GetClassName( | |
windowHandle, | |
buffer, | |
buffer.Capacity) > 0) | |
{ | |
var className = buffer.ToString(); | |
if (classNames.Contains(className)) | |
{ | |
if (GetWindowRect( | |
windowHandle, | |
out RECT windowRect)) | |
{ | |
var monitorHandle = MonitorFromWindow( | |
windowHandle, | |
MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL); | |
if (monitorHandle != IntPtr.Zero) | |
{ | |
var monitorInfo = new MONITORINFOEX { cbSize = (uint)Marshal.SizeOf<MONITORINFOEX>() }; | |
if (GetMonitorInfo( | |
monitorHandle, | |
ref monitorInfo)) | |
{ | |
windows.Add((windowHandle, className, windowRect, monitorInfo.szDevice, monitorInfo.rcMonitor, monitorInfo.rcWork)); | |
if (windows.Count >= count) | |
return false; | |
} | |
} | |
} | |
} | |
} | |
return true; | |
} | |
} | |
private static string ConvertDeviceInstanceId(string devicePath) | |
{ | |
int index = devicePath.IndexOf("DISPLAY", StringComparison.Ordinal); | |
if (index < 0) | |
return null; | |
var fields = devicePath.Substring(index).Split('#'); | |
if (fields.Length < 3) | |
return null; | |
return string.Join(@"\", fields.Take(3)); | |
} | |
private static TaskbarAlignment GetTaskbarAlignment(Rect monitorRect, Rect taskbarRect) | |
{ | |
return (left: (monitorRect.Left == taskbarRect.Left), | |
top: (monitorRect.Top == taskbarRect.Top), | |
right: (monitorRect.Right == taskbarRect.Right), | |
bottom: (monitorRect.Bottom == taskbarRect.Bottom)) switch | |
{ | |
(true, true, right: false, true) => TaskbarAlignment.Left, | |
(true, true, true, bottom: false) => TaskbarAlignment.Top, | |
(left: false, true, true, true) => TaskbarAlignment.Right, | |
(true, top: false, true, true) => TaskbarAlignment.Bottom, | |
_ => default | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment