为OpenFile通用对话框增添预展功能
用户使用Window应用程序时,经常要打开文件,可有时用户打开文件时却打开了一个不是要求的文件,这显得非常麻烦。因此,许多应用程序便给OpenFile通用对话框增添了预展功能,使得用户在选择文件时,可以先预视其内容。在这里,笔者也向你介绍这种功能的编程方法。 WINDOW的通用对话框OpenFile,在使用,可以安装用户定义的勾子函数。这样,原OpenFile窗口的消息先通过用户的勾子函数过滤。若用户在通用对话框中增加一个Edit控制(此处,以预展文本文件内容为例),处理OpenFile通用对话框中的 CDN_SELCHANGE消息(文件名选择更改消息),即可完成这种功能。 用户定义的勾子函数,WINDOW要求其原形定义如下: typedef UINT (APIENTRY *LPCCHOOKPROC) (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 对于OpenFile通用对话框中的一些常见消息,WINDOWS会向用户定义的勾子发送WM_NOTIFY消息,此时,lParam指向一数据结构如下的指针: typedef struct _OFNOTIFYA
{ NMHDR hdr;
LPOPENFILENAMEA lpOFN;
LPSTR pszFile; // May be NULL
} OFNOTIFYA, FAR *LPOFNOTIFYA;
而NMHDR在WINDOWS定义如下: typedef struct tagNMHDR {
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
其中:hwndFrom 表示发送消息控件的句柄;idFrom表示控件的ID值,而且code表示通告的消息。 在WINDOWS95中通告的消息在commdlg.h中定义如下 #define CDN_SELCHANGE (CDN_FIRST-0x0001) //文件名列表中的文件名更改时发送的消息
#define CDN_FOLDERCHANGE (CDN_FIRST - 0x0002) //路径更改时发送的消息
#define CDN_SHAREVIOLATION (CDN_FIRST - 0x0003) //共享按钮改更时发送的消息
#define CDN_HELP (CDN_FIRST - 0x0004) //HELP按钮按下消息
#define CDN_FILEOK (CDN_FIRST - 0x0005) //OK按钮按下消息
因为此处,我们仅处理用户选择的文件名发生更改时,才预展,因此我们定义的勾子函数中,仅处理CDN_SELCHANGE消息。 另外,当什么文件都预展时,用户会发现WINDOWS的速度明显减慢,因此,我决定在OpenFile对话框中增加一个"是否预展"的CheckBox按钮。只有当用户选中了它时才预展。当然,此时,还需要我们自己处理这个CheckBox发送的消息。 以上问题解决后,我们只需要将一个Edit和一个CheckBox增加到一个无窗体的对话框中,并将WINDOWS本身的OpenFile对话框用一个TEXT控件代替,ID值设置为stc32(定义在dlgs.h中)。 此时还需要将OPENFILENAME结构中的lpfnHook成员,设置成用户定义的勾子函数,将lpTemplateName成员设置成用户定义对话框的ID值(需要用MAKEINTRESOURCE),此外,成员Flags需要设置成OFN_EXPLORER(WINDOW32中使用) OFN_ENABLEHOOK(允许使用用户定义的勾子函数) OFN_ENABLETEMPLATE(允许用户定义的对话框模板)。 现在一切就绪,剩下的工作便是着手编写这一程序,看看这动人的一幕。 以下为我设计的全部源程序。此程序在PWIN95,Borland C++5.0中通过。 //OpenFile.rh
#ifndef _OPENFILE_RH
#define _OPENFILE_RH
#define IDD_OPENFILE 100 //打开文件
#define IDM_FILEOPEN 106 //打开文件菜单命令
#define IDM_EXIT 107 //退出
#define IDE_EDIT 1022 //预展EDIT
#define ID_PRESHOW 1023 //预展CheckBox按钮
#endif
//OpenFile.h
#define STRICT
#ifdef RC_INVOKED
#include
#else
#include
#endif
#define MAX_PATH 260
long APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
BOOL CALLBACK OpenFileNameDlgProc(HWND ,UINT,UINT,LONG);
BOOL InitApplication(HANDLE);
BOOL InitInstance(HANDLE, int);
void CALLBACK SetErrorMsg(HWND,UINT,const char *,char *);
BOOL OpenTheFile( OPENFILENAME *);
BOOL NEAR PASCAL TestNotify(HWND, LPOFNOTIFY);
//OpenFile.RC
#include "openfile.rh"
#include
IDD_OPENFILE DIALOG 0, 0, 251, 130
STYLE DS_3DLOOK DS_CONTROL WS_CHILD WS_VISIBLE WS_CLIPSIBLINGS
FONT 9, "MS Sans Serif"
{
LTEXT "", stc32, 4, 4, 124, 125
CONTROL "以文本方式预展", ID_PRESHOW, "button", BS_AUTOCHECKBOX WS_CHILD
WS_VISIBLE WS_TABSTOP, 132, 121, 104, 9
CONTROL "", IDE_EDIT, "edit", ES_LEFT ES_MULTILINE ES_AUTOVSCROLL
ES_AUTOHSCROLL WS_CHILD WS_VISIBLE WS_BORDER WS_TABSTOP,
132, 4, 116, 112
}
OPENFILEMENU MENU
{
POPUP "&Options"
{
MENUITEM "&File Open...", IDM_FILEOPEN
MENUITEM "E&xit", IDM_EXIT
}
}
//OpenFile.h
#include
#include
#include
#include "openfile.rh"
#include "openfile.h"
HINSTANCE g_hInst; //应用程序的实例句柄
OPENFILENAME OpenFileName; //打开文件结构
char szFile[MAX_PATH]; //文件名
#pragma argsused
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{ MSG msg;
OSVERSIONINFO osVer;
osVer.dwOSVersionInfoSize = sizeof(osVer);
if (!GetVersionEx(&osVer)) //取版本信息不成功返回
return (FALSE);
if (osVer.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
{//不在Window95环境下
MessageBox(NULL, "此程序需要在Windows 95下运行.", "警告", MB_OK );
return (FALSE);
}
if (!InitApplication(hInstance)) //初始化应用程序失败
return (FALSE);
if (!InitInstance(hInstance, nCmdShow))
return (FALSE);
while (GetMessage(&msg,NULL,0,0))
{ TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (msg.wParam);
}
BOOL InitApplication(HANDLE hInstance)
{ WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "OpenFileMenu";
wc.lpszClassName = "OpenFileNameClass";
return (RegisterClass(&wc));
}
BOOL InitInstance( HANDLE hInstance, int nCmdShow)
{ HWND hWnd;
g_hInst = hInstance; //保存当前应用程序的实例句柄
hWnd = CreateWindow(
"OpenFileNameClass",
"为你的OpenFile增加预视功能",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
g_hInst,
NULL
);
if (!hWnd) //窗口不能建立
return (FALSE);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return (TRUE);
}
LONG APIENTRY MainWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam)
{ static HWND hwndEdit;
CHAR lpszHello[] = "请从菜单上选择Open File,可看到OpenFile中的预视功能.";
switch (message) {
case WM_CREATE:
OpenFileName.lStructSize = sizeof(OPENFILENAME); //结构大小
OpenFileName.hwndOwner = hWnd;
OpenFileName.hInstance = g_hInst;
OpenFileName.lpstrFilter= "文本文件名(*.txt)\0*.TXT\0所有文件(*.*)\0*.*\0\0";
OpenFileName.lpstrCustomFilter =NULL;
OpenFileName.nMaxCustFilter = 0;
OpenFileName.nFilterIndex = 0;
OpenFileName.lpstrFile = szFile;
OpenFileName.nMaxFile = sizeof(szFile);
OpenFileName.lpstrFileTitle = NULL;
OpenFileName.nMaxFileTitle = 0;
OpenFileName.lpstrInitialDir = NULL;
OpenFileName.lpstrTitle = "打开文件"; //窗口标题
OpenFileName.nFileOffset = 0;
OpenFileName.nFileExtension = 0;
OpenFileName.lpstrDefExt = "txt"; //扩展文件名
hwndEdit = CreateWindow( //建立一编辑窗口
"EDIT", NULL,
WS_CHILD WS_VISIBLE WS_VSCROLL
ES_LEFT ES_MULTILINE ES_AUTOVSCROLL,
0, 0, 0, 0,
hWnd,
NULL,
(HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
//设置信息
SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM) lpszHello);
return 0;
case WM_SIZE:
MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
return 0;
case WM_COMMAND:
switch( LOWORD( wParam ))
{ case IDM_FILEOPEN: //选中了打开文件菜单项
if(OpenTheFile( &OpenFileName))
SendMessage(hwndEdit, WM_SETTEXT, 0,
(LPARAM)OpenFileName.lpstrFile);
break;
case IDM_EXIT:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (0);
}
//函数作用:打开文件
BOOL OpenTheFile(OPENFILENAME *OpenFileName)
{ OpenFileName->lCustData = NULL;
OpenFileName->lpfnHook = (LPOFNHOOKPROC) OpenFileNameDlgProc;
OpenFileName->lpTemplateName = MAKEINTRESOURCE(IDD_OPENFILE);
OpenFileName->Flags =OFN_EXPLORER OFN_HIDEREADONLY
OFN_ENABLEHOOK OFN_ENABLETEMPLATE;
return(GetOpenFileName(OpenFileName));
}
BOOL NEAR PASCAL TestNotify(HWND hDlg, LPOFNOTIFY pofn)
{ HFILE hFile; //打开文件名柄
long dwBytesRead; long dwFileSize;
LPSTR lpBuf; char szFile[MAX_PATH];
if (pofn->hdr.code!=CDN_SELCHANGE) //文件选择改变
return(FALSE);
if (SendDlgItemMessage(hDlg,ID_PRESHOW,BM_GETCHECK,0,0L)==0)
//用户没有选中预展按钮,则直接返回
return(FALSE);
if (CommDlg_OpenSave_GetSpec(GetParent(hDlg), //获取选中的文件名
szFile, sizeof(szFile)) <= sizeof(szFile))
{ hFile =_lopen((LPSTR)szFile,OF_READ);
if (hFile==HFILE_ERROR)
{SetErrorMsg(hDlg,IDE_EDIT,"文件[%s]不能开",szFile);
return FALSE; //打开文件失败失败,返回
}
dwFileSize =_llseek(hFile,0L,FILE_END); //取文件大小
if (dwFileSize == HFILE_ERROR) //失败,则返回
{ SetErrorMsg(hDlg,IDE_EDIT,"移动文件[%s]指针出错.",szFile);
_lclose(hFile); return FALSE;
}
_llseek(hFile,0L,FILE_BEGIN);
lpBuf = (char *)GlobalAlloc( GMEM_FIXED, dwFileSize );
if (lpBuf == NULL ) //分配内存失败失败
{SetErrorMsg(hDlg,IDE_EDIT,"不能为文件[%s],分配内存",szFile);
_lclose( hFile );
return FALSE;
}
dwBytesRead= _lread(hFile,(LPVOID)lpBuf, dwFileSize);
//读文件到lpBUf处
if (dwBytesRead ==HFILE_ERROR) //读文件失败
{ SetErrorMsg(hDlg,IDE_EDIT,"读文件[%s]失败",szFile);
GlobalFree(lpBuf);
_lclose( hFile );
return FALSE;
}
SetDlgItemText(hDlg,IDE_EDIT,lpBuf);
_lclose(hFile); //关闭文件
GlobalFree(lpBuf);//释放分配的内存
return TRUE;
}
else return(TRUE);
}
#pragma argsused
void CALLBACK SetErrorMsg(HWND hDlg,UINT id,const char *format,char *msg)
{ char buf[MAX_PATH];
wsprintf(buf,format,msg);
SetDlgItemText(hDlg,id,buf);
}
#pragma argsused
BOOL CALLBACK OpenFileNameDlgProc(HWND hDlg, UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{ char buf=0;
OFNOTIFY of;
switch (uMsg)
{ case WM_COMMAND: //用户在PreShow按钮上改变了显示
if(wParam==ID_PRESHOW)
{ if (!SendDlgItemMessage(hDlg,ID_PRESHOW,BM_GETCHECK,0,0L))
//没有选中,则将预展EDIT窗口设置为空
SetDlgItemText(hDlg,IDE_EDIT,&buf);
else
{of.hdr.code=CDN_SELCHANGE; //否则,调用TestNotify处理
TestNotify(hDlg,(LPOFNOTIFY)&of);
}
}
return FALSE;
case WM_NOTIFY:
TestNotify(hDlg, (LPOFNOTIFY)lParam);
return(TRUE);
default:
return FALSE;
}
}