[LIB] MDMF - Multiple Display Monitor Functions

Post your working scripts, libraries and tools
just me
Posts: 5660
Joined: 02 Oct 2013, 08:51
Location: Germany

[LIB] MDMF - Multiple Display Monitor Functions

18 Sep 2014, 04:16

Some more or less useful functions for multiple-display environments. Most of it can be accomplished with SysGet too.

Related: Resize restrained to vertical

Lib:

Code: Select all

; ======================================================================================================================
; Multiple Display Monitors Functions -> msdn.microsoft.com/en-us/library/dd145072(v=vs.85).aspx =======================
; ======================================================================================================================
; Enumerates display monitors and returns an object containing the properties of all monitors or the specified monitor.
; ======================================================================================================================
MDMF_Enum(HMON := "") {
   Static EnumProc := RegisterCallback("MDMF_EnumProc")
   Static Monitors := {}
   If (HMON = "") ; new enumeration
      Monitors := {}
   If (Monitors.MaxIndex() = "") ; enumerate
      If !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", &Monitors, "UInt")
         Return False
   Return (HMON = "") ? Monitors : Monitors.HasKey(HMON) ? Monitors[HMON] : False
}
; ======================================================================================================================
;  Callback function that is called by the MDMF_Enum function.
; ======================================================================================================================
MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) {
   Monitors := Object(ObjectAddr)
   Monitors[HMON] := MDMF_GetInfo(HMON)
   Return True
}
; ======================================================================================================================
;  Retrieves the display monitor that has the largest area of intersection with a specified window.
; ======================================================================================================================
MDMF_FromHWND(HWND) {
   Return DllCall("User32.dll\MonitorFromWindow", "Ptr", HWND, "UInt", 0, "UPtr")
}
; ======================================================================================================================
; Retrieves the display monitor that contains a specified point.
; If either X or Y is empty, the function will use the current cursor position for this value.
; ======================================================================================================================
MDMF_FromPoint(X := "", Y := "") {
   VarSetCapacity(PT, 8, 0)
   If (X = "") || (Y = "") {
      DllCall("User32.dll\GetCursorPos", "Ptr", &PT)
      If (X = "")
         X := NumGet(PT, 0, "Int")
      If (Y = "")
         Y := NumGet(PT, 4, "Int")
   }
   Return DllCall("User32.dll\MonitorFromPoint", "Int64", (X & 0xFFFFFFFF) | (Y << 32), "UInt", 0, "UPtr")
}
; ======================================================================================================================
; Retrieves the display monitor that has the largest area of intersection with a specified rectangle.
; Parameters are consistent with the common AHK definition of a rectangle, which is X, Y, W, H instead of
; Left, Top, Right, Bottom.
; ======================================================================================================================
MDMF_FromRect(X, Y, W, H) {
   VarSetCapacity(RC, 16, 0)
   NumPut(X, RC, 0, "Int"), NumPut(Y, RC, 4, Int), NumPut(X + W, RC, 8, "Int"), NumPut(Y + H, RC, 12, "Int")
   Return DllCall("User32.dll\MonitorFromRect", "Ptr", &RC, "UInt", 0, "UPtr")
}
; ======================================================================================================================
; Retrieves information about a display monitor.
; ======================================================================================================================
MDMF_GetInfo(HMON) {
   NumPut(VarSetCapacity(MIEX, 40 + (32 << !!A_IsUnicode)), MIEX, 0, "UInt")
   If DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", &MIEX) {
      MonName := StrGet(&MIEX + 40, 32)    ; CCHDEVICENAME = 32
      MonNum := RegExReplace(MonName, ".*(\d+)$", "$1")
      Return {Name:      (Name := StrGet(&MIEX + 40, 32))
            , Num:       RegExReplace(Name, ".*(\d+)$", "$1")
            , Left:      NumGet(MIEX, 4, "Int")    ; display rectangle
            , Top:       NumGet(MIEX, 8, "Int")    ; "
            , Right:     NumGet(MIEX, 12, "Int")   ; "
            , Bottom:    NumGet(MIEX, 16, "Int")   ; "
            , WALeft:    NumGet(MIEX, 20, "Int")   ; work area
            , WATop:     NumGet(MIEX, 24, "Int")   ; "
            , WARight:   NumGet(MIEX, 28, "Int")   ; "
            , WABottom:  NumGet(MIEX, 32, "Int")   ; "
            , Primary:   NumGet(MIEX, 36, "UInt")} ; contains a non-zero value for the primary monitor.
   }
   Return False
}
Sample:

