Hotkey doesn't work while interface is minimized - vb.net

I created a simple auto clicker in visual basic using a timer and a couple of buttons.
I assigned keybinds to my start and stop buttons but they only work when the interface is open, and I want to use them while the program is minimized.
How might I go about doing that? Below is some of the more important code for context. If you need more information just let me know.
Declare Sub mouse_event Lib "user32.dll" Alias "mouse_event" (ByVal dwFlags As Int32, ByVal dx As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo As Int32, v As Integer)
Private Sub frmAutoClicker_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles MyBase.KeyPress
If e.KeyChar = Microsoft.VisualBasic.ChrW(Keys.Z) Then
btnStart.PerformClick()
End If
End Sub
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
Timer1.Start()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
mouse_event(&H2, 0, 0, 0, 1)
mouse_event(&H4, 0, 0, 0, 1)
End Sub

You could try SetWindowsHookEx from user32.dll, it'a an aggressive method... but it will work.. (search SetWindowsHookEx in this forum...)
Or you couldtry to add a message filter to the application:
Application.AddMessageFilter

You can use RegisterHotkey to implement VB.NET Detecting Keypress While Minimized.
It is the first step to know the Virtual-Key Codes.
In order to facilitate the test, I set the automatic click limit of Timer1_Tick, which is limited to 50 times.
Sample code:
Imports System.Runtime.InteropServices
Public Class frmAutoClicker
Public Const WM_HOTKEY As Integer = &H312
Public Const VK_1 As Integer = &H5A '0x5A Z key
Dim i As Integer = 0
<DllImport("User32.dll")>
Public Shared Function RegisterHotKey(ByVal hwnd As IntPtr,
ByVal id As Integer, ByVal fsModifiers As Integer,
ByVal vk As Integer) As Integer
End Function
<DllImport("User32.dll")>
Public Shared Function UnregisterHotKey(ByVal hwnd As IntPtr,
ByVal id As Integer) As Integer
End Function
Declare Sub mouse_event Lib "user32.dll" Alias "mouse_event" (ByVal dwFlags As Int32, ByVal dx As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo As Int32, v As Integer)
Private Sub frmAutoClicker_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim retVal1 As Boolean = RegisterHotKey(Me.Handle, 10, 0, VK_1)
If retVal1 = False Then
MsgBox("The hotkeys could not be registered!", MsgBoxStyle.Critical)
Application.Exit()
End If
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_HOTKEY Then
Dim id As IntPtr = m.WParam
Select Case (id.ToString)
Case "10"
btnStart.PerformClick()
End Select
End If
MyBase.WndProc(m)
End Sub
Private Sub frmAutoClicker_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
UnregisterHotKey(Me.Handle, 10)
End Sub
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
Timer1.Start()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If i > 50 Then
Timer1.Stop()
i = 0
Else
mouse_event(&H2, 0, 0, 0, 1)
mouse_event(&H4, 0, 0, 0, 1)
End If
i = i + 1
End Sub
End Class
Result:

Related

How to resize a parent form and panel to fit a child window

I use the code below to start a cmd.exe windows and move it into a panel after execution. At this moment, the child window is resized to fit into Panel1 on my Form1. I would like it to be the other way around: my Form1 (and thus Panel1) should resize to fit the size of the child window, so no matter what the size of the default cmd.exe window is on the local computer.
Does anybody know how I should do that?
Thanks for any help in advance!
Kind regards,
Eric
Imports System.Runtime.InteropServices
Public Class Form1
Private WithEvents Tmr As New Timer With {.Interval = 100}
Private Const HWND_BOTTOM As Integer = &H1
Private WithEvents proc As New Process
<DllImport("user32.dll", EntryPoint:="SetParent")>
Private Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="SetWindowPos")>
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Text = "My title"
proc.EnableRaisingEvents = True
proc.StartInfo.FileName = "cmd"
proc.Start()
Tmr.Start()
End Sub
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
If SetParent(proc.MainWindowHandle, Panel1.Handle) <> IntPtr.Zero Then
Tmr.Stop()
SetWindowPos(proc.MainWindowHandle, New IntPtr(HWND_BOTTOM), 0, 0, Panel1.ClientSize.Width, Panel1.ClientSize.Height, 0)
End If
End Sub
Private Sub Proc_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles proc.Exited
Invoke(Sub() Close())
End Sub
End Class
As suggested by #Jimi, I used DwmGetWindowAttribute to retrieve the size of the child, before moving it into the panel. Afterwards I use ShowWindow to maximize it, to get rid of the borders.
The changed code can be found in a different post of mine.

