浅析C++中的打开文件、保存文件(OPENFILENAME)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangshubo1989/article/details/49283121

首先看看msdn上如何描述的:

Contains information that the GetOpenFileName and GetSaveFileName functions use to initialize an Open or Save As dialog box. After the user closes the dialog box, the system returns information about the user’s selection in this structure.

OPENFILENAME是一个结构体,我们看看这个结构体如何定义的:

typedef struct tagOFN {
  DWORD lStructSize;//指定这个结构的大小,以字节为单位。
  HWND  hwndOwner;//指向所有者对话框窗口的句柄。这个成员可以是任意有效窗口句柄,或如果对话框没有所有者它可以为NULL。
  HINSTANCE  hInstance;
  LPCTSTR    lpstrFilter;
  LPTSTR     lpstrCustomFilter;
  DWORD      nMaxCustFilter;
  DWORD      nFilterIndex;
  LPTSTR     lpstrFile;
  DWORD      nMaxFile;
  LPTSTR     lpstrFileTitle;
  DWORD      nMaxFileTitle;
  LPCTSTR    lpstrInitialDir;
  LPCTSTR    lpstrTitle;
  DWORD      Flags;
  WORD       nFileOffset;
  WORD       nFileExtension;
  LPCTSTR    lpstrDefExt;
  LPARAM     lCustData;
  LPOFNHOOKPROC lpfnHook;
  LPCTSTR     lpTemplateName;
#if (_WIN32_WINNT >= 0x0500)
  void          *pvReserved;
  DWORD         dwReserved;
  DWORD         FlagsEx;
#endif 
} OPENFILENAME, *LPOPENFILENAME;

结构体中每个参数的意义。
hInstance
如果在Flags成员中设置了OFN_ENABLETEMPLATEHANDLE标记,hInstance成员指向包含一个对话框模板的内存对象。如果OFN_ENABLETEMPLATE标记被设置,hInstance是一个指向通过lpTemplateName成员命名的对话框模板的模块。如果两者都没有被设置,这个成员被忽略。
如果OFN_EXPLORER标记被设置,系统使用Explorer风格的默认对话框的子窗口作为指定模板来建立一个对话框。如果OFN_EXPLORER标记没有被设置,系统使用模板建立一个旧风格的对话框。

lpstrFilter
指向一对以空字符结束的过滤字符串的一个缓冲。缓冲中的最后一个字符串必须以两个NULL字符结束。
第一个字符串是过滤器描述的显示字符串(例如,“文本文件”),第二个字符指定过滤样式(例如,“.TXT”)。要为一个显示字符串指定多个过滤样式,使用分号(“;”)分隔样式(例如,“.TXT;.DOC;.BAK”)。一个样式字符串中可以包含有效的文件名字字符及星号(*)通配符。不能在样式字符串中包含空格。
系统不能改变过滤器的次序。它按lpstrFilter指定的次序显示在文件类型组合框中。
如果lpstrFilter是NULL,对话框不能显示任何过滤器。

lpstrCustomFilter
指向一个静态缓冲,它包含了一对以空字符结束的过滤器字符串,这个字符串是为了保留用户选择的过滤样式。第一个字符串是描述定制过滤器的显示字符串,第二个字符串是被用户选择的过滤器样式。第一次你的应用程序建立对话框,你指定的第一个字符串可以是任何非空的字符串。当用户选择了一个文件时,对话框复制当前过滤样式到第二个字符串。保留过滤样式可以是在lpstrFilter缓冲中指定的样式之一,或是用户输入的过滤器样式。在下一次对话框被建立时系统使用这个字符串去初始化用户自定义的文件过滤器。如果nFilterIndex成员是0,对话框使用定制过滤器。
如果这个成员是NULL,对话框不能保留用户自定义过滤器样式。
如果这个成员不是NULL,nMaxCustFilter成员的值必须指定以TCHARs为单位的lpstrCustomFilter缓冲的大小。对于ANSI版本,是字节的个数;对于Unicode版本,是字符的个数。

nMaxCustFilter
指定特意为lpstrCustomFilter准备的以TCHARs为单位的缓冲大小。对于ANSI版本,是字节的个数;对于Unicode版本,是字符的个数。这缓冲应该最小在40个字符长。如果lpstrCustomFilter成员是NULL或是指向NULL的字符串,这个成员被忽略。