Code: Select all

#NoEnv
#Include MDMF.ahk
Monitors := MDMF_Enum()
Gui, Margin, 20, 20
Gui, +OwnDialogs
Gui, Add, ListView, w660 r10 Grid, HMON|Num|Name|Primary|Left|Top|Right|Bottom|WALeft|WATop|WARight|WABottom
For HMON, M In Monitors {
   LV_Add("", HMON, M.Num, M.Name, M.Primary, M.Left, M.Top, M.Right, M.Bottom, M.WALeft, M.WATop, M.WARight, M.WABottom)
}
Loop, % LV_GetCount("Column")
   LV_ModifyCol(A_Index, "AutoHdr")
Gui, Show, ,Monitors
Return
GuiClose:
ExitApp
In case that similar has been already written: I didn't search and didn't copy.
User avatar
hoppfrosch
Posts: 328
Joined: 07 Oct 2013, 04:05
GitHub: hoppfrosch
Location: Rhine-Maine-Area, Hesse, Germany
Contact:

Re: [LIB] MDMF - Multiple Display Monitor Functions

18 Sep 2014, 05:15

Funny - recently working on something similar: https://github.com/hoppfrosch/AHK_EDE/b ... torEnv.ahk (as part of a bigger project)
iPhilip
Posts: 317
Joined: 02 Oct 2013, 12:21

Re: [LIB] MDMF - Multiple Display Monitor Functions

08 Feb 2016, 20:58

Hi just me,

Thank you for this library. I am finding it useful is dealing with code that I want to run under both AHK v1.1 and v2. I wanted to point out that these two lines in the function MDMF_GetInfo():

Code: Select all

      MonName := StrGet(&MIEX + 40, 32)    ; CCHDEVICENAME = 32
      MonNum := RegExReplace(MonName, ".*(\d+)$", "$1")
appear to be redundant. The information is duplicated in the Return statement.

Also, I see a difference in the output between v1.1 and v2 and I am curious if you noticed it as well. The StrGet function to extract MonName doesn't seem to end the string properly under v2.0-a073.

I would appreciate any insight into this issue.
Windows 7 Pro (64 bit) - AutoHotkey v1.1+ (Unicode 32-bit)
just me
Posts: 5660
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [LIB] MDMF - Multiple Display Monitor Functions

09 Feb 2016, 03:32

Hi iPhilip,

I don't use v2 as long as it's alpha and subject of breaking changes. If I'm still active when it will become beta, I'll test my important scripts for compatibility.
guest3456
Posts: 2472
Joined: 09 Oct 2013, 10:31

Re: [LIB] MDMF - Multiple Display Monitor Functions

17 Nov 2016, 22:59

I've added your library funcs to the Gdip_All lib for AHK v2 and also some wrapper funcs. Do these look ok?

Code: Select all

GetMonitorCount()
{
   Monitors := MDMF_Enum()
   for k,v in Monitors
      count := A_Index
   return count
}

GetMonitorInfo(MonitorNum)
{
   Monitors := MDMF_Enum()
   for k,v in Monitors
      if (v.Num = MonitorNum)
         return v
}

GetPrimaryMonitor()
{
   Monitors := MDMF_Enum()
   for k,v in Monitors
      return v.Primary
}

