Are VBA userforms always child of Windows desktop?

R

RB Smissaert

Had a look at some code in the the book of Bullen et Al, Professional Excel
Development.
I thought that it was suggested that VBA Userforms are the children of the
Excel
desktop window, but it appears that this is not the case and that these
windows are the
direct children of the Windows desktop.
Is this always the case?
I am interested in this as I need the form's hwnd.
I used to do this with the FindWindow API and this only needs the form's
class name
(ThunderDFrame or ThunderXFrame) and the form's caption.
Possibly it is faster (not tested yet) to get this hwnd with the
FindWindowEx API, but then
I need to know for sure that the Windows desktop is indeed always the parent
of this form.
This is directly after the window is created as I know I can alter the
parent window of a form.
Thanks for any advice/insight.

RBS
 
P

Peter T

Hi Bart,

Whenever I've looked, which is not that often, the Userform's window's
parent has always been the Desktop. But to check perhaps -

Private Declare Function FindWindowA Lib "user32" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function GetAncestor Lib "user32.dll" ( _
ByVal hwnd As Long, ByVal gaFlags As Long) As Long

Private Declare Function GetDesktopWindow Lib "user32" () As Long
Const GA_PARENT = 1

Private Sub UserForm_Activate()
Dim hwnMe&, hwnParent&, hwnDesktop&

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnParent = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
MsgBox hwnMe & vbCr & hwnParent & vbCr & hwnDesktop

End Sub
as I know I can alter the parent window of a form.

Do you know if it's possible to make a borderless/captionless VB6 form the
child of a userform, so it will remain withing the userform ?

Regards,
Peter T
 
P

Peter T

A couple more to add to that Activate event

Private Declare Function GetParent Lib "user32" ( _
ByVal hwnd As Long) As Long


Dim hwnOwner&, hwnXL&
' code
hwnXL = FindWindowA("XLMAIN", Application.Caption)
hwnOwner = GetParent(hwnMe)

Regards,
Peter T
 
R

RB Smissaert

Hi Peter,

Not sure so sure now what the parent window is.
Spy++ always says it is the Windows desktop, but
running this code makes me doubt now:

