SetCtlColors with variables
Author: Afrow UK (talk, contrib) |
Description
This is an alternative to the built-in SetCtlColors which allows variables for the colours. It does not support /BRANDING but it does support "transparent" for the background colour.
The reason the built-in SetCtlColors does not support variables is because it converts the given colours from RGB to BGR (required for COLORREF) at compile time before storing the converted values in the executable header inside a "ctlcolors" structure. My function simulates the code that is built into NSIS by allocating its own ctlcolors structure and passing its memory pointer to the chosen control using SetWindowLong/GWL_USERDATA. For this reason, the instance of the ctlcolors structure must remain in memory until the control is destroyed, or to be on the safe side, until .onGUIEnd is called. If we free the allocated memory any sooner (System::Free), NSIS can't read from the ctlcolors structure and a memory access violation will occur.
The Function
Function SetCtlColors Exch $R0 Exch Exch $R1 Exch Exch 2 Exch $R2 Push $R3 Push $R4 !if "${NSIS_PTR_SIZE}" > 4 !error "TODO: Not 64-bit compatible, 64-bit uses a different ctlcolors struct" !endif !define ctlcolors (i,i,i,i,i,i)i !define BS_SOLID 0 !define BS_NULL 1 !define TRANSPARENT 1 !define OPAQUE 2 !define CC_TEXT 1 !define CC_BK 4 !define CC_BKB 16 !define GWL_USERDATA -21 !macro RGB2BGR Var IntOp $R3 ${Var} & 0xFF IntOp $R3 $R3 << 16 IntOp $R4 ${Var} & 0xFF00 IntOp $R3 $R3 | $R4 IntOp $R4 ${Var} & 0xFF0000 IntOp $R4 $R4 >> 16 IntOp ${Var} $R3 | $R4 !macroend !insertmacro RGB2BGR $R1 ${If} $R0 == transparent System::Call *${ctlcolors}(R1,0,${BS_NULL},0,${TRANSPARENT},${CC_TEXT}|${CC_BKB}).R0 ${Else} !insertmacro RGB2BGR $R0 System::Call *${ctlcolors}(R1,R0,${BS_SOLID},0,${OPAQUE},${CC_TEXT}|${CC_BK}|${CC_BKB}).R0 ${EndIf} System::Call user32::SetWindowLong(iR2,i${GWL_USERDATA},lR0) Pop $R4 Pop $R3 Pop $R2 Pop $R1 Exch $R0 FunctionEnd !macro SetCtlColors Hwnd Text Background Memory Push ${Hwnd} Push ${Text} Push ${Background} Call SetCtlColors Pop ${Memory} !macroend !define SetCtlColors `!insertmacro SetCtlColors`
Usage
Var SetCtlColorsVar1 Section FindWindow $R0 `#32770` `` $HWNDPARENT GetDlgItem $R0 $R0 1006 StrCpy $R1 0xFF0000 StrCpy $R2 0x00FF00 ${SetCtlColors} $R0 $R1 $R2 $SetCtlColorsVar1 SectionEnd Function .onGUIEnd System::Free $SetCtlColorsVar1 FunctionEnd
The above code is the same as SetCtlColors $R0 0xFF0000 0x00FF00. Notice the System::Free call at the end in .onGUIEnd. You will have a memory leak if you don't use it. Also note that for each call to SetCtlColors you will need a new SetCtlColorsVar to store a pointer to the allocated memory. Reusing SetCtlColorsVar1 (in this example) is not an option.