Skip to content

Instantly share code, notes, and snippets.

@emoacht
Last active February 24, 2021 00:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emoacht/aa8e20497623d6491d636979d3809eb9 to your computer and use it in GitHub Desktop.
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.
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