I've made a tool that will continuously click for the user if they decide to have it on with toggle keys etc, it all works fine in ordinary windows e.g google chrome, but when it comes to games it doesn't always work correctly.
(well it does in some games, then others it doesn't)
The code is designed to click fast while holding LButton, then stop when it's let go to act as an autoclicker (user has control of speed) which again works, but when in a game it clicks alot slower than it's suppose to / any other window / app.
I've figured out adding a delay using
Thread.Sleep(200)
fixes the speed of the autoclicker in game, but then it messes up the keybind which results in the autoclicker always clicking even when LButton isnt held / pressed.
Is there anything else that I could use as a delay, or anything else I can do to the code so it works correctly?
I've been trying many different variations and searching online the last few days trying to get it working, but none succeeded.
Here's all the code got to do with autoclicking in my project, i've added some notes to try and explain which part is doing what / speeds the timers are set to.
Imports System.Threading
Public Class Form1
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Integer) As Short
Private Declare Sub mouse_event Lib "user32" (ByVal dwflags As Integer, ByVal dx As Integer, ByVal cbuttons As Integer, ByVal dy As Integer, ByVal dwExtraInfo As Integer)
Private Const mouseclickup = 4
Private Const mouseclickdown = 2
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Timer1 isnt doing the clicking, Timer1 is just listening for LButton
'clicks which is why I have it always on aswell as a low interval.
Timer1.Start()
Timer1.Interval = 1
'LButton is the timer that will do the clicking.
LButton.Interval = 100
End Sub
Private Sub LButton_Tick(sender As Object, e As EventArgs) Handles LButton.Tick
If GetAsyncKeyState(Keys.LButton) Then
mouse_event(mouseclickup, 0, 0, 0, 0)
'Without Thread.Sleep(200) the code works as it's suppose to, clicks
'when LButton is held, stops clicking when LButton is let go,
'although without Thread.Sleep(200) it will not work in all games,
'but with it, it will continuously click even when LButton isn't held.
Thread.Sleep(200)
mouse_event(mouseclickdown, 0, 0, 0, 0)
Else
LButton.Stop()
End If
End Sub
Private Sub Timer1_Tick_1(sender As Object, e As EventArgs) Handles Timer1.Tick
'This is what will listen for the left clicks and also stop the left
'LButton timer if LButton is not held
If GetAsyncKeyState(Keys.LButton) Then
LButton.Start()
Else
LButton.Stop()
End If
End Sub
End Class
Quoting the MSDN documentation, GetAsyncKeyState() determines:
whether a key is up or down at the time the function is called, and whether the key was pressed after a previous call to GetAsyncKeyState.
So when you check the function via If GetAsyncKeyState(Keys.LButton) Then, it will return non-zero at least three times, thus execute the code more than you want (which is what you experience when you add Thread.Sleep(200)).
To check if the key is held down you have to check if the most significant bit is set, which for a Short is 0x8000 in hex and 32768 in decimal.
Checking a bit flag is done by checking (<number> And <bit>) = <bit> - where And is the bitwise And operator.
This would result in your code looking like this:
Const KeyDownBit As Integer = &H8000
Private Sub LButton_Tick(sender As Object, e As EventArgs) Handles LButton.Tick
If (GetAsyncKeyState(Keys.LButton) And KeyDownBit) = KeyDownBit Then
mouse_event(mouseclickup, 0, 0, 0, 0)
Thread.Sleep(200)
mouse_event(mouseclickdown, 0, 0, 0, 0)
Else
LButton.Stop()
End If
End Sub
Private Sub Timer1_Tick_1(sender As Object, e As EventArgs) Handles Timer1.Tick
If (GetAsyncKeyState(Keys.LButton) And KeyDownBit) = KeyDownBit Then
LButton.Start()
Else
LButton.Stop()
End If
End Sub
I'm not sure whether your second timer (Timer1) is actually needed in this case.
here is a link to the button im referring to
http://blog.laptopmag.com/wpress/wp-content/uploads/2012/07/ThinkPad-Keyboard-Face-Off_g4-T420.jpg
and the format im trying to find it coincides with this sort of formatting
{PGUP} = page up button
{LEFT} = left arrow
{ESC} = Escape
anybody know how you can send this as a sendkey? prefer to use this keyboard orientated method than sending right click to a screen as that causes its own issues with finding location through an emulator screen.
cheers to anyone who can help me, even if someone knows for certain it doesn't exist it means i can focus on something else instead :)
EDIT:
TL;DR
"+{F10} might be what you are looking for to bring up what is called the 'context menu' however if you are using a citrix (or similar) application then you might have issues with stuff not being in focus." :)
So what I have discovered on my own. there are several ways to send what I now know is the 'context menu' key. only one however was applicable to my situation.
if I was able to write the code for sendkey then drarig29's answer might have worked but with the application I'm using (BluePrism I can not do it that way unfortunately) this was my solution (that doesn't work)
"+{F10}"
so '+' = SHIFT and F10 = F10 button. So shift F10 will work for people who are looking for a way to bring up the 'right-click menu' however this will not work for me, though im not sure why. I'm automating an application through citrix (emulator screen) and even though I make the mouse click on the screen and the use the sendKey "+{F10}" it does not make the context menu screen appear on the application. So I tried it manually and funny enough it also doesn't work in bringing up the context menu. Then I found out if you right click the area of the screen and the menu comes up then the send key works all of a sudden with no hitch. I think the problem is to do with some windows being in focus and some not but I have to work it out to be sure seeing as i sent a 'click' to the right area of the screen it should be in focus. but anyways cheers for the help :)
This is the context menu key. Its keycode is 93. To send a key using its keycode, use this :
<DllImport("user32.dll")> _
Private Shared Function keybd_event(bVk As Byte, bScan As Byte, dwFlags As UInteger, dwExtraInfo As Integer) As Boolean
End Function
Const KEYEVENTF_KEYDOWN = &H0
Const KEYEVENTF_KEYUP = &H2
Private Sub SendKey(KeyCode As Integer)
keybd_event(CByte(KeyCode), 0, KEYEVENTF_KEYDOWN, 0)
keybd_event(CByte(KeyCode), 0, KEYEVENTF_KEYUP, 0)
End Sub
You have to import System.Runtime.InteropServices (Imports System.Runtime.InteropServices).
With this, to send context menu key, use SendKey(93).
Edit :
Declare Sub mouse_event Lib "user32" Alias "mouse_event" (ByVal dwFlags As Integer, ByVal dx As Integer, ByVal dy As Integer, ByVal cButtons As Integer, ByVal dwExtraInfo As Integer)
Const MOUSEEVENTF_LEFTDOWN As Int32 = &H2
Const MOUSEEVENTF_LEFTUP As Int32 = &H4
Const MOUSEEVENTF_RIGHTDOWN As Int32 = &H8
Const MOUSEEVENTF_RIGHTUP As Int32 = &H10
Enum ClickType
Left = 0
Right = 1
End Enum
Sub SendClick(ClickType As ClickType, DestX As Integer, DestY As Integer)
Select Case ClickType
Case ClickType.Left
Cursor.Position = New Point(DestX, DestY)
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
Case ClickType.Right
Cursor.Position = New Point(DestX, DestY)
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0)
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0)
End Select
End Sub
Use the previous method like that : SendClick(ClickType.Left, 20, 20)
I am developing a media player and I'm trying to change value of Progress Bar using mouse cursor.
what i want is to set the value of progress bar where mouse cursor points after clicking + Dragging.
A progress bar is not the correct control to use for this. You should use a TrackBar instead.
But it can be made to work, with about -10 elegance points. The trickiest problem with ProgressBar is that it animates progress. That makes it slow to respond to your mouse moves. That animation can be disabled, but not perfectly. Closest you can get is:
Private Shared Sub ChangeProgress(bar As ProgressBar, e As MouseEventArgs)
If e.Button = Windows.Forms.MouseButtons.Left Then
Dim mousepos = Math.Min(Math.Max(e.X, 0), bar.ClientSize.Width)
Dim value = CInt(bar.Minimum + (bar.Maximum - bar.Minimum) * mousepos / bar.ClientSize.Width)
'' Disable animation, if possible
If value > bar.Value And value < bar.Maximum Then
bar.Value = value + 1
bar.Value = value
Else
bar.Value = value
End If
End If
End Sub
And call it from MouseDown and MouseMove event handlers:
Private Sub ProgressBar1_MouseMove(sender As Object, e As MouseEventArgs) Handles ProgressBar1.MouseMove
ChangeProgress(ProgressBar1, e)
End Sub
Private Sub ProgressBar1_MouseDown(sender As Object, e As MouseEventArgs) Handles ProgressBar1.MouseDown
ChangeProgress(ProgressBar1, e)
End Sub
It is workable, you'll notice that getting to 100% is a bit awkward. But, really, use a TrackBar instead. It was made to do this.
As the title states, is it possible / how can you create a custom window to draw onto? Normally, you would just use a form and form controls, but I want my own window with a handle that I'll attach hooks to and handle the paint events and the like. Is this possible? Essentially, I just need a container for my program's image that isn't a Form. If not in VB.Net, is it possible in C#?
EDIT:
I'm just not very fond of how the window draws (even with control over paint event). I removed the form border and the control bar and replaced them with my own functions (to place the max/min/exit buttons, title, form borders + sizing, etc) so the form I'm using is essentially just a floating panel - though with built in hooks that are nice of course. But the form still flickers too much and so I wanted to handle everything myself. I use doublebuffering on all controls I use and I use setbounds to move/resize controls as opposed to setting width/height individually (reduced some of the flicker). I draw the form border in the form's paint event, the rest is drawn as controls (including the form's top bar).
I mostly hate the black boxes that I see when I expand the form (generally don't see that when decreasing window size, but still some small amount of flicker). An alternative method, perhaps a different draw style (in VB 2010) or something, would work as well I guess.
EDIT (again):
The black box issue happens regardless of how many controls are on the form. If I try to manually resize it (the custom empty form control posted below that inherits from Form), using setbounds on each mousemove during a click and drag event (does not occur when not intended, so I know it's not running the sub more than it has to).
EDIT (code):
http://img211.imageshack.us/img211/900/j9c.png
So even on a blank "SimpleForm" (as posted in the first answer") with no controls, when resized to be larger (in the pic, resized northeast), black boxes are drawn under where the form will be drawn. Controlstyles / backbuffering done as posted in the second answer, as well as the createparams posted by Hans. This is what I used to set the form bounds:
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = cp.ExStyle Or &H2000000
cp.Style = cp.Style Or &H2000000
Return cp
End Get
End Property 'CreateParams
Public Sub New(ByRef ContentFolder As String, ByRef x As Integer, ByRef y As Integer, ByRef w As Integer, ByRef h As Integer)
FormBorderStyle = FormBorderStyle.None
'Note, I have tried the original suggested control styles in many combinations
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint
UpdateStyles()
OL = x 'Used for resizing, to know what the original bounds were - especially in maximizing, didn't like the standards maximize call
OT = y
OW = w
OH = h
BackColor = Color.White
BorderColor = New Pen(BarColor.Color)
MinimumSize = New Size(200, 200)
TransparencyKey = Color.FromArgb(255, 255, 0, 128)
CF = ContentFolder
ControlBar = New FormBar(Me, "Explorer woo", CF)
AddHandler Me.Load, AddressOf EF_Load
AddHandler Me.MouseUp, AddressOf EF_MouseUp
AddHandler Me.MouseDown, AddressOf EF_MouseDown
AddHandler Me.MouseMove, AddressOf EF_MouseMove
AddHandler Me.LostFocus, AddressOf EF_LostFocus
End Sub
Public Sub EF_Load(ByVal sender As Object, ByVal e As EventArgs)
SetFormBounds(OL, OT, OW, OH)
End Sub
Protected Overrides Sub OnSizeChanged(ByVal e As EventArgs)
ControlBar.SetBar(Width) 'Sets the width of controlbar to new width, and updates position of the 3 top-right form buttons
If Not (_backBuffer Is Nothing) Then
_backBuffer.Dispose()
_backBuffer = Nothing
End If
RaiseEvent Resized(Me, e) 'Resizes controls in custom handler, in this example, it is unused - with controls, they don't flicker when resized though
MyBase.OnSizeChanged(e)
End Sub
Private Sub SetFormBounds(ByRef l As Integer, ByRef t As Integer, ByRef w As Integer, ByRef h As Integer)
If w < Me.MinimumSize.Width Then
w = Me.MinimumSize.Width
l = Left
End If
If h < Me.MinimumSize.Height Then
h = Me.MinimumSize.Height
t = Top
End If
If l = Left AndAlso t = Top AndAlso w = Width AndAlso h = Height Then Exit Sub
ControlBar.SetBar(w)
SetBounds(l, t, w, h)
'Used for detecting if user coords are on the form borders with L-shaped areas so as to not include too much of the interior of the bar, Borderthickness = pixel width of border
CornerRects = New List(Of Rectangle) From {{New Rectangle(0, 0, BorderThickness, 15)}, {New Rectangle(0, 0, 15, BorderThickness)}, {New Rectangle(Width - 15, 0, 15, BorderThickness)}, {New Rectangle(Width - BorderThickness, 0, BorderThickness, 15)}, {New Rectangle(0, Height - 15, BorderThickness, 15)}, {New Rectangle(BorderThickness, Height - BorderThickness, 10, BorderThickness)}, {New Rectangle(Width - BorderThickness, Height - 15, BorderThickness, 15)}, {New Rectangle(Width - 15, Height - BorderThickness, 10, BorderThickness)}}
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
If _backBuffer Is Nothing Then
_backBuffer = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
End If
Dim g As Graphics = Graphics.FromImage(_backBuffer)
g.Clear(SystemColors.Control)
'Draw Control Box
g.TextRenderingHint = Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit
g.FillRectangle(BarColor, 0, 0, Width, ControlBar.Height)
If ControlBar.Title <> "" Then g.DrawString(ControlBar.Title, ControlBar.Font, ControlBar.FontBrush, ControlBar.TextLeft, ControlBar.TextTop)
g.DrawImage(FormBar.bmpCorners(0), 0, 0) 'Makes transparent corner, very small bitmap created at run-time
g.DrawImage(FormBar.bmpCorners(1), Width - FormBar.bmpCorners(0).Width, 0)
'Draw Control Box buttons top right
If ControlBar.ExitButton.Enabled = True Then g.DrawImage(ControlBar.ExitButton.Img, ControlBar.ExitButton.Rect.X, ControlBar.ExitButton.Rect.Y)
If ControlBar.MaximizeButton.Enabled = True Then g.DrawImage(ControlBar.MaximizeButton.Img, ControlBar.MaximizeButton.Rect.X, ControlBar.MaximizeButton.Rect.Y)
If ControlBar.MinimizeButton.Enabled = True Then g.DrawImage(ControlBar.MinimizeButton.Img, ControlBar.MinimizeButton.Rect.X, ControlBar.MinimizeButton.Rect.Y)
If Not ControlBar.Ico Is Nothing Then g.DrawImage(ControlBar.Ico, 5, 5) 'Draw Control Box icon (program icon) if it is set
'Draw the form border
For i = 0 To BorderThickness - 1
g.DrawLine(BorderColor, i, ControlBar.Height, i, Height - 1)
g.DrawLine(BorderColor, Width - 1 - i, ControlBar.Height, Width - 1 - i, Height - 1)
g.DrawLine(BorderColor, BorderThickness, Height - 1 - i, Width - BorderThickness, Height - 1 - i)
Next
g.Dispose()
e.Graphics.DrawImageUnscaled(_backBuffer, 0, 0)
End Sub
Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
End Sub
It is not really possible at all, in either language. This isn't a language thing, or even a framework (i.e. WinForms) thing. Rather, it's more because of the design of Windows itself. Essentially, everything in Windows is a window, and the Form class represents a basic top-level window that can be displayed directly on the desktop. If you want a window displayed on the desktop, you need to use the Form class. Moreover, if you want to have a window handle that you can attach hooks to, you'll need to use this class; it's the one with all the necessary plumbing to get that going.
But that doesn't mean it has to look like a default Form object does. The appearance is infinitely customizable. Start by setting the FormBorderStyle property of your form to remove the default window frame/chrome. That will give you a completely blank slate. Then, do like you said and handle its Paint event. Except that when you're wanting to handle the events of a derived class, you should override the OnXxx method directly, instead of subscribing to the events. So you'd have this code:
Public Class SimpleForm : Inherits Form
Public Sub New()
' Alter the form's basic appearance by removing the window frame,
' which gives you a blank slate to draw onto.
FormBorderStyle = FormBorderStyle.None
' Indicate that we're painting our own background.
SetStyle(ControlStyles.Opaque, True)
End Sub
Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
' Call the base class.
MyBase.OnPaint(e)
' Paint the background...
e.Graphics.FillRectangle(Brushes.MediumAquamarine, Me.ClientRectangle)
' ...and then the foreground.
' For example, drawing an 'X' to mark the spot!
Using p As New Pen(Color.Navy, 4.0)
e.Graphics.DrawLine(p, 0, 0, Me.Width, Me.Height)
e.Graphics.DrawLine(p, Me.Width, 0, 0, Me.Height)
End Using
End Sub
End Class
Of course, such a window has severe usability problems. For starters, the user has no way to move it around on the screen or to close it. You'll need to handle those things yourself if you're eliminating the default border.
Can you show the method you are using to enable double buffering? Here's an article that addresses this. Perhaps it will help.
https://web.archive.org/web/20140811193726/http://bobpowell.net/doublebuffer.aspx
Basically, the code is like this (from the article):
Private _backBuffer As Bitmap
Public Sub New
InitializeComponents()
Me.SetStyle(ControlStyles.AllPaintingInWmPaint OR _
ControlStyles.UserPaint OR _
ControlStyles.DoubleBuffer, True)
End Sub
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
If _backBuffer Is Nothing Then
_backBuffer = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
End If
Dim g As Graphics = Graphics.FromImage(_backBuffer)
'Paint on the Graphics object here
g.Dispose()
'Copy the back buffer to the screen
e.Graphics.DrawImageUnscaled(_backBuffer, 0, 0)
End Sub 'OnPaint
'Don't allow the background to paint
Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
End Sub 'OnPaintBackground
Protected Overrides Sub OnSizeChanged(ByVal e As EventArgs)
If Not (_backBuffer Is Nothing) Then
_backBuffer.Dispose()
_backBuffer = Nothing
End If
MyBase.OnSizeChanged(e)
End Sub 'OnSizeChanged
I have a form positioned slightly above the taskbar/system tray in the bottom right corner of my desktop window. It's sort of like a pop-up notification. The notification itself looks great, but there's a button on it which resizes the form up, animating the sizing in increments of 5px whilst keeping it's position relative to the bottom-right corner of the screen.
The problem with this is that it doesn't look very smooth. Adjusting Me.Width resizes from the left so you have to then move the form to the left with Me.Left to compensate. Me.SetBounds just seems to be a wrapper for setting those properties anyway.
Is there anything I can do to have the form smoothly (or at least appear to) resize outwards from the left of the form?
ETA: You can actually do this using SetBounds, as SetBounds will delegate to SetBoundsCore which, in turn will invoke SetWindowPos. So SetBounds will internally really set all bounds at once and the window manager will redraw the window only after all properties are set.
Another option would be to import the MoveWindow function and use that instead. It produces a smooth animation here, as it can set size and position simultaneously before telling the window manager to redraw the window.
My test code looked like this (converted from C# via IL to VB with help from Reflector):
Private button1 As Button
Private components As IContainer = Nothing
Private tm As Timer = New Timer
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
MyBase.Left = (Screen.PrimaryScreen.WorkingArea.Width - MyBase.Width)
MyBase.Top = (Screen.PrimaryScreen.WorkingArea.Height - MyBase.Height)
Me.tm.Enabled = False
Me.tm.Interval = 20
AddHandler Me.tm.Tick, Function
If (MyBase.Width < 500) Then
Form1.MoveWindow(MyBase.Handle, (MyBase.Left - 5), (MyBase.Top - 5), (MyBase.Width + 5), (MyBase.Height + 5), True)
Else
Me.tm.Enabled = False
End If
End Function
End Sub
<DllImport("user32.dll", SetLastError:=True)> _
Friend Shared Function MoveWindow(ByVal hWnd As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal bRepaint As Boolean) As Boolean
End Function
I never tried this, but have you tried setting the DoubleBuffer property of the form to True? This should smooth the drawing.
You might try reducing the pixel steps it's incrementing down to 1-2 as well, 5px seems like a lot to be adjusting size, I could see how this could appear choppy.