Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win


GetWindowRect API from within a control

Posted on 1998-11-19
Medium Priority
Last Modified: 2013-12-25
Here's something which is seemingly correct (I think), but it doesn't work.

I've placed the following inside the control. The purpose is to provide a property get to obtain the screen coordinates of the control itself. Ie, coordinates relative to the screen, and not just the container.

Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Dim ControlRect As RECT

Public Property Get X_Left() As Long
    GetWindowRect UserControl.hwnd, ControlRect
    X_Left = ControlRect.Left
End Property

(there are actually 3 other property get for the other 3 values, but it doesn't matter since they are basically the same)


The GetWindowRect API is supposed to give the rect in screen coordinates, but it doesn't seem to work if this is inside the code of a control.

At runtime, reading the property actually returns a 0. Effectively, the 4 values of the rect returns, but the top left hand corner is 0,0. I.e. the rect returned is actually (0,0,something,something). The size of the control is returned correctly, but the position is wrong.

I've tried the same code on a form, but changed 'UserControl' to 'Picture1', and placed a dummy picture control, just to try obtaining the coordinates of the pic control. It works correctly in this case.

I know that by calculating the position of the control relative to the form would pretty much do what I want, but it is a requirement that the algorithm be within the control. I must be able to obtain the screen coord by just reading a control property.

That's my problem and it's giving me hard time. Any help will be greatly appreciated.
Thanks a lot in advance.

Question by:Shak
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions

Expert Comment

ID: 1488381
I think you can't get values from public Properties
I will give you another simple way of solving your problem.
Put your property Private
create global variables or constantes for x and y.

In your private property
assign to x = form.left + control + left, this gives you the position of control relative to screen.

you can do the same for top.

if you need to know the control area you can do the same using the control width and hight.



Expert Comment

ID: 1488382
If you want actual screen coordinates there's more to do than the code that you have.

You'll need to call ClientToScreen to convert the top/left and bottom/right points to screen coordinates.

If you'd like I can provide some sample code.


Author Comment

ID: 1488383
This control is to be used by others, and the user of the control should be able to get the values just be reading a property. Making something global would not be advisable

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!


Author Comment

ID: 1488384
On the comment from kswinney,
I've actually tried out the ClientToScreen API but it doesn't seem to work. This is the new code:


Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function ClientToScreen Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type POINTAPI
        x As Long
        y As Long
End Type

Dim ControlRect As RECT
Dim ControlPnt As POINTAPI

Public Property Get X_Left() As Long
    GetWindowRect UserControl.hwnd, ControlRect
    ControlPnt.x = ControlRect.Left
    ControlPnt.y = ControlRect.Top
    ClientToScreen UserControl.hwnd, ControlPnt
    X_Left = ControlPnt.x
End Property
Public Property Get X_Right() As Long
    GetWindowRect UserControl.hwnd, ControlRect
    ControlPnt.x = ControlRect.Right
    ControlPnt.y = ControlRect.Bottom
    ClientToScreen UserControl.hwnd, ControlPnt
    X_Right = ControlPnt.x
End Property


In this case, I'm just reading the x value of the top-left and bottom-right corner. The y value would be similar.

When the container of this control is ran, the value that is returned from the property get is not in screen coordinates. The 1st x value is always 2 (dunno why), while the 2nd x value reflects the size of the control correctly. If I move the form around the screen, the 2 x values remain the same. They should be different since the control is now on a different part of the screen (though still in the same place on the form). Also, if I place the control on a different part on the form, the x values still remain the same. In effect, the result that I'm getting from ClientToScreen is the same as that from GetWindowRect.

Hope to get any kind of assistance.
Thanks a million in advance.


LVL 13

Expert Comment

ID: 1488385
take a look at the values of  parent.left/top and extender.left/top and convert these values use clienttoscreen.


Author Comment

ID: 1488386
Thanks to Mirkwood for suggesting,

But in this case, your suggestion won't work because I need my control to be invisible at runtime. Thus, the left/top properties can not be retrieved when the container is running.

More description of what my control should be able to do:
It is basically an invisible control that sits on the container, but the container (may be IE script or VB form) should be able to obtain coordinates of the location of the invisible control at runtime. Therefore, the script or form will be able to determine exactly where the control is on the screen, even if the form or IE has been moved around.

Thanks a lot anyway.
LVL 14

Accepted Solution

Matti earned 800 total points
ID: 1488387
I made this so that it can't have focus but this focus thing has no meaning to the code.

Option Explicit
Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Controlrect As RECT
'Default Property Values:
Const m_def_cx = 0
Const m_def_cy = 0
Const m_def_cx1 = 0
Const m_def_cy1 = 0
'Property Variables:
Dim m_cx As Long
Dim m_cy As Long
Dim m_cx1 As Long
Dim m_cy1 As Long

Private Sub Timer1_Timer()
'Timer interval was 20 on first test, but it is better make it adjustable and on/off property is also needed, no need to load the processor if new values are not needed, this timer alow update values when user moves the form. If you don't want timer
solution then make a update property to call this in sub

GetWindowRect UserControl.hwnd, Controlrect
cx = Controlrect.Left
cy = Controlrect.Right
cx1 = Controlrect.Bottom
cy1 = Controlrect.Top
Label1.Caption = Str$(cx) ' It has 4 labels to see the test values all the time these may be removed
Label4.Caption = Str$(cx1)
Label3.Caption = Str$(cy)
Label2.Caption = Str$(cy1)
End Sub
Public Property Get cx() As Long
    cx = m_cx
End Property

Public Property Let cx(ByVal New_cx As Long)
    m_cx = New_cx
    PropertyChanged "cx"
End Property

Public Property Get cy() As Long
    cy = m_cy
End Property

Public Property Let cy(ByVal New_cy As Long)
    m_cy = New_cy
    PropertyChanged "cy"
End Property

Public Property Get cx1() As Long
    cx1 = m_cx1
End Property

Public Property Let cx1(ByVal New_cx1 As Long)
    m_cx1 = New_cx1
    PropertyChanged "cx1"
End Property

Public Property Get cy1() As Long
    cy1 = m_cy1
End Property

Public Property Let cy1(ByVal New_cy1 As Long)
    m_cy1 = New_cy1
    PropertyChanged "cy1"
End Property

'Initialize Properties for User Control
Private Sub UserControl_InitProperties()
    m_cx = m_def_cx
    m_cy = m_def_cy
    m_cx1 = m_def_cx1
    m_cy1 = m_def_cy1
End Sub

'Load property values from storage
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)

    m_cx = PropBag.ReadProperty("cx", m_def_cx)
    m_cy = PropBag.ReadProperty("cy", m_def_cy)
    m_cx1 = PropBag.ReadProperty("cx1", m_def_cx1)
    m_cy1 = PropBag.ReadProperty("cy1", m_def_cy1)
