1. Christopher Craft
  2. PowerBuilder
  3. Saturday, 22 January 2022 02:06 AM UTC

I am trying to use the Windows API GetMonitorInfo but my returned structure seems to always be NULL.  I followed this  ( https://rgagnon.com/pbdetails/powerbuilder-display-on-the-second-monitor.html ) as my starting point but it is not working.  The one odd thing is his example has the structure set with 'rect' as the type but that is not a valid entry so I used 'rectangle'.  I also changed the declarations multiple times but still nothing.  

   function Long MonitorFromWindow (Long hwnd, Long dwFlags) LIBRARY "user32.dll"
   function Long GetMonitorInfo (long hMonitor, ref str_monitorinfo moninfo) LIBRARY "user32.dll" Alias for "GetMonitorInfoW"

Has anyone ever used this?  What I am trying to do is get the monitor handle of the frame so I can determine if this monitor is still valid the next time they launch the application. If it is then I restore the position.

Thanks,

Chris Craft

Accepted Answer
John Fauss Accepted Answer Pending Moderation
  1. Saturday, 22 January 2022 04:03 AM UTC
  2. PowerBuilder
  3. # Permalink

The following code will work in both 32-bit and 64-bit:

The s_rect structure:

global type s_rect from structure
  long  l_left    descriptor "comment" = "X-coordinate of upper-left corner of rectangle (can be negative)"
  long  l_top     descriptor "comment" = "Y-coordinate of upper-left corner of rectangle (can be negative)"
  long  l_right   descriptor "comment" = "X-coordinate of lower-right corner of rectangle (can be negative)"
  long  l_bottom  descriptor "comment" = "Y-coordinate of lower-right corner of rectangle (can be negative)"
end type

The s_monitorinfo structure:

global type s_monitorinfo from structure
  unsignedlong  ul_structsize  descriptor "comment" = "The size of this structure, in bytes (always 40)"
  s_rect        str_monitor    descriptor "comment" = "The display monitor rectangle, in virtual screen coordinates"
  s_rect        str_workarea   descriptor "comment" = "The work area rectangle of the display monitor, in virtual screen coordinates"
  unsignedlong  ul_flags       descriptor "comment" = "Bit flags. Bit 1 (xxxx xxx1) = 1 when this is the primary monitor"
end type

External function declarations:

FUNCTION Longptr MonitorFromWindow ( &
   Longptr hWnd, &
   UnsignedLong dwFlags &
   ) LIBRARY "user32.dll"

FUNCTION Boolean GetMonitorInfo ( &
   Longptr hMonitor , &
   REF s_monitorinfo str_MonitorInfo ) &
   LIBRARY "user32.dll" ALIAS FOR "GetMonitorInfoW"

Code that calls the MonitorFromWindow and GetMonitorInfo API functions:

Constant UnsignedLong MONITOR_DEFAULTTONULL    = 0
Constant UnsignedLong MONITOR_DEFAULTTOPRIMARY = 1
Constant UnsignedLong MONITOR_DEFAULTTONEAREST = 2

Boolean lb_rc
Longptr llptr_hwnd, llptr_monitor
Long    ll_monitor_x, ll_monitor_y, &
        ll_monitor_right, ll_monitor_bottom, &
        ll_monitor_width, ll_monitor_height
s_monitorinfo lstr_mi

llptr_hwnd    = Handle(Parent)
llptr_monitor = MonitorFromWindow(llptr_hwnd,MONITOR_DEFAULTTONEAREST)

lstr_mi.ul_structsize = 40  // size of s_monitor structure in memory: 4 + (4+4+4+4) + (4+4+4+4) + 4
lb_rc = GetMonitorInfo(llptr_monitor,lstr_mi)

ll_monitor_x      = lstr_mi.str_monitor.l_left
ll_monitor_y      = lstr_mi.str_monitor.l_top
ll_monitor_right  = lstr_mi.str_monitor.l_right
ll_monitor_bottom = lstr_mi.str_monitor.l_bottom
ll_monitor_width  = ll_monitor_right  - ll_monitor_x
ll_monitor_height = ll_monitor_bottom - ll_monitor_y

MessageBox('Monitor Information', 'Position = ('+String(ll_monitor_x)+','+ &
           String(ll_monitor_y)+')~r~n~r~n'+ &
           'Width = '+String(ll_monitor_width)+'~r~n' + &
           'Height = '+String(ll_monitor_height))

Sample result:

 

 

Comment
  1. John Fauss
  2. Saturday, 22 January 2022 20:58 PM UTC
You're welcome!

Potentially, yes, that MIGHT NOT work in 64-bit, as all Windows handles are 64-bit integers in a 64-bit process. True, I've never seen a 64-bit Windows handle that would not fit entirely in a 32-bit integer, but there is no guarantee that will always be the case.

Since there is no Longptr(string) conversion function in PowerScript, you would need to ensure you are saving/retrieving a 64-bit integer into the Registry as a numeric value when the app runs as 64-bit, or use the LongLong(string) PowerScript function if you choose to save the monitor handle as a string in the Registry.. Rather than go to all the trouble and add bitness-dependent code to your app, I suggest you always perform the MonitorFromWindow API function, as it is very efficient and will always return the monitor handle in the proper bitness as long as you use the Longptr (pseudo-)datatype in 64-bit. Using the Handle(window) PowerScript function is also very efficient.

Something to consider.
  1. Helpful 3
  1. Chris Pollach @Appeon
  2. Saturday, 22 January 2022 21:41 PM UTC
Nice job John! :-)
  1. Helpful
  1. Steen Jakobsen
  2. Tuesday, 1 March 2022 08:37 AM UTC
