I have an application where I do not want the user to be able to close the form. Okay, easy enough. Just set ControlBox = false for the form.
However, I would like to keep the application icon in the upper left corner of the form. Minor I know, but details mean something to me.
Setting the Controlbox = false also makes the application's icon go away in the upper left corner of the form. Is there a way around this??
Here is the code I used.
My VB.Net version of it.
Private Const CP_NOCLOSE_BUTTON As Integer = &H200
Protected Overrides ReadOnly Property CreateParams() As Windows.Forms.CreateParams
Get
Dim mdiCp As Windows.Forms.CreateParams = MyBase.CreateParams
mdiCp.ClassStyle = mdiCp.ClassStyle Or CP_NOCLOSE_BUTTON
Return mdiCp
End Get
End Property
I needed an option that would disable the close conditionally (like the standard MessageBox does for a YesNo question) so the accepted answer wouldn't work for me or possibly I failed to see how I could get it to work. I ended up with this
Private Const MF_BYPOSITION As Int32 = &H400
Private Const MF_REMOVE As Int32 = &H1000
Private Declare Auto Function GetSystemMenu Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal bRevert As Int32) As IntPtr
Private Declare Auto Function GetMenuItemCount Lib "user32.dll" (ByVal hMenu As IntPtr) As Int32
Private Declare Function DrawMenuBar Lib "user32.dll" (ByVal hwnd As IntPtr) As Boolean
Private Declare Auto Function RemoveMenu Lib "user32.dll" (ByVal hMenu As IntPtr, ByVal nPosition As Int32, ByVal wFlags As Int32) As Int32
Public Sub DisableCloseButton(ByVal hwnd As IntPtr)
Dim hMenu As IntPtr, n As Int32
hMenu = GetSystemMenu(hwnd, 0)
If Not hMenu.Equals(IntPtr.Zero) Then
n = GetMenuItemCount(hMenu)
If n > 0 Then
RemoveMenu(hMenu, n - 1, MF_BYPOSITION Or MF_REMOVE)
RemoveMenu(hMenu, n - 2, MF_BYPOSITION Or MF_REMOVE)
DrawMenuBar(hwnd)
End If
End If
End Sub
Call it via
DisableCloseButton(MyForm.Handle)
As I was using it for a custom message box, I didn't test how to re-enable.
Related
Hi All,
I have been working on a couple user forms within Excel VBA for a few days now along with assistance from another gentleman on an issue. Whilst working on them I realised it's a nightmare as I can't click off of them onto my spreadsheet, which is what has lead me to ask the question; Is there a way to create a minimize button next to the close icon at the top right of the userform? I would prefer this to just a random button, but if either is possible, would anyone be able to assist. I have already attempted it .hide, but that doesn't appear to work.
If it helps, I am using Excel 2010
Thanks in advance :)
Try like this:-
Private Sub UserForm_Initialize()
InitMaxMin Me.Caption
End Sub
Put This code in module
Option Explicit
Public Declare Function FindWindowA& Lib "user32" (ByVal lpClassName$, ByVal lpWindowName$)
Public Declare Function GetWindowLongA& Lib "user32" (ByVal hwnd&, ByVal nIndex&)
Public Declare Function SetWindowLongA& Lib "user32" (ByVal hwnd&, ByVal nIndex&, ByVal dwNewLong&)
' Déclaration des constantes
Public Const GWL_STYLE As Long = -16
Public Const WS_MINIMIZEBOX = &H20000
Public Const WS_MAXIMIZEBOX = &H10000
Public Const WS_FULLSIZING = &H70000
'Attention, envoyer après changement du caption de l'UF
Public Sub InitMaxMin(mCaption As String, Optional Max As Boolean = True, Optional Min As Boolean = True _
, Optional Sizing As Boolean = True)
Dim hwnd As Long
hwnd = FindWindowA(vbNullString, mCaption)
If Min Then SetWindowLongA hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) Or WS_MINIMIZEBOX
If Max Then SetWindowLongA hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) Or WS_MAXIMIZEBOX
If Sizing Then SetWindowLongA hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) Or WS_FULLSIZING
End Sub
I can't figure out how to make my ComboBox (which lists all open windows(I can see these already)) to make my form type on that selected window. I just can't make it so when screen1 is selected, it clicks on that screen.
Example:
I have 2 different forms open.
Form (1) clicks on a specified screen. (screen1)
Form (2) clicks on a different specified screen. (screen2)
The forms are the same program just opened twice.
I want my form to be able to click on a certain screen even if it's not open.
Imports System.Runtime.InteropServices
Imports System.Diagnostics
Public Class W1
Public Declare Function EnumWindows Lib "user32.dll" (ByVal lpEnumFunc As EnumWindowsProc, ByVal lParam As Int32) As Int32
Public Declare Function IsWindowVisible Lib "user32.dll" (ByVal hwnd As IntPtr) As Boolean
Public Delegate Function EnumWindowsProc(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Boolean
Public Declare Function GetWindowText Lib "user32.dll" Alias "GetWindowTextA" (ByVal hwnd As IntPtr, ByVal lpString As String, ByVal cch As Int32) As Int32
Public Declare Function GetWindowTextLength Lib "user32.dll" Alias "GetWindowTextLengthA" (ByVal hwnd As IntPtr) As Int32
Public Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hwnd As IntPtr, ByVal nIndex As Int32) As Int32
Public Declare Function GetParent Lib "user32.dll" (ByVal intptr As IntPtr) As IntPtr
Private Declare Function GetWindowRect Lib "user32.dll" (ByVal hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
Public Property SelectedItem As Object
Public Const GWL_HWNDPARENT As Int32 = -8
Private newwindowlist As List(Of String)
Private newhandlelist As List(Of IntPtr)
Private Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
Public Sub New(ByVal _left As Integer, ByVal _top As Integer, ByVal _right As Integer, ByVal _bottom As Integer)
left = _left
top = _top
right = _right
bottom = _bottom
End Sub
End Structure
Private Function EnumWinProc(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Boolean
If IsWindowVisible(hwnd) Then
If GetParent(hwnd) = IntPtr.Zero Then
If GetWindowLong(hwnd, GWL_HWNDPARENT) = 0 Then
Dim str As String = String.Empty.PadLeft(GetWindowTextLength(hwnd) + 1)
GetWindowText(hwnd, str, str.Length)
If Not String.IsNullOrEmpty(str.Substring(0, str.Length - 1)) Then
newwindowlist.Add(str.Substring(0, str.Length - 1))
End If
End If
End If
End If
EnumWinProc = True
End Function
Private Sub RefreshWindowList()
newwindowlist = New List(Of String)
EnumWindows(AddressOf EnumWinProc, CInt(True))
End Sub
And
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
RefreshWindowList()
For Each item As String In newwindowlist
ComboBox1.Items.Add(item)
ComboBox1.SelectedItem = Convert.ToInt64(GWL_HWNDPARENT)
Next
End Sub
I'm working on a VB.Net project where in part of it I catch a pop up message box when it's displayed and handle it in some way.
My problem is that I have to know what buttons are included in this pop up window (their captions mainly).
Is this possible? Can someone please tell me how this can be done? A sample would be much appreciated.
Thanks.
Update:
Since I got a down vote, and Ed Cottrell told me that this is because of not adding any code related to my question. Now I have the answer to my question so I'm adding some code:
My app catches a pop-up window displayed by another windows application, I used EnumWindows API to know when a new pop-up window is displayed.
Public Declare Function EnumWindows Lib "User32.dll" (ByVal WNDENUMPROC As EnumWindowDelegate, ByVal lparam As IntPtr) As Boolean
Delegate Function EnumWindowDelegate(ByVal hWnd As IntPtr, ByVal Lparam As IntPtr) As Boolean
When I catch this window, I use its handle that I got from EnumWindows result and get its child windows using EnumChildWindows (which are the controls I'm looking for , since controls are kind of windows too):
APIs I used
<DllImport("User32.dll")> _
Public Function EnumChildWindows _
(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, _
ByVal lParam As IntPtr) As Boolean
End Function
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
' Get window text length signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As Int32
' Get window text signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As StringBuilder) As Int32
Structure ApiWindow
Public Structure ApiWindow
Public MainWindowTitle As String
Public ClassName As String
Public hWnd As Int32
End Structure
Functions
Public Function GetChildWindows(ByVal ParentHandle As IntPtr) As IntPtr()
Dim ChildrenList As New List(Of IntPtr)
Dim ListHandle As GCHandle = GCHandle.Alloc(ChildrenList)
Try
EnumChildWindows(ParentHandle, AddressOf EnumWindow, GCHandle.ToIntPtr(ListHandle))
Finally
If ListHandle.IsAllocated Then ListHandle.Free()
End Try
Return ChildrenList.ToArray
End Function
Public Function EnumWindow(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
Dim ChildrenList As List(Of IntPtr) = GCHandle.FromIntPtr(Parameter).Target
If ChildrenList Is Nothing Then Throw New Exception("GCHandle Target could not be cast as List(Of IntPtr)")
ChildrenList.Add(Handle)
Return True
End Function
Now when I have the handle of pop-up window (parentHandle), I can get its children windows:
Dim hndls() As IntPtr = GetChildWindows(parentHandle)
Dim window As ApiWindow
For Each hnd In hndls
window = GetWindowIdentification(hnd)
'Add Code Here
Next
Where GetWindowIdentification is:
''' <summary>
''' Build the ApiWindow object to hold information about the Window object.
''' </summary>
Public Function GetWindowIdentification(ByVal hwnd As Integer) As ApiWindow
Const WM_GETTEXT As Int32 = &HD
Const WM_GETTEXTLENGTH As Int32 = &HE
Dim window As New ApiWindow()
Dim title As New StringBuilder()
' Get the size of the string required to hold the window title.
Dim size As Int32 = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0)
' If the return is 0, there is no title.
If size > 0 Then
title = New StringBuilder(size + 1)
SendMessage(hwnd, WM_GETTEXT, title.Capacity, title)
End If
' Set the properties for the ApiWindow object.
window.MainWindowTitle = title.ToString()
window.hWnd = hwnd
Return window
End Function
I found this answer on the net on another website, I thought I should share it in case someone sees this question in the future:
Buttons are nothing but more windows. You just look for more child
windows of the message box window you found. You can NOT just get
the handle from a window and then try to cast it to a Form object and
expect all the properties to work. It just doesn't work that way.
Lets say I have two windows form applications written in vb.NET, App1 and App2.
App1 and 2 are two completely separated solutions.
Is it possible to share a panel(or another container) in App1 so that App2 can control the content of this panel?
By control the content i mean add textboxs and buttons that will trigger events in App2.
We have perform that duty before at my enterprise and we use some WinAPI to communicate between windows:
You have to DLLImport the following user32.dll functions:
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, _
ByVal lParam As IntPtr) As Integer
You use the SendMessage function from App1 to send the message to App2.
You can listen to Window messages overriding the WndProc Sub in the App2 application:
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_COPYDATA Then
'Read the message and perform some task,
'Create textboxes, modify controls, whatever
Else
MyBase.WndProc(m)
End If
End Sub
An example to send the Window Message through the Application 1 would be the following:
Dim hWnd As IntPtr
Dim mCopyData As COPYDATASTRUCT
hWnd = FindWindow(Nothing, App2WindowName)
Dim message As New System.Text.StringBuilder
If (hWnd <> 0) Then
message.Append(Mensaje)
Dim pCopyData As IntPtr = Marshal.AllocHGlobal(message.Length() + 40)
mCopyData.lpData = Marshal.StringToHGlobalAnsi(message.ToString)
mCopyData.cbData = message.Length
mCopyData.dwData = _messageID
Marshal.StructureToPtr(mCopyData, pCopyData, False)
SendMessage(hWnd, WM_COPYDATA, sender.Handle, pCopyData)
Marshal.FreeHGlobal(mCopyData.lpData)
Marshal.FreeHGlobal(pCopyData)
End If
You need to declare in your code the COPYDATASTRUCT to use the winapi:
<StructLayout(LayoutKind.Sequential)> _
Private Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cbData As Integer
Public lpData As IntPtr
End Structure
and the WM_COPYDATE message integer:
Const WM_COPYDATA As Integer = 74
I want to send "{TAB}" Key to another application window(send the key to the window not to textbox).
I tried:
SendMessage(hWnd, WM_SETHOTKEY, VK_TAB, 0)
Nothing happened.
my goal is:
send tab key to my application Or other application when the application window is not in focus.
(i know that sendkey is not professional in this case there is no choice(This is the first time that I'm using it).)
I made many attempts and I always returned to the same result:
Nothing happened.
Does anyone know the answer?
SendKeys requires the application that you are sending the Keys to, to be active.
From above Link:
Use SendKeys to send keystrokes and keystroke combinations to the active application.
I order to get around this limitation you will have to resort to using the WinApi Functions.
FindWindow pInvoke.net
FindWindowEx pInvoke.net
sendMessage pInvoke.net
See this MSDN Forum Post for an example
Here is a modified example from that Posting:
Public Class Form1
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd As IntPtr, ByVal hWndChildAfterA As IntPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As IntPtr
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
Const WM_SETTEXT As Integer = &HC
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim destination As IntPtr = FindWindow(Nothing, "Untitled - Notepad")
Dim destControl As IntPtr = FindWindowEx(destination, IntPtr.Zero, "Edit", Nothing)
SendMessage(destControl, WM_SETTEXT, IntPtr.Zero, "Hello" & vbTab & "GoodBye" & vbCrLf)
End Sub
End Class
Added an Additional Example using WM_KEYDOWN I created another small application with the Window Title set to TestForm and overrode the WndProc Method to determine if the application got the TabKey.
Sending Form
Public Class Form1
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
Const WM_KEYDOWN As Integer = &H100
Const VK_TAB As Integer = &H9
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim destination As IntPtr = FindWindow(Nothing, "TestForm")
SendMessage(destination, WM_KEYDOWN, VK_TAB, 0)
End Sub
End Class
Test Form
Put a breakpoint on MyBase.WndProc(m) and look at m to see what has been sent.
Public Class Form1
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
MyBase.WndProc(m)
End Sub
End Class
Having struggled with this type of this a few times before, i would suggest a couple of things to look at.
The 1st is autoit which includes a dll you can reference from vb.net, and is very simple you use, and well documented. I tend to use that whenever i need to control a 3rd party program.
The other is the ui automation classes
See this for an example:
http://blog.functionalfun.net/2009/06/introduction-to-ui-automation-with.html
you need make the other window active first. check Change focus to another window in VB.NET . then use send key.