|
目录处理函数
5 ?* E& g! D: c6 [. a: p2 e8 X编程语言:C++ Builder 作者:王行舟
8 b" ^ i1 G% }4 ~ m, E* } 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
) P4 `$ p T( X: _4 C一、判断目录是否存在: 2 F: v5 Q2 K6 X# z! x
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: 0 x! a& ^9 u, N1 q$ M" t
设char *Dir为带判断的目录, \& Y2 n+ E' D6 X" h
bool Exist; // 最后结果,表示目录是否存在
. X% F1 }+ U% J% ?2 L; Iif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”+ V+ u/ y* E( W$ p1 w
WIN32_FIND_DATA wfd; // 查找% y# P9 r& p* y( P% G: i* Y |
HANDLE hFind=FindFirstFile(Dir,&wfd); 7 ~: P1 P% e- D% O
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
2 r5 B8 _! J3 w6 i1 Ielse& p8 @0 w8 X. v7 A) u5 g; \: |
{
- p+ [' W! G5 b# ?! h9 W if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录& h( w; \: @4 T! T6 G% f
Exist=true; // 是目录,目录存在
7 X8 p# F+ c. _/ ^/ ~ else
8 z5 K b* \* E5 i Exist=false; // 是目录,目录不存在
% { a% B) Y ^ FindClose(hFind);
, G: } d/ L4 N; d5 @3 H. }}
3 a0 m- F4 N/ B# q) r7 I! ^$ ]4 i- p二、打开目录选择对话框选择一个目录:
- w, A$ u) a* W& n/ f0 } 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: 9 S, l- x" q% l
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle , H- E1 j: ~4 q/ N
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL* `+ U m: T" i- e
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 / y6 j' A; b! ]/ Z5 h6 h/ n
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
4 C! n5 y$ A1 j4 m) \. pUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
7 w% L; N2 a8 XBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
! p c! } s- H8 r3 `LPARAM lParam; // 预定义的对话框传递给回调函数的值
! m3 w" y, N" f. i. jint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
: y% c; u, n- _3 r' Z3 k% N) }$ c2 ?可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
% Q# E7 \& ~8 ^% B, x8 z$ \' t#include <shlobj.h> // 必须包含的头文件
6 b7 u N. W2 `/ n7 ^9 Tchar SelectedDir[MAX_PATH]; // 最终结果
. r# D+ F1 F7 V7 g4 P' `BROWSEINFO bi; // 入参7 Y. ]/ o& z6 F- p) P
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font - x, l8 |: I5 V
LPITEMIDLIST ItemID; // 所选目录的系统标志指针
9 M$ e6 f* d# d5 ~& t# wmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果0 c& a9 B7 c1 e) S) o7 F
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据$ g# f3 h, N* X S4 p0 y8 c, j- x
bi.hwndOwner = Application->Handle;
% `3 E3 j4 b0 P9 _) E \bi.pszDisplayName = FolderName;( q7 |1 L* l. w# x2 Q
bi.lpszTitle = "请选择目录"; // 改成自己希望的
+ g2 a. w" W [bi.ulFlags=BIF_RETURNONLYFSDIRS;
8 H' g3 a! T, B2 n2 B" CItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
! C# l: s% F! Q$ Sif(ItemID) M3 ^# o% N' d8 E: h7 D; ^6 {4 p# N
{" O* e1 N8 b8 f$ u
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
& m! h& q0 s+ H& G$ P0 ~ GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放* X$ s9 d+ C" @: a3 y
}
. s# l6 \: U% Y& W" n: e8 N三、直接建立多级目录: ) A& [3 T* Y9 G+ Y. v
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 4 ~* e5 ~; T0 m; o5 w0 U: y
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false") q: f) g) {4 E" U
{. y0 h1 D; V$ S5 P
if(P.IsEmpty())return false;9 `+ Y- x4 s p+ h& V& {* E8 J
int len=P.Length();
% {6 B# h4 K; a7 A8 Q/ F char *Path=P.c_str();4 x* b' c# i5 b. U4 ~
if(Path[len-1]=='\\')
4 o( u6 x6 A. T$ ?3 s3 q {
1 R3 T; \' g# h len--;& L% |; I0 A3 @# Q2 X/ B
Path[len]='\0';
* J( d5 y: N6 K o, |! G! K } // 删除末尾的"\"
. @$ i! z7 Q2 m! ?! d AnsiString Dir=Path;, Q: o8 `. B5 }! ^ i
// 分开父目录和本身目录名称) ~8 s. P2 M' x3 V% V: ?0 t, }+ z
AnsiString Parent; l1 P+ N0 Q/ ^0 M1 ]/ I0 e) K
for(int i=len-1;i>0;i--)
3 j/ d% B7 h+ g {9 n$ ~! P2 ?8 J9 g
if(Dir.IsPathDelimiter(i))
7 S- Z) u0 h3 s3 ^ {
% u& t. \' y7 x: Y. N Parent=Dir.SubString(0,i);! M! }1 w( f" B1 x X( n s
break; B- A ?% g( ~; a; \
}: m" e! ~" W# ]: P
}( F9 S$ |' {0 R7 A7 t
if(Parent.IsEmpty())return false; // 目录名称错误9 a/ b$ o! m6 [( S
bool Ret=true;- g" {5 P( f. A! O% n! z! p6 b0 t7 I
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录" j0 x" ?& }" q0 d6 x" p: @
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在; Z. a; l5 G. J% V
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录5 X5 `6 D1 [" c7 _
if(Ret) // 父目录存在,直接创建目录
( p2 ~" R- }/ r' x. Q! ~9 \' w {
0 a6 u+ }* w3 F+ H' Y SECURITY_ATTRIBUTES sa;9 J1 [9 Y1 H; m
sa.nLength=sizeof(SECURITY_ATTRIBUTES);; {& H. Y$ q' N5 J6 m
sa.lpSecurityDescriptor=NULL;. w" J1 g2 r% a# d- D9 W$ ^6 _
sa.bInheritHandle=0;
+ Z8 E: c% m: r( y Ret=CreateDirectory(Path,&sa);
; ~' X7 \# d4 J }
& D% z6 X' A) k4 A return Ret;: i, B2 F4 D! Y! j- Y
}
7 P3 {6 c8 w/ j 可以看出基本方法是:
7 p& i. O/ B5 L7 V; t先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
* K4 j( \. L; B t! ] h如果父目录存在,则直接创建目录,否则自我调用创建父目录。
5 j) s5 t- }& f0 Q6 S% `
$ b- S' @' T. A3 D四、直接删除整个目录: / f Z" {9 a; c) n+ u1 A. h7 _9 S+ D
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
7 r) T8 ?3 o+ P* L查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
l: f1 G( R3 @, m如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
3 s4 x7 f4 N% P如果找到目录,则进行自我调用,即开始递归过程。 2 O# l3 K+ {8 W# X. \7 p
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 ) h8 Q. l- W( i4 }' O# f
具体程序代码如下: $ Z7 I8 E5 D% i
bool DeleteDirectoryEx(const AnsiString & )
7 x9 M7 X* d# ^7 y{/ i1 v7 G4 T+ p4 ]5 i2 F
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白9 I+ O4 ^ k; _) z% V6 Y! i
int len=P.Length();
2 ^- {; N* n( K0 j; x% _; _ char *Path=P.c_str();
$ v- U) l: _0 j; h' ` AnsiString Dir=Path;
9 F5 w2 F, p1 X2 Q1 A. A if(Path[len-1]!='\\')Dir=Dir+'\\';9 C2 n; D5 f( u/ r2 }
AnsiString Files=Dir+"*.*";
4 `/ k$ F9 c/ c! U8 [: X: v WIN32_FIND_DATA wfd;' s- q0 D3 r( {, H) d/ Q; W
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
. ~7 h0 a$ t. @ bool Ret=true;
0 k6 E2 r% R. ?2 f! S+ M AnsiString Tmp;1 S- D, q( |# I& i1 H
if(hFind!=INVALID_HANDLE_VALUE)
; h! L- }0 s" p5 Y8 B {
5 z4 y6 o, i$ K/ v1 @: O4 L bool bFind=true;0 p- R) q* g8 h \" t! w* ~, `
while(bFind)
, i% ?, G( s( u4 w$ }3 B {- x' R. e% @) u/ [
if(wfd.cFileName[0]!='.') // . ..% x! N! r" ?- r, d0 D6 T
{
5 F) l- ^/ ]) c9 ~5 {7 M- m Tmp=Dir+wfd.cFileName;
$ f) M4 y# h% `; a if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)! a% k+ l, c5 c( a
{ // 删除所有子目录# x- t; o+ e8 O, E+ }1 S7 A1 N
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
1 s# F) B, W! X3 n$ j }else
0 l( u+ x( T/ Z3 V' p { // 删除所有文件
1 J# X( j: x/ d' O- S: a3 U$ O, y SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);0 U. T, k3 W) ?: m
Ret=Ret&&DeleteFile(Tmp.c_str());$ |% M+ m E, A8 U8 \& a
}
: ~' z& h3 U7 X; ?1 I0 S* m }
* R. B p& y# P e9 [ bFind=FindNextFile(hFind,&wfd);
( W# ^: _) V' K/ A0 O }
/ h( W3 p# }+ i6 ? FindClose(hFind);1 t' d+ H$ C# o% W; K1 a0 c
}; m) w6 r9 ~+ U7 L( P2 F
if(Ret)return RemoveDirectory(Path);" I3 N5 L1 {- K7 B' U
return false;- C+ H/ ^1 ?0 x* t/ S+ D, r
} 2 D4 W* Q1 n- m1 ]' t1 A: p# H
完
' X2 @( a9 |! p9 N
p9 N/ s- o; \% J. u P! d) k |