just me
Posts: 5660
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [LIB] MDMF - Multiple Display Monitor Functions

18 Nov 2016, 03:35

I'm not sure about the last one. IMO, it should be

Code: Select all

GetPrimaryMonitor()
{
   Monitors := MDMF_Enum()
   for k,v in Monitors
      If (v.Primary)
         return v.Num
}
just me
Posts: 5660
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [LIB] MDMF - Multiple Display Monitor Functions

18 Nov 2016, 09:15

Hi guest3456,

here's an alternate version of the MDMF lib which might fit better. It is'nt tested thoroughly because I don't have a multi monitor environment.

Code: Select all

; ==================================================================================================================================
; Multiple Display Monitors Functions -> msdn.microsoft.com/en-us/library/dd145072(v=vs.85).aspx ===================================
; ==================================================================================================================================
; Enumerates display monitors and returns an object containing the properties of all monitors or the specified monitor.
; ==================================================================================================================================
MDMF_Enum(MonNum := "") { ; MonNum is the monitor number.
   Static EnumProc := RegisterCallback("MDMF_EnumProc")
   Static Monitors := {Count: 0, Primary: 0}
   If (MonNum = "") ; new enumeration
      Monitors := {Count: 0, Primary: 0}
   If (Monitors.MaxIndex() = "") ; enumerate
      If !DllCall("User32.dll\EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", EnumProc, "Ptr", &Monitors, "UInt")
         Return False
   Return (MonNum = "") ? Monitors : Monitors.HasKey(MonNum) ? Monitors[MonNum] : False
}
; ==================================================================================================================================
;  Retrieves the handle of the display monitor that has the largest area of intersection with a specified window.
; ==================================================================================================================================
MDMF_FromHWND(HWND) {
   Return DllCall("User32.dll\MonitorFromWindow", "Ptr", HWND, "UInt", 0, "UPtr")
}
; ==================================================================================================================================
; Retrieves the handle of thedisplay monitor that contains a specified point.
; If either X or Y is empty, the function will use the current cursor position for this value.
; ==================================================================================================================================
MDMF_FromPoint(X := "", Y := "") {
   VarSetCapacity(PT, 8, 0)
   If (X = "") || (Y = "") {
      DllCall("User32.dll\GetCursorPos", "Ptr", &PT)
      If (X = "")
         X := NumGet(PT, 0, "Int")
      If (Y = "")
         Y := NumGet(PT, 4, "Int")
   }
   Return DllCall("User32.dll\MonitorFromPoint", "Int64", (X & 0xFFFFFFFF) | (Y << 32), "UInt", 0, "UPtr")
}
; ==================================================================================================================================
; Retrieves the handle of the display monitor that has the largest area of intersection with a specified rectangle.
; Parameters are consistent with the common AHK definition of a rectangle, which is X, Y, W, H instead of
; Left, Top, Right, Bottom.
; ==================================================================================================================================
MDMF_FromRect(X, Y, W, H) {
   VarSetCapacity(RC, 16, 0)
   NumPut(X, RC, 0, "Int"), NumPut(Y, RC, 4, Int), NumPut(X + W, RC, 8, "Int"), NumPut(Y + H, RC, 12, "Int")
   Return DllCall("User32.dll\MonitorFromRect", "Ptr", &RC, "UInt", 0, "UPtr")
}
; ==================================================================================================================================
; Retrieves information about a display monitor specified by its handle. For internal use only!!!
; ==================================================================================================================================
MDMF_GetInfo(HMON) {
   NumPut(VarSetCapacity(MIEX, 40 + (32 << !!A_IsUnicode)), MIEX, 0, "UInt")
   If DllCall("User32.dll\GetMonitorInfo", "Ptr", HMON, "Ptr", &MIEX) {
      Name := StrGet(&MIEX + 40, 32)
      Return {HMON:      HMON
            , Name:      Name
            , Num:       RegExReplace(Name, ".*(\d+)$", "$1")
            , Left:      NumGet(MIEX, 4, "Int")    ; Display rectangle
            , Top:       NumGet(MIEX, 8, "Int")    ; "
            , Right:     NumGet(MIEX, 12, "Int")   ; "
            , Bottom:    NumGet(MIEX, 16, "Int")   ; "
            , WALeft:    NumGet(MIEX, 20, "Int")   ; Working Area
            , WATop:     NumGet(MIEX, 24, "Int")   ; "
            , WARight:   NumGet(MIEX, 28, "Int")   ; "
            , WABottom:  NumGet(MIEX, 32, "Int")   ; "
            , Primary:   NumGet(MIEX, 36, "UInt")} ; Contains a non-zero value for the primary monitor.
   }
   Return False
}
; ==================================================================================================================================
;  Callback function called by MDMF_Enum function.
; ==================================================================================================================================
MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) {
   Monitors := Object(ObjectAddr)
   MonInfo := MDMF_GetInfo(HMON)
   Monitors.Count++
   If (MonInfo.Primary)
      Monitors.Primary := MonInfo.Num
   Monitors[MonInfo.Num] := MonInfo
   Return True
}
guest3456
Posts: 2472
Joined: 09 Oct 2013, 10:31

