Page 1 of 1

MASM - ChooseColor Dialog

#1 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 864
  • View blog
  • Posts: 2,308
  • Joined: 28-March 11

Posted 05 January 2012 - 06:37 PM

Ah, the simple and plain ChooseColor Dialog used for the times you need a user to select a color and don't want to write a custom dialog. The ChooseColor Dialog is one of the dialogs that has NOT changed (if any) through the many versions of Windows. You CAN customize the dialog if you wanted to, but you would have to modify color.dlg from the SDK and include the customized dialog into your resource section and tell ChooseColor to use that template instead of the default. Not going to describe that one here, maybe at a later time after I customize it.

What I will show is how to call ChooseColor, load and save the 16 "custom" colors from file, load and save the selected color to file, and get the Hue, Sat, Lum, Red, Blue and Green from selected color.

Code first then a breakdown:
include masm32rt.inc

IDD_MAIN       equ      1000
IDC_COLOR      equ      1001
BTN_SELECT     equ      1002
IDC_HUE        equ      1004
IDC_SAT        equ      1005
IDC_LUM        equ      1007
IDC_RED        equ      1009
IDC_GREEN      equ      1011
IDC_BLUE       equ      1013

; ChooseColor Dialog equates
; From ColorDlg.h
COLOR_HUE       equ     703
COLOR_SAT       equ     704
COLOR_LUM       equ     705
COLOR_RED       equ     706
COLOR_GREEN     equ     707
COLOR_BLUE      equ     708

DialogProc PROTO hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

.data
szFile          BYTE    "settings.ini", 0
szCustomColors  BYTE    "Custom Colors", 0
szColors        BYTE    "Colors", 0
szStartColor    BYTE    "Start", 0
cc              CHOOSECOLOR <sizeof cc, ?, ?, 0, ?, CC_FULLOPEN or CC_RGBINIT or CC_ENABLEHOOK, NULL, offset CCHookProc, NULL>
DefColors       DWORD   0ff0000h,0ff00h,0ffh,0ffff00h,0ffffh
                DWORD   0cc0000h,0cc00h,0cch,0cccc00h,0cccch
                DWORD   0800000h,08000h,080h,0808000h,08080h
                DWORD   0400000h
ClrSize         equ     ($ - DefColors)

.data?
hInst           DWORD   ?
MyColors        DWORD   ClrSize dup (?)
lpszFile        BYTE    MAX_PATH + 1 dup (?)
hColorCtl       DWORD   ?
hBrush          DWORD   ?
hMain           DWORD   ?

.code
start:
    invoke  GetAppPath, offset lpszFile
    invoke  szCatStr, offset lpszFile, offset szFile

    invoke  GetModuleHandle, NULL
    mov     hInst, eax
	invoke	DialogBoxParam, eax, IDD_MAIN, NULL, offset DialogProc, NULL
    invoke  ExitProcess, 0
    
DialogProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
	mov		eax,uMsg
	.if eax==WM_INITDIALOG
	    push    hWin
	    pop     hMain
	    
	    invoke  GetDlgItem, hWin, IDC_COLOR
	    mov     hColorCtl, eax
	    
        invoke exist, offset lpszFile
        .if eax !=0
            invoke  GetPrivateProfileStruct, offset szCustomColors, offset szColors, offset MyColors, lengthof MyColors, offset lpszFile
            invoke  GetPrivateProfileStruct, offset szCustomColors, offset szStartColor, offset cc.rgbResult, sizeof CHOOSECOLOR.rgbResult, offset lpszFile
        .else
            invoke  MemCopy, offset DefColors, offset MyColors, ClrSize
            mov     cc.rgbResult, 00B68427H
        .endif
        
	.elseif eax==WM_COMMAND
		mov		edx,wParam
		movzx	eax,dx
		shr		edx,16
		.if edx==BN_CLICKED
			.if eax==BTN_SELECT
			    push    hInst
			    pop     cc.hInstance
			    push    hWin
			    pop     cc.hwndOwner
			    mov     cc.lpCustColors, offset MyColors
                invoke  ChooseColor, offset cc
                test    eax, eax
                jz      @F
                invoke  DeleteObject, hBrush
                invoke  CreateSolidBrush, cc.rgbResult
                mov     hBrush, eax
                invoke  InvalidateRect, hColorCtl, NULL, TRUE
                @@:
			.endif
		.endif
		
    .elseif eax == WM_CTLCOLORSTATIC
        mov     ecx, hColorCtl
        .if lParam == ecx
           mov      eax, hBrush
           ret
        .endif

	.elseif eax==WM_CLOSE
        invoke  WritePrivateProfileStruct, offset szCustomColors, offset szColors, offset MyColors, lengthof MyColors, offset lpszFile
        invoke  WritePrivateProfileStruct, offset szCustomColors, offset szStartColor, offset cc.rgbResult, sizeof CHOOSECOLOR.rgbResult, offset lpszFile
	    invoke  DeleteObject, hBrush
		invoke  EndDialog,hWin,NULL
	.else
		mov		eax,FALSE
		ret
	.endif
	mov		eax,TRUE
	ret