nFilterIndex
指定在文件类型控件中当前选择的过滤器的索引。缓冲指向被lpstrFilter包含的一对定义了的过滤器的字符串。过滤器的第一对字符串的索引值为1,第二对为2,等等。0索引指出是通过lpstrCustomFilter指定的定制过滤器。你可以为对话框指定一个索引作为最初的过滤器描述及过滤器样式。当用户选择了一个文件时,nFilterIndex返回当前显示的过滤器的索引。
如果nFilterIndex是0及lpstrCustomFilter是NULL,系统使用在lpstrFilter缓冲中的第一个过滤器。如果所有的三个成员都是0或NULL,系统不使用任何过滤器,在对话框的列表文件中不显示任何文件。

lpstrFile
指向包含初始化文件名编辑控件使用的文件名的缓冲。如果不需要初始值,这个缓冲的第一个字符必须是NULL。当GetOpenFileName或GetSaveFileName函数返回成功时,这个缓冲包含驱动器,路径,文件名,及所选择的文件的扩展名。
如果OFN_ALLOWMULTISELECT标记被设置并且用户选择了多个文件,缓冲包含了当前目录下被选择文件的文件名。对于Explorer风格对话框,目录和文件名字符串是被NULL分开的,在文件名之后有一个额外的NULL。对于旧风格对话框,字符串是被空格分开的并且函数为带有空格的文件名使用短文件名。你可以使用FindFirstFile函数在长短文件名之间转换。如果用户只选择了一个文件,lpstrFile字符串在路径和文件名之间没有分隔。
如果缓冲太小,函数返回FALSE并且CommDlgExtendedError函数返回FNERR_BUFFERTOOSMALL.。既然这样,lpstrFile缓冲的首先两个字节包含必需的大小(字节或字符)。

nMaxFile
指定lpstrFile缓冲的大小,以TCHARs为单位。对于ANSI版本,是字节的个数;对于Unicode版本,是字符的个数。这个缓冲必须足够存储路径和文件名字符串,包含结尾的null字符。如果缓冲太小,GetOpenFileName和GetSaveFileName函数返回假(FALSE)缓冲最小应该在256个字符长。

lpstrFileTitle
指向接收选择的文件的文件名和扩展名的缓冲(不带路径信息)。这个成员可以是NULL。

nMaxFileTitle
指定lpstrFileTitle缓冲的大小,以TCHARs为单位。对于ANSI版本,是字节的个数;对于Unicode版本,是字符的个数。如果lpstrFileTitle是NULL,这个成员被忽略。

lpstrInitialDir
指向以空字符结束的字符串,可以在这个字符串中指定初始目录。Pointer to a null terminated string that can specify the initial directory. 在不同的平台上,为选择初始目录有不同的运算法则。

lpstrTitle
指向在对话框的标题栏中放置的字符串。如果这个成员是NULL,系统使用默认标题(另存为或打开)
Flags位标记的设置,你可以使用来初始化对话框。当对话框返回时,它设置的这些标记指出用户的输入。

nFileExtension
指定从路径开始到通过lpstrFile指定的文件名字符串中扩展名基于0的偏移,以TCHARs为单位。对于ANSI版本,是字节的个数;对于Unicode版本,是字符的个数。例如,如果lpstrFile指向下列的字符串,“c:\dir1\dir2\file.ext”,这个成员包含的值是18。如果用户没有输入一个扩展名并且lpstrDefExt是NULL,这个成员指定的偏移是结束字符NULL。如果用户在文件名中输入一个“.”作为最后的字符,这个成员是0。

lpstrDefExt
指向包含默认扩展名的缓冲。如果用户忘记输入扩展名,GetOpenFileName和GetSaveFileName附加这个扩展名到文件名中。这个字符串可以是任一长度,但但只有头三个字符被附加。字符串不应该包含一个句点(.)。如果这个成员是NULL并且用户忘记了输入一个扩展名,那么将没有扩展名被附加。

lCustData
指定应用程序定义的数据,这数据是能被lpfnHook成员识别的系统传到的钩子程序。当系统发送WM_INITDIALOG消息到程序,消息的lParam参数指向当对话框建立时指定的OPENFILENAME结构。钩子程序可以使用这个指针获得lCustData的值。

lpfnHook
指向一个钩子程序。除非Flags成员中包含OFN_ENABLEHOOK标记,要么这个成员将被忽略。
如果在Flags成员中OFN_EXPLORER标记没有被设置,lpfnHook指向一个OFNHookProcOldStyle钩子程序,这个程序有意的从对话框接收消息。钩子程序返回FALSE传递一个消息到默认的对话框程序或返回TRUE丢弃消息。
如果OFN_EXPLORER被设置,lpfnHook指向一个OFNHookProc钩子程序。这个钩子程序接收从对话框发出的通知消息。这个钩子程序也接收你通过一个子对话框模板定义的附加控件的消息。钩子程序不有意接收默认对话框的标准控件的消息。