Re: [LIB] MDMF - Multiple Display Monitor Functions

18 Nov 2016, 11:54

thanks, i fixed the GetPrimaryMon wrapper. i'll stick with the original for now, but feel free to change it throughout the lib and example files and create a pull request

User avatar
lmstearn
Posts: 162
Joined: 11 Aug 2016, 02:32
GitHub: lmstearn

Re: [LIB] MDMF - Multiple Display Monitor Functions

22 Apr 2017, 09:34

Wondering if MDMF can also be used just to obtain a monitor handle:
Something like:

Code: Select all

MDMF_GetMonHandle(targMonitorNum) ; targMonitorNum obtained from Sysget
{
Static Monitors := {Count: 0, targetMonitorNum: targMonitorNum, targhandle : 0}
If (Monitors.MaxIndex() = "") ; enumerate
Static EnumProc := RegisterCallback("MDMF_EnumProc", "", 4)

If !DllCall("User32.dll\EnumDisplayMonitors", "ptr", 0, "ptr", 0, "ptr", EnumProc, &Monitors, "UInt")
Return False
else
Return Monitors.targhandle
}

MonitorEnumProc(hMonitor, hdcMonitor, lprcMonitor, Monitors)
{
Monitors.Count++
if (Monitors.Count = Monitors.targMonitorNum)
{
Monitors.targhandle := hmonitor
return False ;No more iterations required
}
else
Return True
}
Compatible with the SysGet EnumDisplayDevices monitor iteration, this, at least, is a stepping stone for obtaining physical monitors required for low level calls like e.g. Get Timing Report. Also ref. this thread.
Last edited by lmstearn on 22 Apr 2017, 10:11, edited 1 time in total.
:arrow: itros "ylbbub eht tuO kaerB" a ni kcuts m'I pleH
just me
Posts: 5660
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [LIB] MDMF - Multiple Display Monitor Functions

22 Apr 2017, 10:02

Code: Select all

MDMF_GetHandle(MonitorNum) { ; MonitorNum obtained from Sysget
   For Handle, Info In MDMF_Enum()
      If (Info.Num = MonitorNum)
         Return Handle
   Return False
}
should do it.
RiseUp
Posts: 26
Joined: 01 Oct 2013, 21:27

Re: [LIB] MDMF - Multiple Display Monitor Functions

22 Nov 2017, 17:11

Thank you for this. It should help to give me a jump-start in a project I was considering—a controller for multiple monitors' brightness settings (and possibly other settings) via DDC/CI.
iPhilip
Posts: 317
Joined: 02 Oct 2013, 12:21

Re: [LIB] MDMF - Multiple Display Monitor Functions

09 Jul 2018, 17:10

