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!
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.
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.
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...
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
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!
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. ;)
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?
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?"
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.
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...
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
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
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!
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...
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!
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)
intNewCursor = LoadCursorFromFile(Application.StartupPath & "\" & CursorFilePath)
'Activate the new default cursor (ie: replace OCR_NORMAL)
Public Sub RestoreLastCursor()
' Restore last cursor
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!
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!)
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.
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...
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.
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.")
' 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)
If TimeBetweenDownAndUp > 0 Then
Application.DoEvents() ' Allow down position to paint
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)