目录处理函数 / x2 ?. W A& ?& W- C
编程语言:C++ Builder 作者:王行舟 8 {2 ]) j6 z" e' ]
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
6 o" v# ]5 P8 G- H) C一、判断目录是否存在:
8 D+ r" Z8 L9 \5 `; ~: N C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: * w" p2 M+ Y' u. c: q% E
设char *Dir为带判断的目录
a: u( |8 _2 d3 E; G) Dbool Exist; // 最后结果,表示目录是否存在
8 x, T% p: B3 F4 O6 k1 E* Dif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”$ W! A$ Z% J! U+ Y/ j' c9 W! P! \, I
WIN32_FIND_DATA wfd; // 查找5 E3 i U% _$ G, I
HANDLE hFind=FindFirstFile(Dir,&wfd); 4 Z1 }& U* C* b' X6 ?0 p6 T" i
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
/ V7 B% v4 P& l1 ^, {else1 H# r. z1 d, Q; t9 Z h8 O q
{$ Q, k9 W/ ]! K- m$ {0 k
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
7 ~. y5 g+ X+ Z- Q/ ~+ _ Exist=true; // 是目录,目录存在
: w, Z* p: f/ L+ t: J else) o$ T; z' i+ s0 Y+ a" |$ ^% n
Exist=false; // 是目录,目录不存在
' G+ [, X% L% G1 \ FindClose(hFind);
3 u2 W. Q( E# t( g# f' g5 W}
5 v, V( u, L: D" U二、打开目录选择对话框选择一个目录: / W8 G9 f7 y" a: w: X7 @3 y
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
$ O# G' }+ M3 h/ `' `( aHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
/ Z2 D% ]5 u2 x) e* VLPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
6 _1 h9 ^6 x! U& a) _LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
' D, {0 T- R* T8 `! ~LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 2 m3 ^# ]: d* w
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
) |3 v! L- ?; j! y7 ~7 B: d4 ~BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL & T' X& v5 r2 c
LPARAM lParam; // 预定义的对话框传递给回调函数的值
) F8 Q3 X V. w* t/ Kint iImage; // 与所选目录相关联的图标在系统图标集合中的索引 % \; S" a5 g1 }& E+ Q
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: : P8 A3 Y% j3 R4 W$ X! v
#include <shlobj.h> // 必须包含的头文件7 G' s ?6 K$ H2 ?% e* p9 g
char SelectedDir[MAX_PATH]; // 最终结果! B2 C4 G. d& }% f
BROWSEINFO bi; // 入参; e6 ?4 x( G1 y1 J
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
! z" D( ~; k X% C( n) u8 S+ WLPITEMIDLIST ItemID; // 所选目录的系统标志指针
$ c$ I" G# A, Z z, Smemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果5 U$ r6 @9 ]: D5 z+ p$ T
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据% y$ C) @ U0 ?* t: h% G0 G& h
bi.hwndOwner = Application->Handle;
8 C/ t3 K6 A# \6 F4 dbi.pszDisplayName = FolderName;
7 }" x5 S9 s# j( hbi.lpszTitle = "请选择目录"; // 改成自己希望的
( ]# K1 n! n+ x% P; Wbi.ulFlags=BIF_RETURNONLYFSDIRS;4 T$ v9 B4 s- ]( `3 T
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框1 G1 U6 ?1 l$ N) S: j8 {9 W
if(ItemID), L0 p1 ^* J' J# _ |4 W
{
& F3 [4 n+ f U: P SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名( `( ^) f- W B- h- h% [" s9 r! M
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
' Z$ k# u; r# n} / l- i- S! J9 ^# {8 i" m! i. b
三、直接建立多级目录: . o# ^% t, T! n8 B# @# R
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
- V& b: _0 Z- H H. dbool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"& U# y) j) b2 n9 H% B" n8 w, K
{6 H% z: y% U# x% }3 O) d$ Z
if(P.IsEmpty())return false;3 F t) W+ x _3 {* r
int len=P.Length();8 w1 k( P" r# u
char *Path=P.c_str();, n7 a+ |" J1 f( Q7 d9 _
if(Path[len-1]=='\\')
- m+ R' c5 ]& j9 I7 q {
' c2 ] ?) _; R n& _0 O* `' }/ c len--;' X t2 ?! F: X; d! ]
Path[len]='\0';' H( K# [0 q/ Z& q q) H" P
} // 删除末尾的"\"8 r! l+ O, E( `! T% A( [2 r( S
AnsiString Dir=Path;' y9 m, ?, z: w# _
// 分开父目录和本身目录名称& Q9 f$ ^0 D2 ~% b& K9 {
AnsiString Parent;) D1 M1 N0 r* ]$ W
for(int i=len-1;i>0;i--)
8 \+ y8 v) d3 J" d4 P8 j {
. U5 h1 x2 E; B& {0 r" Z% @ if(Dir.IsPathDelimiter(i)); P. h3 H o3 I" d) M) S
{2 G4 d, M) ^# I5 Y
Parent=Dir.SubString(0,i);
/ s) `1 J* z. u) k& Q break;5 T1 h8 }+ h9 K+ J) k: V7 v. p4 P* ^
}3 f# c! v1 Q! r+ ^, C2 `, u/ V0 C2 M
}
- x1 s5 ?6 H( Y9 y! l if(Parent.IsEmpty())return false; // 目录名称错误/ y B5 b" B3 ^" r
bool Ret=true;
5 P3 d* ~: y7 H8 Y; {/ ? if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
* s5 b! I! N; {4 w5 [' B Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在) l* l) b5 T- c% n/ Z
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
4 }# ?- w2 v5 S4 ?& Y if(Ret) // 父目录存在,直接创建目录' ~. m/ s7 i r1 J
{! d5 M- h& Z* K) a+ [# I% M& v8 }
SECURITY_ATTRIBUTES sa;7 y( b0 X; R( b, B' w" I* X
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
) f$ l# Z5 v: |+ h% I sa.lpSecurityDescriptor=NULL;
( U8 z. k4 @; Y9 o& | sa.bInheritHandle=0;3 Q# X1 r2 ]; ^! A% R
Ret=CreateDirectory(Path,&sa);
' E! k; l( ?( |, P, Z }
m# h7 a/ z' s# ]4 V return Ret;% j! q1 v- N# a" u/ H7 v
}
$ l' B4 k" W! O L: u 可以看出基本方法是:! j6 c* K5 I# G" ?
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
6 N; x- i+ E- a" ~' M& j. E* T; h如果父目录存在,则直接创建目录,否则自我调用创建父目录。
' M* }) J7 J5 l# E9 w& k I' t3 q; |3 m- U! B9 O% W ]2 e# c" l3 v8 ^
四、直接删除整个目录:
$ |- J1 |* Z) o7 l; W 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
# J; c8 Q* H5 P查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) ! J* @1 b" s# e X, C
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 , A: w- G* P; z6 B* J
如果找到目录,则进行自我调用,即开始递归过程。
) h: n6 q5 J! s& q; Z如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 G% |: v' Q! m! P2 H& v1 j: R( L
具体程序代码如下: 5 [5 [ O0 ~$ X2 J4 j0 u
bool DeleteDirectoryEx(const AnsiString & )
7 I, V9 ?! e* H* T! ~& d+ A{9 H: E. S ]4 F5 d) V( N6 v* w
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白/ |. A) H: H/ }- y. _' @7 l( o( s
int len=P.Length();
; }% R5 Y# h2 n( W r char *Path=P.c_str();$ j8 m2 l- P1 h$ Y, n$ T9 M
AnsiString Dir=Path;- M0 S% f% F& G1 ]
if(Path[len-1]!='\\')Dir=Dir+'\\';2 H& L) d7 Q' S1 M5 z% }# F# F4 K
AnsiString Files=Dir+"*.*";+ {4 u) e; s7 ~4 Y
WIN32_FIND_DATA wfd;
' E' |: ]* @, ]# a& \/ w @' K HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);8 j; S; B% Y7 l( F. n
bool Ret=true;* E) F$ V, u; n0 z8 X5 z) i
AnsiString Tmp;
* m* W# J5 y3 `( l2 s! J; c6 Q if(hFind!=INVALID_HANDLE_VALUE)
! X. \& B8 }/ c {
2 T" }8 A5 Y" \ bool bFind=true;& c; c7 a* @" K1 v" Y
while(bFind)
* L9 a2 ?, s' F0 K" t) j/ o {
# j+ B' E8 g" j l4 r8 A! ~ if(wfd.cFileName[0]!='.') // . ..
5 t2 d1 r7 r) m {
V' u; ?- I( [9 T6 a Tmp=Dir+wfd.cFileName;
+ }# t3 ?. Y. v7 {7 x4 W if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)* W4 _4 r u# ]! w+ D( T6 \' z9 e! b
{ // 删除所有子目录
$ d2 U |# B& Y) A: m" R0 v Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);! C, V- u. X K7 e1 H
}else" \$ E+ T) M `8 Z$ r8 Q+ {) \3 R
{ // 删除所有文件, F$ m+ @6 J& s' a% G/ j; F
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);7 ] ?5 [- f; D, x, ~& c
Ret=Ret&&DeleteFile(Tmp.c_str());8 Z- w0 R, t; ^. s5 p+ z* l
}: j) a5 L8 @$ p; y) `' u% H; _$ t
}8 T: c8 P" `9 n$ z1 a- r( G
bFind=FindNextFile(hFind,&wfd);! u3 C6 O$ k1 c5 z' f: b! D
}
1 z+ D3 v* J. z- }* V. R1 i FindClose(hFind);
; D0 X( w; y [( ]3 l }
Y! M& | ~ J if(Ret)return RemoveDirectory(Path);% z" V% A1 G3 o2 x
return false;6 O8 G$ S. w, O, u6 S
} & D# i" h4 G% m$ o
完
& d0 W7 x% p0 D
5 A2 W2 Q- R' W5 ? |