Unable to find RECT size with DwmGetWindowAttribute

I'm trying to find to find the size of the cmd.exe window which is started as a child. I like to use this size to resize my form accordingly. For some reason the size returned by DwmGetWindowAttribute is always zero, so I must be doing something wrong here, but I can't find it. Any help will be greatly appreciated.
Kind regards,
Eric
Imports System.Runtime.InteropServices
Public Class Form1
Private WithEvents Tmr As New Timer With {.Interval = 100}
Private Const HWND_BOTTOM As Integer = &H1
Private WithEvents proc As New Process
Public Const DWMWA_EXTENDED_FRAME_BOUNDS As Integer = 9
<DllImport("user32.dll", EntryPoint:="SetParent")>
Private Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="SetWindowPos")>
Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("dwmapi.dll")>
Shared Function DwmGetWindowAttribute(ByVal hwnd As IntPtr, ByVal dwAttribute As Integer, ByRef pvAttribute As RECT, ByVal cbAttribute As Integer) As Integer
End Function
Public Structure RECT
Public left, top, right, bottom As Integer
End Structure
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Text = "My title"
proc.EnableRaisingEvents = True
proc.StartInfo.FileName = "cmd"
proc.Start()
Tmr.Start()
End Sub
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
If SetParent(proc.MainWindowHandle, Panel1.Handle) <> IntPtr.Zero Then
Tmr.Stop()
Dim Width As Integer
Dim Hight As Integer
Dim WindowRect As New RECT
DwmGetWindowAttribute(proc.MainWindowHandle, DWMWA_EXTENDED_FRAME_BOUNDS, WindowRect, Marshal.SizeOf(WindowRect))
Width = WindowRect.right - WindowRect.left
Hight = WindowRect.bottom - WindowRect.top
MessageBox.Show("Hight: " & Hight & " Width: " & Width)
'Me.Size = New Size(Width, Hight)
SetWindowPos(proc.MainWindowHandle, New IntPtr(HWND_BOTTOM), 0, 0, Panel1.ClientSize.Width, Panel1.ClientSize.Height, 0)
End If
End Sub
Private Sub Proc_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles proc.Exited
Invoke(Sub() Close())
End Sub
End Class
After implementing all valuable additions of #Jimi, this is the code that works:
Imports System.Runtime.InteropServices
Public Class Form1
Private WithEvents proc As New Process
Public Const WM_NCLBUTTONDOWN As Long = &HA1
Public Const SW_SHOWMAXIMIZED As UInt32 = 3
Public Const WM_CLOSE = &H10
Public Const DWMWA_EXTENDED_FRAME_BOUNDS As Integer = 9
'Function to set the parent window
Private Declare Function SetParent Lib "user32" (ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
'Function to set the child window position
Private Declare Function SetWindowPos Lib "user32" (ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
'Function to allow the child to be maximized
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal nCmdShow As Int32) As Boolean
'Function to retrieve the initail child size
Private Declare Function DwmGetWindowAttribute Lib "dwmapi" (ByVal hwnd As IntPtr, ByVal dwAttribute As Integer, ByRef pvAttribute As RECT, ByVal cbAttribute As Integer) As Integer
'Function to set focus to the child window
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As IntPtr) As Long
'Function used to set terminate child window
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
'Rectangle size, used later to resize the form
Public Structure RECT
Public left, top, right, bottom As Integer
End Structure
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Text = "My title"
proc.EnableRaisingEvents = True
proc.StartInfo.FileName = "cmd"
proc.Start()
End Sub
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Shown
'Wait for 200 ms for the form to load
proc.WaitForExit(200)
Dim WindowRect As New RECT
Dim Width, Hight As Integer
'Retrieve the initial size of the child window
DwmGetWindowAttribute(proc.MainWindowHandle, DWMWA_EXTENDED_FRAME_BOUNDS, WindowRect, Marshal.SizeOf(WindowRect))
Width = WindowRect.right - WindowRect.left
Hight = WindowRect.bottom - WindowRect.top + 23
'Set the form size to the initial size of the child window
Me.Size = New Size(Width, Hight)
'When the child is started, move the child into the panel and maximize it
If SetParent(proc.MainWindowHandle, Panel1.Handle) <> IntPtr.Zero Then
SetWindowPos(proc.MainWindowHandle, IntPtr.Zero, 0, 0, Width, Height, 0)
ShowWindow(proc.MainWindowHandle, SW_SHOWMAXIMIZED)
End If
End Sub
'Exit form when child terminates
Private Sub Proc_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles proc.Exited
Invoke(Sub() Close())
End Sub
'Set focus on child when the form is activated
Private Sub Form1_UnFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
SetForegroundWindow(proc.MainWindowHandle)
End Sub
'Set focus on child when the parent titlebar is clicked
Protected Overrides Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
If CLng(m.Msg) = WM_NCLBUTTONDOWN Then
SetForegroundWindow(proc.MainWindowHandle)
End If
MyBase.DefWndProc(m)
End Sub
'Properly terminate child when the form is closed by the user
Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Closed
SendMessage(proc.MainWindowHandle, WM_CLOSE, 0, 0)
End Sub
End Class
Kind regards,
Eric

