UI通用对话框多少会一点之浏览文件夹与遍历目录
要想让用户选择一个文件夹,可以调用SHBrowseForFolder()
函数∶
PIDLIST_ABSOLUTE SHBrowseForFolder(_in_ BROWSEINFO lpbi);
函数返回值为PIDLIST_ABSOLUTE数据类型,在shtypes.h头文件中定义如下︰
#define PIDLIST_ABSOLUTE LPITEMIDLIST
typedef ITEMIDLIST *LPITEMIDLIST
typedef struct _ITEMIDLIST
{
SHITEMID mkid;
} ITEMIDLIST;
typedef struct _SHITEMID
{
USHORT cb;
BYTE abID[ 1 ];
}SHITEMID;
返回值类型PIDLIST_ABSOLUTE
指定所选文件夹相对于命名空间根目录的位置。如果用户在对话框中单击了"取消"
按钮、关闭了对话框或发生了错误,则返回值为NULL.
lpbi参数是一个指向BROWSEINFO结构的指针,BROWSEINFO结构在ShlObj.h
头文件中定义如下∶
typedef struct _browseinfo {
HWND hwndOwner; //对话框的拥有者窗口句柄
PCIDLIST_ABSOLUTE pidlRoot; //开始浏览的根文件夹的位置,不需要可以设置为NULL
LPTSTR pszDisplayName;//返回用户选择的文件夹名称的缓冲区
LPCTSTR lpszTitle; //显示在对话框上部静态控件中的文字
UINT ulFlags;//标志位
BFFCALLBACK lpfn;
LPARAM lParam;
int ilmage;
}BROWSEINFO,*PBROWSEINFO,*LPBROWSEINFO;
- ulFlags字段指定标志位,常用的标志如下表所示。
标识宏 | 含义 |
---|---|
BIF_RETURNONLYFSDIRS | 如果用户选择不属于文件系统的文件夹(例如网络、家庭组),则对话框的“确定"按钮将显示为灰色 |
BIF_NEWDIALOGSTYLE | 使用新的用户界面,新的用户界面具有多项新功能 |
BIF_EDITBOX | 在对话框中包含一个编辑控件,允许用户输入文件夹名称 |
BIF_USENEWUI | 使用新用户界面,对话框中包含一个编辑控件,相当于BIF_NEWDIALOGSTYLE |
BIF_BROWSEINCLUDEFILES | 对话框中显示文件和文件夹,通常不设置该标志 |
BIF_BROWSEINCLUDEURLS | 对话框可以显示URL快捷方式,还必须设置BIF_USENEWUI和BIF_BROWSEINCLUDEFILES标志,通常不设置该标志 |
SHBrowseForFolder()
函数返回一个PIDLIST_ABSOLUTE类型值,即指向ITEMIDLIST结构的指针。不必深究这个结构,因为使用SHGetPathFromIDList()
函数可以很方便地将它转换成目录名称字符串(完整路径)∶
BOOL SHGetPathFromIDList
(
_in_ PCIDLIST_ABSOLUTE pid, //指向ITEMIDLIST结构的指针
_out_LPTSTR pszPath //返回目录名称的缓冲区
);
要逼历一个目录中的子目录或文件,首先调用FindFirstFile()
函数。该函数返回一个HANDLE类型的查找句柄hFindFile,并返回找到的第一个子目录或文件的信息﹔如果FindFirstFile()
函数执行成功,则接下来可以利用hFindFile句柄循环调用FindNextFile()
函数继续查找其他目录或文件,直到FindNextFile()
函数返回FALSE为止;最后调用FindClose()
函数关闭hFindFile查找句柄。使用这几个函数查找文件的代码通常如下所示∶
wWIN32_FIND_DATA fd = { 0 };
//遍历目录
hFindFile = FindFirstFile(szDir, &fd);
if (hFindFile != INVALID_HANDLE_VALUE)
{
do
{
//处理本次找到的文件
} while (FindNextFile(hFindFile, &fd));
FindClose(hFindFile);
}
- lpFileName参数指定要查找的文件名。如果文件名中不包含路径,那么将在当前目录中查找,包含路径的话将在指定路径中查找。在文件名中可以使用通配符
"*"
或"?"
指定查找符合指定特征的文件。下面是文件名格式的几个示例︰
c:\Windows\*.* //在C:\Windows目录中查找所有类型的文件
c.\Windows\System32\*.dll //在C:\Windows\System32目录中查找所有扩展名为.dll的文件
C:\Windows\System.ini //在C:\Windows目录中查找System.ini文件
C:\Windowsla???.* //在C:\Windows目录中查找所有以a开头的长度为4个字符的任何文件
Test.dat //在当前目录中查找Test.dat文件
*.*//在当前目录中查找所有文件
lpFindFileData
参数是一个指向查找结构WIN32_FIND_DATA
的指针。该结构返回找到的目录或文件的信息,在minwinbase.h
头文件中定义如下∶
typedef struct _WIN32_FIND_DATA
{
DWORD dwFileAttributes;//文件系统属性
FILETIME ftCreationTime; // FILETIME格式的文件创建时间
FILETIME ftLastAccessTime; //FILETIME格式的最后访问时间
FILETIME ftLastWriteTime; //FILETIME格式的最后修改时间
DWORD nFileSizeHigh; //文件大小的高32位DWORD值,以字节为单位
DWORD nFileSizeLow; //文件大小的低32位DWORD值,以字节为单位
DWORD dwReserved0; //保留字段
DWORD dwReservedl; //保留字段
TCHAR cFileName[MA×_PATH] //文件名称(不包括路径)
TCHAR cAlternateFileName[14];//该文件的替代名称,8.3文件名格式,通常用不到
} WIN32_FIND_DATA,*PWIN32_FIND_DATA,*LPWIN32_FIND_DATA;
- dwFileAttributes字段包含文件系统属性,通过这个字段可以检查找到的究竟是一个子目录还是一个文件,以及其他文件系统属性,常见的属性如下表所示。
属性宏 | 含义 |
---|---|
FILE_ATTRIBUTE_NORMAL | 普通文件 |
FILE_ATTRIBUTE_DIRECTORY | 找到的是一个目录 |
FILE_ATTRIBUTE_READONLY | 只读文件 |
FILE_ATTRIBUTE_TEMPORARY | 用于临时存储的文件 |
FILE_ATTRIBUTE_HIDDEN | 隐藏文件或目录 |
FILE_ATTRIBUTE_SYSTEM | 操作系统使用的文件或目录 |
FILE_ATTRIBUTE_ARCHIVE | 存档文件或目录 |
FILE_ATTRIBUTE_COMPRESSED | 压缩的文件或目录 |
FILE_ATTRIBUTE_ENCRYPTED | 已加密的文件或目录 |
如果FindFirstFile()
函数执行成功,则返回值是在后续调用FindNextFile()
或FindClose()
时使用的查找句柄,而lpFindFileData参数指向的WIN32_FIND_DATA结构包含找到的第一个目录或文件的信息﹔如果函数执行失败或无法从lpFileName参数指定的搜索字符串中找到目录或文件,则返回值为INVALID_HANDLE_VALUE(-1),这种情况下的IpFindFileData参数指向的结构的内容不确定。
FindNextFile函数以查找句柄和查找结构为参数继续查找目录或文件∶
BOOL WINAPl FindNextFile(
_in_ HANDLEhFindFile, //查找句柄
_out_ LPWIN32_FlIND_DATA lpFindFileData //指向查找结构WIN32_FIND_DATA的指针
);
如果函数执行成功,则返回值为TRUE,lpFindFileData参数指向的结构包含找到的下一个目录或文件的信息﹔如果函数执行失败,则返回值为FALSE,这种情况下的lpFindFileData参数指向的结构的内容不确定。
下面我们来做一个小练习,调用SHBrowseForFolder()
函数创建一个浏览文件夹对话框,然后调用SHGetPathFromIDList()
函数把上一个函数的返回值转换为完整目录名称字符串,拼接一个查找字符串szSearch
用于FindFirstFile()
函数,接着就是FindNextFile(hFindFile,&findData)
的循环调用。如果找到的是一个目录,那么应该递归往下一层找。这有点麻烦,本例中仅处理了找到的各种文件,例如普通文件、临时文件、隐藏文件等;
WIN32实现
- resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 SHBrowseForFolder.rc 使用
//
#define IDD_MAIN 101
#define IDC_STATIC_DIR 1001
#define IDC_LIST_FILENAME 1002
#define IDC_BTN_BROWSE 1003
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1004
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
- SHBrowseForFolder.cpp
#include <windows.h>
#include <Shlobj.h>
#include <tchar.h>
#include <strsafe.h>
#include "resource.h"
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE g_hInstance;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
g_hInstance = hInstance;
// 创建模态对话框
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
}
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hwndList;
PIDLIST_ABSOLUTE pItemIdList; // SHBrowseForFolder函数返回值
TCHAR szDir[MAX_PATH] = { 0 }; // SHGetPathFromIDList函数返回的目录名称的缓冲区
HANDLE hFindFile = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA findData = { 0 };
TCHAR szSearch[MAX_PATH] = { 0 };
TCHAR szDirFile[MAX_PATH] = { 0 };
if (uMsg == WM_INITDIALOG)
{
hwndList = ::GetDlgItem(hwndDlg, IDC_LIST_FILENAME);
return TRUE;
}
else if (uMsg == WM_COMMAND)
{
if (wParam == IDC_BTN_BROWSE)
{
BROWSEINFO bi = { 0 };
bi.hwndOwner = hwndDlg;
bi.lpszTitle = TEXT("请选择一个文件夹");
bi.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
pItemIdList = SHBrowseForFolder(&bi);
if (pItemIdList)
{
//根据IDList 查当前用户选择目录的全路径
SHGetPathFromIDList(pItemIdList, szDir);
//设置STATIC控件文本值
SetDlgItemText(hwndDlg, IDC_STATIC_DIR, szDir);
if (szDir[_tcslen(szDir) - 1] != TEXT('\\'))
{
StringCchCat(szDir, _countof(szDir), TEXT("\\"));
}
// 拼接搜索字符串
StringCchCopy(szSearch, _countof(szSearch), szDir);
StringCchCat(szSearch, _countof(szSearch), TEXT("*.*"));
hFindFile = ::FindFirstFile(szSearch, &findData);
if (hFindFile != INVALID_HANDLE_VALUE)
{
//清空 ListBox内容
SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
do
{
// 查这个目录下文件 不进行处理子目录。
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
StringCchCopy(szDirFile, _countof(szDirFile), szDir);
StringCchCat(szDirFile, _countof(szDirFile), findData.cFileName);
::SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szDirFile);
}
} while (::FindNextFile(hFindFile, &findData));
// 关闭查找句柄
::FindClose(hFindFile);
}
}
}
else if (wParam == IDCANCEL)
{
::EndDialog(hwndDlg, 0);
}
}
return FALSE;
}
文章来源:https://www.toymoban.com/news/detail-786563.html
MFC实现
MFC 中 CFolderPickerDialog
同样是对WIN32 进行的二次封装,来实现浏览文件夹的功能。这里笔者相同需求下,给出MFC版本的实现。文章来源地址https://www.toymoban.com/news/detail-786563.html
// HelloMFCDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "HelloMFC.h"
#include "HelloMFCDlg.h"
#include "afxdialogex.h"
#include <strsafe.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
BEGIN_MESSAGE_MAP(CHelloMFCDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_BROWSE, &CHelloMFCDlg::OnBnClickedBtnBrowse)
END_MESSAGE_MAP()
CHelloMFCDlg::CHelloMFCDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_HELLOMFC_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CHelloMFCDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BOOL CHelloMFCDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
return TRUE;
}
void CHelloMFCDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
CDialogEx::OnSysCommand(nID, lParam);
}
void CHelloMFCDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this);
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
HCURSOR CHelloMFCDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CHelloMFCDlg::OnBnClickedBtnBrowse()
{
// TODO: 在此添加控件通知处理程序代码
HWND hwndList;
TCHAR szSearch[MAX_PATH];
CFileFind cFileFinder;
BOOL bRet = FALSE;
CFolderPickerDialog
cFolderPikerDialog(NULL, BIF_USENEWUI | BIF_RETURNONLYFSDIRS,this,0);
cFolderPikerDialog.m_ofn.lpstrTitle = TEXT("请选择一个文件夹");
cFolderPikerDialog.ApplyOFNToShellDialog();
hwndList = ::GetDlgItem(this->m_hWnd, IDC_LIST_FILENAME);
if (cFolderPikerDialog.DoModal() == IDOK)
{
::SetDlgItemText(this->m_hWnd, IDC_STATIC_DIR,cFolderPikerDialog.GetPathName());
::StringCchCopy(szSearch,_countof(szSearch), cFolderPikerDialog.GetPathName());
if (szSearch[_tcslen(szSearch) - 1] != TEXT('\\'))
{
::StringCchCat(szSearch, _countof(szSearch), TEXT("\\"));
}
::StringCchCat(szSearch, _countof(szSearch), TEXT("*"));
bRet = cFileFinder.FindFile(szSearch);
if (bRet)
{
::SendMessage(hwndList, LB_RESETCONTENT,0,0);
do
{
bRet = cFileFinder.FindNextFile();
if (!cFileFinder.IsDirectory())
{
::SendMessage(hwndList,LB_ADDSTRING,0, (LPARAM) cFileFinder.GetFilePath().GetString());
}
} while (bRet);
}
}
}
到了这里,关于28_UI通用对话框多少会一点之浏览文件夹与遍历目录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!