Page 1 of 1

MASM - Customize Open Dialog as icon browser

#1 GunnerInc  Icon User is online

  • "Hurry up and wait"
  • member icon




Reputation: 870
  • View blog
  • Posts: 2,310
  • Joined: 28-March 11

Posted 25 December 2011 - 07:11 PM

I took this from an program I wrote a few years ago. I will show how to modify the Open Common Dialog to include a listview that will display any icons in the selected file along with the resource ordinal or string name.

There are 2 ways you can modify the open dialog:
1. Create controls with CreateWindowEx and set the parent of the control to the open dialog.
2. Create a dialog "template" that will be added to the open dialog.
I will show how to use #2.

Create a dialog with:
No Border, is a child, clip siblings, no sysmenu, no min/max buttons, no scroll bar, not a popup, DS_3DLOOK, DS_CONTROL.
And for this sample 2 controls:
A listview control with the style LVS_ICON
and a "Secret" control that the open dialog will use to position our dialog.
Whatever control you use make sure it is NOT visible and the ID is 1119 this is important as windows will look for a control with this ID and place our control(s) accordingly. This secret control is defined in dlgs.h as stc32.
This control will also dictate the width or height of the open dialog. In the following pics, I made the stc32 control red to show what its placement does.

If you place stc32 at the top of your dialog, our controls will be at the bottom of the open dialog:

Attached Image

If at the bottom, controls will be at top:

Attached Image

If at the left, controls will be on the right, if on right, controls will be on left:

Attached Image

This gives us a very easy way to place our child dialog. You can also hide/position the default controls, change the text, modify explorers listview MANY things can be done. We will hide a few controls we dont' need.

For this tutorial, besides the child dialog for the open dialog, we will need a main dialog with 2 static controls to display our selected icons.

This is our proc for our main dialog (Got a bit lazy and decided to use dialogs :dozingoff:)
MainDlg	proc	hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    
	mov	eax,uMsg
	.if	eax==WM_INITDIALOG		
		invoke  GetDlgItem, hWin, IDC_IMG_LRG
		mov     hImg32, eax
				
		invoke  GetDlgItem, hWin, IDC_IMG_SM
		mov     hImg16, eax
		
    .elseif	eax==WM_COMMAND
		mov edx,wParam
		movzx eax,dx
		shr edx,16
		.if edx==BN_CLICKED
			.if eax==IDC_OK
				invoke  RtlZeroMemory, addr ofn, sizeof ofn
				mov     ofn.lStructSize, sizeof OPENFILENAME
				push    hWin
				pop     ofn.hwndOwner
				push    hInst
				pop     ofn.hInstance
				mov     ofn.lpstrFilter, offset DlgFilter
				mov     ofn.lpstrTitle, offset DlgTitle
				mov     ofn.Flags, OFN_EXPLORER or OFN_ENABLETEMPLATE \
				          or OFN_ENABLEHOOK or OFN_HIDEREADONLY 
				mov     ofn.lpfnHook, offset DlgHook
				mov     ofn.lpTemplateName, IDD_DLG		                
		        invoke  GetOpenFileName, addr ofn

			.elseif eax==IDC_CANCEL
				invoke	SendMessage,hWin,WM_CLOSE,NULL,NULL
			.endif
		.endif
		
	.elseif	eax==WM_CLOSE
	    invoke  SendMessage, hImg32, STM_GETICON, 0, 0
	    invoke  DestroyIcon, eax
        
        invoke  SendMessage, hImg16, STM_GETICON, 0, 0
        invoke  DestroyIcon, eax
        
		invoke	EndDialog,hWin,0
	.else
		mov	eax,FALSE
		ret
	.endif
	mov	eax,TRUE
	ret
MainDlg endp

WM_INITDIALOG
Here we get the handles to our static controls that will display our 32x32 and 16x16 icons.

WM_COMMAND