Thank you John.

That was really helpful. Thank you :-)
  1. Helpful
There are no comments made yet.
Roland Smith Accepted Answer Pending Moderation
  1. Tuesday, 1 March 2022 13:39 PM UTC
  2. PowerBuilder
  3. # 1

I have an example of MonitorFromWindow in this example:

https://www.topwizprogramming.com/freecode_pbeditor.html

See n_appobject.of_IsOnMonitor.

The main window wm_wininichange event calls it. If the window was on monitor 2 the last run and you currently have only 1 monitor, the window will automatically shift to monitor 1.

Comment
There are no comments made yet.
Christopher Craft Accepted Answer Pending Moderation
  1. Thursday, 27 January 2022 01:45 AM UTC
  2. PowerBuilder
  3. # 2

Thank you guys for all your help!  Thought I would post this so if anyone else needs the same functionality. There are a couple calls that are in my framework but I can send that if anyone wants it.  The key to this is in the Close event of my w_frame I always save the frames position. This makes it so I do not need to call the GetMonitorInfo and do a bunch of those other calcs. Anyway, here it is:

Open event of w_frame

...

// Restore Position if Key Exists!
IF App.iuRegistry.GetFramePosition(lWinState, llX, llY, llWidth, llHeight) = 1 THEN
   // Move the Frame so we can check to see if the position is on a Monitor
   IF This.Move(llX, llY) = 1 THEN
      lptrMonitor = guOS.GetMonitor(Handle(This), MONITOR_DEFAULTTONULL)
      IF lptrMonitor <> 0 AND (NOT IsNull(lptrMonitor)) THEN
         lbPositionRestored = TRUE
         CHOOSE CASE lWinState
            CASE Normal! // Just use the stored dimensions!
               This.Resize(llWidth, llHeight)
            CASE Maximized! // If Maximized then Center the Frame on the Monitor first so if user hits 'Restore Down' it will be Centered!
               // We don't need any Monitor Info since we always store the Maximized dimensions but if we ever do then use this call!
               //IF guOS.MonitorInfo(lptrMonitor, lstrMonitor) THEN
               //END IF
               // Use the stored dimensions to calculate the center!
               llX = llX + ((llWidth / 2) - (This.Width / 2))
               llY = llY + ((llHeight / 2) - (This.Height / 2))
               This.Move(llX, llY)
               WindowState = lWinState
            CASE ELSE // Should never get here but in case we do just use the Default!
               lbPositionRestored = FALSE
         END CHOOSE
      END IF
   END IF
END IF

IF NOT lbPositionRestored THEN
   // Default
   WindowState = Maximized!
END IF

...

 

Comment
  1. John Fauss
  2. Thursday, 27 January 2022 04:50 AM UTC
Thank you for sharing your code, Chris!
  1. Helpful
  1. Chris Pollach @Appeon
  2. Thursday, 27 January 2022 14:39 PM UTC
That is what the STD framework does Chris. You do not need to know the Monitor as saving the MDI frame's location/size and then restoring those upon restart is all you need. That's been working for me solid for about a decade with no issues. However (food for thought), what if your App is not an MDI app (ie: SDI or MIMD) and thus, you have no MDI Frame? My framework handles these App styles as well. ;-)
  1. Helpful
There are no comments made yet.
Chris Pollach @Appeon Accepted Answer Pending Moderation
  1. Saturday, 22 January 2022 03:40 AM UTC
  2. PowerBuilder
  3. # 3

Hi Chris;

   My framework uses the follow SDK call ...

FUNCTION Integer GetSystemMetrics ( Integer nIndex ) LIBRARY "USER32.dll"

  The number of monitors is found by ....

ii_number_of_monitors  =  THIS.GetSystemmetrics ( 80 ) 

   The App's monitor can be found as follows:

    Environment   lo_env                                                                                            // Work vars
    GetEnvironment ( lo_env )                                                                                    // Instantiate Env object
    IF  THIS.x > lo_env.screenwidth  THEN                                                                   // Window> Monitor #1 width
       // Monitor   "2."                                                                                                // YES=>Set as #2
    else
       // Monitor   "1."                                                                                                // NO=>Set as #1
    END IF

Just modify the above code to calculate 3 monitors vs 2. Simple, but effective

HTH

Regards ... Chris

Comment
  1. Christopher Craft
  2. Saturday, 22 January 2022 18:51 PM UTC
Hi Chris - My issue is I need to know if the last monitor used for the application is still valid so I can restore it to that monitor.



Example - user has the primary monitor for their docking station set to be one of the docked monitors. By default the application will open to the primary monitor. They then move the frame to the other docked monitor and close. The next time they launch the app it needs to go to that monitor. And then on top of that I also need to know if that monitor is still valid because they could have undocked and the launched the app.
  1. Helpful
  1. Chris Pollach @Appeon
  2. Saturday, 22 January 2022 21:41 PM UTC
ii_number_of_monitors = THIS.GetSystemmetrics ( 80 ) should do it. If monitor #3 is missing, then you can't place your App there. Same for monitor #2. That's what my framework checks.

I like John's solution though as it gives you the dimensions of the monitor. Which would be nice if your trying to center your App. My framework though does not care about that as it tries to restore the App's exact location & size as when it was last used. If the monitor is not there, then it uses Monitor #1 but with the same relative location as on the previous monitor that was last active. ;-)
  1. Helpful
There are no comments made yet.
John Fauss Accepted Answer Pending Moderation
  1. Saturday, 22 January 2022 03:12 AM UTC
  2. PowerBuilder
  3. # 4

32-bit or 64-bit? There is a difference in many WinAPI calls depending on the bitness.

Comment
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.