See the question and my original answer on StackOverflow

WinRT classes (DisplayMonitor, etc.) are really what you want to use. The difficulty is they don't (AFAIK) expose the relation with the GDI device names (\\?\DISPLAY1, etc.).

But their implementation is based on the native Connecting and configuring displays API and this API can give you the GDI name using a code like this:

public static string GetGdiDeviceName(int adapterIdHigh, uint adapterIdLow, uint sourceId)
{
    var info = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
    const int DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1;
    info.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
    info.header.size = Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
    info.header.adapterIdHigh = adapterIdHigh;
    info.header.adapterIdLow = adapterIdLow;
    info.header.id = sourceId;
    var err = DisplayConfigGetDeviceInfo(ref info);
    if (err != 0)
        throw new Win32Exception(err);

    return info.viewGdiDeviceName;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
{
    public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string viewGdiDeviceName;

    public override string ToString() => viewGdiDeviceName;
}

[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_DEVICE_INFO_HEADER
{
    public int type;
    public int size;
    public uint adapterIdLow;
    public int adapterIdHigh;
    public uint id;
}

[DllImport("user32")]
private static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DEVICE_NAME requestPacket);

Now, adapterId is easy to find, but the sourceId is more complex. It doesn't seem to be exposed directly by WinRT, you must use the IDisplayPathInterop Interface, so:

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("A6BA4205-E59E-4E71-B25B-4E436D21EE3D")]
private interface IDisplayPathInterop
{
    [PreserveSig]
    int CreateSourcePresentationHandle(out IntPtr value);

    [PreserveSig]
    int GetSourceId(out uint sourceId);
}

Now, you can dump all monitors with their GDI names, and then you can use that GDI name to correlate using other Windows API : MONITOR, DISPLAY_DEVICE, event the old Winform's Screen, etc. like you do with a code like this:

using (var mgr = DisplayManager.Create(DisplayManagerOptions.None))
{
    var state = mgr.TryReadCurrentStateForAllTargets().State;
    foreach (var view in state.Views)
    {
        foreach (var path in view.Paths)
        {
            var monitor = path.Target.TryGetMonitor();
            if (monitor != null)
            {
                var ip = WinRT.CastExtensions.As<IDisplayPathInterop>(path);
                ip.GetSourceId(out var sourceId);
                var gdiDeviceName = GetGdiDeviceName(monitor.DisplayAdapterId.HighPart, monitor.DisplayAdapterId.LowPart, sourceId);
                Console.WriteLine(monitor.DisplayName + " on " + gdiDeviceName);
                // TODO: use gdiDeviceName to correlate with other APIs
            }
        }
    }
}

Which will display this on my system (note display 2 is my primary screen - with sourceId 1, not 0 -, this demonstrates you cannot just use indices to match):

C27HG7x on \\.\DISPLAY2
DELL U2715H on \\.\DISPLAY1