DialogProc endp

CCHookProc proc hDlg:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
	mov		eax,uMsg
	.if eax==WM_COMMAND
		mov		edx,wParam
		movzx	eax,dx
		shr		edx,16
		.if edx==BN_CLICKED
			.if eax==IDOK
			    invoke  GetDlgItemInt, hDlg, COLOR_HUE, NULL, FALSE
			    invoke  SetDlgItemInt, hMain, IDC_HUE, eax, FALSE

			    invoke  GetDlgItemInt, hDlg, COLOR_SAT, NULL, FALSE
			    invoke  SetDlgItemInt, hMain, IDC_SAT, eax, FALSE

			    invoke  GetDlgItemInt, hDlg, COLOR_LUM, NULL, FALSE
			    invoke  SetDlgItemInt, hMain, IDC_LUM, eax, FALSE

			    invoke  GetDlgItemInt, hDlg, COLOR_RED, NULL, FALSE
			    invoke  SetDlgItemInt, hMain, IDC_RED, eax, FALSE

			    invoke  GetDlgItemInt, hDlg, COLOR_GREEN, NULL, FALSE
			    invoke  SetDlgItemInt, hMain, IDC_GREEN, eax, FALSE

			    invoke  GetDlgItemInt, hDlg, COLOR_BLUE, NULL, FALSE
			    invoke  SetDlgItemInt, hMain, IDC_BLUE, eax, FALSE
			.endif
		.endif
    .endif
    xor     eax, eax
    ret
CCHookProc endp

end start

Yeah, I am a bit "lazy" lately and am using dialogs...
; ChooseColor Dialog equates
; From ColorDlg.h
COLOR_HUE       equ     703
COLOR_SAT       equ     704
COLOR_LUM       equ     705
COLOR_RED       equ     706
COLOR_GREEN     equ     707
COLOR_BLUE      equ     708

These are the control IDs for the 6 edit controls on the ChooseColor dialog. All of the control IDs can be found in ColorDlg.h (Part of the SDK)
szFile          BYTE    "settings.ini", 0
szCustomColors  BYTE    "Custom Colors", 0
szColors        BYTE    "Colors", 0
szStartColor    BYTE    "Start", 0
cc              CHOOSECOLOR <sizeof cc, ?, ?, ?, ?, CC_FULLOPEN or CC_RGBINIT or CC_ENABLEHOOK, NULL, offset CCHookProc, NULL>
DefColors       DWORD   0ff0000h,0ff00h,0ffh,0ffff00h,0ffffh
                DWORD   0cc0000h,0cc00h,0cch,0cccc00h,0cccch
                DWORD   0800000h,08000h,080h,0808000h,08080h
                DWORD   0400000h
ClrSize         equ     ($ - DefColors)

We will be using ini file APIs to save and load our colors, so our file will be called settings.ini
cc is a "pre-initialized" CHOOSECOLOR structure:
lStructSize We will let the Assembler fill in the structure size,
hwndOwner We don't know the handle of the dialogs owner, so we will leave that as uninitialized (fill it in later)
hInstance Same for the hInstance.
rgbResult, we will leave this uninitialized for now, and fill it in later. If the CC_RGBINIT flag is set (In our case it is) we set the initially set color here.
lpCustColors this is an array of 16 DWORDS that are the custom colors the dialog displays, we will fill this in later.
Flags We are using - CC_FULLOPEN or CC_RGBINIT or CC_ENABLEHOOK
There are a few other flags we can use:
  • CC_ANYCOLOR
  • CC_ENABLETEMPLATE
  • CC_ENABLETEMPLATEHANDLE
  • CC_PREVENTFULLOPEN
  • CC_SHOWHELP
  • CC_SOLIDCOLOR

  • CC_FULLOPEN This causes the ChooseColor Dialog to fully open, without this, the user has to press the "Define Custom Color" button.
  • CC_RGBINIT This tells the dialog to use the color we give it as the initial color
  • CC_ENABLEHOOK This tells the dialog we want to hook into it and pass its messages on to our hook proc