When you click the choose icon button, we will fill the OPENFILENAME structure then call GetOpenFileName.
lStructSize = as most windows structure, the OS uses this to know how many members to expect.
hwndOwner = the parent of the Open Common Dialog - our main dialog
hInstance = the instance of our app which windows will load our dialog template from.
lpstrFilter = pointer to our string to use as the file filter
lpstrTitle = The caption of the open dialog
Flags = bits to initialize the dialog. Here we will use:
OFN_EXPLORER <---- Need this to use a template
OFN_HIDEREADONLY <---- Hide the read only check box
OFN_ENABLETEMPLATE <---- Tells the OS to use a template
OFN_ENABLEHOOK <---- Need this to use a template
lpfnHook = the address of our hook procedure to process the open dialog and our listview messages
lpTemplateName = the ID of our dialog that we are going to use

Now, the "magic" happens in our DlgHook procedure:
DlgHook proc uses esi edi ebx hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    mov		eax,uMsg
    .if eax==WM_INITDIALOG
        ; Image list for browse for icons dialog
        invoke  ImageList_Create, 32, 32, ILC_MASK or ILC_COLORDDB, 2, 500
        mov     himlDlg, eax

        ; Get handle to open file dialog
        invoke  GetParent, hWin
        mov     ebx, eax
        mov     hParent, eax
        
        ; I don't want these 3 controls, so hide em
        ; Hide file name static control
        invoke  SendMessage, eax, CDM_HIDECONTROL, stc3, 0
        ; Hide file name edit control
        invoke  SendMessage, ebx, CDM_HIDECONTROL, edt1, 0
        ; Hide ok button
        invoke  SendMessage, ebx, CDM_HIDECONTROL, IDOK, 0
        
        ; Now move the next 3 controls up
        ; Adjust file type static top
        invoke  memfill, offset tTR, sizeof RECT, NULL
        invoke  memfill, offset wLp, sizeof POINT, NULL
        
        invoke  GetDlgItem, ebx, stc2
        mov     edi, eax
        
        invoke  GetWindowRect, edi, offset tTR
        invoke  ScreenToClient, ebx, offset wLp
        mov     eax, wLp.x
        add     eax, tTR.left
        mov     esi, wLp.y
        add     esi, tTR.top
        sub     esi, 30
        invoke  SetWindowPos, edi, 0, eax, esi, 0, 0, SWP_NOSIZE

        ; Adjust file type combo top
        invoke  GetDlgItem, ebx, cmb1
        mov     edi, eax
        
        invoke  GetWindowRect, edi, offset tTR
        mov     eax, wLp.x
        add     eax, tTR.left
        sub     esi, 4
        invoke  SetWindowPos, edi, 0, eax, esi, 0, 0, SWP_NOSIZE
        
        ; Adjust cancel button top
        invoke  GetDlgItem, ebx, IDCANCEL
        mov     edi, eax
        
        invoke  GetWindowRect, edi, offset tTR
        mov     eax, wLp.x
        add     eax, tTR.left
        sub     esi, 4
        invoke  SetWindowPos, edi, 0, eax, esi, 0, 0, SWP_NOSIZE
        
        ; Get handle to our listview
        invoke  GetDlgItem, hWin, IDC_LSV1
        mov     hLVDlg, eax
        
        ; Set image list to listview
        invoke  SendMessage, eax, LVM_SETIMAGELIST, LVSIL_NORMAL, himlDlg
  
    .elseif eax==WM_COMMAND
        mov		edx,wParam
        movzx	eax,dx
        shr		edx,16
        .if edx==BN_CLICKED
            .if eax==IDOK

            .elseif eax==IDCANCEL
                invoke  SendMessage, hWin, WM_CLOSE, NULL, NULL
            .endif
        .endif
        
    .elseif eax == WM_NOTIFY
        .if wParam == IDC_LSV1
            mov     eax, lParam
            .if dword ptr(NMHDR ptr [eax]).code == LVN_ITEMCHANGING
                .if dword ptr(NMLISTVIEW ptr [eax]).uNewState == LVIS_FOCUSED or LVIS_SELECTED
                    invoke  ImageList_GetIcon, himlDlg, dword ptr(NMLISTVIEW ptr [eax]).iItem, ILD_TRANSPARENT
                    push    eax
                    invoke  SendMessage, hImg32, STM_SETICON, eax, 0
                    invoke  DestroyIcon, eax
                    pop     eax
                    
                    invoke  CopyImage, eax, IMAGE_ICON, 16, 16, LR_COPYFROMRESOURCE
                    invoke  SendMessage, hImg16, STM_SETICON, eax, 0
                    invoke  DestroyIcon, eax
                    
                    invoke  PostMessage, hParent, WM_CLOSE, 0, 0
                    ret
                .else
                    jmp     PassThrough
                .endif
            .else
                jmp     PassThrough
            .endif
            
        .else
            mov     eax, lParam
            .if dword ptr(NMHDR ptr [eax]).code == CDN_SELCHANGE
                ; Get path of selected file
                mov     byte ptr [DlgFilePath], 0
                invoke  memfill, offset DlgFilePath, MAX_PATH + 1, NULL
                invoke  SendMessage, hParent, CDM_GETFILEPATH, MAX_PATH + 1, offset DlgFilePath
                invoke  PathFindExtension, offset DlgFilePath
                invoke  Cmpi, eax, offset szExtIco
                test    eax, eax
                jz      @F
                call    ShowIcons
                @@:
                jmp     PassThrough
                
            .elseif dword ptr(NMHDR ptr [eax]).code == CDN_FILEOK
                invoke  PathFindExtension, offset DlgFilePath
                invoke  Cmpi, eax, offset szExtIco
                .if eax == 0
                    ; Icon was selected, show 32 and 16 sizes
                    invoke  LoadImage, NULL, offset DlgFilePath, IMAGE_ICON, 32, 32, LR_LOADFROMFILE
                    push    eax
                    invoke  SendMessage, hImg32, STM_SETICON, eax, 0
                    invoke  DestroyIcon, eax
                    pop     eax
                    
                    invoke  CopyImage, eax, IMAGE_ICON, 16, 16, LR_COPYFROMRESOURCE
                    invoke  SendMessage, hImg16, STM_SETICON, eax, 0
                    invoke  DestroyIcon, eax
                    ; close the dialog
                    invoke  SetWindowLong, hWin, DWL_MSGRESULT, FALSE
                .else
                    ; not an ico file, leave dialog open
                    invoke  SetWindowLong, hWin, DWL_MSGRESULT, TRUE
                .endif
                
            .else
                jmp     PassThrough    
            .endif
        .endif
    .elseif eax==WM_CLOSE
        invoke  ImageList_Destroy, himlDlg
        invoke  DestroyWindow, hWin
    .else
        PassThrough:
            mov		eax, FALSE
            ret
    .endif
    
    mov		eax, TRUE
    ret