lpTemplateName
指向一个以空字符结束的字符串,字符串是对话框模板资源的名字,资源保存在能被hInstance成员识别的模块中。对于有限的对话框资源,这可以是通过MAKEINTRESOURCE返回的值。除非在Flags成员中设置了OFN_ENABLETEMPLATE标记,要么这个成员被忽略。

但是最为一个程序猿,你没必要记住上诉所有的参数含义,只要能满足使用就好了。

特别重要的是,别忘了引入头文件。#include “commdlg.h”

OPENFILENAME opfn;
WCHAR file_name[MAX_PATH];//file name 
ZeroMemory(&opfn, sizeof(OPENFILENAME));
opfn.lStructSize = sizeof(OPENFILENAME);//指定这个结构的大小
opfn.lpstrFilter = L"所有文件\0*.*\0";//指向一对以空字符结束的过滤字符串的一个缓冲。
                                    //缓冲中的最后一个字符串必须以两个  NULL字符结束。
opfn.nFilterIndex = 1;    //指定在文件类型控件中当前选择的过滤器的索引
opfn.lpstrFile = file_name; //指向包含初始化文件名编辑控件使用的文件名的缓冲
opfn.lpstrFile[0] = '\0'; //这个缓冲的第一个字符必须是NULL
opfn.nMaxFile = sizeof(file_name);//指定lpstrFile缓冲的大小,以TCHARs为单位。
                                  //对于ANSI版本,是字节的个数;对于Unicode版本,是字符的个数。
opfn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;  //OFN_FILEMUSTEXIST 指定用户仅可以在文件名登录字段中
                                                     //输入已存在的文件的名字。
                                 //如果这个标记被指定的并且用户输入了一个无效的名字,对话框程序显示一个等待消息框。
                                      //如果这个标记被指定,OFN_PATHMUSTEXIST标记也被使用。
if (GetOpenFileName(&opfn)) 
{
    ...
}

趁热看看GetOpenFileName function:
语法:

BOOL WINAPI GetOpenFileName(
  _Inout_ LPOPENFILENAME lpofn
);

看到参数了吗?
A pointer to an OPENFILENAME structure that contains information used to initialize the dialog box.
我们之前折腾一阵,设置OPENFILENAME结构体的一系列值,就是为了把它的地址(指向OPENFILENAME的指针)传给函数。
看看下面的代码,你会好很多:

------------------------- OpenFileDialog.h --------------------------

#pragma once

#include <Windows.h>
#include <Commdlg.h>
#include <tchar.h>

class OpenFileDialog
{
public:
    OpenFileDialog(void);

    TCHAR*DefaultExtension;
    TCHAR*FileName;
    TCHAR*Filter;
    intFilterIndex;
    intFlags;
    TCHAR*InitialDir;
    HWNDOwner;
    TCHAR*Title;

    bool ShowDialog();
};

------------------------- OpenFileDialog.cpp -------------------------

#include "OpenFileDialog.h"

OpenFileDialog::OpenFileDialog(void)
{
    this->DefaultExtension = 0;
    this->FileName = new TCHAR[MAX_PATH];
    this->Filter = 0;
    this->FilterIndex = 0;
    this->Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
    this->InitialDir = 0;
    this->Owner = 0;
    this->Title = 0;
}

bool OpenFileDialog::ShowDialog()
{
    OPENFILENAME ofn ;

    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = this->Owner;
    ofn.lpstrDefExt = this->DefaultExtension;
    ofn.lpstrFile = this->FileName;
    ofn.lpstrFile[0] = '\0';
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrFilter = this->Filter;
    ofn.nFilterIndex = this->FilterIndex;
    ofn.lpstrInitialDir = this->InitialDir;
    ofn.lpstrTitle = this->Title;
    ofn.Flags = this->Flags;

    GetOpenFileName(&ofn);

    if (_tcslen(this->FileName) == 0) return false;

    return true;
}

同样的道理,既然能open就能save,所以GetSaveFileName这个函数就不再赘述!
补充一点,上面的两段代码都使用了ZeroMemory(&ofn, sizeof(ofn));,那就简单谢谢ZeroMemory的使用吧!
作用:Fills a block of memory with zeros. 文言文的意思就是0初始化一段内存。
语法如下:

void ZeroMemory(
  [in] PVOID  Destination,
  [in] SIZE_T Length
);

第一个参数:A pointer to the starting address of the block of memory to fill with zeros.
第二个参数:The size of the block of memory to fill with zeros, in bytes.
而为了求第二个参数,可以使用sizeof语法,如果你有一点困惑,可以参见博客《浅析C++中sizeof操作符的用法

展开阅读全文

没有更多推荐了,返回首页