Option Explicit
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetActiveWindow Lib "user32" () As Long
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Private Declare Function GetWindowThreadProcessId _
Lib "user32" _
(ByVal hwnd As Long, _
ByRef lpdwProcessId As Long) As Long
Private Declare Function FindWindowEx Lib "user32" _
Alias "FindWindowExA" _
(ByVal hWnd1 As Long, _
ByVal hWnd2 As Long, _
ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function FindWindow _
Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long
Private lExcelHwnd As Long

Function GetExcelHwnd() As Long

'---------------------------------------------------------
'Finds a top-level window of the given class and
'caption that belongs to this instance of Excel,
'by matching the process IDs
'sClass The window class name to look for
'sCaption The window caption to look for
'Returns: Long The handle of Excel's main window
'---------------------------------------------------------
Dim hWndDesktop As Long
Dim hwnd As Long
Dim hProcThis As Long
Dim hProcWindow As Long
Dim sClass As String
Dim sCaption As String

If Val(Application.Version) >= 10 Then
GetExcelHwnd = Application.hwnd
Exit Function
End If

sClass = "XLMAIN"
sCaption = Application.Caption

'All top-level windows are children of the desktop,
'so get that handle first
hWndDesktop = GetDesktopWindow 'isn't this always 0?

'Get the ID of this instance of Excel, to match
hProcThis = GetCurrentProcessId

Do
'Find the next child window of the desktop that
'matches the given window class and/or caption.
'The first time in, hWnd will be zero, so we'll get
'the first matching window. Each call will pass the
'handle of the window we found the last time, thereby
'getting the next one (if any)
hwnd = FindWindowEx(hWndDesktop, hwnd, sClass, sCaption)

'Get the ID of the process that owns the window we found
GetWindowThreadProcessId hwnd, hProcWindow

'Loop until the window's process matches this process,
'or we didn't find the window
Loop Until hProcWindow = hProcThis Or hwnd = 0

GetExcelHwnd = hwnd

End Function

Function GetUserFormHwnd(strWindowCaption As String) As Long

Dim strFormType As String

'Determine the form type
If Val(Application.Version) >= 9 Then
strFormType = "ThunderDFrame"
Else
strFormType = "ThunderXFrame"
End If

'Find the userform window
GetUserFormHwnd = FindWindowEx(GetDesktopWindow, 0, _
strFormType, strWindowCaption)

End Function

Function GetWorkbookHwnd(lXLHwnd As Long, _
strWindowCaption As String) As Long

Dim lHwndXLDesk As Long

If lXLHwnd = 0 Then
lXLHwnd = GetExcelHwnd()
lExcelHwnd = lXLHwnd
End If

'Find the Excel desktop
lHwndXLDesk = FindWindowEx(lXLHwnd, 0, "XLDESK", vbNullString)

'Find the Workbook window
GetWorkbookHwnd = FindWindowEx(lHwndXLDesk, 0, _
"EXCEL7", strWindowCaption)

End Function

Sub test()

Dim lHwnd As Long
Dim lHwndParent As Long
Dim lHwndDesktop As Long
Dim lHwndWorkbook As Long
Dim MyStr As String

MyStr = String(100, Chr$(0))

Load UserForm1
UserForm1.Show 0

lExcelHwnd = GetExcelHwnd()
lHwnd = GetUserFormHwnd(UserForm1.Caption)
lHwndParent = GetParent(lHwnd)
lHwndDesktop = GetDesktopWindow()
lHwndWorkbook = GetWorkbookHwnd(lExcelHwnd, ThisWorkbook.Name)

GetWindowText lHwndParent, MyStr, 100

MyStr = Left$(MyStr, InStr(MyStr, Chr$(0)) - 1)

MsgBox "Hwnd of the userform: " & lHwnd & vbCrLf & _
"Hwnd of the Windows desktop: " & lHwndDesktop & vbCrLf & _
"Hwnd of the parent of the userform: " & lHwndParent & vbCrLf & _
"Hwnd of the active workbook: " & lHwndWorkbook & vbCrLf & _
"Text of the parent window: " & MyStr, , _
"windows related to this userform"

End Sub

Do you know if it's possible to make a borderless/captionless VB6 form the
child of a userform, so it will remain withing the userform

I would think so, but will need to test.

RBS
 
R

RB Smissaert

Can find the Userform Hwnd from the active workbook window as well:

Option Explicit
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetActiveWindow Lib "user32" () As Long
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Private Declare Function GetWindowThreadProcessId _
Lib "user32" _
(ByVal hwnd As Long, _
ByRef lpdwProcessId As Long) As Long
Private Declare Function FindWindowEx Lib "user32" _
Alias "FindWindowExA" _
(ByVal hWnd1 As Long, _
ByVal hWnd2 As Long, _
ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function FindWindow _
Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long

'Stop flashing. The system restores the window to its original state.
Const FLASHW_STOP = 0
Const FLASHW_CAPTION = &H1 'Flash the window caption.
Const FLASHW_TRAY = &H2 'Flash the taskbar button.
'Flash both the window caption and taskbar button.
'This is equivalent to setting the FLASHW_CAPTION Or FLASHW_TRAY flags.
Const FLASHW_ALL = (FLASHW_CAPTION Or FLASHW_TRAY)
'Flash continuously, until the FLASHW_STOP flag is set.
Const FLASHW_TIMER = &H4
'Flash continuously until the window comes to the foreground.
Const FLASHW_TIMERNOFG = &HC
Private Type FLASHWINFO
cbSize As Long
hwnd As Long
dwFlags As Long
uCount As Long
dwTimeout As Long
End Type
Private Declare Function FlashWindowEx Lib "user32" _
(pfwi As FLASHWINFO) As Boolean
Private lExcelHwnd As Long

Function GetExcelHwnd() As Long

'---------------------------------------------------------
'Finds a top-level window of the given class and
'caption that belongs to this instance of Excel,
'by matching the process IDs
'sClass The window class name to look for
'sCaption The window caption to look for
'Returns: Long The handle of Excel's main window
'---------------------------------------------------------
Dim hWndDesktop As Long
Dim hwnd As Long
Dim hProcThis As Long
Dim hProcWindow As Long
Dim sClass As String
Dim sCaption As String

If Val(Application.Version) >= 10 Then
GetExcelHwnd = Application.hwnd
Exit Function
End If

sClass = "XLMAIN"
sCaption = Application.Caption

'All top-level windows are children of the desktop,
'so get that handle first
hWndDesktop = GetDesktopWindow 'isn't this always 0?

'Get the ID of this instance of Excel, to match
hProcThis = GetCurrentProcessId

Do
'Find the next child window of the desktop that
'matches the given window class and/or caption.
'The first time in, hWnd will be zero, so we'll get
'the first matching window. Each call will pass the
'handle of the window we found the last time, thereby
'getting the next one (if any)
hwnd = FindWindowEx(hWndDesktop, hwnd, sClass, sCaption)

'Get the ID of the process that owns the window we found
GetWindowThreadProcessId hwnd, hProcWindow

'Loop until the window's process matches this process,
'or we didn't find the window
Loop Until hProcWindow = hProcThis Or hwnd = 0

GetExcelHwnd = hwnd

End Function

Function GetUserFormHwnd(strWindowCaption As String, _
Optional lParentHwnd As Long = -1) As Long

Dim strFormType As String
Dim lStartWindowHwnd As Long

'Determine the form type
If Val(Application.Version) >= 9 Then
strFormType = "ThunderDFrame"
Else
strFormType = "ThunderXFrame"
End If

If lParentHwnd = -1 Then
lStartWindowHwnd = GetDesktopWindow()
End If

'Find the userform window
GetUserFormHwnd = FindWindowEx(lStartWindowHwnd, 0, _
strFormType, strWindowCaption)

End Function

Function GetWorkbookHwnd(lXLHwnd As Long, _
strWindowCaption As String) As Long

Dim lHwndXLDesk As Long

If lXLHwnd = 0 Then
lXLHwnd = GetExcelHwnd()
lExcelHwnd = lXLHwnd
End If

'Find the Excel desktop
lHwndXLDesk = FindWindowEx(lXLHwnd, 0, "XLDESK", vbNullString)

'Find the Workbook window
GetWorkbookHwnd = FindWindowEx(lHwndXLDesk, 0, _
"EXCEL7", strWindowCaption)

End Function

Sub test()

Dim lHwndUserForm As Long
Dim lHwndParent As Long
Dim lHwndDesktop As Long
Dim lHwndWorkbook As Long
Dim MyStr As String
Dim FlashInfo As FLASHWINFO

MyStr = String(100, Chr$(0))

Load UserForm1
UserForm1.Show 0

lExcelHwnd = GetExcelHwnd()
lHwndWorkbook = GetWorkbookHwnd(lExcelHwnd, ThisWorkbook.Name)
lHwndUserForm = GetUserFormHwnd(UserForm1.Caption, lHwndWorkbook)
lHwndParent = GetParent(lHwndUserForm)
lHwndDesktop = GetDesktopWindow()

GetWindowText lHwndParent, MyStr, 100

MyStr = Left$(MyStr, InStr(MyStr, Chr$(0)) - 1)

MsgBox "Hwnd of the userform: " & lHwndUserForm & vbCrLf & _
"Hwnd of the Windows desktop: " & lHwndDesktop & vbCrLf & _
"Hwnd of the parent of the userform: " & lHwndParent & vbCrLf & _
"Hwnd of the active workbook: " & lHwndWorkbook & vbCrLf & _
"Text of the parent window: " & MyStr, , _
"windows related to this userform"

'Specifies the size of the structure.
FlashInfo.cbSize = Len(FlashInfo)
'Specifies the flash status
FlashInfo.dwFlags = FLASHW_ALL Or FLASHW_TIMER
'Specifies the rate, in milliseconds, at which the window will be flashed.
'If dwTimeout is zero, the function uses the default cursor blink rate.
FlashInfo.dwTimeout = 0
'Handle to the window to be flashed.
'The window can be either opened or minimized.
FlashInfo.hwnd = lHwndUserForm
'Specifies the number of times to flash the window.
FlashInfo.uCount = 3

'just to make sure we got the right Hwnd
'---------------------------------------
FlashWindowEx FlashInfo

End Sub


In any case it always seems to find the right Hwnd of the Userform.


RBS
 
P

Peter T

Not sure so sure now what the parent window is.

I think there's a difference between Parent, as in Parent/Child, and Owner
(as in belongs to) windows, though they can be the same. I might be wrong in
my way of thinking but I use the GetParent API to get 'owner' and the
GetAncestor API (with GA_PARENT) to get Parent.

I've always found the Parent of a Userform is the Desktop but its Owner is
the XLMAIN window (assuming the Userform is being run in Excel VBA).

I suppose which window you need will depend on the overall objective.

Regards,
Peter T
 
P

Peter T

In any case it always seems to find the right Hwnd of the Userform.

Why might the simple one-liner fail to find the correct Hwnd, particularly
if the Userform's caption is temporarily changed to something unique.

Regards,
Peter T

<snip>
 
R

RB Smissaert

Looks like I may need to read the Appleman API book about this.
I only need this Hwnd of the parent (that seems to be the right word in
this case), owner whatever to
use as the first argument (hwndParent) in FindWindowEx, to get the Hwnd of
the Userform.
I have now used the Hwnd of the Windows desktop, the Active workbook and the
Excel application
for this argument and they all work, so it doesn't seems to matter much.

RBS
 
P

Peter T

The one in my first post <g>

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)

or with your code -

lHwnd_Userform = FindWindow("ThunderDFrame", Userform1.Caption) ' xl9 or
later

Regards,
Peter T
 
R

RB Smissaert

Yes, I suppose there is nothing wrong with that and that is the one I have
been using for years
with no trouble.
Somehow I thought that FindowWindowEx might be a bit faster as it can start
from an initial
window that is not the desktop. Also it is the one that is used in the
Bullen book.
But if the parent of the userform is the desktop then I suppose it can't
make a difference.
So, all in all there probably isn't much in it.

RBS
 
R

RB Smissaert

One (final?) thing about this.
If I run the following code (from a button in the worksheet) directly after
moving from the VBE to
the worksheet then the form doesn't flash. If I then run it again it will
flash. Any idea what that is?

Option Explicit
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetActiveWindow Lib "user32" () As Long
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Private Declare Function GetWindowThreadProcessId _
Lib "user32" _
(ByVal hwnd As Long, _
ByRef lpdwProcessId As Long) As Long
Private Declare Function FindWindowEx Lib "user32" _
Alias "FindWindowExA" _
(ByVal hWnd1 As Long, _
ByVal hWnd2 As Long, _
ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function FindWindow _
Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long
Private Const GA_PARENT As Long = 1
Private Const GA_ROOT As Long = 2
Private Const GA_ROOTOWNER As Long = 3
Private Declare Function GetAncestor Lib "user32.dll" _
(ByVal hwnd As Long, _
ByVal gaFlags As Long) As Long
'Stop flashing. The system restores the window to its original state.
Const FLASHW_STOP = 0
Const FLASHW_CAPTION = &H1 'Flash the window caption.
Const FLASHW_TRAY = &H2 'Flash the taskbar button.
'Flash both the window caption and taskbar button.
'This is equivalent to setting the FLASHW_CAPTION Or FLASHW_TRAY flags.
Const FLASHW_ALL = (FLASHW_CAPTION Or FLASHW_TRAY)
'Flash continuously, until the FLASHW_STOP flag is set.
Const FLASHW_TIMER = &H4
'Flash continuously until the window comes to the foreground.
Const FLASHW_TIMERNOFG = &HC
Private Type FLASHWINFO
cbSize As Long
hwnd As Long
dwFlags As Long
uCount As Long
dwTimeout As Long
End Type
Private Declare Function FlashWindowEx Lib "user32" _
(pfwi As FLASHWINFO) As Boolean
Private lExcelHwnd As Long

Function GetExcelHwnd() As Long

'---------------------------------------------------------
'Finds a top-level window of the given class and
'caption that belongs to this instance of Excel,
'by matching the process IDs
'sClass The window class name to look for
'sCaption The window caption to look for
'Returns: Long The handle of Excel's main window
'---------------------------------------------------------
Dim hWndDesktop As Long
Dim hwnd As Long
Dim hProcThis As Long
Dim hProcWindow As Long
Dim sClass As String
Dim sCaption As String

If Val(Application.Version) >= 10 Then
GetExcelHwnd = Application.hwnd
Exit Function
End If

sClass = "XLMAIN"
sCaption = Application.Caption

'All top-level windows are children of the desktop,
'so get that handle first
hWndDesktop = GetDesktopWindow 'isn't this always 0?

'Get the ID of this instance of Excel, to match
hProcThis = GetCurrentProcessId

Do
'Find the next child window of the desktop that
'matches the given window class and/or caption.
'The first time in, hWnd will be zero, so we'll get
'the first matching window. Each call will pass the
'handle of the window we found the last time, thereby
'getting the next one (if any)
hwnd = FindWindowEx(hWndDesktop, hwnd, sClass, sCaption)

'Get the ID of the process that owns the window we found
GetWindowThreadProcessId hwnd, hProcWindow

'Loop until the window's process matches this process,
'or we didn't find the window
Loop Until hProcWindow = hProcThis Or hwnd = 0

GetExcelHwnd = hwnd

End Function

Function GetUserFormHwnd(strWindowCaption As String, _
Optional lParentHwnd As Long = -1) As Long

Dim strFormType As String
Dim lStartWindowHwnd As Long

'Determine the form type
If Val(Application.Version) >= 9 Then
strFormType = "ThunderDFrame"
Else
strFormType = "ThunderXFrame"
End If

If lParentHwnd = -1 Then
lStartWindowHwnd = GetDesktopWindow()
End If

'Find the userform window
GetUserFormHwnd = FindWindowEx(lStartWindowHwnd, 0, _
strFormType, strWindowCaption)

End Function

Function GetWorkbookHwnd(lXLHwnd As Long, _
strWindowCaption As String) As Long

Dim lHwndXLDesk As Long

If lXLHwnd = 0 Then
lXLHwnd = GetExcelHwnd()
lExcelHwnd = lXLHwnd
End If

'Find the Excel desktop
lHwndXLDesk = FindWindowEx(lXLHwnd, 0, "XLDESK", vbNullString)

'Find the Workbook window
GetWorkbookHwnd = FindWindowEx(lHwndXLDesk, 0, _
"EXCEL7", strWindowCaption)

End Function

Sub test()

Dim lHwndUserForm As Long
Dim lHwndParent As Long
Dim lHwndDesktop As Long
Dim lHwndWorkbook As Long
Dim lHwndGetAncestorGA_PARENT As Long
Dim lHwndGetAncestorGA_ROOT As Long
Dim lHwndGetAncestorGA_ROOTOWNER As Long
Dim MyStr As String
Dim FlashInfo As FLASHWINFO

MyStr = String(100, Chr$(0))

Load UserForm1
UserForm1.Show

If lExcelHwnd = 0 Then
lExcelHwnd = GetExcelHwnd()
End If
lHwndWorkbook = GetWorkbookHwnd(lExcelHwnd, ThisWorkbook.Name)
lHwndUserForm = GetUserFormHwnd(UserForm1.Caption, lExcelHwnd)
lHwndParent = GetParent(lHwndUserForm)
'lHwndUserForm = GetUserFormHwnd(UserForm1.Caption, lHwndParent)
lHwndDesktop = GetDesktopWindow()
lHwndGetAncestorGA_PARENT = GetAncestor(lHwndUserForm, GA_PARENT)
lHwndGetAncestorGA_ROOT = GetAncestor(lHwndUserForm, GA_ROOT)
lHwndGetAncestorGA_ROOTOWNER = GetAncestor(lHwndUserForm, GA_ROOTOWNER)

GetWindowText lHwndParent, MyStr, 100

MyStr = Left$(MyStr, InStr(MyStr, Chr$(0)) - 1)

MsgBox "Hwnd of the Excel application: " & lExcelHwnd & vbCrLf & _
"Hwnd of the userform: " & lHwndUserForm & vbCrLf & _
"Hwnd of the Windows desktop: " & lHwndDesktop & vbCrLf & _
"Hwnd of the parent (GetParent API) of the userform: " &
lHwndParent & _
vbCrLf & _
"Hwnd of the active workbook: " & lHwndWorkbook & vbCrLf & vbCrLf &
_
"Text of the parent window: " & MyStr & vbCrLf & vbCrLf & _
"Hwnd obtained with GetAncestor with GA_PARENT: " & _
lHwndGetAncestorGA_PARENT & vbCrLf & _
"Hwnd obtained with GetAncestor with GA_ROOT: " & _
lHwndGetAncestorGA_ROOT & vbCrLf & _
"Hwnd obtained with GetAncestor with GA_ROOTOWNER: " & _
lHwndGetAncestorGA_ROOTOWNER, , _
"windows related to this userform"

'Specifies the size of the structure.
FlashInfo.cbSize = Len(FlashInfo)
'Specifies the flash status
FlashInfo.dwFlags = FLASHW_ALL Or FLASHW_TIMER
'Specifies the rate, in milliseconds, at which the window will be flashed.
'If dwTimeout is zero, the function uses the default cursor blink rate.
FlashInfo.dwTimeout = 0
'Handle to the window to be flashed.
'The window can be either opened or minimized.
FlashInfo.hwnd = lHwndUserForm
'Specifies the number of times to flash the window.
FlashInfo.uCount = 3

'just to make sure we got the right Hwnd
'---------------------------------------
FlashWindowEx FlashInfo

End Sub


RBS
 
P

Peter T

I suppose it might be a nano second faster to use FindowWindowEx to restrict
the search to first level windows from the Desktop -

FindWindowEx(0&, 0&, "ThunderDFrame", UserForm1.Caption)

This of course assumes the Userform's 'parent' is indeed always the desktop,
which I think it is.

Regards,
Peter T
 
R

RB Smissaert

it might be a nano second faster

Yes, you are right.
Not really much to get excited about.

RBS
 
R

RB Smissaert

Yes, I often use SetParent for this reason.

For VBA userforms I can't see a benefit of using FindWindowEx instead of
FindWindow now.

RBS
 
N

NickHK

Peter,
Whilst I would be the first to acknowledge my hazy understanding of Parent
v. Owner, I do notice this:

A quick look with Spy++ shows the owner and parent of a modeless userform to
be XLMAIN.
Not that sure what GetAncestor actually means, but here it returns something
different to GetParent

<From API-guide>
[in] Specifies the ancestor to be retrieved. This parameter can be one of
the following values.
GA_PARENT
Retrieves the parent window. This does not include the owner, as it does
with the GetParent function.
</From API-guide>

Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long

Private Sub UserForm_Activate()
Dim hwnMe&, hwnAncestor&, hwnDesktop&, hwnParent&, hwnXLMain&
Dim MstStr As String

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnAncestor = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
hwnParent = GetParent(hwnMe)
hwnXLMain = FindWindowA("XLMAIN", Application.Caption)

MstStr = MstStr & "Me: " & hwnMe & vbCr
MstStr = MstStr & "Ancestor: " & hwnAncestor & vbCr
MstStr = MstStr & "Desktop: " & hwnDesktop & vbCr
MstStr = MstStr & "Parent : " & hwnParent & vbCr
MstStr = MstStr & "XLMAIN : " & hwnXLMain

MsgBox MstStr

End Sub

NickHK
 
P

Peter T

Hi Nick,

My terminology in distinguishing parent/owner may have been wrong, as you
say the same, but my badly expressed way of thinking was, in a wrong kind of
way I think, right!

The code you posted below is pretty much the same as combined in my first
and follow-up posts. To me it shows the 'owner' of a userform's window is
the Excel, ie XLMAIN. Yet in a treeview of the windows structure both the
XLMAIN's and Userform's windows are the first level down from the Desktop.
Ie they are both 'Child' windows of the 'Ancestor's desktop.

The ambiguity in my terminology is to think that because the userform is the
child of the desktop, by definition one might think its parent is the
desktop. The Userform's 'owner' XLMAIN is a sibling window, whilst at the
same time is its parent. Confused! reminds me of the introduction to an

I answered my own question, and indeed can 'SetParent' to a VBA Userform.
Sizing and positioning the VB6 form in the Userform seems a bit of a black
art. FWIW, need to size it first before SetParent, assuming the scalemode is
twips -
form.width = userform-dimension-width * 1440 / 72 ' similar for height
SetParent(
form.Left = 0 + left position
form.top = -form.top + top position ' note the -ve top

Trying to set the size after SetParent moves left and/or top in opposite
directions for some reason I don't understand.

Regards,
Peter T



NickHK said:
Peter,
Whilst I would be the first to acknowledge my hazy understanding of Parent
v. Owner, I do notice this:

A quick look with Spy++ shows the owner and parent of a modeless userform to
be XLMAIN.
Not that sure what GetAncestor actually means, but here it returns something
different to GetParent

<From API-guide>
[in] Specifies the ancestor to be retrieved. This parameter can be one of
the following values.
GA_PARENT
Retrieves the parent window. This does not include the owner, as it does
with the GetParent function.
</From API-guide>

Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long

Private Sub UserForm_Activate()
Dim hwnMe&, hwnAncestor&, hwnDesktop&, hwnParent&, hwnXLMain&
Dim MstStr As String

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnAncestor = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
hwnParent = GetParent(hwnMe)
hwnXLMain = FindWindowA("XLMAIN", Application.Caption)

MstStr = MstStr & "Me: " & hwnMe & vbCr
MstStr = MstStr & "Ancestor: " & hwnAncestor & vbCr
MstStr = MstStr & "Desktop: " & hwnDesktop & vbCr
MstStr = MstStr & "Parent : " & hwnParent & vbCr
MstStr = MstStr & "XLMAIN : " & hwnXLMain

MsgBox MstStr

End Sub

NickHK

Peter T said:
Hi Bart,

Whenever I've looked, which is not that often, the Userform's window's
parent has always been the Desktop. But to check perhaps -

Private Declare Function FindWindowA Lib "user32" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function GetAncestor Lib "user32.dll" ( _
ByVal hwnd As Long, ByVal gaFlags As Long) As Long

Private Declare Function GetDesktopWindow Lib "user32" () As Long
Const GA_PARENT = 1

Private Sub UserForm_Activate()
Dim hwnMe&, hwnParent&, hwnDesktop&

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnParent = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
MsgBox hwnMe & vbCr & hwnParent & vbCr & hwnDesktop

End Sub


Do you know if it's possible to make a borderless/captionless VB6 form the
child of a userform, so it will remain withing the userform ?

Regards,
Peter T
 
R

RB Smissaert

It looks Spy++ presents this a bit confusing.
When I load a modeless userform then in Spy++ the treeview suggests that the
form is a child of the desktop.
Also when you open up the hierarchy of XLMain then you I can't find the
form.
But if you right-click the form icon in Spy++ and do properties, windows
then you can see that XLMain is
the parent window.
Exactly same applies to a modal form.

RBS

NickHK said:
Peter,
Whilst I would be the first to acknowledge my hazy understanding of Parent
v. Owner, I do notice this:

A quick look with Spy++ shows the owner and parent of a modeless userform
to
be XLMAIN.
Not that sure what GetAncestor actually means, but here it returns
something
different to GetParent

<From API-guide>
[in] Specifies the ancestor to be retrieved. This parameter can be one of
the following values.
GA_PARENT
Retrieves the parent window. This does not include the owner, as it does
with the GetParent function.
</From API-guide>

Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As
Long

Private Sub UserForm_Activate()
Dim hwnMe&, hwnAncestor&, hwnDesktop&, hwnParent&, hwnXLMain&
Dim MstStr As String

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnAncestor = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
hwnParent = GetParent(hwnMe)
hwnXLMain = FindWindowA("XLMAIN", Application.Caption)

MstStr = MstStr & "Me: " & hwnMe & vbCr
MstStr = MstStr & "Ancestor: " & hwnAncestor & vbCr
MstStr = MstStr & "Desktop: " & hwnDesktop & vbCr
MstStr = MstStr & "Parent : " & hwnParent & vbCr
MstStr = MstStr & "XLMAIN : " & hwnXLMain

MsgBox MstStr

End Sub

NickHK

Peter T said:
Hi Bart,

Whenever I've looked, which is not that often, the Userform's window's
parent has always been the Desktop. But to check perhaps -

Private Declare Function FindWindowA Lib "user32" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function GetAncestor Lib "user32.dll" ( _
ByVal hwnd As Long, ByVal gaFlags As Long) As Long

Private Declare Function GetDesktopWindow Lib "user32" () As Long
Const GA_PARENT = 1

Private Sub UserForm_Activate()
Dim hwnMe&, hwnParent&, hwnDesktop&

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnParent = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
MsgBox hwnMe & vbCr & hwnParent & vbCr & hwnDesktop

End Sub


Do you know if it's possible to make a borderless/captionless VB6 form
the
child of a userform, so it will remain withing the userform ?

Regards,
Peter T
 
N

NickHK

Peter,
Yes, I know just enough about Windows hierarchy/relationships to thorough
confuse myself, and possibly other.
Spy++ has usually been sufficient to tell what I need, but for something
like this, it seems I have actually have to understand it <g>.

NickHK

Peter T said:
Hi Nick,

My terminology in distinguishing parent/owner may have been wrong, as you
say the same, but my badly expressed way of thinking was, in a wrong kind of
way I think, right!

The code you posted below is pretty much the same as combined in my first
and follow-up posts. To me it shows the 'owner' of a userform's window is
the Excel, ie XLMAIN. Yet in a treeview of the windows structure both the
XLMAIN's and Userform's windows are the first level down from the Desktop.
Ie they are both 'Child' windows of the 'Ancestor's desktop.

The ambiguity in my terminology is to think that because the userform is the
child of the desktop, by definition one might think its parent is the
desktop. The Userform's 'owner' XLMAIN is a sibling window, whilst at the
same time is its parent. Confused! reminds me of the introduction to an

I answered my own question, and indeed can 'SetParent' to a VBA Userform.
Sizing and positioning the VB6 form in the Userform seems a bit of a black
art. FWIW, need to size it first before SetParent, assuming the scalemode is
twips -
form.width = userform-dimension-width * 1440 / 72 ' similar for height
SetParent(
form.Left = 0 + left position
form.top = -form.top + top position ' note the -ve top

Trying to set the size after SetParent moves left and/or top in opposite
directions for some reason I don't understand.

Regards,
Peter T



NickHK said:
Peter,
Whilst I would be the first to acknowledge my hazy understanding of Parent
v. Owner, I do notice this:

A quick look with Spy++ shows the owner and parent of a modeless
userform
to
be XLMAIN.
Not that sure what GetAncestor actually means, but here it returns something
different to GetParent

<From API-guide>
[in] Specifies the ancestor to be retrieved. This parameter can be one of
the following values.
GA_PARENT
Retrieves the parent window. This does not include the owner, as it does
with the GetParent function.
</From API-guide>

Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long

Private Sub UserForm_Activate()
Dim hwnMe&, hwnAncestor&, hwnDesktop&, hwnParent&, hwnXLMain&
Dim MstStr As String

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnAncestor = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
hwnParent = GetParent(hwnMe)
hwnXLMain = FindWindowA("XLMAIN", Application.Caption)

MstStr = MstStr & "Me: " & hwnMe & vbCr
MstStr = MstStr & "Ancestor: " & hwnAncestor & vbCr
MstStr = MstStr & "Desktop: " & hwnDesktop & vbCr
MstStr = MstStr & "Parent : " & hwnParent & vbCr
MstStr = MstStr & "XLMAIN : " & hwnXLMain

MsgBox MstStr

End Sub

NickHK

Peter T said:
Hi Bart,

Whenever I've looked, which is not that often, the Userform's window's
parent has always been the Desktop. But to check perhaps -

Private Declare Function FindWindowA Lib "user32" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function GetAncestor Lib "user32.dll" ( _
ByVal hwnd As Long, ByVal gaFlags As Long) As Long

Private Declare Function GetDesktopWindow Lib "user32" () As Long
Const GA_PARENT = 1

Private Sub UserForm_Activate()
Dim hwnMe&, hwnParent&, hwnDesktop&

hwnMe = FindWindowA("ThunderDFrame", Me.Caption)
hwnParent = GetAncestor(hwnMe, GA_PARENT)
hwnDesktop = GetDesktopWindow
MsgBox hwnMe & vbCr & hwnParent & vbCr & hwnDesktop

End Sub

as I know I can alter the parent window of a form.

Do you know if it's possible to make a borderless/captionless VB6 form the
child of a userform, so it will remain withing the userform ?

Regards,
Peter T


Had a look at some code in the the book of Bullen et Al, Professional
Excel
Development.
I thought that it was suggested that VBA Userforms are the children
of
the
Excel
desktop window, but it appears that this is not the case and that these
windows are the
direct children of the Windows desktop.
Is this always the case?
I am interested in this as I need the form's hwnd.
I used to do this with the FindWindow API and this only needs the form's
class name
(ThunderDFrame or ThunderXFrame) and the form's caption.
Possibly it is faster (not tested yet) to get this hwnd with the
FindWindowEx API, but then
I need to know for sure that the Windows desktop is indeed always the
parent
of this form.
This is directly after the window is created as I know I can alter the
parent window of a form.
Thanks for any advice/insight.

RBS
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top