VB.Net Hidden Program Hotkeys

Is there a way to register hotkeys to toggle a form from an invisible(hidden) program? I’ve tired normal methods and they only work when the form is either visible, or the active window. Thank you in advance for any help!
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.KeyPreview = True
Me.ShowInTaskbar = False
Me.ShowIcon = False
Me.Visible = False
End Sub
This is the code that hides the program.
You want to use Global Hotkeys. Just make sure you unregister when the program closes.
From a MSDN article that helped me in the past:
Firstly, you need to know the Virtual-Key Codes.
http://msdn2.microsoft.com/en-us/library/ms927178.aspx You can then
P/Invoke RegisterHotKey/UnregisterHotKey APIs to register/Unregister
the hotkey. Code sample: Register multiple hotkeys such as Alt+D,
Alt+C, etc.
Imports System.Runtime.InteropServices
Public Class Form1
Public Const MOD_ALT As Integer = &H1 'Alt key
Public Const WM_HOTKEY As Integer = &H312
<DllImport("User32.dll")> _
Public Shared Function RegisterHotKey(ByVal hwnd As IntPtr, _
ByVal id As Integer, ByVal fsModifiers As Integer, _
ByVal vk As Integer) As Integer
End Function
<DllImport("User32.dll")> _
Public Shared Function UnregisterHotKey(ByVal hwnd As IntPtr, _
ByVal id As Integer) As Integer
End Function
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
RegisterHotKey(Me.Handle, 100, MOD_ALT, Keys.D)
RegisterHotKey(Me.Handle, 200, MOD_ALT, Keys.C)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_HOTKEY Then
Dim id As IntPtr = m.WParam
Select Case (id.ToString)
Case "100"
MessageBox.Show("You pressed ALT+D key combination")
Case "200"
MessageBox.Show("You pressed ALT+C key combination")
End Select
End If
MyBase.WndProc(m)
End Sub
Private Sub Form1_FormClosing(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.FormClosingEventArgs) _
Handles MyBase.FormClosing
UnregisterHotKey(Me.Handle, 100)
UnregisterHotKey(Me.Handle, 200)
End Sub
End Class

Hotkeys not working anymore

