Professor_Bernd Posted August 22, 2020 Posted August 22, 2020 (edited) Starting point was that I wanted to determine the background color of a label. I did some research, read many articles and sample scripts and tried it out. In the end I found some possibilities, which I summarized in a demo and divided it into 4 columns. See screenshot. Screenshots: On the left the initial position, on the right after resetting the colors of the controls. . In each column there is a test button (green) and a test label (yellow), plus a button to change the color of the test controls, and a button to reset to the original colors. Unfortunately, there is no example of _WinAPI_GetBkColor() in the AutoIt help, so I tried to find out how it works myself. But that only worked when I implemented a _SendMessage() function. In column 1 "WinAPI 1" is a function that tries to use _WinAPI_GetBkColor(). This is the crucial function for this thread. But it does not work for the button or the label. In column 2 "WinAPI 2" is the same function, but with a _SendMessage() function. With this it works wonderfully, but I don't know if I'm doing it right! In column 3 "guinness" is a function of @guinness , which works with _WinAPI_GetPixel(). It does not work reliably with controls with frames, like buttons. In column 4 "Bilgus" is a function plus an additional function "_GetBrushColor()" from @Bilgus . It does not work reliably with controls with transparent backgrounds, like labels. In principle, I just combined different commands from the other functions in the function for column 2 "WinAPI 2". As I said, it seems to be the only way that works reliably. The question is, what is the right way to use _WinAPI_GetBkColor() to determine the background color of controls? Maybe you could include an example in the AutoIt help!? Prof Bernd. Demo GUICtrlGetBKColor.au3 Edited August 22, 2020 by Professor_Bernd
mikell Posted August 23, 2020 Posted August 23, 2020 (edited) For the Bilgus way, be careful with "symetric" colors like 0x00FF00 when doing tests. The function works if is returned _WinAPI_SwitchColor($iColor) For the guinness way, I suspect the border of the button to cause trouble because on my machine if I shift the pixels to check then it also works Local $iColor = _WinAPI_GetPixel($hDC, 3, 3) Good luck to make your choice Edited August 23, 2020 by mikell Professor_Bernd 1
Professor_Bernd Posted August 23, 2020 Author Posted August 23, 2020 Hi mikell. The guinnes method has other problems with _WinAPI_GetPixel, for example with an Input control. The Bilgus method seems to work when using your change, but the method is too big. In 2020 you should not need so much additional code to get the background color of a control. Spoiler expandcollapse popup; ============================================================================== ; GUICtrlGetBkColor (Bilgus) ; ; Description: Retrieves the RGB value of the control background WITHOUT GetPixel. ; ---- Does NOT work e.g. with transparent labels. ---- ; ; Author: Bilgus, 2020-04-30. ; https://www.autoitscript.com/forum/topic/202574-get-color-of-a-solid-brush/?do=findComment&comment=1453833 ; Modified by: Professor Bernd, 2020-08-22. ; ------------------------------------------------------------------------------ Func GUICtrlGetBkColor_Bilgus($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $hWnd_Main = _WinAPI_GetParent($hWnd) Local $hDC = _WinAPI_GetDC($hWnd) Local $hBrush = _SendMessage($hWnd_Main, $WM_CTLCOLORSTATIC, $hDC, $hWnd) Local $iColor = _GetBrushColor($hBrush) _WinAPI_ReleaseDC($hWnd, $hDC) ; _WinAPI_SwitchColor: Tip from mikell, 2020-08-23. Seems to work. :-) ; https://www.autoitscript.com/forum/topic/203675-_winapi_getbkcolor-how-to-use-it-the-right-way/?do=findComment&comment=1462530 Return _WinAPI_SwitchColor($iColor) EndFunc ; ==> GUICtrlGetBkColor (Bilgus) ; ; ============================================================================== ; _GetBrushColor (Bilgus) ; ; Description: Get the color of a SOLID brush. Does NOT work e.g. with transparent labels. ; ; Author: Bilgus, 2020-04-30. ; https://www.autoitscript.com/forum/topic/202574-get-color-of-a-solid-brush/ ; ------------------------------------------------------------------------------ Func _GetBrushColor($hBrush) ;https://devblogs.microsoft.com/oldnewthing/20190802-00/?p=102747 Local $iErr Local Const $BS_SOLID = 0x00000000 Local Const $tagLogBrush = "uint lbStyle; dword lbColor; ulong_ptr lbHatch" Local $tLogBrush = DllStructCreate($tagLogBrush) Local $iSzLogBrush = DllStructGetSize($tLogBrush) If _WinAPI_GetObject($hBrush, $iSzLogBrush, DllStructGetPtr($tLogBrush)) <> $iSzLogBrush Then $iErr = 0x1 ElseIf DllStructGetData($tLogBrush, "lbStyle") <> $BS_SOLID Then $iErr = 0x2 Else Return DllStructGetData($tLogBrush, "lbColor") EndIf Return SetError($iErr, 0, 0xFFFFFFFF) ;CLR_NONE EndFunc ; ==> _GetBrushColor So it should be done with 1 command, for example if there was a function _WinAPI_GetCtrlBkColor(HWND/ID). But this is not bad if it is done with a few extra lines to _WinAPI_GetBkColor($hDC). Hence my question if my way with SendMessage is the right way to use it, because I don't really know what it does. (_WinAPI_SetBkMode($hDC, $OPAQUE) does not work.) Spoiler ; ============================================================================== ; GUICtrlGetBkColor (WinAPI 2) ; ; Description: Retrieves the RGB value of the control background WITHOUT GetPixel. ; ; Author: Bilgus, 2020-04-30. ; https://www.autoitscript.com/forum/topic/202574-get-color-of-a-solid-brush/?do=findComment&comment=1453833 ; Modified by: Professor Bernd, 2020-08-22. ; ------------------------------------------------------------------------------ Func GUICtrlGetBkColor_WinAPI_2($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $hDC = _WinAPI_GetDC($hWnd) ; <== Get the DC before you send the message! ; _WinAPI_SetBkMode($hDC, $OPAQUE) ; <== Does not work. _SendMessage(_WinAPI_GetParent($hWnd), $WM_CTLCOLORSTATIC, $hDC, $hWnd) ; <== What exactly does it do? Local $iColor = _WinAPI_GetBkColor($hDC) _WinAPI_ReleaseDC($hWnd, $hDC) Return $iColor EndFunc ; ==> GUICtrlGetBkColor (WinAPI 2) Could someone post an example of the correct use of "_WinAPI_GetBkColor"? This could then be included as an example in the AutoIt help. Thanks a lot mikell, for your info!
Nine Posted August 23, 2020 Posted August 23, 2020 I think that the explanation lies somewhere under this statement from MSDN : Quote The WM_CTLCOLORSTATIC message is sent to the parent window of a static control when the control is about to be drawn. By responding to this message, the parent window can use the given device context handle to set the text and background colors of the static control. In case of an inquiry, the parent also needs to know the given DC of the control, which makes sense. But the window itself doesn't need its own DC to get its bk color. #include <WinAPI.au3> #include <WindowsConstants.au3> Global $hGui = GUICreate("Demo for GUICtrlGetBKColor", 578, 323) GUISetBkColor (0xFFFF00) Global $idBtn = GUICtrlCreateButton("Test", 64, 64, 89, 33) GUICtrlSetBkColor(-1, 0x00FFFF) GUISetState () ConsoleWrite ("control = " & Hex(_GUICtrlGetBkColor($idBtn)) & @CRLF) Local $hGuiDC = _WinAPI_GetDC($hGUI) Local $iGetColor = _WinAPI_GetBkColor($hGuiDC) _WinAPI_ReleaseDC($hGUI, $hGuiDC) ConsoleWrite ("GUI = " & Hex($iGetColor) & @CRLF) Sleep (1000) Func _GUICtrlGetBkColor($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $hParent = _WinAPI_GetParent($hWnd) Local $hDC = _WinAPI_GetDC($hWnd) _SendMessage($hParent, $WM_CTLCOLORSTATIC, $hDC, $hWnd) Local $RGB = _WinAPI_GetBkColor($hDC) _WinAPI_ReleaseDC($hWnd, $hDC) Return $RGB EndFunc ;==>_GUICtrlGetBkColor Professor_Bernd 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
Professor_Bernd Posted August 23, 2020 Author Posted August 23, 2020 Did you post the same code I already did? I can't see any functional difference between your code in _GUICtrlGetBkColor($hWnd) and my code in GUICtrlGetBkColor_WinAPI_2($hWnd). Or am I missing something? I had already read the explanation in MSDN, but didn't understand it. I do not understand it now. Local $hDC = _WinAPI_GetDC($hWnd) ; <= Here you get the DC of the control, right? _SendMessage($hParent, $WM_CTLCOLORSTATIC, $hDC, $hWnd) ; <== * Local $RGB = _WinAPI_GetBkColor($hDC) ; <== Here I hand over the DC of the control and get the BK Color. * But what's the use of the SendMessage where I use the DC of the control? What is wrong with the DC that it does not work in _WinAPI_GetBkColor($hDC) without SendMessage? What I mean is, _WinAPI_GetBkColor($hDC) needs the DC of the control and I give it the DC of the control. Why do I need SendMessage in addition? Sorry, but this is what confuses me. It doesn't really make sense to me and I only found it out by accident.
Nine Posted August 23, 2020 Posted August 23, 2020 Sorry cannot explain it more clearly that I already did. Lets hope someone else will be better placed to teach it to you. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
Professor_Bernd Posted August 23, 2020 Author Posted August 23, 2020 4 minutes ago, Nine said: Sorry cannot explain it more clearly that I already did. At least I know I'm not alone in this. Thanks for your information!
Bilgus Posted September 2, 2020 Posted September 2, 2020 (edited) @Professor_Bernd _SendMessage($hParent, $WM_CTLCOLORSTATIC, $hDC, $hWnd) This actually selects the background brush into the hdc It starts getting deep quick but its basically this: Windows works on the mantra of 'don't store anything that you can recalculate later' So windows doesn't know what background the hdc has until it is time to redraw the hdc you request that it does that calculation and hold on to the brush Warning: Non system brushes need to be deselected from the DC * save the hbrush returned by sendmessage() and reselect it in to the DC (IIRC its NULL_BRUSH) <in the case of a system brush (the SOLID ones) you don't need to deselect the brush when you are done but you still can.. > Edited September 2, 2020 by Bilgus Professor_Bernd 1
Professor_Bernd Posted September 2, 2020 Author Posted September 2, 2020 Wow, that helps me a lot! I think I understand now how this works. How do I do the deselect? Can you help me? Func GUICtrlGetBkColor_WinAPI_2($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $hDC = _WinAPI_GetDC($hWnd) ; <== Get the DC before you send the message! Local $hBrush = _SendMessage(_WinAPI_GetParent($hWnd), $WM_CTLCOLORSTATIC, $hDC, $hWnd) ; <= save hBrush Local $iColor = _WinAPI_GetBkColor($hDC) _WinAPI_ReleaseDC($hWnd, $hDC) Return $iColor EndFunc ; ==> GUICtrlGetBkColor (WinAPI 2)
Bilgus Posted September 2, 2020 Posted September 2, 2020 to deselect you select the original object back in then you clean up that object ^^note here the static objects do not need to be deselected Func GUICtrlGetBkColor_WinAPI_2($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $hDC = _WinAPI_GetDC($hWnd) ; <== Get the DC before you send the message! Local $hBrushOLD = _SendMessage(_WinAPI_GetParent($hWnd), $WM_CTLCOLORSTATIC, $hDC, $hWnd) ; <= Select background hBrush Local $iColor = _WinAPI_GetBkColor($hDC) $hBrushOLD = _WinAPI_SelectObject($hDC, $hBrushOLD) ;put the original object back ;hBrushOLD now contains the brush selected in with _sendMessage above^^ _WinAPI_DeleteObject($hBrushOLD) ;^ Not needed with the static brushes*** _WinAPI_ReleaseDC($hWnd, $hDC) Return $iColor EndFunc ; ==> GUICtrlGetBkColor (WinAPI 2) now as far as the function you referenced here: ElseIf DllStructGetData($tLogBrush, "lbStyle") <> $BS_SOLID Then this only handles the case of a solid brush you would need to expand it to handle other brush types (listed here) https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-logbrush you can choose as many or as few as you care to implement Professor_Bernd 1
Professor_Bernd Posted September 2, 2020 Author Posted September 2, 2020 I tried it myself, but failed. Your information helps me again. If you keep on giving such good information, my finger will get sore from pressing the "Like" button! Thanks for your information!
Professor_Bernd Posted September 2, 2020 Author Posted September 2, 2020 (edited) $hBrushOLD = _WinAPI_SelectObject($hDC, $hBrushOLD) ;put the original object back ;hBrushOLD now contains the brush selected in with _sendMessage above^^ _WinAPI_DeleteObject($hBrushOLD) ;^ Not needed with the static brushes*** I have thought about it. Can it be right or did I think wrong? (see the $hBrushNew variable.) ;hBrushOLD contains the brush selected in with _sendMessage above^^ $Local $hBrushNew = _WinAPI_SelectObject($hDC, $hBrushOLD) ;put the original object back, return the current object _WinAPI_DeleteObject($hBrushNew) ;^ Not needed with the static brushes*** Edited September 2, 2020 by Professor_Bernd
Bilgus Posted September 3, 2020 Posted September 3, 2020 doing it that way would be bad because someone else could change brush in the meantime and now you have a leak delete the object returned by selectobj when you select its original brush back
Bilgus Posted September 3, 2020 Posted September 3, 2020 If you can find it in print anywhere I'd recommend a read of: Windows Graphics Programming Win32 GDI and DirectDraw Feng Yuan Publisher: Prentice Hall PTR ISBN: 0-13-086985-6, 1234 pages argumentum 1
Professor_Bernd Posted September 3, 2020 Author Posted September 3, 2020 (edited) My mistake. I guess I described it badly. For more clarity here the two variations. The first one is your unchanged code from the posting above, the second one is the same code, but with changed variable name ($hBrushNew) and comments. Func GUICtrlGetBkColor_WinAPI_2($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $hDC = _WinAPI_GetDC($hWnd) ; <== Get the DC before you send the message! Local $hBrushOLD = _SendMessage(_WinAPI_GetParent($hWnd), $WM_CTLCOLORSTATIC, $hDC, $hWnd) ; <= Select background hBrush Local $iColor = _WinAPI_GetBkColor($hDC) $hBrushOLD = _WinAPI_SelectObject($hDC, $hBrushOLD) ;put the original object back ;hBrushOLD now contains the brush selected in with _sendMessage above^^ _WinAPI_DeleteObject($hBrushOLD) ;^ Not needed with the static brushes*** _WinAPI_ReleaseDC($hWnd, $hDC) Return $iColor EndFunc ; ==> GUICtrlGetBkColor (WinAPI 2) Func GUICtrlGetBkColor_WinAPI_2($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $hDC = _WinAPI_GetDC($hWnd) ; <== Get the DC before you send the message! Local $hBrushOLD = _SendMessage(_WinAPI_GetParent($hWnd), $WM_CTLCOLORSTATIC, $hDC, $hWnd) ; <= Select background hBrush Local $iColor = _WinAPI_GetBkColor($hDC) Local $hBrushNew = _WinAPI_SelectObject($hDC, $hBrushOLD) ;put the original object back, return the current object ;hBrushNew contains the brush selected in with _sendMessage above^^ _WinAPI_DeleteObject($hBrushNew) ;^ Not needed with the static brushes*** _WinAPI_ReleaseDC($hWnd, $hDC) Return $iColor EndFunc ; ==> GUICtrlGetBkColor (WinAPI 2) Actually I wanted to know what _WinAPI_SelectObject returns at this point, the old brush or the new one. But I did not read attentively, because in the comment you already gave the answer. To clarify this, I changed the variable names. Did I understand that correctly? Edited September 3, 2020 by Professor_Bernd
Professor_Bernd Posted September 4, 2020 Author Posted September 4, 2020 Then I can publish it now. Thank you for your effort and patience! 👍 Bilgus 1
LWC Posted May 13, 2024 Posted May 13, 2024 Both of your codes seem to rely on the user previously using GUISetBkColor / GUICtrlSetBkColor. But if the user did that, then the user could have stored the colors anyway. I wonder how to make your functions also work on default colors.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now