Hi All,
Here I have a child window such as Microsoft Word window set to a VB Form. The issue is that the Word window is movable inside the form. This is undesirable. The only way I have tried to prevent this movement at the moment was to remove the WS_CAPTION attribute from the window like this:
Code:
dim defaultAttribute as Integer = GetWindowLong(hWnd, GWL_STYLE)
dim preferedAttribute as Integer = defaultAttribute And (Not WS_CAPTION)
SetWindowLong(hWnd, GWL_STYLE, preferedAttribute)
The window can no longer be moved because the Toolbar has been removed. But I need the Toolbar and the window caption in the window. Anybody knows how I can resolve this problem?
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
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
you can render a form immoveable by removing the Move item from the form's System Menu.
don't know if that'll be helpful in your app:
Code:
Public Class Form1
Private Declare Function GetSystemMenu Lib "user32" Alias _
"GetSystemMenu" (ByVal hwnd As IntPtr, _
ByVal bRevert As Integer) As IntPtr
Private Declare Auto Function RemoveMenu Lib "user32.dll" ( _
ByVal hMenu As IntPtr, _
ByVal nPosition As Integer, _
ByVal wFlags As Integer _
) As Integer
Private Const MF_BYPOSITION As Integer = &H400
Private Const MF_REMOVE As Integer = &H1000
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim iMenu As IntPtr = GetSystemMenu(Me.Handle, 0)
RemoveMenu(iMenu, 1, MF_BYPOSITION + MF_REMOVE)
End Sub
End Class
Thank you very much. Your suggestion was exactly what I wanted.
I need to understand the RemoveMenu (...) command. However, I could not find the complete list of the possible flags for this command in MSDN. Where can I see the complete list that includes the MF_REMOVE?
Another thing, in your example; RemoveMenu(iMenu, 1, MF_BYPOSITION + MF_REMOVE) what menu item does the value 1 refer to?
Again, I thank you. I would have given up on my little trivia task even before I started it, but folks like you kept me going. I am almost there.
The code is still having issues loading PowerPoint or Excel files. The program sometimes misses the handles for these particular applications and therefore not position them on the preferred location. Sometimes, it just freezes while loading PowerPoint or Excel. It works satisfactorily when loading Word, Notepad or PDF documents. Perhaps, the Virtual machine I am using is not powerful enough. I will compile the program and run it on a physical, more powerful computer and see what happens.
I ran the program on a physical computer that has 16GB RAM. The program did not catch the handle PowerPoint or Excel and it still got frozen. It worked very well for Word, Notepad and PDF.
The main part of my code is attached here. To run it, just place a command button on a form, copy and paste the code. The click event of the command button will bring up a FileOpen Dialog box to enable the user select the file to open. That's about it.
I will really be happy to know why the program is missing the handles and why it becomes non responsive after the miss.
Please note, I could not insert the whole code as it was more than the stipulated length. The complete version is in the attachment. Thanks.
Code:
Imports System
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Text.RegularExpressions
Public Class Form1
'Declare the required constants
Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2
Private Const GW_HWNDPREV = 3
Private Const GWL_STYLE = (-16)
Private Const WS_VISIBLE = &H10000000
Private Const MAX_LEN = 1024
Private Const GW_OWNER = 4
Private Const SW_HIDE = 0
Private Const SW_SHOWNORMAL = 1
Private Const SW_NORMAL = 1
Private Const SW_SHOWMINIMIZED = 2
Private Const SW_SHOWMAXIMIZED = 3
Private Const SW_MAXIMIZE = 3
Private Const SW_SHOWNOACTIVATE = 4
Private Const SW_SHOW = 5
Private Const SW_MINIMIZE = 6
Private Const SW_SHOWMINNOACTIVE = 7
Private Const SW_SHOWNA = 8
Private Const SW_RESTORE = 9
Private Const SW_SHOWDEFAULT = 10
Private Const SW_MAX = 10
Private Const WM_USER = 1024
Private Const WM_CLOSE = &H10
'Declare the WinAPI
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow(ByVal strClass As String, ByVal lpWindow As String) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal Hwnd As IntPtr, ByVal Msg As IntPtr, ByVal wParam As IntPtr,
ByVal lParam As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SetForegroundWindow(ByVal Hwnd As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal Hwnd As IntPtr, ByVal nCmdShow As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetClassName(<[In]()> ByVal hWnd As IntPtr, <[Out]()> ByVal lpClassName As StringBuilder,
<[In]()> ByVal nMaxCount As Integer) As IntPtr
' Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetDesktopWindow() As IntPtr
' Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetWindow(ByVal Hwnd As IntPtr, ByVal wCmd As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowLong(ByVal Hwnd As IntPtr, ByVal nIndex As Integer) As IntPtr
'Leave empty
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SetWindowLong(ByVal Hwnd As IntPtr, ByVal nIndex As Integer, ByVal dNewValue As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowText(ByVal Hwnd As IntPtr, ByVal lpString As String, ByVal aint As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowThreadProcessId(<[In]()> ByVal hWnd As IntPtr, <[Out]()> ByVal lpdwProcessId As IntPtr) As IntPtr
'Leav empty
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function IsWindowVisible(ByVal hWnd As IntPtr) As Boolean
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function IsWindow(ByVal hWnd As IntPtr) As Boolean
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function IsIconic(ByVal hWnd As IntPtr) As Boolean
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetTopWindow(ByVal hwnd As IntPtr) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Int32,
ByVal Y As Int32, ByVal CX As Int32, ByVal CY As Int32, ByVal wFlags As Int32) As Boolean
'Leave empty
End Function
<DllImport("user32.dll")> _
Public Shared Function MoveWindow(ByVal hWnd As IntPtr, ByVal x As Int32, ByVal y As Int32, ByVal nWidth As Int32,
ByVal nHeight As Int32, ByVal bRepaint As Boolean) As Boolean
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetSystemMenu(ByVal hwnd As IntPtr, ByVal bRevert As Boolean) As IntPtr
'Leave empty
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function RemoveMenu(ByVal hMenu As IntPtr, ByVal nPosition As Integer, ByVal wFlags As Integer) As Boolean
'Leave empty
'http://msdn.microsoft.com/en-us/library/windows/desktop/ms647994(v=vs.85).aspx
'http://msdn.microsoft.com/en-us/library/windows/desktop/ms647993(v=vs.85).aspx
'http://msdn.microsoft.com/en-us/library/windows/desktop/ms646360(v=vs.85).aspx
End Function
'Declare module level variables
Dim application As Process
Dim relativeName2 As String
Dim nameLength As Integer
Dim newHandle As IntPtr
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
StartApplication()
End Sub
Private Function EnumerateOpenedWindows() As Boolean
Dim desktopHandle, windowHandle As IntPtr
Dim caption As String = vbNullString
Dim defaultAttribute As Integer
Dim preferedAttribute As Integer
Dim windowPositionFlag As UInteger
Try
Do
desktopHandle = GetDesktopWindow()
'Get the first (top most) child window on the Desktop
windowHandle = GetWindow(desktopHandle, GW_CHILD)
newHandle = windowHandle
Do
If IsWindowVisible(windowHandle) AndAlso (Not IsIconic(windowHandle)) Then
'Get the file name without extension from the window's caption and compare it with
'the file name without extension of the application that has justed been started.
caption = GetWindowCaption(windowHandle)
If LSet(caption, nameLength) = relativeName2 Then
'set the paste the child form to its container
SetParent(windowHandle, Me.CurrentChildHandle)
'get the default attributes of the application window
defaultAttribute = GetWindowLong(windowHandle, GWL_STYLE)
'Set the prefered attributes of the application window
preferedAttribute = defaultAttribute And
(Not SomeWindowStyles.WS_MAXIMIZEBOX) And (Not SomeWindowStyles.WS_MINIMIZE) And
(Not SomeWindowStyles.WS_MINIMIZEBOX) And (Not SomeWindowStyles.WS_MAXIMIZE)
SetWindowLong(windowHandle, GWL_STYLE, New IntPtr(preferedAttribute))
'Size the window before positioning it.
windowPositionFlag = SetWindowPosFlags.SWP_SHOWWINDOW Or SetWindowPosFlags.SWP_DRAWFRAME Or
SetWindowPosFlags.SWP_FRAMECHANGED Or SetWindowPosFlags.SWP_NOZORDER
'Make the window immovable. Note, the value 1 in the command corresponds with the zero-based
'index of the Move menu on a standard system window.
Dim iMenu As IntPtr = GetSystemMenu(windowHandle, vbFalse)
RemoveMenu(iMenu, 1, MenuFlags.MF_BYPOSITION + MenuFlags.MF_REMOVE)
'Now, set the window's position on its parent.
SetWindowPos(windowHandle, IntPtr.Zero, Me.ClientRectangle.Left,
Me.ClientRectangle.Top, Me.ClientRectangle.Width, Me.ClientRectangle.Height, windowPositionFlag)
'There is no need to continue.
Return True
Exit Function
End If
End If
'Get the next child window of the desktop. The retrieved handle identifies the window just
'below the current window in the Z order.
windowHandle = GetWindow(windowHandle, GW_HWNDNEXT)
newHandle = windowHandle
Loop Until windowHandle = 0 'reached the end of the child windows collection
Loop Until LSet(caption, nameLength) = relativeName2
Catch ex As Exception
MsgBox(ex.Message, vbApplicationModal + vbAbort, "Error!")
'The function has failed
Return False
Exit Function
End Try
'If the execution got to this point, then it was successful.
Return True
End Function
Private Function GetWindowCaption(ByVal winHandle As IntPtr) As String
Dim caption As String = vbNullString
Dim counter As Integer = 0
Try
'The string variable must already have space for the maximum number of characters it is to hold.
caption = StrDup(MAX_LEN - 1, "*")
'The GetWindowText(Hwnd, caption, MAX_LEN) returns a positive value if successful, else it returns 0.
counter = GetWindowText(winHandle, caption, MAX_LEN)
If counter <= 0 Then
GetWindowCaption = vbNullString
Exit Function
End If
GetWindowCaption = Trim(caption)
Catch ex As Exception
MsgBox(ex.Message, vbApplicationModal + vbAbort, "Error!")
Return vbNullString
Exit Function
End Try
End Function
Private Function StartApplication() As Boolean
Dim slashPosition, fullFilePath, relativeName1 As String
Dim lastDotIndex As Integer
Dim fileOpen As New OpenFileDialog()
Dim result As DialogResult = fileOpen.ShowDialog()
'Unless you select a file name or type in a file name, the operation will not continue.
Try
If (result = DialogResult.OK) AndAlso (Trim(fileOpen.FileName).Length > 0) Then
fullFilePath = fileOpen.FileName
'Get the relative file name with extension
slashPosition = fullFilePath.LastIndexOf("\")
relativeName1 = Mid(fullFilePath, slashPosition + 2)
'Not all applications shows relative file name with extension on its window title.
lastDotIndex = relativeName1.LastIndexOf(".")
relativeName2 = LSet(relativeName1, lastDotIndex)
'Get the length of the relativeName2
nameLength = Len(Trim(relativeName2))
'Start the application
application = Process.Start(fullFilePath)
application.WaitForInputIdle()
application.Refresh()
'Walkthrough the opened windows
Call EnumerateOpenedWindows()
'MsgBox("Application will now close", vbApplicationModal + vbOKOnly, "Shutting down!")
'SendMessage(newHandle, WM_CLOSE, 0, 0)
'If execution got to this point, then things must have gone well.
Return True
Exit Function
ElseIf (result = DialogResult.OK) AndAlso (Trim(fileOpen.FileName).Length = 0) Then
MsgBox("You have not selected a file name", vbExclamation + vbOKOnly, "Choose file to open")
Return False
Exit Function
Else
'User cancelled operation. This was not due to a failure of the function.
Return True
Exit Function
End If
Catch ex As Exception
MsgBox(ex.Message, vbApplicationModal + vbOKOnly, "Choose file to open")
Return False
Exit Function
End Try
'If execution got to this point, then things must have gone wrong.
Return False
End Function
Public Enum MenuFlags As Integer
MF_BYPOSITION = &H400
MF_BITMAP = &H4&
MF_STRING = 0
MF_SEPARATOR = 2048
MF_ENABLED = &H0I
MF_DISABLED = &H2I
MF_GRAYED = &H1I
MF_REMOVE = &H1000
WM_SYSCOMMAND = &H112
End Enum
End Class
Hello All,
I eventually found what was wrong with my code. I had assumed that the window text always starts with a file name. While this is true for Word, NotePad, PDF, JPG , etc, it is not the case with Excel, PowerPoint, etc. So, rather than assuming the file name is at the start of the window text, I used Regex to search the entire window text for the file name of the of the document the user has just opened. If a match is found, I use the corresponding window handle to set the application window to the preferred form. The previously attached code was freezing when attempting to set Excel or PowerPoint window to a parent form because it could not find a matching file name.
You may wish to replace the portion:
Code:
If LSet(caption, nameLength) = relativeName2 Then
'set the paste the child form to its container
SetParent(windowHandle, Me.CurrentChildHandle)
'get the default attributes of the application window
defaultAttribute = GetWindowLong(windowHandle, GWL_STYLE)
'Set the prefered attributes of the application window
preferedAttribute = defaultAttribute And
(Not SomeWindowStyles.WS_MAXIMIZEBOX) And (Not SomeWindowStyles.WS_MINIMIZE) And
(Not SomeWindowStyles.WS_MINIMIZEBOX) And (Not SomeWindowStyles.WS_MAXIMIZE)
SetWindowLong(windowHandle, GWL_STYLE, New IntPtr(preferedAttribute))
'Size the window before positioning it.
windowPositionFlag = SetWindowPosFlags.SWP_SHOWWINDOW Or SetWindowPosFlags.SWP_DRAWFRAME Or
SetWindowPosFlags.SWP_FRAMECHANGED Or SetWindowPosFlags.SWP_NOZORDER
'Make the window immovable. Note, the value 1 in the command corresponds with the zero-based
'index of the Move menu on a standard system window.
Dim iMenu As IntPtr = GetSystemMenu(windowHandle, vbFalse)
RemoveMenu(iMenu, 1, MenuFlags.MF_BYPOSITION + MenuFlags.MF_REMOVE)
'Now, set the window's position on its parent.
SetWindowPos(windowHandle, IntPtr.Zero, Me.ClientRectangle.Left,
Me.ClientRectangle.Top, Me.ClientRectangle.Width, Me.ClientRectangle.Height, windowPositionFlag)
'There is no need to continue.
Return True
Exit Function
End If
End If
'Get the next child window of the desktop. The retrieved handle identifies the window just
'below the current window in the Z order.
windowHandle = GetWindow(windowHandle, GW_HWNDNEXT)
newHandle = windowHandle
Loop Until windowHandle = 0 'reached the end of the child windows collection
Loop Until LSet(caption, nameLength) = relativeName2
with:
Code:
dim searchFilenameInCaption as string
If Trim(caption).Length <> 0 Then searchFilenameInCaption = Regex.IsMatch(caption, relativeName2)
If searchFilenameInCaption = vbTrue Then
'set the paste the child form to its container
SetParent(windowHandle, Me.CurrentChildHandle)
'get the default attributes of the application window
defaultAttribute = GetWindowLong(windowHandle, GWL_STYLE)
'Set the prefered attributes of the application window
preferedAttribute = defaultAttribute And
(Not SomeWindowStyles.WS_MAXIMIZEBOX) And (Not SomeWindowStyles.WS_MINIMIZE) And
(Not SomeWindowStyles.WS_MINIMIZEBOX) And (Not SomeWindowStyles.WS_MAXIMIZE)
SetWindowLong(windowHandle, GWL_STYLE, New IntPtr(preferedAttribute))
'Size the window before positioning it.
windowPositionFlag = SetWindowPosFlags.SWP_SHOWWINDOW Or SetWindowPosFlags.SWP_DRAWFRAME Or
SetWindowPosFlags.SWP_FRAMECHANGED Or SetWindowPosFlags.SWP_NOZORDER
'Make the window immovable. Note, the value 1 in the command corresponds with the zero-based
'index of the Move menu on a standard system window.
Dim iMenu As IntPtr = GetSystemMenu(windowHandle, vbFalse)
RemoveMenu(iMenu, 1, MenuFlags.MF_BYPOSITION + MenuFlags.MF_REMOVE)
'Now, set the window's position on its parent.
SetWindowPos(windowHandle, IntPtr.Zero, Me.ClientRectangle.Left,
Me.ClientRectangle.Top, Me.ClientRectangle.Width, Me.ClientRectangle.Height, windowPositionFlag)
'There is no need to continue.
Return True
Exit Function
End If
End If
'Get the next child window of the desktop. The retrieved handle identifies the window just
'below the current window in the Z order.
windowHandle = GetWindow(windowHandle, GW_HWNDNEXT)
newHandle = windowHandle
Loop Until windowHandle = 0 'reached the end of the child windows collection
Loop Until searchFilenameInCaption = vbTrue
and try the code again. Do remember Imports System.Text.RegularExpressions