目录处理函数 ) C( d: n0 ~$ X$ g4 D4 k1 z. ]
编程语言:C++ Builder 作者:王行舟
3 z/ e+ q; U9 E; z: v, V5 y 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 4 R A! e- G4 k+ p: b( b6 b2 p
一、判断目录是否存在: $ l& j0 {) J! u* `: E; L
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: " ]# \9 T7 A8 X6 ~# y3 G5 g( G
设char *Dir为带判断的目录& q* q* g0 _( G) h, _+ H0 h
bool Exist; // 最后结果,表示目录是否存在7 u: X i, T5 \& d4 o
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”7 }# K+ k! Y5 \6 }) a
WIN32_FIND_DATA wfd; // 查找
0 L8 ?& g f1 n$ U! h9 k: F; KHANDLE hFind=FindFirstFile(Dir,&wfd); % K: J6 l& o+ z
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在9 M9 O" ^+ j/ `: Y6 k* b/ P" G2 Z
else
8 _+ K& f" A8 _/ J- y. B! ?{4 V2 u: C$ a6 h: p$ B
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录0 u1 F% y, s* m: a4 ]# L! p0 F( |
Exist=true; // 是目录,目录存在
0 O4 O7 x0 I; \, I; H( D# B0 o& | else
9 C; h/ c) O6 J4 }" F! `( U Exist=false; // 是目录,目录不存在' N- o( e6 a* M8 i+ M3 n" T
FindClose(hFind);; {1 B8 ~3 O% D
}
0 x2 |2 t# r* j4 ^* Z二、打开目录选择对话框选择一个目录:
# |: f. i1 `0 k. }% S; E6 I 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
6 S; O4 p: \2 {2 E. UHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle " Z/ d# W& c3 a( O3 O* Q) q
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL2 B) t0 p/ i" B) M. F7 y9 ~
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
) a7 g. H# G# q7 NLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
4 i" u9 ?, e/ D# \0 pUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
3 @1 K2 E! f; T6 T. UBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
* L8 Q# h# N# W( fLPARAM lParam; // 预定义的对话框传递给回调函数的值
; M; C$ n: H( D# Z5 cint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
- r d- v4 i" R: z可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
( I0 a; U7 B0 I. o, V! n5 M#include <shlobj.h> // 必须包含的头文件/ ?6 l/ o* _ f# Y( f
char SelectedDir[MAX_PATH]; // 最终结果! D a8 O6 W4 m6 _
BROWSEINFO bi; // 入参 T S3 I) F) h M( \
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font & x/ ~6 i: H9 R: ^
LPITEMIDLIST ItemID; // 所选目录的系统标志指针 ( {, _, j* O4 z1 W5 ?, c3 t3 V# ^
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
( z# p3 K$ G' {) }, Smemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
+ j: X6 C2 o8 G, j% Q3 Obi.hwndOwner = Application->Handle;& ^ {7 O5 A0 `5 {2 m9 L
bi.pszDisplayName = FolderName;! f- l% }2 H1 o8 d6 k
bi.lpszTitle = "请选择目录"; // 改成自己希望的) S( y- i6 m5 b3 [ W- E
bi.ulFlags=BIF_RETURNONLYFSDIRS;, F B5 _ B" H0 V/ w
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框' o/ B+ j8 r6 ~: C0 y
if(ItemID)
+ e7 O: `: ^5 e: ~) \( E. j& P{
% h8 D' O" {1 R, r6 w5 [ SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名* i5 n/ m6 V6 G% l+ g
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
8 U* ?& f8 v. j7 X2 }2 t- z& q8 E! I- Q} / x% ~9 d" ^* M" A
三、直接建立多级目录:
7 L6 b& b7 E7 V4 P8 w2 w# _ Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
$ k7 j( x8 u2 L& Ubool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"; d3 r2 x+ R* _6 }9 V
{
, O3 j4 [& C A$ l if(P.IsEmpty())return false;
9 K6 r" K: J: t2 A; { W/ ^ int len=P.Length();
7 p: S: y! \% C, f char *Path=P.c_str();
8 I) V" M8 ~0 N3 Z! O1 g if(Path[len-1]=='\\')2 ~6 `5 D- e" h% M
{
! l5 m, f. O I6 D' B len--;
1 Y( @2 ` U" d: |! o Path[len]='\0';8 U7 U; J# V+ f c! C- d
} // 删除末尾的"\" @0 X A/ @& k W- P0 b- ~5 ~
AnsiString Dir=Path;0 ^/ A' q- N# H2 Z: \
// 分开父目录和本身目录名称! Z; g) N5 T, Z* @
AnsiString Parent;4 `, ]2 R, @+ [
for(int i=len-1;i>0;i--)
! f% l9 |9 Y6 l! j( A$ R. G {1 ]# {9 v1 s7 l/ M' M
if(Dir.IsPathDelimiter(i))
- r7 E R& D( h3 f) X {
/ g8 J }5 s) b* v- l' \ Parent=Dir.SubString(0,i);
. z, q/ r% b5 K) B' a. Q2 c4 p- {" G* ] break;- p5 e% h6 ?5 N4 c$ V
}. n4 u" Q* Z. z5 k
}
7 ~ }0 _5 q- W' x: B, ^" ~' I if(Parent.IsEmpty())return false; // 目录名称错误7 ~' B/ W3 C+ U1 u/ S. Y
bool Ret=true;
# ~% T, `3 p. C. ~+ { if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录! @2 B$ m# c$ H! C% Q
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在; |, n4 C$ m$ P0 [- s( h E
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录5 n3 i! f+ N, G+ c+ P' z/ M9 C% i3 y
if(Ret) // 父目录存在,直接创建目录9 j, O* P- k6 q! s' u h
{
1 F: X8 j# w8 c) I( R7 j2 h+ b+ R SECURITY_ATTRIBUTES sa;. x8 T- Q) |) T/ C
sa.nLength=sizeof(SECURITY_ATTRIBUTES);# e. ]+ U+ o7 ?+ [- c+ F
sa.lpSecurityDescriptor=NULL;3 S- R& p3 x; Q& G/ T* O( Q( s. i
sa.bInheritHandle=0;
5 B/ t7 N" T9 n0 g( O2 o Ret=CreateDirectory(Path,&sa); J' f+ z! y# Y) K
}; z# L6 Q$ \9 E1 [6 }
return Ret;
3 l- y' r. z! P2 X: r}
' T" h0 _1 G; h* P 可以看出基本方法是:
$ K: X# L1 W; h. W先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
h) Y. b( W+ X' t$ r# m( g0 z3 Y如果父目录存在,则直接创建目录,否则自我调用创建父目录。 + w) G* ]; L" v! h, c1 ~; U
z! w1 J" _2 e% `9 z四、直接删除整个目录:
! e& u" D _, I: ] 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: 2 C3 U8 R8 X7 j6 z. D) g; q2 f4 n
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) * v+ p& c1 Y- s! _$ q2 S0 G
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 ; G7 R+ }. a+ U
如果找到目录,则进行自我调用,即开始递归过程。 7 J; D4 y! l% Q; I0 W0 V! q
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
, ~( w; Y' F; C' S具体程序代码如下:
' q% ~$ T$ c( L. U% r& xbool DeleteDirectoryEx(const AnsiString & )3 q. T- N- C6 }" A) ^
{
6 m0 V* E9 b- ^1 f+ V if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
; x$ a+ Z( n! d+ ^2 Q. [* s1 m int len=P.Length();% ?4 |4 a8 P. L& p
char *Path=P.c_str();8 T4 n' l( J- K3 s- b
AnsiString Dir=Path;
% i A$ D3 R9 x( u8 O+ L3 r" C& V if(Path[len-1]!='\\')Dir=Dir+'\\';
- f9 r8 ^* J% i! i; R AnsiString Files=Dir+"*.*";
; \6 T& o5 E4 v$ \$ x WIN32_FIND_DATA wfd;
* @, [3 n, f5 @! h HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);. q9 _- ] N# A! M8 E4 r
bool Ret=true;
+ k4 O# D7 V$ n& k V; y | AnsiString Tmp;
9 @* y- F' i8 L7 c if(hFind!=INVALID_HANDLE_VALUE)5 U7 F# G1 l; x/ ~! i4 w: W8 _( v
{( e8 U" d& R1 {' a# I5 H
bool bFind=true;; n) H+ w1 ~$ N& Q3 W% a
while(bFind)
) M; p/ e2 @- A, p {
( D* V5 P6 O4 o6 ]! n, u* m if(wfd.cFileName[0]!='.') // . ..
/ B! i/ m8 X; V/ s4 r" w {8 ]" M: k+ `5 [
Tmp=Dir+wfd.cFileName;
* t+ k- e% e! z1 g if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
% l5 O% Z' G/ T8 g* b { // 删除所有子目录
% }- v2 Z) g: X: @ Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
" v& C6 P! d/ B2 } }else* U3 l$ J4 I& |& i! [0 {; Q+ ^
{ // 删除所有文件5 N# P6 D6 v! ?& }8 w" N. p J
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
2 C" |& B i: T7 j7 c+ N. r+ w Ret=Ret&&DeleteFile(Tmp.c_str());# c7 m( ?7 ~% g" A/ U
}
, c. ~% a# _+ z }
! A' }. g3 T( u! F( o! {5 g bFind=FindNextFile(hFind,&wfd);+ L: H- D# D' ^+ D3 V8 m0 y
}
) y8 i! @8 \1 O5 q" s/ b5 y5 w' x- D1 p FindClose(hFind);
. s: e. Z2 i, M8 b q }3 K1 b! |' _$ j1 @ E B4 m
if(Ret)return RemoveDirectory(Path);
6 ?! d. V: K8 E7 q* C E9 ^ return false;
, V* g& F, m, `4 g0 d) i2 y}
7 w; U/ o4 ` @. i1 p完
# f$ o- e) Z2 L" V3 f6 F* R( n
) v2 l. C0 M+ {5 x |