Hi just me,

Thank you again for you help with these functions. I have a question about a situation where I occasionally get negative values for the monitor handles. See the image below. I went back to read about the RegisterCallback() function and modified the MDMF_EnumProc() function as follows to make sure that the HMON parameter is an unsigned 32-bit integer:

Code: Select all

MDMF_EnumProc(HMON, HDC, PRECT, ObjectAddr) {
   HMON &= 0xFFFFFFFF  ; <<<<<<<<
   Monitors := Object(ObjectAddr)
   Monitors[HMON] := MDMF_GetInfo(HMON)
   Return True
}
Unfortunately, that didn't fix the problem. I am curious if you can help to clarify this issue for me. I suspect it has to do with how the EnumDisplayMonitors function handles the application-defined data (&Monitors) that it passes directly to the MDMF_EnumProc() function.

Thank you,

iPhilip
Attachments
Example of Negative Handles.jpg
Example of Negative Handles.jpg (51.85 KiB) Viewed 135 times
Windows 7 Pro (64 bit) - AutoHotkey v1.1+ (Unicode 32-bit)
just me
Posts: 5660
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [LIB] MDMF - Multiple Display Monitor Functions

11 Jul 2018, 06:11

Hi iPhilip,

what are the actual problems with 'negative values for the monitor handles'?
iPhilip
Posts: 317
Joined: 02 Oct 2013, 12:21

Re: [LIB] MDMF - Multiple Display Monitor Functions

11 Jul 2018, 13:26

just me wrote:Hi iPhilip,

what are the actual problems with 'negative values for the monitor handles'?
Hi just me,

Thank you for replying.

In principle, I don't see a problem with negative handle values since a negative value is simply an interpretation of a binary number. Part of my confusion came about because several of your functions in the library return a "UPtr" value. Thus, while the monitor handle is reported as a negative value in the Monitors array, other library functions, for example MDMF_FromHWND(HWND) report a positive value. I fixed that by simply changing the "UPtr" string to "Ptr". For example,

Code: Select all

MDMF_FromHWND(HWND) {
   Return DllCall("User32.dll\MonitorFromWindow", "Ptr", HWND, "UInt", 0, "Ptr")
}
Does that make sense to you?

My other curiousity/confusion has to do with why a change in the value of HMON in the MDMF_EnumProc() function, as in the statement

Code: Select all

   HMON &= 0xFFFFFFFF
doesn't get reflected in the Monitors array. I suspect I am in need of some education so if you can help me understand that I would appreciate it.

- iPhilip
Windows 7 Pro (64 bit) - AutoHotkey v1.1+ (Unicode 32-bit)
just me
Posts: 5660
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [LIB] MDMF - Multiple Display Monitor Functions

12 Jul 2018, 05:17

Hi iPhilip,

it seems to be an AHK 32-bit issue, caused by
Objects/Basic Usage/Remarks/Keys wrote:Some limitations apply to which values can be used as keys in objects created with [], {} or the new operator:
  • Integer keys are stored using the native signed integer type. AutoHotkey 32-bit supports integer keys in the range -2147483648 to 2147483647. ...
AHK v1.1 32-bit does not support unsigned integer keys as yet. This has been changed in v2.
iPhilip
Posts: 317
Joined: 02 Oct 2013, 12:21

Re: [LIB] MDMF - Multiple Display Monitor Functions

19 Jul 2018, 17:24

Hi just me,

I did some further testing and I agree with you. It has to do with the AutoHotkey 32-bit limitation on integer keys. An AHK v2 version of your library eliminates the issue. Still, it works quite nicely as it stands.

Thank you for your support and for showing me how to use the EnumDisplayMonitors function in the OP.

Cheers!

- iPhilip
Windows 7 Pro (64 bit) - AutoHotkey v1.1+ (Unicode 32-bit)

Return to “Scripts and Functions”

Who is online

Users browsing this forum: No registered users and 23 guests