Results 1 to 12 of 12

Thread: Monitor Numbering

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Monitor Numbering

    I'm finding that the monitor numbering that Windows Displays Settings uses and
    and the numbering generated when calling EnumDisplayMonitors are not synched.

    Can someone confirm this?

    If this is true, is there a way to force them to be the same?

  2. #2
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: Monitor Numbering

    EnumDisplayMonitors and the Windows Display Settings are only loosely related. The Windows Display Settings could be calling EnumDisplayMonitors, however it can then choose to number the information it reports in whatever way it sees fit.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  3. #3
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Monitor Numbering

    Perhaps this is correct:

    Give each "monitor" you track (array, Collection, etc.) Index, Number, and Primary properties

    Build the list by enumerating monitors, assigning Index from 0 to n as you go, capturing Primary = True for the primary.

    Find PrimaryIndex (or assign it when you capture Primary = True).

    Assign Mon(Primary Index).Number = 1, assign a MonNumber variable = 1.

    Scan the list from I = 0 to n. If Not Mon(I)Primary Then MonNumber = MonNumber + 1:Mon(I).Number = MonNumber


    Ok, that's an awkward description. I'm suggesting that perhaps monitor 1 is always the primary and the rest are numbered above 1 in the enumeration sequence. What have you got to lose by trying it?


    Note: While your program is running the user can change the primary monitor, so you may need some way to refresh the list from scratch. There might be some broadcast message to tell you when you must refresh, I'm not sure.

  4. #4

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Numbering

    I will give your approach a try.

  5. #5
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Monitor Numbering

    Personally, I've never been able to get this to work (correct numbering) with EnumDisplayMonitors. However, I haven't examined the order in which the monitors are returned by the WMI. Maybe they're correctly ordered (per Windows settings) when listing them that way.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  6. #6

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Numbering

    I guess I have to give up on this, as I am spending way too much time and getting nowhere really.

    The only thing I can determine with certainty is that the primary monitor always has top-left coordinates of (0,0)

    I can find no way to tie numbering from EnumDisplayMonitors to Windows Settings numbering.


    Name:  VirtualScreen.jpg
Views: 297
Size:  11.8 KB

    Code for virtual screen requires VB project with
    Picture1 on Form1
    Picture2 inside Picture1
    Label1 inside Picture2

    Form1 code
    Code:
    Option Explicit
    
    'https://docs.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen
    
    Private Sub Form_Load()
    
        Me.ScaleMode = vbPixels
        Me.Caption = "Virtual Screen"
        Me.BorderStyle = 1
    
        'Picture1 - background
        Picture1.ScaleMode = vbPixels
        Picture1.Appearance = 0
        Picture1.BorderStyle = 1
        Picture1.BackColor = vbWhite
        
        'Picture2 - virtual screen
        Picture2.ScaleMode = vbPixels
        Picture2.Appearance = 0
        Picture2.BorderStyle = 1
        Picture2.BackColor = &H8000000A
        
        'Label1 - monitors
        Label1(0).Appearance = 0
        Label1(0).BorderStyle = 1
        Label1(0).BackColor = vbCyan
        
        Call DrawVirtualScreen
    
    End Sub
    Module1 code
    Code:
    Option Explicit
    
    
    Private Declare Function EnumDisplayMonitors Lib "user32" _
        (ByVal hdc As Long, lprcClip As Any, ByVal lpfnEnum As Long, dwData As Any) As Long
         
    Private Declare Function UnionRect Lib "user32" _
        (lprcDst As RECT, lprcSrc1 As RECT, lprcSrc2 As RECT) As Long
    
    Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
    End Type
    
    Dim rectMonitors() As RECT    'coordinates array for all monitors
    Dim rectVS         As RECT    'coordinates for Virtual Screen
    
    Dim ret As Long
    
    
    Function DrawVirtualScreen() As Long
        
        Dim i As Long
        
        ret = EnumDisplayMonitors(0, ByVal 0&, AddressOf MonitorEnumProc, i)
    
        'size virtual screen
        Form1.Picture2.Width = Round((rectVS.Right - rectVS.Left) * 0.18, 0)
        Form1.Picture2.Height = Round((rectVS.Bottom - rectVS.Top) * 0.18, 0)
        Form1.Picture2.Left = 10
        Form1.Picture2.Top = 10
    
        'scale virtual screen
        Form1.Picture2.ScaleLeft = rectVS.Left
        Form1.Picture2.ScaleWidth = rectVS.Right - rectVS.Left
        Form1.Picture2.ScaleTop = rectVS.Top
        Form1.Picture2.ScaleHeight = rectVS.Bottom - rectVS.Top
    
        'size background based on virtual screen
        Form1.Picture1.Width = Form1.Picture2.Width + 22
        Form1.Picture1.Height = Form1.Picture2.Height + 22
        Form1.Picture1.Left = 10
        Form1.Picture1.Top = 10
    
        'size form based on background
        Form1.Width = (Form1.Picture1.Width + 36) * Screen.TwipsPerPixelX
        Form1.Height = (Form1.Picture1.ScaleHeight + 61) * Screen.TwipsPerPixelY
    
        'position monitors on virtual screen
        For i = 0 To i - 1
    
            'load control array
            If i > 0 Then
                Load Form1.Label1(i)
                Form1.Label1(i).Visible = True
            End If
    
            'move monitors to proper position on virtual screen
            Form1.Label1(i).Left = rectMonitors(i).Left
            Form1.Label1(i).Top = rectMonitors(i).Top
            Form1.Label1(i).Width = rectMonitors(i).Right - rectMonitors(i).Left
            Form1.Label1(i).Height = rectMonitors(i).Bottom - rectMonitors(i).Top
            
            'display metrics
            Form1.Label1(i).Caption = _
                "Monitor " & i + 1 & vbLf & _
                vbLf & _
                rectMonitors(i).Right - rectMonitors(i).Left & " x " & _
                    rectMonitors(i).Bottom - rectMonitors(i).Top & vbLf & _
                "(" & rectMonitors(i).Left & ", " & rectMonitors(i).Top & ")-(" & _
                    rectMonitors(i).Right & ", " & rectMonitors(i).Bottom & ")"
                
            If rectMonitors(i).Left = 0 And rectMonitors(i).Top = 0 Then
                Form1.Label1(i).Caption = Form1.Label1(i).Caption & _
                                                            vbLf & vbCrLf & "PRIMARY"
            End If
    
        Next
        
    End Function
    
    
    Private Function MonitorEnumProc(ByVal hMonitor As Long, ByVal hdcMonitor As Long, _
                                     lprcMonitor As RECT, dwData As Long) As Long
    
        ' A MonitorEnumProc function is an application-defined callback function
        ' that is called by the EnumDisplayMonitors function.
    
        ReDim Preserve rectMonitors(dwData)
        rectMonitors(dwData) = lprcMonitor
    
        UnionRect rectVS, rectVS, lprcMonitor   'merge all monitors together to get virtual screen
        dwData = dwData + 1                     'increase monitor count
        
        MonitorEnumProc = 1                     'continue
        
    End Function

  7. #7
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: Monitor Numbering

    Quote Originally Posted by mms_ View Post
    I guess I have to give up on this, as I am spending way too much time and getting nowhere really.
    Did you see this thread?
    https://superuser.com/questions/1455...mbers-identity

    What I read there gave me some ideas, things I'd try if I were doing this.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  8. #8

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Numbering

    Thanks Niya for the link.
    No, I had not seen that before.

    If you are referring to a possible solution as making registry changes,
    then I'd rather stay away from that.

  9. #9
    Lively Member vbLewis's Avatar
    Join Date
    Feb 2009
    Location
    USA
    Posts
    126

    Re: Monitor Numbering

    here is how I did it, a call to GetMonitorInfo then check the Left property of each monitors work space to determine the order

    Name:  ss.jpg
