HookHandle = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC) Hooker, (HINSTANCE)NULL, (DWORD)GetCurrentThreadId());
For our purposes, the window handle and the message are the most important. Much processing will depend on these two parameters.
The main reason we have this hook is that we cannot subclass the controls on the dialog straight away, since some of the controls, such as the list view control and toolbar do not even exist in the common dialog template, which is present in the VC++ include directory. Also, their ids are not known-they are not available in the template, which makes things much harder to work with. Stranger still, there is a list box control with an id lst1 that seems to be in the template for no rhyme or reason. This is hidden, and the list view control sits on top of it, when the dialog shows up.
Two of the most important things when working with windows are its handle and id. One major problem with the sub-classing approach is as follows: In order to sub-class a control, the control must be there first! The list view control on start up shows the folders and files. We can now sub-class it, but then, the damage is already done-it shows the folders and files-we need only the files! Ideally, we need something that intercepts the list view control before it initializes, so that we can remove the folders-and then, we can let the list view control continue to display only the files. Using a hook is most convenient for us since it circumvents certain problems of traditional sub-classing. One of the main advantages of using a WH_CALLBACK type hook is that before a control gets the message, we get it first. Therefore, we can trap an intended message for the list view control, and modify it by changing processing. Further, the CWPSTRUCT pointer passed in to the hook via the LPARAM has vital information that we can put to use-the window handle and message.
The hook is a catchall. In other words, all messages generated go to the hook first. Since we want to select which windows we need to modify, we must first identify the target window handle. This is done by using the GetClassName() API on the window handle obtained via the CWPSTRUCT. GetClassName() returns the class name as a string, of the window were interested in. For the list view controls handle, GetClassName() returns "syslistview32" and for the toolbar, it returns "toolbarwindow32". And for the last control we want to modify, the edit control, it returns "edit".
The following code shows how to obtain the class name for a control whose handle is identified in the CWPSTRUCT pointer:
CWPSTRUCT *x = (CWPSTRUCT*)lParam; GetClassName(x -> hwnd, szClassName, MAX_CHAR);
Using the relevant class names, we do processing accordingly. For example, if it is a "syslistview32" based control, do something, if it is a "toolbarwindow32" based control, do something else, etc. This is achieved by making simple calls to strcmp().
if(strcmp(_strlwr(szClassName), "syslistview32") == 0)
{
switch(x -> message)
{
case WM_NCPAINT :
case LAST_LISTVIEW_MSG : // Magic message sent after all items are inserted
int count = ListView_GetItemCount(x -> hwnd);
for(int i = 0; i < count; i++)
{
item.mask = LVIF_TEXT | LVIF_PARAM;
item.iItem = i;
item.iSubItem = 0;
item.pszText = szItemName;
item.cchTextMax = MAX_CHAR;
ListView_GetItem(x -> hwnd, &item);
if(GetFileAttributes(szItemName) == FILE_ATTRIBUTE_DIRECTORY)
ListView_DeleteItem(x -> hwnd, i);
}
break;
} // end switch
HideToolbarBtns(hWndToolbar);
} // end if
Since we now have a handle to the list view control, we can perform ordinary list view control operations-in this case, we simply run down the entire list of items, checking to see if any item has the FILE_ATTRIBUTE_DIRECTORY attribute set-which would mean it is a directory. If so, we delete it. Finally, we hide the toolbars buttons by calling the helper function HideToolbarBtns() that passes receives the toolbars handle. How did we come to have the handle of the toolbar? The following code does this-it simply saves the toolbars handle for later use:
if(strcmp(_strlwr(szClassName), "toolbarwindow32") == 0)
{
if(!CCustomFileDlg::OnceOnly) // Save toolbar's handle only once
{
hWndToolbar = x -> hwnd;
++CCustomFileDlg::OnceOnly;
}
}
void HideToolbarBtns(HWND hWndToolbar)
{
TBBUTTONINFO tbinfo;
tbinfo.cbSize = sizeof(TBBUTTONINFO);
tbinfo.dwMask = TBIF_STATE;
tbinfo.fsState = TBSTATE_HIDDEN | TBSTATE_INDETERMINATE;
::SendMessage(hWndToolbar,TB_SETBUTTONINFO,
(WPARAM)TB_BTN_UPONELEVEL,(LPARAM)&tbinfo);
::SendMessage(hWndToolbar,TB_SETBUTTONINFO,
(WPARAM)TB_BTN_NEWFOLDER,(LPARAM)&tbinfo);
}
const int TB_BTN_UPONELEVEL = 40961; const int TB_BTN_NEWFOLDER = 40962;
Finally, we need to make sure that the user cannot change directories by entering different paths in the edit box. For this, we need to trap the Return key event, which happens when the user presses enter after keying in a path inside the edit box. Here is the code:
if(strcmp(_strlwr(szClassName), "edit") == 0)
{
switch(x -> message)
{
case EDIT_ENTER: // User presses Enter
::GetWindowText(x -> hwnd, szEditBuff, MAX_CHAR);
if(ParseForDelims(szEditBuff))
::SetWindowText(x -> hwnd, "");
break;
} // end switch
} // end if
BOOL ParseForDelims(const TCHAR* szEditBuff)
{
for(int i = 0; i < (int)strlen(szEditBuff); ++i)
if(szEditBuff[i] == '\\' || szEditBuff[i] == ':' || szEditBuff[i] == '.')
return TRUE;
return FALSE;
}
if(ParseForDelims(szEditBuff)) ::SetWindowText(x -> hwnd, "");
You must have javascript enabled in order to post comments.
Comments
Vist a problems
Posted by gintonic42 on 08/03/2010 06:03amHy, i have used this for my own openfile dialog. Now i have problems, when my app is running under Vista and W7. Anybody has an idea, of improving my software to run under Vista? I am using MSVC 2008 C++ MFC Thanks Bodo
ReplyUp one level
Posted by Legacy on 03/27/2002 12:00amOriginally posted by: Kazima
Replysolution to backspace problem
Posted by Legacy on 03/14/2002 12:00amOriginally posted by: Ryan J O'Boril
Replyhow to use CFileDialog to select
Posted by Legacy on 03/07/2002 12:00amOriginally posted by: prasanna
how to use CFileDialog class ,such that it provide a way for the user to select a file from the displayed items.
we should get the name of file &path
please reply soon
ReplyThank's in advance
does not work as it should
Posted by Legacy on 02/24/2002 12:00amOriginally posted by: Lolo
user still can access directory if he presses the backspace key
ReplyChange MessageBox occur in CFileDialog
Posted by Legacy on 10/11/2001 12:00amOriginally posted by: Vo Thanh Hoa
I can change all the texts in the appearance of the File Dialog ( "Look in", "OK", "Cancel") to my own texts. But I can not catch the message box showing up when you enter a wrong path to customize it.
ReplyI have try many ways without success ( message handle "OnFileNameOK", hook procedure in OPENFILESTRUTURE)
Code Rocks!
Posted by Legacy on 10/27/2000 12:00amOriginally posted by: piperatom
ReplyFTP/HTTP open dialog
Posted by Legacy on 04/20/2000 12:00amOriginally posted by: Roman Panlyuk
I'd like to customize Open/Save dialogs so that I could browse web directories af if they were files. All I need is to be able to intercept ChengeDir action and fill it with my own items. I do not see any events for doung this. Am I bnlind or this is impossible.
Please also answer to e-mail john@eleks.lviv.ua
Thanks,
ReplyRoman
Very cool with a little hint...
Posted by Legacy on 04/08/2000 12:00amOriginally posted by: Klaus Bucher
ReplyResource ID's for the Common File Dialog
Posted by Legacy on 04/07/2000 12:00amOriginally posted by: Niall
I am trying to find the ID's for the toolbar buttons
in the top right hand corner of the dialog e.g.
the Move Up button of the commom File Open Dialog
ReplyLoading, Please Wait ...