目录处理函数 $ |/ ~* Q! |+ C4 l, g
编程语言:C++ Builder 作者:王行舟
% o5 z( m2 X. Z2 ?; _ 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
7 R4 r! J+ y4 W( [一、判断目录是否存在:
- ?; ?% @& `2 N C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
# N+ k+ ~, u. j设char *Dir为带判断的目录
" R5 @4 n# y. ]bool Exist; // 最后结果,表示目录是否存在
M- M- T* r5 t# z) l. \1 t, e& \if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”* }$ ]6 f' H- D( C+ g+ M( X
WIN32_FIND_DATA wfd; // 查找- o4 S3 ~3 G% c8 I
HANDLE hFind=FindFirstFile(Dir,&wfd);
2 d* d# E/ W1 m4 f0 U- `& oif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在. U; ^; F2 `) L0 q0 ^+ o+ J
else
7 F6 G H/ A. H) Q3 D5 z9 `{
( x X, C; [' P2 I. l if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
% n+ Y; D/ k. R0 ~0 r: o3 W Exist=true; // 是目录,目录存在
+ k! G. f2 j0 P9 Q/ N' _: r# y else
, K1 j" W" e$ n/ v {* n% o Exist=false; // 是目录,目录不存在) X( P) t0 V/ A, _: s9 B& m
FindClose(hFind);
3 W1 g6 d& J2 A7 k5 p} ! T8 w8 N* I$ R% u( H
二、打开目录选择对话框选择一个目录: 8 E- G% I, @/ J: ^4 Z, J
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
1 I8 C w4 C* j: z% d7 wHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
* F, J/ f0 V7 qLPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL) @( F" {1 ^% C! k, Z0 o! \
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
6 `, [! p7 K. ]* _# tLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 % ^4 B7 }4 G2 |) o& I) m/ @
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS 1 {& w4 f4 b: k
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL 5 J/ L3 C* S3 y. ]0 n' n7 Y
LPARAM lParam; // 预定义的对话框传递给回调函数的值) |0 Z0 Q5 ^3 M2 ^ N
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引 9 ]0 J. M$ q ~ B) e# C9 x3 T
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
. T0 Z. T4 I- Z% Z7 {#include <shlobj.h> // 必须包含的头文件) B1 ~8 k3 L' b$ D, G& ?3 C M
char SelectedDir[MAX_PATH]; // 最终结果+ _( r. \+ S. G( a& p8 V
BROWSEINFO bi; // 入参4 q4 t- U) f8 K! S g: _
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
. B5 x6 h$ o$ l3 MLPITEMIDLIST ItemID; // 所选目录的系统标志指针 ) N! ^- Q9 e6 S" ]* X
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果( F1 F' g$ [* Z9 o" w: e8 d
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
! o$ ^6 ]% u% N4 B+ q8 @# bbi.hwndOwner = Application->Handle;
& e6 K1 X& l9 K0 o, f+ H' mbi.pszDisplayName = FolderName;
1 P0 O$ q$ c0 A9 ]1 T' k% lbi.lpszTitle = "请选择目录"; // 改成自己希望的/ V: i- c. l. g+ A. w5 R
bi.ulFlags=BIF_RETURNONLYFSDIRS;1 s6 N7 g& R' T- O+ |) Q( n
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框0 }" @% G- V. V3 ^; n+ {7 U
if(ItemID)% c0 R' v* s3 W& m
{
! {. ?0 R* J) B4 H SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名 X4 `& W9 @; m
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
4 G% S2 m+ \+ v. o. o/ w7 N0 H}
9 g4 t8 W# p6 ~! N2 f% q三、直接建立多级目录: - C# a$ d6 @( Q8 N: G3 ^9 \
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 X$ b v& W- _! h; y+ Y+ z
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
& @% T& l1 M% s3 C{4 e$ t0 H7 C9 J% X
if(P.IsEmpty())return false;
. `, s: o; y$ x7 z int len=P.Length();
5 {1 B/ s* L7 P+ @) j# k# {0 u3 \ char *Path=P.c_str();
- F0 Y- l/ [' G1 v2 I4 V if(Path[len-1]=='\\')
3 J; O/ e5 o, d/ }* m9 B- |2 y7 m0 \ {
' P7 i* z1 [+ U/ F9 h2 M len--;
0 _2 q, i7 \% `$ V9 i Path[len]='\0';$ b4 y1 ?5 _+ d: H1 [. b; K
} // 删除末尾的"\"
/ T" j& f2 W$ [9 _- ?9 `: P* u0 J AnsiString Dir=Path;
& [! Z2 J0 t. h6 G+ s0 P) g7 l // 分开父目录和本身目录名称/ J9 ^' K8 y) j( ]
AnsiString Parent;0 @6 |$ J: L% J* i! P& A B
for(int i=len-1;i>0;i--)
+ @6 P5 @& a' d5 d: r7 d {
9 O& \: a- [3 z if(Dir.IsPathDelimiter(i))
6 K, A1 v% p( m {
. R# d3 H* x# q+ \8 B Parent=Dir.SubString(0,i);& m/ b8 Y3 F0 ~# {3 E: v
break;9 _& W$ V. l4 n7 V- a3 ?( X
}
2 G; }$ O# u4 L, j$ Z3 b2 m }
4 C, H K/ O7 L$ |1 ~- I+ k if(Parent.IsEmpty())return false; // 目录名称错误
2 P& @7 x! n6 |& e; u bool Ret=true;* X6 M( v. |1 E" z* t+ R- q; R
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
, r) A. q0 f( v0 [" t% G1 V/ \ Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
# \3 Y: w% C1 S! u# o if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
" d9 D, }1 a( @ if(Ret) // 父目录存在,直接创建目录) y/ R6 U* s2 A9 G
{
1 [# n/ g+ b/ N( J1 n; A8 ` SECURITY_ATTRIBUTES sa;7 y5 F4 X' X0 G+ C
sa.nLength=sizeof(SECURITY_ATTRIBUTES);1 I+ Q+ P1 K, ]
sa.lpSecurityDescriptor=NULL;! P8 M1 [3 n& L% K
sa.bInheritHandle=0;2 Q& d5 [& t8 h) |- x
Ret=CreateDirectory(Path,&sa);
; B4 r# ^! S. Y& |5 c: v }
7 } c/ i# x' p, p. ` return Ret;
/ s \7 n3 o- o, B# W} ; Z# H# `; Y! r" V3 m
可以看出基本方法是:
4 R2 E% O/ ]$ \先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; $ ^, Y7 T3 P1 t4 M' C
如果父目录存在,则直接创建目录,否则自我调用创建父目录。
0 g* c7 x3 c$ W# L' G6 w, E" n. z' J$ A
四、直接删除整个目录:
6 A5 @8 A+ T, l+ b: ~ G 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: 0 x0 p; ^9 k+ b" Q
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) ' B/ ?+ N8 J! D3 R2 [5 |' i4 h; B
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
s0 y7 E9 M6 j5 h ?) l! o如果找到目录,则进行自我调用,即开始递归过程。
2 {" k/ h$ ~! [7 p如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 , {* R' J+ X/ `
具体程序代码如下: 5 f( L2 b5 Q( y4 s5 u, X
bool DeleteDirectoryEx(const AnsiString & )
8 q. E6 {" u$ h) J{
7 A- R' R! X+ r& U* R& f if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
1 }$ J% P8 p! x) m8 W int len=P.Length();
8 g- G( _& X) T2 I0 E char *Path=P.c_str();* w0 A. o( U" k4 [7 d6 k" n3 W
AnsiString Dir=Path;
' [' {4 _3 J; ]* I# ?* s if(Path[len-1]!='\\')Dir=Dir+'\\';
" D. v* Y( l* Z2 v AnsiString Files=Dir+"*.*";) c& ?, |8 d9 p o. j
WIN32_FIND_DATA wfd;* e1 @; @+ J& N' E" g' x
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
! C; b" A0 B+ a2 F; P bool Ret=true;
. w/ g4 p$ E7 i# z AnsiString Tmp;
9 _+ B( `8 Q8 P3 I if(hFind!=INVALID_HANDLE_VALUE)& x. E o6 P9 `$ @ v
{
' p' Z; n1 q, b/ V# t1 G bool bFind=true;
) k/ w- Z0 Q5 F/ x2 S( V3 y while(bFind)5 L/ B7 p" N+ D& _5 d: t5 j
{- q* W( A- x6 a9 t4 k& D
if(wfd.cFileName[0]!='.') // . ..% k( ~) s5 O' g8 }# ^. n% ?" T, {2 o
{
7 ]3 S) V X/ O% v Tmp=Dir+wfd.cFileName;
0 ?. H3 s" B- D1 ? if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)' d+ |6 i3 |+ S' e
{ // 删除所有子目录
/ {$ B3 i4 A% F1 }. b/ F Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
, S6 i+ Y. l5 n }else& h! L6 |) z/ ~" r2 Q+ ^
{ // 删除所有文件
% B2 a3 G4 g4 b- T0 f' a SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);3 U6 K) b9 _0 z& A% F0 D* x! R
Ret=Ret&&DeleteFile(Tmp.c_str());
! ]8 k6 K. ^; z9 o4 X/ r1 x- F }
" H) a; ? O7 F9 j2 a- ]1 C }
$ c8 n9 I4 v1 m& g( i$ Q! n' E bFind=FindNextFile(hFind,&wfd);' z* J' H0 f& {# i
}
- r) Y0 N) U" M/ V, |! \ FindClose(hFind);
; b( i% I: R2 P( g4 y, s }
( U! j& l" n% v. n7 j if(Ret)return RemoveDirectory(Path);
. F$ N- D$ R& M9 ` return false;7 J7 |8 s! @$ I9 L3 w3 ?" u
}
$ \/ Z" X. S3 t e7 D# a: I完
W' J v: S+ p$ E @) t/ Q
- x) l o, G1 ^0 { |