none
Using Win32 API to get mouse click

    General discussion

  • Hi,

    I'm developing a small "colour picker" app in VB.Net to get the colour at a particular pixel anywhere on the screen (ie: not in the form).  

    I understand I have to call the Win32 API to do this, perhaps trapping the WM_LBUTTONDOWN event but I'm really confused as to how I am meant to do this from a VB.Net program.   I know I have to define the functions I want to use from user32.dll (or whatever dll I use) but over and above that I'm lost as to how to actually trap the left mouse-button down event.... can anybody give me an example or point me to where to look for examples?   I looked at MSDN - but it just made me more confused and couldn't even find a single example of how to do it!

    Any help would be greatly appreciated!

    Mike.
    Sunday, May 11, 2003 11:58 PM

All replies

  • It sounds like you want to be able to track mouse movement anywhere on the screen, and read the pixel color from the screen at the current mouse point.

    Here's a rough idea of where to start...

    Tracking mouse moves is just a matter of making your form handle the MouseMove event (in VB.NET select the Events view in the Property window and double-click the MouseMove event, then insert your code in the Sub provided.

    However, a form normally only recieves MouseMove events while the mouse is over it, not while the mouse is outside it. To work around that, you can just set the Form.Capture property to true on your form. You'll probably want to set this to true on mouse down, and set it back to false on mouse goes up (so your user's don't get 'stuck' in your app!). So just add MouseDown and MouseUp event handlers to your form and set the property in there accordingly.

    Once your form is getting mouse moves for the entire screen, you simply need to convert the incoming mouse coordinates from client (form) coords to screen coords using the PointToScreen method.

    Now all you need to do is read the pixel at that screen coordinate. This is a bit trickier - maybe someone else has sample code for this? May require calling raw Win32 graphics APIs. You basically need to get the DC for the screen - using GetDC or GetScreenDC - then call GetPixel on that DC, then release the DC afterwards.

    Having a Graphics object to play with would certainly be easier than calling the raw Win32 APIs. But I'm not sure you can create a .NET Graphics object for the entire screen. Maybe try calling Graphics.FromHwnd or Graphics.FromHDC with a null handle, and see if that works? Just a guess. 

    Hope this helps.

    Iain Heath,
    Windows Forms.
    Tuesday, May 13, 2003 7:03 PM
  • Iain,

    Thanks very much for that - just the sort of thing I was looking for!

    I already have a routine that calls the API to get the cursor position and the colour under it (GetPixel fom GDI32.dll) but it was the mouse click handling I wanted to add.

    I'll have a look into doing it the way you suggested and maybe get back to you if I have any more Q's.

    Cheers,

    Mike.
    Thursday, May 15, 2003 3:26 AM
  • Iain,

    Just wanted to let you know that it worked beautifully!  I put a button on my form which the user clicks when they want to pick any colour on the screen.  I turn form.capture on when this button is clicked then wherever the mouse button is subseqeuetly released I get that pixel position, turn capture off then call the class I already had to do the gdi32.dll DC and GetPixel stuff stuff... et  voila - I have the exact colour of any pixel on the screen!

    I think it was the form.capture that was the secret here... I had no knowledge of this function - yet another example of the fantastic abilities of .Net!!

    Many thanks again for the tips... 

    Mike.
    Thursday, May 15, 2003 5:19 PM
  • Mike,

    Although I believe .NET is an excellent foundation, this is not a "fantastic" feature. ;)

    Actually, the Win32 API has had this for a long time is is normally used extensively in drawing and "drag and drop" type applications (Including Visual Studio and other WYSYWIG type editors).

    The actual Function is:

    HWND SetCapture(HWND hWnd);

    And is in the in the User32.dll

    Thursday, May 15, 2003 8:09 PM
  • OK, may be it's not "fantastic" but I know I prefer to call these sort of functions thru the .Net framework than to have to define calls to an external DLL, cast data types, etc, etc... and have to get to know a whole new programming model as well.  I'm a business app & intranet developer so I don't need to access low(er) level API's very often - and when I do if I can do it in one line of code via the framework then I'm happy - and more importantly, so are my clients!

    But thanks anyway, at least I know where it comes from etc... always keen to learn!

    Mike
    Friday, May 16, 2003 4:21 AM
  • Sorry Mike,

    I was in no way trying to belittle you. D*mn, I knew I should have used more smilies!!! :-)

    To further clarify, there are quite a few devs that I have run across that actually think that some of this is new functionality.  As you pointed out, it's not that it is new but that it is packaged SO WELL!!!!! That is why I like the .NET Framework. I have done both VB (<= 6.0) and VC++ windows development and working with the .NET framework beats them both hands down when it comes to ease.

    Now, if they could just get the IL interpretation built into the processor like they are planning on doing with the next gen XBox, WOW.... Then not only would we have a rockin Framework to work with, it would have some 'balls' behind it. That is my only complaint at this moment. .NET Win Forms UIs can't hold a candle to the speed of a VC++\Delphi\<= VB6 UI. Oh well, I am sure that will come around soon enough. ;)

    Sorry again....

    Cal
    Friday, May 16, 2003 7:57 AM
  • Cal,

    I should used a few more smilies too - on re-reading my reply I think it was  a tad abrasive.  I really value forums such as this one (and www.ASP.Net) and especially appreciate people who are willing to offer an answer or suggestion or even just some more info they think I should know... that's all you were doing and my reply should have reflected that... so sorry from me too!

    You clearly have quite a deep background in lower level Windows development... my point was that .Net is a saviour for me because I don't have to get involved in (as many) VC++, API calls etc as with previous MS dev tools.  This is a good thing for me as I am often under tight deadlines and don't have the time to investigate complex API calls etc.  Personally, I hope they build in loads more seamless access to underlying API functionality in future releases of the framework... the more clients I can please with a short and simple development cycle the more clients I'll get (in theory!).

    Having said all that, I am still a developer and love mucking around with code and trying new things... my appetite has been whetted by getting my app going with simple API calls, so now I'm thinking about buying a .Net / API development book so I can at least find out what's possible in similiar situations int he future... can you suggest anything good?

    Cheers,

    Mike.
    Friday, May 16, 2003 7:00 PM
  • QUOTE: ".... so now I'm thinking about buying a .Net / API development book so I can at least find out what's possible in similiar situations int he future... can you suggest anything good?"

    Mike,

    Hmmmm..... Do you mean an book related to doing Win32 API Interop from .NET or a book on the lower level details of the .NET Framework, possibly dealing with API calls?

    To be honest, outside of a few "primer" type books covering the .NET Framework and/or Win. Forms, I haven't bothered to look a whole lot. I have been rathered bothered by many of the lower quality books that have been hitting the market in the past couple of years.

    I know that our own "JacobMVP" (frequent poster on the .NET news groups and to this forum) was working on an Advanced Win. Forms book but I haven't talked to him about it in a while so I am not sure where that sits. Maybe he'll pop in here and provide us with an update and his opinion on a good set of reference books for you.

    To provide a bit more help, if you don't already, always keeping an instance of the latest MSDN open, or at least the MSDN website.  Research the System.Runtime.InteropServices namespace. It contains ALL kinds of goodies for doing that fun, low level stuff.  Also, Google Groups is your friend and there are a lot of bright people that have posted a wealth of info out on MS's news groups regarding .NET and Interop.

    Wish I could give you a better answer, but that is the best I can do at the moment.

    Cal
    Saturday, May 17, 2003 3:46 PM
  • The Advanced Windows Forms book that I am working on will not be out for awhile...  It's positioned to coincide with the release of version 2 of .NET.

    As far as reference books, not too sure.  Chris Sells has a <a href="http://www.amazon.com/exec/obidos/ASIN/0321125193">windows forms book</a> coming in out in October.  I don't know anything about it, but Chris is a solid and knowledgeable guy and I would trust most anything he put out.  People praise Petzold's (wow, alliteration is fun) <a href="http://www.microsoft.com/mspress/books/toc/6259.asp#TableOfContents">Windows Forms book</a>, but honestly, I don't care for it.  For a general .NET book, you can't go wrong with anything written by Jeff Richter.  Specifically the book found <a href="http://www.amazon.com/exec/obidos/ASIN/0735614229">here</a>.  With respect to WinAPI books, I honestly don't know of many.  I know that Dan Appleman has one or two out from a few years ago... I also think Petzold put one out in 98-ish that might be useful.  Personally, I use Spy++ quite a bit to find out what messages or notifications are being sent to windows and then try and mimic or intercept it...
    Saturday, May 17, 2003 4:17 PM
  • Cali/Jacob,

    Thanks for your suggestions re books.  Guess I was referring to documentation about the Win API's and using with VB6/VB.Net, but have found that a combo of MSDN and Google (only the best search engine ever for everything!) have been OK for what little API stuff I've done so far.

    Now here's another one... as part of what I got going in my earlier posts (ie: a colour picker application), I also want to change the system cursor while the program waits for the left mouse button to be clicked.  I want to use the crosshair cursor so the user would be able to pick the *exact* pixel they wanted to get the colour for.  However, I can't this to work no matter what I try!

    I discovered that I had to use LoadCursor and SetCursor in USER32.DLL and defined those functions at the top of my program (class) as so:
    Public Class Form1
      Inherits Windows.Forms.Form

      Public Declare Function SetCursor Lib "user32.dll" (ByVal hCursor As Integer) As Integer
      Public Declare Function LoadCursor Lib "user32.dll" Alias "LoadCursorA" (ByVal hInstance As Integer, ByVal lpCursorName As String) As Integer

      Dim hcursor As Integer ' receives handle to application starting cursor 
      Dim holdcursor As Integer ' receives handle to previously used cursor 
      Dim retval As Integer ' throw-away return value 

      '...etc...
    After a user has clicked a button to enable them to pick a colour anywhere on the screen, I do the following in the event handler:
        hcursor = LoadCursor(0, "IDC_CROSS")       ' load Windows's crosshair cursor 
        holdcursor = SetCursor(hcursor)                  ' set it to the new cursor 

        'Ensure the form stays on top at all times while capturing.
        TopMost = True

        'Turn capture mode on to track user's mouse clicks.
        Capture = True
    Finally, when the user has selected a pixel I then do this in the MouseUp handler:
          ' Get the screen coordinates of the current mouse position.
        Dim pt As Point = MousePosition

        Capture = False

        'Reset the system cursor to it's original state.
        retval = SetCursor(holdcursor) ' set it to the previous cursor
    All it does is make the cursor disappear while it's over the form. When the cursor is moved to anywhere off the form the standard windows pointer is there, not the crosshair.
    Any idea why this doesn't work?  I assume it's because I'm not doing something right, but have played with it quite a bit and can't seem to make sense of it at all... as always any help would be appreciated!

    TIA...

    Mike.
    Tuesday, June 03, 2003 3:28 AM
  • I spent awhile trying to get this to work and I was wildly unsuccessful.  I went looking around for apps that I use that can do something like this and the closest I came up with was Spy++.  The difference, of course, is that is a Drag/Drop operation rather than a Move/Click operation.  Perhaps that could work for you?  If you have never used Spy++, it provides a "Find Window" function with a little tool icon that you drag atop the window you want to Spy on.  In your color picker app, you could do the same thing.  Then, as the user is dragging, you could even provide a "blown up" view of the pixel they are looking at by providing a picture box with a larger image containing only that color...  I see some possibilities there.  Search your computer for Spyxx.exe and run it.  Go to Spy -> Find Window and you should see a finder tool...
    Tuesday, June 03, 2003 7:24 PM
  • Jacob,

    Thanks for the suggestion. I'd not used Spy++ before and I can see what you're suggesting could be a way of getting the exact pixel. I actually found a similiar tool to mine a while ago called ColorCop which does it's colour picker functionality via drag and drop in this way and thought it was a bit clumsy so decided to do it my way which is much simpler (in theory!!) - maybe the author of ColorCop had the same problem.  

    Anyway, I've just spent about 20 minutes trying to work out what my app is doing using Spy++ and I can see that the system seems to do a WM_SETCURSOR for every mouse move (not surprising - it oubviously has to redraw it each time) so I guess that's why my crosshair cursor is disappearing.  But you'd think you could still set the actual cursor and have it persist - I doubt the kernal cares which cursor is current when it redraws.

    The frustrating thing is that I'm sure I've seen other programs change the system cursor (although to be honest I can't think of any offhand) and I'm sure this must be possible.  Well, in the meantime maybe I'll look at the drag and drop method - I'm sure I'll learn loads of new stuff doing that anyway - and this is one of the main reasons for writing this app... I'm *meant* to be setting up a Sharepoint portal for my prospective clients to see all the stuff I've done in .Net but right now but am having too much fun doing this!!

    Thanks for your help - if you stumble accross the answer I'm seeking please be sure to post it!

    Mike.

    Tuesday, June 03, 2003 8:44 PM
  • SOLVED!!

    I had a deeper think about this and *knew* this must be possible as you can go into Settings/Mouse and change the default system pointer to whatever you want... and obviously that program must make an API call.

    So I did a bit more research on MSDN and found a whole cursors section which had details of the SetSystemCursor routine.  I added the following routines into my code (adapted from <A href="http://www.developer.com/net/vb/article.php/10926_1541491_2">this article</a>), took out the SetCursor stuff I had before and replaced it with a call to each of the routines where applicable... and it works!  (Obviously you need to define the various USER32.DLL routines as usual but I've not added them here to save space.)

    So, after the user has clicked the 'Pick colour button' and I've set Capture = true I do this: 
      ChangeCursor("cross_l.cur")Then to switch back to the old cursor I simply call RestoreLastCursor and the cursor switches back to the default system cursor.

    Here are my routines for anyone that's interested - very simple as you can see:
      Public Sub ChangeCursor(ByVal CursorFilePath As String)

        'Create a copy of the current cursor for Windows NT compatibility.
        intOldCursor = CopyIcon(GetCursor())

        'Check the passed string, if it contains a solid file path, then load the cursor
        'from file. If not, add the App.Path *then* load cursor...
        If InStr(1, CursorFilePath, "\") Then
          intNewCursor = LoadCursorFromFile(CursorFilePath)
        Else
          intNewCursor = LoadCursorFromFile(Application.StartupPath & "\" & CursorFilePath)
        End If

        'Activate the new default cursor  (ie: replace OCR_NORMAL)
        SetSystemCursor(intNewCursor, OCR_NORMAL)

      End Sub

      Public Sub RestoreLastCursor()

        ' Restore last cursor
        SetSystemCursor(intOldCursor, OCR_NORMAL)

      End Sub
    Anyway, that's how I did it - I know this is pretty minor API stuff but it makes me feel really happy that I got to the bottom of this... it's been a good learning experience and I hope it can help someone else at some stage.... Jacob and Cali, thanks again for the help!

    Mike.
    Wednesday, June 04, 2003 12:13 AM
  • Well done!  I didn't think about using SetSystemCursor.  So, is this an app that we might see posted up somewhere when all is said and done?  Or is it not for public consumption in it's complete form?
    Wednesday, June 04, 2003 10:35 AM
  • Thanks Jacob - in answer to your question, yes, I intend to post it to my web site - when I've worked out how to package it all up properly in a zip file.  Do you just ensure everything (the exe, images, icons etc) are in the release directory and zip that?  Normally I just copy the app and it's associated files to the target PC/server - so I need to work out what the best way to package a .Net app is for posting on the web... just one more thing to learn!  What would you suggest?  (In the meantime I will be doing some reading of the "deployment" chapter in my bible, Programming VB.Net by Francesco Balena!)

    TIA...

    Mike.
    Wednesday, June 04, 2003 6:59 PM
  • You don't have to use Win32 API to get the mouse coordinates.
    Here is the simple code on retrieving the mouse coordinates on the screen:

    Cursor.Position.X   'Will return the X coordinate of the mouse
    Cursor.Position.Y   'Will return the Y coordinate of the mouse

    As you can see Cursor.Position is actually a Point object, and it contains the position information of the mouse on the screen.
    Wednesday, January 14, 2004 8:05 PM
  • Hmm, interesting... I haven't gone back to this since early last year as I finished the app but if that is the case maybe I will give it a try and see how it works.  Are you sure it gives you the position anywhere on the entire screen or is it just in the window that the app is running in?  I thought it was the latter but am quite happy to be proved wrong.

    Thanks for the input...

    Mike.
    Thursday, January 15, 2004 7:40 AM
  • I've used in an app before, and it returns the position of the mouse anywhere on the entire screen.
    Thursday, January 15, 2004 5:20 PM
  • what about to simulate a mouse click ? using vb.net, i'm trying to create a program that will perform a mouse click wherever the cursor is resting after a few seconds or so. not click on my own form but on any page that  i may be viewing. my app would have  start and stop buttons to to turn this process on and off. i would like  to set up a timer where you can choose the amount of time you want to elapse between clicks in seconds. maybe use radio button to select a certain time. i haven't been able to find any code examples on this and i'm having trouble figuring out how to do this one. thanks for any help. 
    Wednesday, January 19, 2005 3:58 PM

  • here you go

        Private Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Integer, ByVal dX As Integer, ByVal dY As Integer, ByVal dwData As Integer, ByVal dwExtraInfo As Integer)
        Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Integer)
        Public Shared Sub MouseClick(ByVal Button As MouseButtons, Optional ByVal TimeBetweenDownAndUp As Integer = 250)

            ' Flags that are used for mouse_event
            Const MOUSEEVENTF_LEFTDOWN As Integer = &H2S    ' left button down
            Const MOUSEEVENTF_LEFTUP As Integer = &H4S   ' left button up
            Const MOUSEEVENTF_MIDDLEDOWN As Integer = &H20S    ' middle button down
            Const MOUSEEVENTF_MIDDLEUP As Integer = &H40S    ' middle button up
            Const MOUSEEVENTF_MOVE As Integer = &H1S    ' mouse move
            Const MOUSEEVENTF_RIGHTDOWN As Integer = &H8S  ' right button down
            Const MOUSEEVENTF_RIGHTUP As Integer = &H10S    ' right button up
            ' Const MOUSEEVENTF_WHEEL As Integer = &H800S   ' wheel button rolled
            ' Const MOUSEEVENTF_ABSOLUTE As Integer = &H8000      ' absolute move

            Select Case Button
                Case MouseButtons.None, MouseButtons.XButton1, MouseButtons.XButton2
                    Throw New ArgumentException("Button must be Left, Middle or Right.")
            End Select

            ' Click the mouse, with a delay to simulate human timing
            Select Case Button
                Case MouseButtons.Left : Call mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
                Case MouseButtons.Middle : Call mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0)
                Case MouseButtons.Right : Call mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0)
            End Select
            If TimeBetweenDownAndUp > 0 Then
                Application.DoEvents() ' Allow down position to paint
                Call Sleep(TimeBetweenDownAndUp)
            End If
            Select Case Button
                Case MouseButtons.Left : Call mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
                Case MouseButtons.Middle : Call mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0)
                Case MouseButtons.Right : Call mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0)
            End Select

        End Sub
    Thursday, March 17, 2005 2:01 PM

  • i forgot sometething:
    for more mouse stuff visit <a href="http://vb.mvps.org/articles/ap200007.htm">this page</a>, at the bottom of the page.
    and the sure to download the <a href="http://vb.mvps.org/articles/code/ap200007c.zip">sample code!</a>

    mzl

    Thursday, March 17, 2005 2:06 PM
  • I was wondering if you were still around and available to post the code to your color picker. I am interested in using it for finding the color in an area and comparing its value.

    Thanks in advance.

    Wednesday, February 08, 2006 6:30 PM
  • if (GetAsyncKeyState(VK_LBUTTON))

    {

     

    }

     

    Obvious answer here.  Why do you fools overcomplicate things for the poor guy and every Googler who comes to this page?

    Thursday, April 28, 2011 4:55 PM