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.

Using Win32 API to get mouse click
Alan Foster
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
Les Smith
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.
heguangm
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.
Sam_res03
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.
MarcBeuret
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
dicjb
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.
TT_Lee
Mali Buda
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
Ethan W.
Aurak
But thanks anyway, at least I know where it comes from etc... always keen to learn!
Mike
sharon sharon
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.
BrantF
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.
Kitz
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...
Taillo
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.