DlgHook endp


WM_INITDIALOG
We create an Image List for our List view, and get the parent of our child dialog which is the Open Dialog.

Next I hide the Ok button and file name static and edit controls, and move the file type static and combo, and cancel buttons up.

WM_NOTIFY
This is where the fun happens! We check wParam to see where the message is from, if it is from our listview we check to see if the message is LVN_CHANGING, if it is, we then check the state of the item (this way we know something was selected) if an item is selected we:
Get the icon from the image list using the index of the listview item, then we take that icon and display it in our 32x32 and 16x16 controls on our main dialog and then close the open dialog. If it is not of those messages, we pass them on so the OS can handle them.

If wParam isn't our listview ID, then it must be from the Open dialog. We are interested in CDN_SELCHANGE and CDN_FILEOK otherwise we pass the message on to the OS.

CDN_SELCHANGE
We send the message CDM_GETFILEPATH telling the open dialog that we want the path of the selected item in explorers listview, then we get the extenstion and check to see if it is .ico and if it is pass CDM_GETFILEPATH on. If it is not .ico, we process and show the icons with ShowIcon and EnumResNamesProc.

CDN_FILEOK
Here we check to see if the extension is ico, and if it is we load the icon and display it then close the open dialog with: invoke SetWindowLong, hWin, DWL_MSGRESULT, FALSE if the extension is not .ico we keep the open dialog open with: invoke SetWindowLong, hWin, DWL_MSGRESULT, TRUE