End Sub

'Write property values to storage
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)

    Call PropBag.WriteProperty("cx", m_cx, m_def_cx)
    Call PropBag.WriteProperty("cy", m_cy, m_def_cy)
    Call PropBag.WriteProperty("cx1", m_cx1, m_def_cx1)
    Call PropBag.WriteProperty("cy1", m_cy1, m_def_cy1)
End Sub

'The debuger project code:
This project has a form and four labels and one timer on it when this form is moved by the uses the control place can be seen on labels.

Option Explicit

Private Sub Timer1_Timer()
Label1.Caption = Str(UserControl11.cx)
Label2.Caption = Str(UserControl11.cy)
Label3.Caption = Str(UserControl11.cx1)
Label4.Caption = Str(UserControl11.cy1)
End Sub

'**Extra comments ******************************

I used the VB interface wizard for ActiveX controls to get property codes automaticly.
All return values are now pixels on screen.

I tested this whit VB5 desingtime and exe and Win98  IE 4.72.3110.4  (SP1)

Then I try to chance it similar like your code, it stop working when I remove the timer, it give the first
cordinates only when the window appears your case it is the upper left corner.

Then I released that you never write those properities to properitybag correctly.


Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes some techniques which will make your VBA or Visual Basic Classic code easier to understand and maintain, whether by you, your replacement, or another Experts-Exchange expert.
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…
Suggested Courses

636 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question