Jump to content

Recommended Posts

Posted (edited)

I wasn't able to find a ChooseColor hook example in the forum, so I went ahead and made one. It's dark mode, but realistically you could color it however you want.

I do need some help, however. Please. :)

While everything seems to work quite well, color returns and so on, I need some help to ensure that I have done things properly. So here is a working example of a custom hook for the ChooseColor dialog box (in dark mode). If anyone is able to help improve it, please feel free. It is for the community, as always. And if the community wants to help improve upon it, I am always open to that.

#include <GUIConstantsEx.au3>
#include <APIGdiConstants.au3>
#include <SendMessage.au3>
#include <WinAPISysWin.au3>
#include <WinAPITheme.au3>
#include <WindowsStylesConstants.au3>
#include <WinAPIGdiInternals.au3>
#include <WinAPIGdiDC.au3>
#include <AutoItConstants.au3>
#include <WindowsNotifsConstants.au3>
#include <WinAPIGdi.au3>
#include <Misc.au3>

; Initialize System DPI awareness
DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", @AutoItX64 ? "int64" : "int", -2)

Global Const $CC_ENABLEHOOK = 0x00000010
Global Const $COLOROKSTRING = "commdlg_ColorOK"

Global $g_hColorProc = 0

_WinAPI_RegisterWindowMessage($COLOROKSTRING)

ConsoleWrite(_ChooseColorHooked(2, 0x0080C0, 2) & @CRLF)


