|
目录处理函数 # t T9 D4 K; l, y4 t" j3 T8 H
编程语言:C++ Builder 作者:王行舟 : O) [: |/ j! ~" {
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 3 V4 c) R( E4 ]$ C% f
一、判断目录是否存在:
# t- E( e0 u9 j+ O. Y& [/ d2 y* { C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
2 [, a/ f! C/ I. j* b: A设char *Dir为带判断的目录, E8 D8 j- w7 y' e0 b
bool Exist; // 最后结果,表示目录是否存在
" U, q% ^; {6 i8 [if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
! {/ k( G" Z0 P8 d, j8 n6 F0 o UWIN32_FIND_DATA wfd; // 查找1 u- P- f$ D' v) y3 D
HANDLE hFind=FindFirstFile(Dir,&wfd); # O8 B8 |& I! V7 M
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
" N) t. w; }1 Ielse3 ^+ A; H1 |7 K# t$ M$ G
{2 |3 f: x- q- x
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录 d9 C8 `/ g; k( t5 c
Exist=true; // 是目录,目录存在
3 B1 a8 V; v2 V) Q8 s4 Q else/ k0 m6 U8 C; l( D7 j, y
Exist=false; // 是目录,目录不存在
- f* E! |0 O5 ]! a9 H ` FindClose(hFind);+ \- ~$ P. Z+ S% E0 h
} & X* m5 U3 F8 B3 M0 M( H0 }
二、打开目录选择对话框选择一个目录:
1 w( r1 r6 q8 m% C. G# B7 r 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
8 N) F. Z' l0 @5 hHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle . c: U+ ]7 T K
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL4 T" m5 w2 w. G* ^- N" g
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
" _" P E3 C8 z, c; w9 e! H+ f# HLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 " z: k b: U+ G; D' o2 l' G
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS % {) G* E" w7 [ f
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
. w2 N o# S' L ~; lLPARAM lParam; // 预定义的对话框传递给回调函数的值
! X4 \/ T( q$ M6 }% k( w& iint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
9 Y. w! i2 X# x1 [' [可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: % j9 y% i, k0 s
#include <shlobj.h> // 必须包含的头文件9 h$ K/ f6 y+ c! e2 z, u3 Y
char SelectedDir[MAX_PATH]; // 最终结果
# b+ h5 z; q: gBROWSEINFO bi; // 入参/ E* `3 u2 a8 K3 b [. @8 X
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
# `8 S) v4 B$ ~) w4 L2 Z2 k/ h9 NLPITEMIDLIST ItemID; // 所选目录的系统标志指针 + \7 G. a* U l) r3 n" e: A! T
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果( ]! F$ C8 M {5 R9 A
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据( Y% C" @' i. a( c, C. N. Z/ h& t4 h
bi.hwndOwner = Application->Handle;! l8 C! M% b: B# C, r8 ^+ o
bi.pszDisplayName = FolderName;8 t- G# E9 Z. ~2 X5 ^0 H
bi.lpszTitle = "请选择目录"; // 改成自己希望的
, y+ O$ H; J+ K6 T( B/ H' }6 B+ \* Gbi.ulFlags=BIF_RETURNONLYFSDIRS;
w: t& D$ U. G, ^3 J1 mItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框4 ~8 [0 F7 Q) J
if(ItemID)
& f+ O' V4 |; b" x E{/ H: `6 B( i% j( Z: R
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
) Z, }0 y/ B+ E& V& j- N GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放' x* d# w% r2 P+ `$ V; L) D' V- i
} _+ H j8 C) d/ \1 i
三、直接建立多级目录:
- Z; [3 f8 X+ e* k7 h0 {2 v Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
: n: x- J5 F+ `* H5 ubool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
- U$ H& F8 i) d3 p0 d3 R/ z{2 |3 L( r! m$ d! P, x* h0 A% V6 v P# ]
if(P.IsEmpty())return false;
0 C; [$ j( _# X Q+ e# K( e int len=P.Length();
) R4 a6 d/ Q4 `3 F' f& u char *Path=P.c_str();3 K$ d6 ^& Y; j5 b
if(Path[len-1]=='\\')$ G$ N: A1 D0 @: b
{0 j5 A6 ]3 U5 U i7 L ]! V, j& c
len--;
8 Y8 K- E' U2 C: j! ~1 m+ V2 l Path[len]='\0';
# v* W- Q" V/ |- w6 }1 H } // 删除末尾的"\"
" b6 {3 d' s# q- n. J4 }& Q! F; ~ AnsiString Dir=Path;2 r4 v1 o" O: O5 n. f) t& t
// 分开父目录和本身目录名称
* N2 h: |/ }$ O: Q# n. h AnsiString Parent;
* p$ y+ Q( j9 N for(int i=len-1;i>0;i--)9 Q1 \( N2 d& Y: i4 j0 D; R
{* o" L |7 N) N4 W9 t9 {
if(Dir.IsPathDelimiter(i))6 h" y; x6 w; C* J3 e
{
! S6 z* I) ?4 A$ w* r Parent=Dir.SubString(0,i);4 J1 I e2 R0 g+ W5 a: |. O
break;6 A7 p: w, ]( I+ c) g9 L
}
* M" {2 d, \6 }; d, s, L$ N }
7 A; P/ L5 v7 }) K2 q- U0 B% F3 y if(Parent.IsEmpty())return false; // 目录名称错误
* S4 e$ x( @! P7 O* x8 L- B bool Ret=true;
- j* t0 Y2 H# |, p3 { m/ k5 N W if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录7 i( W" p$ m/ X/ ^1 c4 H
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在6 I/ v$ s5 d( ^) |7 z6 c! ^3 _
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
. u' U9 U S& v) n; C9 \$ C! o if(Ret) // 父目录存在,直接创建目录3 p, @$ c2 O, E; Z
{* q1 C6 _1 z* m8 V# y/ [* W6 q
SECURITY_ATTRIBUTES sa;
% X2 O* F) Y* Z/ Q sa.nLength=sizeof(SECURITY_ATTRIBUTES);9 s; T4 n- }' [; x8 r* E
sa.lpSecurityDescriptor=NULL;
1 g+ V b1 ]& ^8 M sa.bInheritHandle=0;
* h5 \5 v p8 V Ret=CreateDirectory(Path,&sa);
( Z6 k5 t4 v: c1 `9 _ }" E) S7 v: [5 W3 h. Q
return Ret;
7 S0 ?3 l$ R( H6 U- ^}
k5 t: @9 p6 o" `8 J9 Y 可以看出基本方法是:
+ v! S0 f) F! J, N先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
6 }4 E' |( T- v+ G如果父目录存在,则直接创建目录,否则自我调用创建父目录。
. i! S8 ]0 w. q5 ^: R4 D$ C0 d9 o" P9 ~/ [( T9 S
四、直接删除整个目录: 2 s, h' J; L6 Q/ e4 K
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: 7 B$ h9 [! q k
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
) n X. _# V% r1 D0 Y- e1 l& a Q如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 ( W8 G: r; z# e
如果找到目录,则进行自我调用,即开始递归过程。 + T+ E! a% J4 S3 p, Q: z+ N2 d
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
" d$ F/ d( p3 Z' Y, f% Q, l! d/ I1 M具体程序代码如下: 3 L/ Q4 D% ^- J
bool DeleteDirectoryEx(const AnsiString & )! w# b1 V! I h1 Y9 `* h& N. I
{
4 q5 D, ?" I& T2 f( T; y if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白0 G$ h& V7 G, h# z+ n- \; J( D/ Z! f
int len=P.Length();( L/ V' c3 G: l5 d, l- B) N
char *Path=P.c_str();8 X& \0 \" X. @. d }7 P3 Y
AnsiString Dir=Path;. n9 o0 B, T0 h L" d. G
if(Path[len-1]!='\\')Dir=Dir+'\\';
' F# h8 o$ u1 N9 C0 @8 T( v6 y% j% T1 b AnsiString Files=Dir+"*.*";
' j( L3 x: y# F7 J3 @ WIN32_FIND_DATA wfd;4 x# R( K' Q* e' A" T ]
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);, J1 S. C3 E' a/ s3 u. ?! |( Z" n
bool Ret=true;' L" k; R2 n( h, _' e2 |+ Z* P6 ~
AnsiString Tmp;9 P& a0 v9 t3 }( ?7 S8 @
if(hFind!=INVALID_HANDLE_VALUE)
% A6 p# z3 W( E( c+ n6 G* X {
% T0 H9 P2 A; c* p7 g8 \3 y* U0 b bool bFind=true;! c9 {6 K ?) Z4 @( n1 A
while(bFind) U2 h$ k& g4 B. t: ?& V6 d: d
{, _* L! q' e- m( m, y9 c1 R
if(wfd.cFileName[0]!='.') // . ..
3 w! q# D/ u- r# N {
; h, V5 ?! G3 ]9 R Tmp=Dir+wfd.cFileName;/ T8 n! ^. E* n; Q
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
: P" m9 X0 T. N$ T" v, ? { // 删除所有子目录
7 m5 t; J7 u1 ^* |, A5 X! l: ^ Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
9 t6 j$ n& R9 s( B$ {2 Q }else
0 p B7 C8 _& L { // 删除所有文件6 @- w; |+ T" k5 b
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
0 X- i$ a) i# w6 T' _3 k+ x* b8 n9 f Ret=Ret&&DeleteFile(Tmp.c_str());
9 x% U2 M. Y' R4 s }
6 o& z* N3 W0 p( K3 E% e! g9 ~8 K }
! `# ]8 r* i/ y Z. _" B% o( b bFind=FindNextFile(hFind,&wfd);& Y+ f' m: g0 M A
}6 j* q: d0 B S6 P
FindClose(hFind);% [# N, A& j0 y8 f1 q5 Y
}
" `6 W& J2 q; o& s& L, \ if(Ret)return RemoveDirectory(Path);
8 K1 m5 L4 H) R. u return false;
" L5 L, g( Q* ]) F. I |} / z; P# `7 i; K; x8 s5 V
完 0 n! H! W8 J8 T
- ~% t: O& R. `1 |( ?$ a |