|
目录处理函数
: H5 `: E! k5 _& U0 ?! A编程语言:C++ Builder 作者:王行舟 " x: ?) ^1 u+ A/ N
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 8 K; t3 {- {; V
一、判断目录是否存在:
. y$ F: y9 Q' f8 \( Q- _ C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
9 M% \) x$ b8 F l4 q设char *Dir为带判断的目录( h' s; z' o7 S% |* K3 E! ?
bool Exist; // 最后结果,表示目录是否存在
0 T9 ~/ B9 H4 x, l% G( i1 lif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”6 x0 N1 }; Z5 @2 o& j1 _
WIN32_FIND_DATA wfd; // 查找4 u2 n1 V# p. F* Y! x1 `
HANDLE hFind=FindFirstFile(Dir,&wfd);
" e$ w' P) r( kif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在2 h+ [3 X2 A6 o9 q$ x
else
# b/ u( F# D! {0 A+ j% x3 g{. l4 a2 d0 b6 l2 V
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录) m2 u! }7 R2 f' b( b1 ~% i
Exist=true; // 是目录,目录存在
7 h/ \" O1 }4 C4 `8 i else
- P. u9 S3 q' @; d% V+ ~ Exist=false; // 是目录,目录不存在
$ Y" a" V3 \6 w) t7 n; x FindClose(hFind);# S; k# `, e3 [5 F
}
6 O. a7 @/ k4 r# ^ z+ }* K二、打开目录选择对话框选择一个目录: 6 z9 N& t$ u9 R8 I) d$ m7 B
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: 2 O1 p; s, G* J- x
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 9 ^8 Q7 D' o; a: ?9 ?0 f
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
! T H$ N! X+ B4 F9 X% m, T' d2 BLPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 0 N; X" f5 d) c8 i+ x% Q
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 ; A+ x% @4 C, s* C5 F9 s
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS $ g6 n$ n, B# `
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL : I7 F+ P! f+ P5 Q7 E% |+ t
LPARAM lParam; // 预定义的对话框传递给回调函数的值
2 Z n9 x2 B' e) gint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
/ { \* J! s1 `* r8 U( h- ?; A& i% C可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
1 J. f4 S" E; a; C#include <shlobj.h> // 必须包含的头文件; n+ ~6 H/ G' d- s% L7 m5 t1 ]
char SelectedDir[MAX_PATH]; // 最终结果5 g! b$ X3 W5 S, \1 e+ C
BROWSEINFO bi; // 入参
8 l' N9 @: D- M0 Dchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font 0 K! n) s9 x1 k& v
LPITEMIDLIST ItemID; // 所选目录的系统标志指针 . s: W" D' }- O8 I
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果' z. ]7 M Y+ \! W+ H/ s; o
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
^( Q X4 s, [3 ?! Ybi.hwndOwner = Application->Handle;
6 X+ j; N2 }. ]* Bbi.pszDisplayName = FolderName;+ P" A: E0 W. G/ k
bi.lpszTitle = "请选择目录"; // 改成自己希望的
9 ^7 ^, j& B$ Q1 K. _bi.ulFlags=BIF_RETURNONLYFSDIRS;$ x! J% L# a$ C8 J7 b9 O' x( Y
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框7 J( T- B+ x* S' X# t- B
if(ItemID)
; G6 |7 b, g& R6 A3 {- m{/ b2 e3 U$ X, f
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名& K+ `0 V! x" U& i
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
7 a0 V8 G1 c& D# [} $ x% G4 i4 I# h1 M" X0 m
三、直接建立多级目录: 2 [! s. ~1 o% T$ |
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
! ?, g) S# X. J1 L6 N; Xbool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"4 k$ ~0 `8 B7 ]5 }- U( L/ K# i* w' V
{7 q- j% |: F2 p7 \4 x: X- l
if(P.IsEmpty())return false;* a p6 }" e* z/ Y
int len=P.Length();+ q; K/ \0 `. G4 K
char *Path=P.c_str();+ P5 E8 ^$ u9 |# W
if(Path[len-1]=='\\')
1 S1 p( \$ M* c8 {* \8 m% f {" y. l7 t& w; o; h" R
len--;
! O+ }. }% _' A$ M* D" f1 v Path[len]='\0';; r4 j7 r& B' z
} // 删除末尾的"\"1 S, `: u+ C3 f2 Q* [3 ]; X
AnsiString Dir=Path;
& P* W2 @% O+ I: v // 分开父目录和本身目录名称
' m! u; u9 q" g9 v' D% b! S& g* ? AnsiString Parent;
, t; k; P3 J0 v8 a0 S: S# c for(int i=len-1;i>0;i--)
/ a5 B" Q/ J: W5 N+ H! v/ X0 o/ u0 t {
8 t# j0 c5 ^' E4 |$ C+ m$ ?, H if(Dir.IsPathDelimiter(i))# V$ L" e- y6 U- E4 y6 `) F3 K
{
- e( A, ]4 u7 {& w( y8 p Parent=Dir.SubString(0,i);; Y- t& u# ^5 [$ L
break;
% ^4 b/ b. I5 P; Q }
/ {5 k1 u) j; K1 L }
9 b9 R" _/ ~% ^ if(Parent.IsEmpty())return false; // 目录名称错误
0 S3 T$ A' _ ]0 P' h7 z bool Ret=true;
4 c. p5 A' x/ O5 R. ~ Y if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
+ G+ H8 N% ~2 v, w Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
" n) v+ U; u k8 V if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录% m' K# M5 l+ i% e7 s) w
if(Ret) // 父目录存在,直接创建目录
4 B; r0 `/ @* `2 O {# N. O8 k T+ s; I+ | v9 R% S
SECURITY_ATTRIBUTES sa;7 a3 M% z5 e( J
sa.nLength=sizeof(SECURITY_ATTRIBUTES);9 @' {" v% P1 h3 w8 G0 b
sa.lpSecurityDescriptor=NULL;
' w( n; C. e6 d) Y8 y3 B sa.bInheritHandle=0;
, b9 V2 \) B" o* A$ F& t Ret=CreateDirectory(Path,&sa);& j! P# @6 u0 b) ~
}
0 u3 x' O$ _0 t' ~* r+ b return Ret;9 ?, E% f/ W1 V) m1 I% ~7 b' _
} * E5 q! R( s$ d8 r5 Q4 t1 n2 g {
可以看出基本方法是:, a8 G& ?$ @6 i" s$ b
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; + w" f6 R; ~. U' B; M# P* u
如果父目录存在,则直接创建目录,否则自我调用创建父目录。
4 W; w% G+ N* Q) g& z; Z. K7 E
0 U9 b' H$ `/ H2 _9 A四、直接删除整个目录: 2 |6 S/ o, A: l8 G2 }) {" p. X! g
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
) `7 b# g$ M4 z5 f查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) 2 g( V& c! H: ~
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
0 d1 q* C, v& O6 Y, v2 J$ s如果找到目录,则进行自我调用,即开始递归过程。
' C" @. S# K! j3 Z% h( j. s如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
/ b, `- n( a) {% n$ ~: \7 }8 [具体程序代码如下:
! x) g" y+ Y0 ~+ Lbool DeleteDirectoryEx(const AnsiString & )* R! A4 S$ C- E: K; f% I
{' M1 x2 k/ p B) M) [
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白2 m# p# p U2 I6 q3 b1 O7 [7 q' v
int len=P.Length();
" o9 \. ~, e& I0 q$ a# m1 u9 G char *Path=P.c_str();; q3 Q7 N- {2 z. u# j* V' ~3 Y
AnsiString Dir=Path;
, g( I% i7 a+ \* {" ]2 M' S+ L( a if(Path[len-1]!='\\')Dir=Dir+'\\';
\5 b; X8 R5 v( }7 o" n AnsiString Files=Dir+"*.*";5 d( m9 ^5 f7 g
WIN32_FIND_DATA wfd;
7 u2 l7 f0 Z( x) v+ Y HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
: E: e! b8 y4 d; X# T6 F bool Ret=true;
1 Y; c8 y3 M0 ]# N1 t' e AnsiString Tmp;- X' R: N- r7 G# \
if(hFind!=INVALID_HANDLE_VALUE)8 M# b) D; y* |1 A" R+ g7 X- o) ?0 a
{# f7 S( }' y' ~7 Y
bool bFind=true;
- @. d, l; ^0 H. F' @, u while(bFind)) a! H; s( q, J0 C v8 B2 }
{
: b, [! {2 q1 V9 B' }, u" g if(wfd.cFileName[0]!='.') // . ..
8 G: j, ` Y" ?4 @7 E {
) U' H0 f9 {& j2 w8 L3 _" @ Tmp=Dir+wfd.cFileName;+ L+ b5 T" q$ I U1 i; ~3 S$ ^
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) \! r2 ~$ p2 m) D
{ // 删除所有子目录
2 w. o. K1 }7 N* k3 o3 ` Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
( j9 S c: \1 m2 z" k }else
2 V* z) V) a9 W% D, g& A* b- P { // 删除所有文件
7 i+ _# C* H, g, a5 H3 o SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);+ q L9 A, d+ f$ p, w1 H
Ret=Ret&&DeleteFile(Tmp.c_str());# j# n- m: h. m: @& A9 a
}6 c9 S' k K1 F
}
) ~* v, M! ?2 M" R @ bFind=FindNextFile(hFind,&wfd);/ I8 D6 L/ P6 T ?8 O! \
}! g0 x4 V2 s+ _6 n7 L
FindClose(hFind);* k9 [5 W$ g {, K
}
0 ~4 ^' o$ S5 H7 D if(Ret)return RemoveDirectory(Path);
. ^: z1 a) d) u! }- p% w+ p return false;9 ~' v6 E% ~- H+ {/ D( u2 ^ p
}
% k8 K- s, J) \: _" h* v完
9 ~9 a! _; \ ~6 N, P
) B$ V. ^1 H! F' X$ g7 s0 r. B |