Func _ChooseColorHooked($vReturnType = 0, $iColorRef = 0, $iRefType = 0, $hWndOwnder = 0)
    Local $tagCustcolors = "dword[16]"

    Local $tChoose = DllStructCreate($tagCHOOSECOLOR)

    Local Static $tCc = DllStructCreate($tagCustcolors) ; keep the colors
    Local $iReturnType, $vReturn

    If $vReturnType = -9 Or $vReturnType = -10 Then
        ; get $tagCustcolors as array
        Local $a_tCc = __ChooseColor_StructToArray($tCc)
        $a_tCc[0] = -1
        If $vReturnType = -10 Then $tCc = DllStructCreate($tagCustcolors)
        Return $a_tCc
    ElseIf $vReturnType > 9 Then
        ; re-init./clear $tCc, and continue on a clean slate, just "10 + ReturnType"
        $tCc = DllStructCreate($tagCustcolors) ; ( maybe unnecessary but the user may want to )
        $iReturnType = $vReturnType - 10
    ElseIf IsArray($vReturnType) Then
        ; user declared Custcolors array
        If UBound($vReturnType, $UBOUND_ROWS) = 17 And UBound($vReturnType, $UBOUND_DIMENSIONS) = 1 Then
            For $n = 1 To 16
                DllStructSetData($tCc, 1, $vReturnType[$n], $n)
            Next
            If $vReturnType[0] = -9 Then
                ; ..consistent with "-9 = return $tagCustcolors as array"
                $vReturnType[0] = 0 ; set back to default "ReturnType"
                Return $vReturnType
            EndIf
            If $vReturnType[0] > 9 Then $vReturnType[0] -= 10 ; just in case the user mistakenly think it needs to
            $iReturnType = $vReturnType[0]
        Else
            ; unexpected array format
            Return SetError(-5, 0, -1)
        EndIf
    Else
        $iReturnType = $vReturnType
    EndIf

    If $iReturnType < 0 Or $iReturnType > 2 Then
        ; unexpected ReturnType
        If IsArray($vReturnType) Then
            $vReturnType[0] = -1
            Return SetError(-4, 0, $vReturnType)
        EndIf
        Return SetError(-4, 0, -1)
    EndIf

    If $iRefType = 1 Then
        ; BGR hex color to colorref
        $iColorRef = Int($iColorRef)
    ElseIf $iRefType = 2 Then
        ; RGB hex color to colorref
        $iColorRef = Hex(String($iColorRef), 6)
        $iColorRef = '0x' & StringMid($iColorRef, 5, 2) & StringMid($iColorRef, 3, 2) & StringMid($iColorRef, 1, 2)
    EndIf

    DllStructSetData($tChoose, "Size", DllStructGetSize($tChoose))
    DllStructSetData($tChoose, "hWndOwnder", $hWndOwnder)
    DllStructSetData($tChoose, "rgbResult", $iColorRef)
    DllStructSetData($tChoose, "CustColors", DllStructGetPtr($tCc))
    $g_hColorProc = DllCallbackRegister(_ColorChooseProc, "int", "hwnd;uint;wparam;lparam")
    DllStructSetData($tChoose, 'lpfnHook', DllCallbackGetPtr($g_hColorProc))
    DllStructSetData($tChoose, "Flags", BitOR($__MISCCONSTANT_CC_ANYCOLOR, $__MISCCONSTANT_CC_FULLOPEN, $__MISCCONSTANT_CC_RGBINIT, $CC_ENABLEHOOK))

    Local $aResult = DllCall("comdlg32.dll", "bool", "ChooseColor", "struct*", $tChoose)
    DllCallbackFree($g_hColorProc)
    Local $iError = @error
    If @error Then
        Local $iExtended = @extended
        If IsArray($vReturnType) Then
            $vReturnType[0] = -1
            Return SetError($iError, $iExtended, $vReturnType)
        EndIf
        Return SetError($iError, $iExtended, -1)
    EndIf

    If $aResult[0] = 0 Then
        ; user selected cancel or struct settings incorrect
        If IsArray($vReturnType) Then
            $vReturnType[0] = -1
            Return SetError(-3, 0, $vReturnType)
        EndIf
        Return SetError(-3, 0, -1)
    EndIf

    Local $sColor_picked = DllStructGetData($tChoose, "rgbResult")

    If $iReturnType = 1 Then
        ; return Hex BGR Color
        $vReturn = '0x' & Hex(String($sColor_picked), 6)
    ElseIf $iReturnType = 2 Then
        ; return Hex RGB Color
        $sColor_picked = Hex(String($sColor_picked), 6)
        $vReturn = '0x' & StringMid($sColor_picked, 5, 2) & StringMid($sColor_picked, 3, 2) & StringMid($sColor_picked, 1, 2)
    Else
        ; return RGB COLORREF
        $vReturn = $sColor_picked
    EndIf
    If IsArray($vReturnType) Then
        $vReturnType = __ChooseColor_StructToArray($tCc)
        $vReturnType[0] = $vReturn
        Return $vReturnType
    EndIf
    Return $vReturn
EndFunc   ;==>_ChooseColorHooked