ShowIcons proc
LOCAL   hLib:DWORD
    ; Clear imagelist
    invoke  ImageList_Remove, himlDlg, -1

    ; Clear listview
    invoke  SendMessage, hLVDlg, LVM_DELETEALLITEMS, 0, 0
    
    ; is the path a directory?  If yes, leave
    invoke  PathIsDirectory, offset DlgFilePath
    test    eax, eax
    jnz     Done
    
    ; Get icon count
    invoke  ExtractIcon, hInst, offset DlgFilePath, -1
    test    eax, eax
    jnz      @F
    invoke  SetDlgItemInt, hMainDlg, 2002, eax, FALSE
    jmp     Done
    
    @@:
    invoke  SetDlgItemInt, hMainDlg, 2002, eax, FALSE
    ; We have icons! load the file
    invoke  LoadLibraryEx, offset DlgFilePath, 0, LOAD_LIBRARY_AS_DATAFILE
    test    eax, eax
    jz      Done
    mov     hLib, eax
    
    ; Now get all the icon names
    invoke  EnumResourceNames, eax, RT_GROUP_ICON, offset EnumResNamesProc, 0
    invoke  FreeLibrary, hLib
   
   Done:
   ret
ShowIcons endp

We made it here, so the file is not an ico file but an executable. First thing we do is delete all icons from the image list and listview.

Then we check to see if the selected item is a directory, if it is we leave, otherwise we get the count of icons in the file, we do that by passing a -1 to ExtractIcon, if the count is 0, we display 0 for our total and leave. Otherwise we display the total and use LoadLibraryEx with the flag - LOAD_LIBRARY_AS_DATAFILE, what that does well what it doesn't do is "execute" the dll, instead it loads the file as it were a data file.
We then pass the returned handle to EnumResourceNames and the address of our callback proc

invoke EnumResourceNames, eax, RT_GROUP_ICON, offset EnumResNamesProc, 0
What EnumResourceNames will do is, enumerate all resources of RT_GROUP_ICON call our callback proc with either the string name or ordinal of the icon.

EnumResNamesProc proc uses esi hModule:DWORD,lpszType:DWORD, lpszName:DWORD, lParam:DWORD
LOCAL   IconTemp:DWORD
LOCAL   LVParam:DWORD
    invoke  LoadImage, hModule, lpszName, IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR
    mov     IconTemp, eax
    invoke  ImageList_ReplaceIcon, himlDlg, -1, IconTemp
    invoke  DestroyIcon, IconTemp
    invoke  ImageList_GetImageCount, himlDlg
    sub     eax, 1
   
    mov     lvi.imask, LVIF_IMAGE or LVIF_TEXT
    mov     lvi.iItem, eax
    mov     lvi.iSubItem, 0
    mov     lvi.iImage, eax
   
    invoke  HeapAlloc, hMainHeap, HEAP_ZERO_MEMORY, 50
    mov     LVParam, eax
    
    invoke  memfill, LVParam, 50, 0
    invoke  IS_INTRESOURCE, lpszName
    test    eax, eax
    jz      @F
    
    invoke  StrLen, lpszName
    invoke  MemCopy, lpszName, LVParam, eax
    jmp     Continue
    
    @@:
    invoke  dwtoa, lpszName, LVParam
   
    Continue:
        mov     eax, LVParam
        mov     lvi.pszText, eax

        invoke  SendMessage, hLVDlg, LVM_INSERTITEM, 0, addr lvi
        
        invoke  HeapFree, hMainHeap, 0, LVParam

        mov     eax, TRUE
        ret
EnumResNamesProc endp

Lines 4 - 14 we load the current icon and add it to the image list and add it to the LVITEM structure.
Line 20, we check to see if the icon resource name is either a string or ordinal. If ordinal we convert it to a string. Then we add it to our listview.

Put it all together and we have:

Attached Image Attached Image

Modify, experiment and have fun with MASM Assembly!
App and complete source attached.

Attached File(s)



Is This A Good Question/Topic? 0
  • +

Page 1 of 1