1. 程式人生 > >淺析C++中的開啟檔案、儲存檔案(OPENFILENAME)

淺析C++中的開啟檔案、儲存檔案(OPENFILENAME)

    首先看看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操作符的用法》