|
目录处理函数
F' G# _( W- o- R6 ]# c+ _编程语言:C++ Builder 作者:王行舟
" ^7 \7 d" P. W/ @ 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 ! j$ `" C$ [( [+ ^0 y5 a6 g
一、判断目录是否存在:
/ k1 e; a2 Z8 t/ [0 _: i C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
5 z% D9 u2 `/ P) G Y设char *Dir为带判断的目录
" s0 Z0 f' P' M4 `0 S; ybool Exist; // 最后结果,表示目录是否存在 h) a: K2 j1 N% @) Z/ ]8 S N
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
' l! y4 g% R8 z0 Y; s7 W# dWIN32_FIND_DATA wfd; // 查找0 c+ ^8 q2 _4 g5 E0 c% Q$ b; x
HANDLE hFind=FindFirstFile(Dir,&wfd); o) q( w6 m/ H% A5 X& S
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
5 h* O$ [* o8 \( Delse
, T, l4 i; C. s( j7 P8 {{' C6 [$ K4 x9 s4 ^
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录: M4 z1 ]! r* w" s& z: J0 e9 ]% U
Exist=true; // 是目录,目录存在
+ s) A+ ~" C; ^0 n4 y7 A# J else6 [9 {- v! u# Y/ |" h: n
Exist=false; // 是目录,目录不存在
7 S6 i! e. X- z9 U; g FindClose(hFind);
2 L% l% T# f1 j3 d- q _: j}
0 J! b8 W" J2 }* [( \6 ~. W* G二、打开目录选择对话框选择一个目录:
: l0 h4 l2 k! Z' d$ N% c 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: $ b# N8 B( p4 O
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle $ Z: s3 A+ p, A- G4 J
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL/ u0 ?: x) [+ q$ B6 ?" C2 r
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
7 g: f2 @& ?: U, Z+ FLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
# n2 C6 P u. e6 i. TUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
2 `6 }5 L& V0 J: MBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL 5 S1 l5 K8 F3 Z& z
LPARAM lParam; // 预定义的对话框传递给回调函数的值8 U9 k+ {/ B7 d/ b) O
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引 $ o- y3 U Z/ F
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
: N" G* Q1 N7 `#include <shlobj.h> // 必须包含的头文件$ K$ z7 }2 {, ^; ?# J
char SelectedDir[MAX_PATH]; // 最终结果
6 b* p( a8 Y# K; l6 mBROWSEINFO bi; // 入参
, h. f1 z* d9 \5 N4 H6 O% |( {char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font : c( ], D. ~( Y" J* j9 w
LPITEMIDLIST ItemID; // 所选目录的系统标志指针
0 y% B$ [/ O" V7 mmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
6 q7 T% a" s) Y5 L0 N) Q3 G% j) fmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据. k( u3 l* K, R O/ w* m7 A- }' j
bi.hwndOwner = Application->Handle;6 Y4 {& M, }4 d9 M
bi.pszDisplayName = FolderName;
; z: X( O: r- t& cbi.lpszTitle = "请选择目录"; // 改成自己希望的
, f* V+ F% \( W0 {: W' b3 _bi.ulFlags=BIF_RETURNONLYFSDIRS;6 c# x7 e! {$ c
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
0 Z, V( ^0 F; zif(ItemID)
) T( S6 E+ K0 b{' A4 ]7 E7 K4 l: ]/ }8 r
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名) i5 L# a4 U+ f
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放7 Z- G* p; r1 M$ r* H$ Y& e0 t
} % k& ` C! M/ i9 l- i- ?, a4 \
三、直接建立多级目录: 7 v& C1 X, X. R) T2 u
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 / t% r$ C6 D5 l% ?% P
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"; T; {0 d& K% k5 G
{+ D3 _% o9 z+ @9 X7 {
if(P.IsEmpty())return false;
& m9 ]" V, h* A& u% P0 \+ C int len=P.Length();3 }; f4 f6 O& v& b4 W
char *Path=P.c_str();* O+ D. G o! i
if(Path[len-1]=='\\')8 ~2 T! |8 e7 o/ [3 |2 ?
{
6 z, d0 x. \" d len--;
7 ] R- [9 Q: g$ C Path[len]='\0';8 T9 X+ \9 R2 [9 G
} // 删除末尾的"\"
# Y. ?4 E6 M1 q AnsiString Dir=Path;2 z3 ~/ h E6 T/ c0 E' B X' R
// 分开父目录和本身目录名称 j& A0 u' h# n7 I
AnsiString Parent;, V7 t- ~6 K1 c7 U" J
for(int i=len-1;i>0;i--)' n+ `/ v r! l( A# B
{* s4 Z+ J% a" u$ I2 |5 u
if(Dir.IsPathDelimiter(i))' h: G6 ~ N H; f% u2 O
{3 C# q; t7 H: U& i6 ]3 p. N L, s
Parent=Dir.SubString(0,i);( D( @. ?* j$ f
break;
0 F% ` r% _% l; |( E4 O }* E" G& K y: n1 A. O" M
}) R% K! C) Z c0 F0 L# J/ l
if(Parent.IsEmpty())return false; // 目录名称错误. Q( h, B, _+ [* e5 j* x1 t, c# t
bool Ret=true;' G, M7 X" W+ a9 R6 H% k9 ]. N2 Z/ k
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
7 X; h+ f7 O% }# P Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
1 {0 P, O& R* H" q if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录0 Q& O7 j6 H: i" X4 U( j6 c0 O( U
if(Ret) // 父目录存在,直接创建目录
1 [9 h7 X% r( f: p7 F5 l4 Q8 _ { t5 c& |. V- \
SECURITY_ATTRIBUTES sa;( D6 q* ]" J' k. J6 T/ [' U7 f
sa.nLength=sizeof(SECURITY_ATTRIBUTES);3 t# H. M$ D. x/ U
sa.lpSecurityDescriptor=NULL;
" T- b% Z: Y/ t# q5 T: s' B% j sa.bInheritHandle=0;
. j- {& i# [9 O9 b$ z% q Ret=CreateDirectory(Path,&sa); A; F$ x6 {5 f# F
}
& }4 l8 o( n+ [& Z$ g- V return Ret;; Z9 C0 e8 P) `& y4 A5 f
} 7 B' I5 U! O% ?9 D+ o
可以看出基本方法是:& F% ]# [: ^5 `0 Z7 n7 p1 W7 w% Z
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
& r* k* h) M2 M如果父目录存在,则直接创建目录,否则自我调用创建父目录。
* I. b4 \8 v1 r* g1 ]# x& w0 B0 ] u/ x
四、直接删除整个目录: ; B0 F6 ?5 h9 T
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
! M$ t8 c6 s x; _9 A& j查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) 2 q6 y5 @6 m1 k F9 `! d) U4 n( d1 i8 X
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 4 X. L5 d O. v& C
如果找到目录,则进行自我调用,即开始递归过程。 7 c% y- a6 D3 R* B' T* u) j
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
4 x* d E8 p3 U9 {/ i具体程序代码如下: 6 X. g% z) w; ~ G
bool DeleteDirectoryEx(const AnsiString & )
9 g u5 D8 N' C; V I{' A& ]5 V3 h. e- V7 {& G- {( i
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白8 `1 f% [3 v2 v1 _2 p
int len=P.Length();8 h" T2 x- W0 b
char *Path=P.c_str();
) N, p1 G' k! A) ^1 l, x AnsiString Dir=Path;
3 f$ \- x$ R) } if(Path[len-1]!='\\')Dir=Dir+'\\';
% z8 L5 e( r8 _1 a J( ` AnsiString Files=Dir+"*.*";
) j, h( g8 b' r4 q% d WIN32_FIND_DATA wfd;
# m7 s! t2 O& `' T" d: z HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);% _" ]" G& S0 q' ]4 f8 B
bool Ret=true; O: }" ?6 i' E( ^* o' e- _
AnsiString Tmp;
0 E( v1 G! ]" c1 l' a, Q if(hFind!=INVALID_HANDLE_VALUE)
: H8 q# |& B' S/ c4 G1 J+ T {# M: b$ |2 d5 L$ u( {" v
bool bFind=true;
0 {: b* `; O, x* q6 S while(bFind)- O) I. B& j* v7 R o. ]
{: H8 w; e8 c3 b; O$ j; I
if(wfd.cFileName[0]!='.') // . ..
; E; Y# W6 L6 E {
- G0 A/ K h- f; {8 I Tmp=Dir+wfd.cFileName;! p# G2 r/ l/ T
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)% S; }( E5 W: H; O
{ // 删除所有子目录* |% Z0 R& a$ i% W1 |: w8 J
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);! z+ `- h; f% q6 [8 w
}else$ \3 m+ M/ A) d# I
{ // 删除所有文件
* a# L8 t. C) m) t% Z9 N8 {6 { SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
. N9 s% J! [1 ? Ret=Ret&&DeleteFile(Tmp.c_str());
5 A" _7 G! I# k0 B. e) V }( ^2 i2 |2 I; V" X F9 |
}) E: M4 g5 ~7 B- V5 o
bFind=FindNextFile(hFind,&wfd);- s4 |1 Q E" g( h2 |: G
}; q8 r* w6 D+ o" _ \
FindClose(hFind);
: j/ l5 a% G) h }
, K! t( I3 C- G% q' J3 D. j if(Ret)return RemoveDirectory(Path);& v# F7 f6 p3 a, N* {
return false;
2 P4 E8 E' p1 ?}
B/ B' R6 S* W( _3 s完
( U: e/ w) a% U2 b2 p5 {' T$ I9 K; s* j1 h% G: Z1 O' ~) P4 J
|