C# Desktop background displaying incorrect location maybe?

278 views Asked by At

Hi I am currently creating an animated desktop background that uses axWindowsMideaPlayer to play video files in a loop I take the WMP and set its handle to the desktop handle, it works great on single display monitor setup but fails with multiple monitors. Why it is failing is because monitors can have their own set position like left/right/topleft/topright/top/bottom/bottomleft and bottom right of the primary screen which puts their position in the negatives and so forth.. My question is how can I position each WMP correctly on each monitor? Here is what I have so far,

this is how I get each monitor...

public class DisplayInfo
{
    

    public bool isPrimary { get; set; }
    public int ScreenHeight { get; set; }
    public int ScreenWidth { get; set; }
    public Rect MonitorArea { get; set; }
    public Rect WorkArea { get; set; }
    public string DeviceName { get; set; }        
}

/// <summary>
/// Collection of display information
/// </summary>
public class DisplayInfoCollection : List<DisplayInfo>
{
    // size of a device name string
    private const int CCHDEVICENAME = 32;

    /// <summary>
    /// The MONITORINFOEX structure contains information about a display monitor.
    /// The GetMonitorInfo function stores information into a MONITORINFOEX structure or a MONITORINFO structure.
    /// The MONITORINFOEX structure is a superset of the MONITORINFO structure. The MONITORINFOEX structure adds a string member to contain a name
    /// for the display monitor.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct MONITORINFOEX
    {
        /// <summary>
        /// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFOEX) (72) before calling the GetMonitorInfo function.
        /// Doing so lets the function determine the type of structure you are passing to it.
        /// </summary>
        public int Size;

        /// <summary>
        /// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates.
        /// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
        /// </summary>
        public Rect Monitor;

        /// <summary>
        /// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications,
        /// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor.
        /// The rest of the area in rcMonitor contains system windows such as the task bar and side bars.
        /// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
        /// </summary>
        public Rect WorkArea;

        /// <summary>
        /// The attributes of the display monitor.
        ///
        /// This member can be the following value:
        ///   1 : MONITORINFOF_PRIMARY
        /// </summary>
        public uint Flags;

        /// <summary>
        /// A string that specifies the device name of the monitor being used. Most applications have no use for a display monitor name,
        /// and so can save some bytes by using a MONITORINFO structure.
        /// </summary>
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
        public string DeviceName;

        public void Init()
        {
            this.Size = 40 + 2 * CCHDEVICENAME;
            this.DeviceName = string.Empty;
        }
    }

    [DllImport("user32.dll")]
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
   EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);
    /*
    [DllImport("user32.dll")]
    static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
    */
    /// <summary>
    /// Returns the number of Displays using the Win32 functions
    /// </summary>
    /// <returns>collection of Display Info</returns>
    public static DisplayInfoCollection GetDisplays()
    {
        DisplayInfoCollection col = new DisplayInfoCollection();

        EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
            delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData)
            {
                MONITORINFOEX mi = new MONITORINFOEX();
                mi.Size = (int)Marshal.SizeOf(mi);
                bool success = GetMonitorInfo(hMonitor, ref mi);
                if (success)
                {
                    DisplayInfo di = new DisplayInfo();
                    di.ScreenWidth = (mi.Monitor.Right - mi.Monitor.Left);
                    di.ScreenHeight = (mi.Monitor.Bottom - mi.Monitor.Top);
                    di.MonitorArea = mi.Monitor;
                    di.WorkArea = mi.WorkArea;
                    di.isPrimary = Convert.ToBoolean(mi.Flags);
                    di.DeviceName = mi.DeviceName;                        
                    col.Add(di);
                }
                return true;
            }, IntPtr.Zero);
        return col;
    }
}

this is how I have tried to call it and use it but it puts WMP all over the place depending on where the monitors are positioned.