Views: 221
Size:  17.9 KB
    Attached Files Attached Files

  10. #10

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Numbering

    A very slick implementation vbLewis, thank you for posting!!

    Your code is well commented, and I can learn a lot from it.

    The left-to-right approach of numbering (0-1-2) is very logical, and that is how your program shows my 3 monitors.
    I notice in your screenshot above, a (1-0-2) set-up, where I assume 0 is your primary monitor? (this is how I was thinking at the outset btw)

    If your SortMonitors routine sorts left to right (which I think it does), how do you end up with (1-0-2)?

    Thank you again for posting, because this will definitely help me.

  11. #11
    Lively Member vbLewis's Avatar
    Join Date
    Feb 2009
    Location
    USA
    Posts
    126

    Re: Monitor Numbering

    enumdisplaymonitors() enumerates the monitors in the order the system has them numbered with the primary display starting at 0 (i think. at least thats how mine is). So no matter how you arrange them in display properties the number they are "enum'ed" at stays the same.

    The idea in my code is that you can use the Index from the Monitor() array to get its physical arrangement index from MonitorOrder() array. So lets say that the Monitor() array has three monitors in it, and you need to get the second monitors physical arrangement in display properties.
    The second Monitor's index in the Monitor() array is 1 (0-1-2) so MonitorOrder(1) would be = to wherever the monitor is arranged in the display properties, in my case its the first one.


    edit: i just noticed in Display Properties the numbers start at 1 so I'm thinking I should probably update my code to add 1 to the internal number.

  12. #12
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: Monitor Numbering

    as for me, I only need to know the current monitor. I actually don't care about what/where/how many.
    so, if Im in monitor 1, I want my maximize/fullscreen buttons to work properly.
    and if I move the form to monitor 2, it should do that correctly for that screen, so my form will not jump to monitor 1 if Im in 2.
    or the resize will stretch to both monitors.

    not sure the purpose here.
    I use:

    Code:
    Private Declare Function MonitorFromRect Lib "user32" (rc As Rect, ByVal dwFlags As Long) As Long
    Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As Long, ByRef lpRect As Rect) As Long
    
    GetWindowRect  form.hwnd, lpRect
    hMonitor = MonitorFromRect(lpRect, 1&)
    hMonitor is the same u get from the Enumeration.
    but since "I don't care" about the other display, I simply parse this one using GetMonitorInfo
    and I get both the screen rect and the working rect. so I can avoid covering the taskbar when "maximize" (as I have both maximize and fullscreen options)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width