|
目录处理函数 0 z9 H9 R" y& ^0 G; M% g
编程语言:C++ Builder 作者:王行舟
% F( z1 {* I b- i% o 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
. o+ H% V9 d9 [/ j9 V( o一、判断目录是否存在:
* _+ o! c- a9 ]* E) \ C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
1 A3 E {, {" M% a( u: H8 ]2 [设char *Dir为带判断的目录' m, ?0 `8 r2 s* N+ j o
bool Exist; // 最后结果,表示目录是否存在( n3 u5 [/ a, ]+ T9 z7 A) i0 R
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”1 X1 {' ]9 { O1 f; |
WIN32_FIND_DATA wfd; // 查找% D( O, ^& l5 u, x" ~( @
HANDLE hFind=FindFirstFile(Dir,&wfd); 1 c+ ^, y9 P9 M$ O4 u
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在! @6 r8 p! X: m3 t+ v
else
* z( o* s8 X. l7 E) A{- i4 D/ k6 c0 a5 F. \+ A ?" H
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
) ^" C# u$ ^- K3 G1 I Exist=true; // 是目录,目录存在
/ T6 u x. g1 t! s9 F else5 S+ p+ q0 V% s; E' w( m# e
Exist=false; // 是目录,目录不存在/ V* o6 ]% {8 }2 @# f. @: `
FindClose(hFind);
9 X2 X# X- j) l6 m$ L, e1 c. X2 n}
p% T2 g3 Z2 T) S$ |, { d. F; i二、打开目录选择对话框选择一个目录: . i- D' ~& ^# A' Q5 q8 v; @2 ^$ \
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: 1 q {/ \0 M1 c; |0 K( d) V g
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
6 t7 _% x+ I/ [- n( s: G6 fLPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
' I7 Q6 B4 B$ M. ~LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 * K9 P' _: a I. R5 F/ I
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
1 s& E- a. {4 P* D/ ]- NUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS : J5 j6 Q0 g" G$ V
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL _2 d) @ k& h, K( ?* F: \# Y; x
LPARAM lParam; // 预定义的对话框传递给回调函数的值( S& m4 F0 m4 s5 i4 B& v" N p8 ?
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
: ]9 d( N6 H" G5 x) l6 p$ M/ K可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
- A9 S+ z' n0 t: M* ~. I% ^# v#include <shlobj.h> // 必须包含的头文件
; A$ C ^4 Y$ [4 Z! v& kchar SelectedDir[MAX_PATH]; // 最终结果2 K7 u9 c8 F/ {( {, R& j$ \
BROWSEINFO bi; // 入参' o9 G) Y: _, O7 e3 h v T% m
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font : c' C/ W- c- @# r [5 `6 O4 U8 `
LPITEMIDLIST ItemID; // 所选目录的系统标志指针 " C: z+ q. \, K* g
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果4 H( W2 f% O. F$ l% i! O
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
R# B9 t+ x4 i7 K/ k3 {# V) Jbi.hwndOwner = Application->Handle;
# j( e; A: l$ z8 g1 f3 x1 `; y) Cbi.pszDisplayName = FolderName;( r: O* J3 p; \6 X
bi.lpszTitle = "请选择目录"; // 改成自己希望的
, n. r0 F1 d3 ?1 s6 c1 h2 ?% ybi.ulFlags=BIF_RETURNONLYFSDIRS;) |# H7 L/ t. h3 ~4 d: X1 f/ r
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
7 r4 @ o9 [" m1 ?if(ItemID)& r! e Q) B2 W6 ~6 Y4 q- A' s
{
8 x+ r( L, b1 v) O SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名$ s, {+ C/ b9 U1 @ Q
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放! ?; ]5 g) V4 r* C. w
} ( U3 P6 P: n" o' H7 ]7 x# Z
三、直接建立多级目录:
s" R* f: A+ D1 U Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
, ?0 Z7 _: M& a) obool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false" W/ g1 ^# ?6 f6 e
{5 I& o4 m1 K& H
if(P.IsEmpty())return false;
* o+ S3 X4 X0 \" L- j1 u% b int len=P.Length();
* K, O' _2 d0 G char *Path=P.c_str();
: d/ E6 n3 I5 S4 B* ~; T# S- O if(Path[len-1]=='\\')0 }9 z7 O& ?4 \( u" w; }
{
3 R, N0 Y( ^3 n: k( y D len--;
- K& M+ j- R) U Path[len]='\0';9 c3 m5 X4 I3 n8 M* Z
} // 删除末尾的"\"
+ e" R( i7 L2 p- [1 q% h! w. y AnsiString Dir=Path;
% c' a3 z+ o( V- {* M# V( b // 分开父目录和本身目录名称2 u- K! u* Q5 |7 p$ f
AnsiString Parent;
' ]1 O9 N3 X; y2 e5 h) W for(int i=len-1;i>0;i--)( Q# V+ E/ x' j/ J$ |
{
( p# h, k# I1 n6 D% M4 L if(Dir.IsPathDelimiter(i)). f' G! s7 _4 B0 d) O' q5 i' N
{4 m+ E3 c7 F& u% q$ s/ @- @9 s
Parent=Dir.SubString(0,i);
, f/ D' ^. L Z, p break;
2 J8 Q( \" S* A. B) v% n; M, o }0 a2 y% F- }0 f8 F* J
}
( H) }+ R+ R. K if(Parent.IsEmpty())return false; // 目录名称错误, ~* Z6 x) H9 h- n+ N" `8 D
bool Ret=true;
1 Z! c1 d1 w6 x if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录3 V9 c, }, ^- t0 `
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
3 e0 A, i7 {2 l if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录! \2 F0 i( s; f i, O t9 t
if(Ret) // 父目录存在,直接创建目录) g: k' S! Y7 N
{
$ q, r5 L/ k* o SECURITY_ATTRIBUTES sa;7 p6 N2 A* {8 B( f5 t$ z
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
8 H0 Y4 @3 ]3 M5 M/ k6 a$ @7 S sa.lpSecurityDescriptor=NULL; T) J* t' I: R* d
sa.bInheritHandle=0;
. F4 N: T. U! E5 ] Ret=CreateDirectory(Path,&sa);( k7 U- _0 t+ |- b J
}" V& U! M! X& H/ m7 `- m4 k
return Ret;( B( K1 q* _% G1 [. F* p, E) M6 t
}
. }1 u% t y! I: _$ A5 v) f" j 可以看出基本方法是:
! J. ^) }; i3 X* Y# a先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
# O& m% K6 T% _如果父目录存在,则直接创建目录,否则自我调用创建父目录。
2 c5 ?/ P# u5 U1 A
$ ]5 V: Y0 }( O四、直接删除整个目录: + q3 d4 f* M8 s( B
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: 7 ^# E. x# W. a3 D0 J+ `
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) / U4 _2 \1 n- h: G! ^2 D
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 ) |5 O3 y2 E Y2 d
如果找到目录,则进行自我调用,即开始递归过程。 & k7 r. C7 z) O3 ]
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
5 l ^# z6 `; u' o* W具体程序代码如下: ( Q m- |: Y$ ~. f
bool DeleteDirectoryEx(const AnsiString & )# l9 }5 s/ G' m' {) S
{4 J% N! i4 Z" P' _9 i
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白/ T) g6 j3 q) T2 ~( [0 P t/ m
int len=P.Length();7 K8 f+ X/ b/ v8 |; v
char *Path=P.c_str();
) U- \0 @6 Q: ~$ O' V9 H! ?& i$ ] AnsiString Dir=Path;
1 a9 z2 Z: \ b2 n if(Path[len-1]!='\\')Dir=Dir+'\\';
9 Q1 S, h. u* U9 E) k$ m# X( k6 N$ _ AnsiString Files=Dir+"*.*";
. j- D A* i5 L2 i5 u WIN32_FIND_DATA wfd;
# T! X/ W# Q$ X# s( U. j) u HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
: n5 y( p- Q8 C2 w bool Ret=true;
7 p5 U: A( z! f2 a3 ~$ F' p; d: X AnsiString Tmp;* O! s* l* y' K8 J$ \
if(hFind!=INVALID_HANDLE_VALUE)
* f. x6 N& g, p# d3 C/ y: Y {* M8 l* [4 I) l
bool bFind=true;
2 a& U2 u8 w" l while(bFind)
[+ i& u) g, @6 {; i% i2 s$ U' p {( j0 k# g7 x, s, \
if(wfd.cFileName[0]!='.') // . ..; }& l/ h @4 H2 e2 K) M
{4 H4 g% M2 i. \" k9 l
Tmp=Dir+wfd.cFileName;. n6 |& U/ U7 I! I7 j8 v( X
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)! S; k* s+ `; R) R! M/ ~! D
{ // 删除所有子目录
' w' E3 Q0 Q: f+ } Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
+ J& G. I/ ~8 l9 Z1 r% L0 Q; z }else" S! I: ?) d9 e' I$ |# `, d
{ // 删除所有文件% `0 A/ l4 G1 C1 j5 I$ S: j
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);/ P f/ J# Q$ _! O
Ret=Ret&&DeleteFile(Tmp.c_str());
1 `( \/ T# g f1 n; ]1 S }# y6 Z* X, b. B5 m' c
}4 J8 m& B8 q! K* U3 _" ^
bFind=FindNextFile(hFind,&wfd);
. B K F5 H8 v! n1 S2 T N, F0 N/ X }
7 ~% i$ _( c: s \2 K, P8 j7 z FindClose(hFind);
; L! X: X0 l+ b: y, J }
6 {8 X) A* t+ W: Z D8 I if(Ret)return RemoveDirectory(Path);* x' K5 ^& I* N1 ?- `. ]4 Y
return false;; E0 e' i- t' T% _4 R; x! K, t; _
}
: j* C N2 \: b6 j% ] i, x完
6 X: Y D8 ^; [, r! b1 ]) {
2 h& `- w1 L, o" H" i0 } |