DisplayInfoCollection dic = DisplayInfoCollection.GetDisplays();
        int count = 0;
        int totalPosX = 0;
        int totalPosY = 0;

        DisplayInfo dInfo = null;
                    
        List<DisplayInfo> di = dic.OrderByDescending(d => d.isPrimary).ToList();
        
        var or = SystemInformation.VirtualScreen;
        foreach (DisplayInfo dm in di)
        {
            bool zeroOutX = false;
            bool zeroOutY = false;
            if (dm.isPrimary)
            {
                totalPosX = or.Left > 0 ? or.Left : -or.Left;
                totalPosY = or.Top > 0 ? or.Top : -or.Top; 
                dInfo = dm;
            }
            else
            {                    
                bool left = false;
                bool top = false;
                bool right = false;
                bool bottom = false;
                bool topLeft = false;
                bool topRight = false;
                bool bottomLeft = false;
                bool bottomRight = false;
                int posY = dm.MonitorArea.Top > 0 ? dm.MonitorArea.Top : -dm.MonitorArea.Top;
                if (dm.MonitorArea.Left < 0)
                {
                    left = true;
                }
                else
                    right = dm.MonitorArea.Left > 0;
                if (dm.MonitorArea.Top < 0)
                {
                    top = true;
                }
                else
                    bottom = dm.MonitorArea.Top > 0;
                bool center = (dm.MonitorArea.Left > 0 ?
                    dm.MonitorArea.Left : -dm.MonitorArea.Left) > 0 ||
                    (dm.MonitorArea.Left > 0 ?
                    dm.MonitorArea.Left : -dm.MonitorArea.Left) < dInfo.ScreenWidth;

                topLeft = left && top;
                topRight = right && top;
                bottomLeft = left && bottom;
                bottomRight = right && bottom;
                if (topLeft || topRight || bottomLeft || bottomRight || left || right)
                {
                    if (dm.MonitorArea.Left < 0)
                        zeroOutX = true;
                    else
                        totalPosX += dInfo.ScreenWidth;
                    if (dm.MonitorArea.Top < 0)
                        zeroOutY = true;
                    else
                        totalPosY += dm.MonitorArea.Top;
                }
                dInfo = dm;
            }

            Display display = new Display(dm.DeviceName, dm.ScreenWidth,
               dm.ScreenHeight, zeroOutX ? 0 : totalPosX,
               zeroOutY ? 0 : totalPosY, Controls, count);
            Displays.Add(display);
            count++;
            
        }

I cant find much help on this matter and have tried numerous ways to do this its a c# windows form and I am new-ish to programming my knowledge is limited in this any help will be appreciated thanks in advance.

2

There are 2 answers

1
Drake Wu On

According to the EnumDisplaySettings:

The EnumDisplaySettings function sets values for the following five DEVMODE members:

  • dmBitsPerPel
  • dmPelsWidth
  • dmPelsHeight
  • dmDisplayFlags
  • dmDisplayFrequency

(Excluding dmPosition), You should try to use EnumDisplaySettingsEx, and specify DM_POSITION to get the correct dmPosition value.

0
Scott On

thanks for all that helped I found a solution to my problem here it is if anyone need to know.

        var or = SystemInformation.VirtualScreen;
        foreach (DisplayInfo dm in dic)
        {                
            int x = or.Left > 0 ? or.Left : -or.Left;
            int y = or.Top > 0 ? or.Top : -or.Top;

            if (dm.isPrimary)
            {
                Rect rect = new Rect();
                rect.Left = x;
                rect.Top = y;
                rect.Right = rect.Left + dm.ScreenWidth;
                rect.Bottom = rect.Top + dm.ScreenHeight;
                dm.MonitorArea = rect;
            }                
            else
            {                 
                Rect rect = new Rect();
                rect.Left = x + dm.MonitorArea.Left;
                rect.Top = y + dm.MonitorArea.Top;
                rect.Right = rect.Left + dm.ScreenWidth;
                rect.Bottom = rect.Top + dm.ScreenHeight;
                dm.MonitorArea = rect;
            }