I'm trying to do screen capture in my app, where I capture the current display. I've been using GraphicsCaptureItem.TryCreateFromDisplayId(Windows.Graphics.DisplayId)
with a dummy new DisplayId(0)
, but that's capturing a point in between both of my monitors. Unfortunately, the DisplayId
class does not indicate how I might create one with the correct value.
How do I create a Windows.Graphics.DisplayId
properly? Or how might I get a Windows.UI.WindowId
, for .TryCreateFromWindowId
instead?
The DisplayId
class seems to be a simple wrapper around an HMONITOR
, so you can obtain an HMONITOR
however you'd like and convert it into a DisplayId
(WindowId
s seem to be wrappers around HWND
s, too). For example, using CsWin32 (or manual PInvoke) to use the MonitorFromWindow
APIs:
// Get the display for an arbitrary HWND
internal static DisplayId GetDisplayIdForHwnd(HWND hwnd)
{
Windows.Win32.Graphics.Gdi.HMONITOR monitor = Windows.Win32.PInvoke.MonitorFromWindow(hwnd, Windows.Win32.Graphics.Gdi.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
var displayId = new DisplayId((ulong)monitor.Value);
return displayId;
}
// Get the default display by harnessing `MONITOR_DEFAULTTOPRIMARY`
public static DisplayId GetDefaultDisplayId()
{
return GetDisplayIdForHwnd(HWND.Null);
}
And then something like:
var displayId = GetDisplayIdForHwnd(hwnd);
var captureItem = GraphicsCaptureItem.TryCreateFromDisplayId(displayId);
You could also use an API like EnumDisplayMonitors
to enumerate HMONITOR
s.
IGraphicsCaptureItemInterop
As of 2019, Windows provides the IGraphicsCaptureItemInterop
interface as compat, which notably offers IGraphicsCaptureItemInterop::CreateForMonitor(HMONITOR)
and IGraphicsCaptureItemInterop::CreateForWindow(HWND)
, which this small sample app explains.
As this other question alludes, you can use this interface from C# if you write the correct PInvoke script (rewritten here with more-modern marshalling):
public static partial class WindowCapture
{
private static Guid GraphicsCaptureItemGuid = new ("79C3F95B-31F7-4EC2-A464-632EF5D30760");
[ComImport]
[Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
private interface IGraphicsCaptureItemInterop
{
IntPtr CreateForWindow(
[In] IntPtr window,
[In] ref Guid iid);
IntPtr CreateForMonitor(
[In] IntPtr monitor,
[In] ref Guid iid);
}
// Obtain the HMONITOR as before, like:
// var monitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTOPRIMARY);
private static GraphicsCaptureItem CreateForMonitor(HMONITOR hmon)
{
var interop = GraphicsCaptureItem.As<IGraphicsCaptureItemInterop>();
var ptr = interop.CreateForMonitor(new IntPtr(hmon.Value), GraphicsCaptureItemGuid);
var captureItem = GraphicsCaptureItem.FromAbi(ptr);
return captureItem;
}
}
If you're using WinAppSDK, there is an official API (Win32Interop.GetDisplayIdFromMonitor(IntPtr)
and vice versa). However, the Interop API actually returns a Microsoft.UI.DisplayId
, which you'd need to convert into a Windows.Graphics.DisplayId
manually anyway:
Windows.Graphics.DisplayId FromMicrosoftDisplayId(Microsoft.UI.DisplayId displayID)
{
return new Windows.Graphics.DisplayId(displayID.Value);
}
See also SimpleRecorder, a Microsoft sample app.