I was using hot keys, but out of nowhere, they are not working anymore. It's very confusing.
It happened when my friend checked my code through team-viewer. Then it stopped working.
Public Class Form2
Public Const MOD_ALT As Integer = &H1 'Alt key
Public Const WM_HOTKEY As Integer = &H312
<DllImport("User32.dll")> _
Public Shared Function RegisterHotKey(ByVal hwnd As IntPtr, _
ByVal id As Integer, ByVal fsModifiers As Integer, _
ByVal vk As Integer) As Integer
End Function
<DllImport("User32.dll")> _
Public Shared Function UnregisterHotKey(ByVal hwnd As IntPtr, _
ByVal id As Integer) As Integer
End Function
Private Sub Form2_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
RegisterHotKey(Me.Handle, 100, MOD_ALT, Keys.E)
RegisterHotKey(Me.Handle, 200, MOD_ALT, Keys.A)
Me.TopMost = True
Me.ShowInTaskbar = False
Me.TransparencyKey = Me.BackColor
Dim leftpos As Long
Dim toppos As Long
leftpos = (My.Computer.Screen.WorkingArea.Right - 2) - Me.Width
toppos = (My.Computer.Screen.WorkingArea.Bottom - 2) - Me.Height
Me.Location = New Point(leftpos, toppos)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_HOTKEY Then
Dim id As IntPtr = m.WParam
Select Case (id.ToString)
Case "100"
Application.Exit()
Case "200"
Form3.Show()
End Select
End If
MyBase.WndProc(m)
End Sub
Private Sub Form2_FormClosing(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.FormClosingEventArgs) _
Handles MyBase.FormClosing
UnregisterHotKey(Me.Handle, 100)
UnregisterHotKey(Me.Handle, 200)
End Sub
Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown
If e.KeyCode = Keys.Enter Then
Form3.Show()
Form3.Activate()
End If
End Sub
End Class
i fixed some api function signatures with the help of a good guy IronRazer in dreamincode as per the below
<DllImport("user32.dll", EntryPoint:="RegisterHotKey")> _
Private Shared Function RegisterHotKey(ByVal hWnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll", EntryPoint:="UnregisterHotKey")> _
Private Shared Function UnregisterHotKey(ByVal hWnd As IntPtr, ByVal id As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
and it worked fine.

Hotkeys in Visual Basic 2010: one works, one does not

I'm creating an "autoclicker" for use in video games that left clicks my mouse repeatedly at the push of a button. I want to have hotkeys set up that work globally without the autoclicker window being in focus. For some reason, only the hotkey that is meant to stop the clicking is currently functioning (F2). The F1 key is supposed to start the clicking, but currently the only way to start it is by pressing the Start button on the form manually. The code seems to throw an error when attempting to be deployed to version 4.0 of the .NET framework, but setting it to 3.5 solves this issue. I've included my entire code below because I can't figure out why the F1 hotkey is not functioning despite the F2 hotkey working flawlessly. Thanks for your time. A good portion of this code I came up with by looking at examples of other peoples' work, and I only understand it to a certain extent. I hope my code is formatted correctly because I'm new to this site and I don't exactly understand how to paste it correctly. Thanks for your help in advance, I really appreciate it.
Public Class Form1
Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vkey As Long) As Integer
Private Declare Sub mouse_event Lib "user32.dll" (ByVal dwflags As Long, ByVal dx As Long, ByVal cbuttons As Long, ByVal dy As Long, ByVal dwExtraInfo As Long)
Dim hotkey1 As Boolean
Dim hotkey2 As Boolean
Private Const mouseclickup = 4
Private Const mouseclickdown = 2
Dim Test As Integer
Dim Interval As Integer
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Click.Start()
End Sub
Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
Click.Stop()
End Sub
Private Sub btnTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnTest.Click
Test = Test + 1
Counter.Text = Test
End Sub
Private Sub Click_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Click.Tick
Interval = CInt(timeTextbox.Text)
Click.Interval = Interval
mouse_event(mouseclickdown, 0, 0, 0, 0)
mouse_event(mouseclickup, 0, 0, 0, 0)
hotkey1 = GetAsyncKeyState(Keys.F1)
If hotkey1 = True Then
btnStart.PerformClick()
End If
hotkey2 = GetAsyncKeyState(Keys.F2)
If hotkey2 = True Then
btnStop.PerformClick()
End If
End Sub
Private Sub timeTextbox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timeTextbox.TextChanged
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
KeyPreview = True
End Sub
End Class
Your declarations were for VB6. The data types need to be changed for VB.Net as shown below.
Also, you need two independent timers. One for detecting the keypress, which has to run at a high rate. And another for doing the actual clicking, whose rate is determined by your TextBox value.
Public Class Form1
Private Const MOUSEEVENTF_LEFTDOWN As Integer = &H2
Private Const MOUSEEVENTF_LEFTUP As Integer = &H4
Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As System.Windows.Forms.Keys) As Short
Private Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Integer, _
ByVal dx As Integer, ByVal dy As Integer, ByVal cButtons As Integer, _
ByVal dwExtraInfo As Integer)
Private WithEvents KeyTimer As New Timer
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
KeyTimer.Interval = 50
KeyTimer.Start()
Click.Enabled = False
End Sub
Private Sub KeyTimer_Tick(sender As Object, e As System.EventArgs) Handles KeyTimer.Tick
If IsKeyDown(Keys.F1) Then
Click.Start()
ElseIf IsKeyDown(Keys.F2) Then
Click.Stop()
End If
End Sub
Private Sub btnTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnTest.Click
Static Test As Integer
Test = Test + 1
Counter.Text = Test
End Sub
Private Sub timeTextbox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timeTextbox.TextChanged
Dim Interval As Integer
If Integer.TryParse(timeTextbox.Text, Interval) Then
Click.Interval = Interval
End If
End Sub
Private Sub Click_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Click.Tick
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
End Sub
Private Function IsKeyDown(ByVal key As System.Windows.Forms.Keys) As Boolean
Return ((GetAsyncKeyState(key) And &H8000) = &H8000)
End Function
End Class