You can do it with
- capture all connected monitors
- applying your get / set functions to Graphics (or its Hdc).
- registration with the
MonitorInfoInvalidated event for reuse if the monitor information becomes invalid.
If you already have a dependency on Windows.Forms dll or are not against using this dependency, you can use its Screen for this, as @HansPassant pointed out in the answer. In this case, you will register an event handler for SystemEvents.DisplaySettingsChanged to cause your get / set functions to be reused, and you will use the interop calls on CreateDC and DeleteDC to get / release the device context descriptor (IntPtr) from the Screen.DeviceName property. The code below shows the shell of this class that helps with this:
If you do not want to take a new dependency on the Windows.Forms dll, the ConnectedMonitors class below provides the same functionality:
/// <summary> /// This is the version that is not dependent on Windows.Forms dll. /// </summary> public static class ConnectedMonitors { private static readonly bool _isSingleMonitor = GetSystemMetrics(SM_CMONITORS) == 0; private static Lazy<List<MonitorInfo>> _monitors = new Lazy<List<MonitorInfo>>(GetMonitors, true); public static event Action MonitorInfoInvalidated; public class MonitorInfo { public readonly IntPtr MonitorHandle; public readonly IntPtr DeviceContextHandle; public readonly string DeviceName; public readonly bool IsPrimary; public readonly Rectangle Bounds; public readonly Rectangle WorkArea; public void WithMonitorHdc(Action<MonitorInfo, IntPtr> action) { var hdc = DeviceContextHandle; var shouldDeleteDC = IntPtr.Zero.Equals(hdc); try { if (shouldDeleteDC) hdc = CreateDC(null, DeviceName, null, IntPtr.Zero); action(this, hdc); } finally { if (shouldDeleteDC && !IntPtr.Zero.Equals(hdc)) DeleteDC(hdc); } } internal MonitorInfo( IntPtr hMonitor, IntPtr hDeviceContext, string deviceName, bool isPrimary, Rectangle bounds, Rectangle workArea) { this.MonitorHandle = hMonitor; this.DeviceContextHandle = hDeviceContext; this.DeviceName = deviceName; this.IsPrimary = isPrimary; this.Bounds = bounds; this.WorkArea = workArea; } } public static void CaptureScreen(MonitorInfo mi, string fileName) { CaptureScreen(mi).Save(fileName); } public static Bitmap CaptureScreen(MonitorInfo mi) { Bitmap screenBmp = default(Bitmap); mi.WithMonitorHdc((m, hdc) => { screenBmp = new Bitmap(m.Bounds.Width, m.Bounds.Height, PixelFormat.Format32bppArgb); using (var destGraphics = Graphics.FromImage(screenBmp)) { var monitorDC = new HandleRef(null, hdc); var destDC = new HandleRef(null, destGraphics.GetHdc()); var result = BitBlt(destDC, 0, 0, m.Bounds.Width, m.Bounds.Height, monitorDC, 0, 0, unchecked((int)BITBLT_SRCCOPY)); if (result == 0) throw new Win32Exception(); } }); return screenBmp; } public static IEnumerable<MonitorInfo> Monitors { get { return _monitors.Value; } } private static List<MonitorInfo> GetMonitors() { // Get info on all monitors var cb = new EnumMonitorsCallback(); EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, cb.Callback, IntPtr.Zero); // Register for events invalidating monitor info. SystemEvents.DisplaySettingsChanging += OnDisplaySettingsChanging; SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged; // Return result. return cb.Monitors; } private class EnumMonitorsCallback { public List<MonitorInfo> Monitors = new List<MonitorInfo>(); public bool Callback(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr lparam) { // Get its info var info = new MONITORINFOEX(); info.Size = Marshal.SizeOf(typeof(MONITORINFOEX)); GetMonitorInfo(hMonitor, ref info); // Decode the info var isPrimary = (hMonitor == (IntPtr)PRIMARY_MONITOR) || ((info.Flags & MONITORINFOF_PRIMARY) != 0); var bounds = Rectangle.FromLTRB(info.Monitor.Left, info.Monitor.Top, info.Monitor.Right, info.Monitor.Bottom); var workArea = Rectangle.FromLTRB(info.WorkArea.Left, info.WorkArea.Top, info.WorkArea.Right, info.WorkArea.Bottom); var deviceName = info.DeviceName.TrimEnd('\0'); // Create info for this monitor and add it. Monitors.Add(new MonitorInfo(hMonitor, hdcMonitor, deviceName, isPrimary, bounds, workArea)); return true; } } private static void OnDisplaySettingsChanging(object sender, EventArgs e) { InvalidateInfo(); } private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { InvalidateInfo(); } private static void InvalidateInfo() { SystemEvents.DisplaySettingsChanging -= OnDisplaySettingsChanging; SystemEvents.UserPreferenceChanged -= OnUserPreferenceChanged; var cur = _monitors; _monitors = new Lazy<List<MonitorInfo>>(GetMonitors, true); var notifyInvalidated = MonitorInfoInvalidated; if (notifyInvalidated != null) notifyInvalidated(); }
Usage examples for your use case using the slightly modified SetLCDbrightness method:
private bool SetLCDbrightness(IntPtr hdc, Color c) { short red = cR; short green = cG; short blue = cB; unsafe { short* gArray = stackalloc short[3 * 256]; short* idx = gArray; short brightness = 0; for (int j = 0; j < 3; j++) { if (j == 0) brightness = red; if (j == 1) brightness = green; if (j == 2) brightness = blue; for (int i = 0; i < 256; i++) { int arrayVal = i * (brightness); if (arrayVal > 65535) arrayVal = 65535; *idx = (short)arrayVal; idx++; } }
Called as:
Please note that this allows some other fun things, such as taking screenshots:
var n = 0; foreach (var m in ConnectedMonitors.Monitors) ConnectedMonitors.CaptureScreen(m, string.Format(@"c:\temp\screen{0}.bmp", n++));