CC_ENABLEHOOK
We will be hooking the BN_CLICKED message and will be looking for a message from the Ok button.
Here is how hooking the ChooseColor dialog works:
You call ChooseColor, and the dialog processes its own WM_INITDIALOG message to set up the dialog, then passes on the WM_INITDIALOG message to our hook proc. If we wanted to get messages from any of the controls, we would subclass the controls here in our WM_INITDIALOG handler.

lCustData this is an application defined value passed to our hook proc, lParam will contain a pointer to the CHOOSECOLOR structure we passed to ChooseColor. We don't need it so it is NULL
lpfnHook the address of our hook proc
lpTemplateName This would be the customized dialog in our resource section. We don't need, so NULL

DefColors is an array of 16 colors we will use to initialize the dialog with if it is the first time our sample app is starting and there is no settings.ini file.
MyColors is an array of DWORD we will pass to the ChooseColor dialog. The dialog will update this array each time a custom color is changed.


DialogProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
	mov		eax,uMsg
	.if eax==WM_INITDIALOG
	    push    hWin
	    pop     hMain
	    
	    invoke  GetDlgItem, hWin, IDC_COLOR
	    mov     hColorCtl, eax
	    
        invoke exist, offset lpszFile
        .if eax !=0
            invoke  GetPrivateProfileStruct, offset szCustomColors, offset szColors, offset MyColors, lengthof MyColors, offset lpszFile
            invoke  GetPrivateProfileStruct, offset szCustomColors, offset szStartColor, offset cc.rgbResult, sizeof CHOOSECOLOR.rgbResult, offset lpszFile
        .else
            invoke  MemCopy, offset DefColors, offset MyColors, ClrSize
            mov     cc.rgbResult, 00B68427H
        .endif
        
	.elseif eax==WM_COMMAND
		mov		edx,wParam
		movzx	eax,dx
		shr		edx,16
		.if edx==BN_CLICKED
			.if eax==BTN_SELECT
			    push    hInst
			    pop     cc.hInstance
			    push    hWin
			    pop     cc.hwndOwner
			    mov     cc.lpCustColors, offset MyColors
                invoke  ChooseColor, offset cc
                test    eax, eax
                jz      @F
                invoke  DeleteObject, hBrush
                invoke  CreateSolidBrush, cc.rgbResult
                mov     hBrush, eax
                invoke  InvalidateRect, hColorCtl, NULL, TRUE
                @@:
			.endif
		.endif
		
    .elseif eax == WM_CTLCOLORSTATIC
        mov     ecx, hColorCtl
        .if lParam == ecx
           mov      eax, hBrush
           ret
        .endif

	.elseif eax==WM_CLOSE
        invoke  WritePrivateProfileStruct, offset szCustomColors, offset szColors, offset MyColors, lengthof MyColors, offset lpszFile
        invoke  WritePrivateProfileStruct, offset szCustomColors, offset szStartColor, offset cc.rgbResult, sizeof CHOOSECOLOR.rgbResult, offset lpszFile
	    invoke  DeleteObject, hBrush
		invoke  EndDialog,hWin,NULL
	.else
		mov		eax,FALSE
		ret
	.endif
	mov		eax,TRUE
	ret

DialogProc endp

In WM_INITDIALOG, we check to see if our settings file exists and if it does we:
Use an underused (IMO) ini API - GetPrivateProfileStruct to load our 16 custom colors and the start color.
If the settings file does NOT exist, we copy our Default colors into MyColors array and set the initial color to a bluey color.

In WM_COMMAND/BN_CLICKED we fill in some missing fields of the cc struct:
We fill in hInstance, hwndOwner and lpCustColors and call ChooseColor and pass it our cc struct.
When the dialog is closed we check to see if the user clicked OK or cancel/X button; if the user selected a color we:
Delete our brush handle, create a new brush with the color from rgbResult, save the brush handle and then force a re paint of our static control on our window to display the selected color. That is done with InvalidateRect.

In WM_CTLCOLORSTATIC we check to see if the message is for our control and if it is we return the handle to our brush.

WM_CLOSE
We save our custom colors array and the selected color to our settings file with WritePrivateProfileStruct, and finally destroy our custom brush.


In our Dialog hook proc, we are only interested in the WM_COMMAND message and if the user clicked OK, this is where we get the text from the Hue, Sat, Lum, Red, Green, and Blue edit controls on the ChooseColor Dialog.
We return zero from our hook proc for ALL messages so the ChooseColor dialog will continue its processing of messages. If we return a non zero, ChooseColor will not process the message. So, if say the user selected red, and you don't want to allow red, return a non zero until the user selected anything but red.

Source and sample program attached.

Attached File(s)


This post has been edited by GunnerInc: 20 January 2012 - 06:03 PM


Is This A Good Question/Topic? 0
  • +

Page 1 of 1