Func _ColorChooseProc($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local Static $hBrush

    Switch $iMsg
        Case $WM_INITDIALOG
            _WinAPI_DwmSetWindowAttribute($hWnd, $DWMWA_USE_IMMERSIVE_DARK_MODE, True)
            $hBrush = _WinAPI_CreateSolidBrush(0x202020)

            ; Find all controls to apply dark theme
            Local $aCtrls = _WinAPI_EnumChildWindows($hWnd, False)
            If @error = 0 Then
                For $i = 1 To $aCtrls[0][0]
                    If $aCtrls[$i][1] = "Button" Then
                        _WinAPI_SetWindowTheme($aCtrls[$i][0], "DarkMode_Explorer")
                        _SendMessage($aCtrls[$i][0], $WM_THEMECHANGED, 0, 0)
                    EndIf
                    If $aCtrls[$i][1] = "Edit" Then
                        _WinAPI_SetWindowTheme($aCtrls[$i][0], "DarkMode_Explorer")
                        _SendMessage($aCtrls[$i][0], $WM_THEMECHANGED, 0, 0)
                        _WinAPI_SetWindowLong($aCtrls[$i][0], $GWL_EXSTYLE, BitAND(_WinAPI_GetWindowLong($aCtrls[$i][0], $GWL_EXSTYLE), BitNOT($WS_EX_CLIENTEDGE)))
                        _WinAPI_SetWindowLong($aCtrls[$i][0], $GWL_EXSTYLE, BitOR(_WinAPI_GetWindowLong($aCtrls[$i][0], $GWL_EXSTYLE), $WS_EX_STATICEDGE))
                        _WinAPI_SetWindowPos($aCtrls[$i][0], 0, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOZORDER, $SWP_FRAMECHANGED))
                    EndIf
                Next
            EndIf

        Case $WM_CTLCOLORSTATIC,  $WM_CTLCOLORDLG
            _WinAPI_SetBkMode($wParam, $TRANSPARENT)
            _WinAPI_SetTextColor($wParam, 0xFFFFFF)
            Return $hBrush

        Case $WM_CTLCOLOREDIT
            _WinAPI_SetBkMode($wParam, $TRANSPARENT)
            _WinAPI_SetTextColor($wParam, 0xFFFFFF)
            Return $hBrush

        Case $WM_CTLCOLORBTN
            Return $hBrush

        Case $WM_DESTROY
            _WinAPI_DeleteObject($hBrush)

    EndSwitch
EndFunc

 

Edited by WildByDesign
Posted

I am following the MSDN info for: To enable a hook procedure for the Color dialog box

I still have to figure out the RegisterWindowMessage stuff:

Quote

The dialog box sends the COLOROKSTRING registered message to the hook procedure when the user clicks the OK button. The hook procedure can reject the selected color and force the dialog box to remain open by returning zero when it receives this message. The hook procedure can force the dialog box to select a particular color by sending the SETRGBSTRING registered message to the dialog box. To use these registered messages, you must pass the COLOROKSTRING and SETRGBSTRING constants to the RegisterWindowMessage function to get a message identifier. You can then use the identifier to detect and process messages sent from the dialog box, or to send messages to the dialog box.

 

Posted
1 hour ago, WildByDesign said:

I still have to figure out the RegisterWindowMessage stuff:

its just to create your own unique WM_ value if you need to use one across apps. 
So you'll see every time you call $WM_MYCODE = _WinAPI_RegisterWindowMessage("myUnique_WM_Code") it'll return the same value - even after the the original process ends.

This registered value will stick around until the end of your windows session.

If you're not working across processes, you'd normally just declare a code in the WM_APP range (0x8000 - 0xBFFF). ie.

Global Const $WM_MYCODE = $WM_APP+ 10

Just be aware AutoIt might be using some of these for it's own purposes!

And to complete the set, there is also the WM_USER range (0x0400 - 0x7FFF) if you need a specify a code for a particular class.  eg. toolbar messages are defined as below.  The messages mean something to the toolbar control, but something else to another control.

#define TB_ENABLEBUTTON         (WM_USER + 1)
#define TB_CHECKBUTTON          (WM_USER + 2)
#define TB_PRESSBUTTON          (WM_USER + 3)
...

 

Posted (edited)

so basically at the top:

Global Const $COLOROKSTRING = _WinAPI_RegisterWindowMessage("commdlg_ColorOK")

then in _ColorChooseProc you can  theoretically

Switch $iMsg
        Case $WM_INITDIALOG
        
        .... etc.
        Case $COLOROKSTRING
            Local $tChooseColor = DllStructCreate($tagCHOOSECOLOR, $lParam)
            ... etc.
            
EndSwitch

Edit:

Without delving too deeply into it, this documentation seems to be wrong...

The dialog box sends the COLOROKSTRING registered message to the hook procedure when the user clicks the OK button.
The hook procedure can reject the selected color and force the dialog box to remain open by returning zero when it receives this message.

Returning 1 keeps the dialog open, 0 closes it and accepts the colour..

Edited by MattyD

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...