|
目录处理函数 - I# R8 a$ O* o- c6 R3 ~( o- I
编程语言:C++ Builder 作者:王行舟
# W$ \0 r( g+ _) ]& x" m# c 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
" O, j1 T( e# k L一、判断目录是否存在:
% \! o/ S6 c2 w2 q9 Q3 q6 ? C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: ; b: D g/ C: F' K- R7 f
设char *Dir为带判断的目录4 F: r/ `3 H4 Q4 s3 Q
bool Exist; // 最后结果,表示目录是否存在
0 X( O! Y7 c8 ~2 gif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
7 {8 g$ i: P3 G7 ^. @, sWIN32_FIND_DATA wfd; // 查找/ s6 U% r( N8 r% _0 n# L7 I8 R
HANDLE hFind=FindFirstFile(Dir,&wfd); 4 O3 w5 T" } Z2 H; j7 ^4 L
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
7 g. [' R* W* n9 qelse
. u K! u9 \ P4 X5 g' [) j" R{ T3 V- o N+ \# K
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录* Z- k) ~) \! M& d
Exist=true; // 是目录,目录存在
- X* k, q8 X; K; k3 b9 T else$ ]4 a) l2 f$ w! C. v* Q" B" @
Exist=false; // 是目录,目录不存在
0 s, k0 K2 S- p FindClose(hFind);
, f" r/ |, j5 V6 t. ^ L8 O1 ^}
1 Z7 d+ K- E# F0 n1 @- _* A二、打开目录选择对话框选择一个目录:
* X7 \3 k8 N1 w2 s 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
& a. Q' T4 G4 ^6 O/ |3 y ^1 OHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
# y3 A. o, _- E5 N" NLPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
9 _ m& c8 h4 `4 m, k1 gLPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
: e+ z$ w* M4 M: ?LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 4 ?' M6 [* G( c, @, H6 |8 S1 {
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
1 T% w( M' t* A3 x3 DBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
. {- v; W* F) _1 eLPARAM lParam; // 预定义的对话框传递给回调函数的值
2 o9 n) G) ~9 y" s% d Cint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
- r) ^) J! J0 q- m9 ?可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: / P, q. C- v; S. z% y
#include <shlobj.h> // 必须包含的头文件
# u' \1 n1 b4 J8 [2 Uchar SelectedDir[MAX_PATH]; // 最终结果
: `2 A& n& W- L3 y6 K* fBROWSEINFO bi; // 入参
- T( C5 s( f* r0 a) N5 r- `" ?" Jchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font W" i J9 z9 W
LPITEMIDLIST ItemID; // 所选目录的系统标志指针 0 s0 L- t+ {# B4 s* F$ k1 b- _8 m
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
; [5 v# p. J) g- ?1 Wmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
+ C4 \8 Q# f& a- {$ O* `8 V* Wbi.hwndOwner = Application->Handle;) b# f! }( v/ Z1 S6 {
bi.pszDisplayName = FolderName;/ v. G- d% U" s/ k% E$ h
bi.lpszTitle = "请选择目录"; // 改成自己希望的
6 v; @7 G7 U/ B4 Z- Jbi.ulFlags=BIF_RETURNONLYFSDIRS;
; W/ X2 [6 I7 I" O7 `ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框, G+ R/ Y. w+ p; g9 W2 b9 m
if(ItemID)4 Z, R Z* e( u$ W( z9 F
{
6 v4 F0 H7 I6 _; m+ i SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名; T! q e! A- J; `6 u! g. V1 X
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
; ?/ d& ~0 y ^} - X4 z/ |1 R) i# T: k' s
三、直接建立多级目录:
9 P) y, L/ f# |) m! b2 ` Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
9 S$ H* N: o4 Sbool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"2 n! h9 k; D/ @3 u2 U2 U
{
6 Y/ X3 J$ l+ U W i2 r/ u. o5 C if(P.IsEmpty())return false;4 ]3 _# O! i+ o: s9 Q$ P. a
int len=P.Length();
/ P$ G- l6 x/ T$ R; F N char *Path=P.c_str();+ o+ z) L8 f/ ?8 I J0 w
if(Path[len-1]=='\\')2 N/ `+ A; ]/ ^- M* @4 u
{
' `/ b6 A9 q1 k; F len--;8 m& c+ y( t( b% l
Path[len]='\0';
6 V) Y" w" k# @4 d1 p/ s } // 删除末尾的"\"
. t" R. n v1 ^4 z9 j7 V, e6 i AnsiString Dir=Path;
7 ]4 s! [- ~( c8 G // 分开父目录和本身目录名称% @% }9 z+ e8 m+ o! H
AnsiString Parent;
/ C) T9 p7 [; z6 i) o for(int i=len-1;i>0;i--)9 G( U" s1 R: i4 p+ ?9 u
{
# R2 H1 \- A" `. ]# j0 ^ if(Dir.IsPathDelimiter(i))7 @& S" }" I5 z' R7 y
{! ]+ a2 Z* ]& _. m, f8 ]) U4 `
Parent=Dir.SubString(0,i);+ J' ^6 S+ I# k8 P$ F
break;
$ @; J Y \8 V1 k% i, I }" a! X, l8 S$ H: t+ o( B) }/ ~
}
- t4 j/ T' Q* j2 Z, R if(Parent.IsEmpty())return false; // 目录名称错误
8 q0 P7 |4 L$ J bool Ret=true;
7 z% h) T8 ~; k' t if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
3 }8 O& s' P8 @; V$ ~ Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在$ y' h; b+ M) n9 i0 Y" L" k
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
* U/ b, g/ K% z if(Ret) // 父目录存在,直接创建目录1 ?, @: u; n$ L4 w
{
( E( r2 J$ a5 _+ W( g SECURITY_ATTRIBUTES sa;
: }* k" S* D; k7 M& k6 Z/ \# y sa.nLength=sizeof(SECURITY_ATTRIBUTES);; I& q k) i# X- R' A# v) h
sa.lpSecurityDescriptor=NULL;, e1 L! I# J% u0 S' |
sa.bInheritHandle=0;# G% j; {8 n$ u0 O6 b5 j
Ret=CreateDirectory(Path,&sa);8 L9 L" R; R% G
}( c" C! M- k; X! l
return Ret;- @( b# V, ~+ J1 } ^
} ( K9 i$ m, D6 @4 w
可以看出基本方法是:$ a5 U8 F7 O. L
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; 3 a; ?- N2 L5 Y/ G% k
如果父目录存在,则直接创建目录,否则自我调用创建父目录。
3 v) R1 F; d$ U+ k( s3 q
2 p: v( |: s% A6 N+ J8 k四、直接删除整个目录:
( a+ ^7 @7 a3 s0 L7 @7 P/ I: B* p 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: " a6 E& J, |" ^/ ?; d
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) ; e" D: O5 p6 J: M! R% W
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 7 Y8 e Q: Y9 v B
如果找到目录,则进行自我调用,即开始递归过程。 & U" @9 q& O5 I8 B' B6 O) ^
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
+ G3 G! |/ V, n具体程序代码如下: 4 m: C8 P9 F5 ~6 ?6 S& o3 F$ c
bool DeleteDirectoryEx(const AnsiString & )
/ X7 w7 h3 G2 F Y+ K" K. H. y{
: M+ x: r9 q. A) n* K& a. [ if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
6 o. {4 k4 C% V* X, y, l int len=P.Length();$ M) k1 L' ? \6 ?6 |
char *Path=P.c_str();4 v/ a0 N/ D/ E/ Y% `# H2 c
AnsiString Dir=Path;
! }, e. k) B! O$ Y& Y% l if(Path[len-1]!='\\')Dir=Dir+'\\';- z2 J) d/ c, q2 n- [3 i
AnsiString Files=Dir+"*.*";8 ]( I+ Y; m) A1 q j
WIN32_FIND_DATA wfd;) B1 L8 I0 J% o
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
' ?5 k e. @7 S4 a bool Ret=true;
5 \/ Y( V2 Z5 n, A( q+ T+ s7 { AnsiString Tmp;& X0 ], y; Z5 ?( D
if(hFind!=INVALID_HANDLE_VALUE)1 Q" Y4 C' w4 |0 n" w
{( E) k/ m0 ^! j6 A/ Q0 _1 K' }
bool bFind=true;
- C4 K- f( O/ ]& `3 N- o while(bFind)
2 w6 |( u6 }' X& m4 @ {
1 _, X+ t B! w if(wfd.cFileName[0]!='.') // . ..
2 E4 L# V ?0 R; X9 t {
$ b3 H7 @& c4 u8 @ Tmp=Dir+wfd.cFileName;
9 e+ n) b6 I6 U0 o0 N5 B if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
( b9 d/ T& {+ {: Q( A { // 删除所有子目录
) ^8 w/ s! x$ Z6 {" U ~+ Y Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);/ F$ Q; O( z- @: B5 P4 s' h8 y
}else
/ }9 n: V/ b1 ~$ R { // 删除所有文件
4 z4 b: o9 p7 t5 @0 \* c SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
' E* q. h2 k( I/ H+ b: p Ret=Ret&&DeleteFile(Tmp.c_str());
\+ E! a0 x+ i# x, l }9 D3 {0 |$ T6 A, b
}/ G# z( ]+ I+ N H4 s
bFind=FindNextFile(hFind,&wfd);
$ p6 v: D) A) d/ W+ B7 H. k }& Z; S* g! U7 R0 \2 _
FindClose(hFind);" {- Q% M, y3 h0 u5 v3 m3 c
}
% c* Z0 z+ W8 T( X, i if(Ret)return RemoveDirectory(Path);
% r# b) n! Q: ]5 h" b return false;5 R3 D$ e3 `4 R3 H: K# w
}
) }' T$ ~) p2 P# p+ a+ S完 9 a9 I% H* `, X; ]% F. D3 R4 ]& c* B
, g# y/ R) `# s* i2 N6 c |