|
目录处理函数
' |9 ~$ S; p% c, q8 h编程语言:C++ Builder 作者:王行舟 : a5 K* K0 {5 t3 s; b/ g) Y' d
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
+ S4 K" c; _( q$ D0 Y* C+ Z1 J一、判断目录是否存在: 0 z( |8 s3 p- E5 b( ~+ }3 c- C' w( z- v
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
5 Q: v: \& j8 W. j- a( g设char *Dir为带判断的目录2 p9 c. Y, F5 Y% _: S
bool Exist; // 最后结果,表示目录是否存在2 a' K* A8 z- n- a0 L& M
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”9 A- Q! I3 e0 i6 f; d! B
WIN32_FIND_DATA wfd; // 查找
) B3 L! B- p" S6 rHANDLE hFind=FindFirstFile(Dir,&wfd); , T: K$ y8 G- d1 ^8 o+ Q8 D/ \
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
- B* @& d" C6 @$ X5 W1 V) {/ Q) i5 Aelse
( {# d& @" c* t: b{8 }1 N% a6 M3 E. C
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录3 I; d% A4 A" v, V9 V$ C- P! a
Exist=true; // 是目录,目录存在
; z* t7 c0 r( q1 I" l2 g! k else: q& F7 M+ b/ h! G
Exist=false; // 是目录,目录不存在
# N W' a3 v# N) Z! O+ \" F FindClose(hFind);! i3 D h! @( s4 o
} - j9 V# L; w4 m
二、打开目录选择对话框选择一个目录:
* g1 T: y. h M( x; h+ Y2 a 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
% A3 Q0 F/ D& r. e# U0 L: T9 `HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle B6 w, |0 b \- q' ~( k
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL0 o$ l) w! G# k. |' `8 ]. @
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
/ C( F/ P% d4 P/ W" oLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 % ^9 e2 P0 o1 U6 [
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS 8 x" _7 I2 J9 p
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL 2 q( G# v7 b5 d: D; g
LPARAM lParam; // 预定义的对话框传递给回调函数的值
. D; U' b. g- Aint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
' j# @0 ?4 ^5 _9 q E; c* d1 l, B' [可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: ! n, B! `' U/ L9 C9 V! ^- R$ t
#include <shlobj.h> // 必须包含的头文件& d3 E! a6 |- U0 `, y
char SelectedDir[MAX_PATH]; // 最终结果
( R0 d( N5 @: b; e( m OBROWSEINFO bi; // 入参
7 {* \1 X( z& U' e7 C- echar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
( X. V8 F1 q+ c \4 bLPITEMIDLIST ItemID; // 所选目录的系统标志指针
1 D+ `. }) U4 T+ }memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
, _% ?5 ^* }& ~5 z' W5 gmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据5 l8 @6 M6 s3 J4 B8 Z; g2 {
bi.hwndOwner = Application->Handle;
" Y) m- N0 k/ s2 [8 M% V0 bbi.pszDisplayName = FolderName;
! ?; y# d: T- l/ g6 W) Sbi.lpszTitle = "请选择目录"; // 改成自己希望的2 ~( x4 c- l% v" r
bi.ulFlags=BIF_RETURNONLYFSDIRS;
% b+ i* k: @5 pItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框8 b1 b; p" x @: b, ?, _
if(ItemID)- e# t- f. ^) y9 Q" y' X9 G
{ k' N( K' ~* \( a4 { }( r
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名5 I) q- r( [( H: X4 G
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放) {4 Z3 W9 b" }: [
}
9 Q; F; `) ^6 ?0 J- n- c$ N; U4 ~% D三、直接建立多级目录: 0 i& i9 {3 u4 |8 M a" v
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 3 ?) [) i& S$ D% j. G n
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
& ]" _$ b, w1 B8 E{
6 {6 W0 X& x) G( c" F! D if(P.IsEmpty())return false;7 r3 x y, s7 B3 i& O
int len=P.Length();
" E2 l) q! }& |5 n8 m char *Path=P.c_str();
8 _% d2 R; R( t. Q if(Path[len-1]=='\\')
+ c) R: q1 l7 Y2 W" i {/ V/ [8 i! P% Y. k( ?1 m
len--;" T. j# e' `# M$ [4 e! k% A( t
Path[len]='\0';
! j9 ~* B- n5 m. P" F+ K2 K } // 删除末尾的"\"
8 `# O( A* U1 h7 z* C9 B( I: D6 J AnsiString Dir=Path;
6 R0 ^. P, Q& v0 {6 Y) w9 H // 分开父目录和本身目录名称& e$ Q' y" j Q$ a5 ~
AnsiString Parent;/ l$ c# N( G( v% f
for(int i=len-1;i>0;i--)
% T" c, R5 \; ~: i8 j. M {# a1 K+ E4 B( M3 z5 ^/ M
if(Dir.IsPathDelimiter(i))% v4 R, u4 Q# w6 W" ~9 P
{% E4 u& H/ k: e4 G
Parent=Dir.SubString(0,i);
# j3 G$ S4 {% u; ^4 i& W a break;" `0 `( V5 {. t
}
1 S+ `2 ]% p* Z( |3 w% l/ w }
9 U1 Q9 E8 Y% A5 w( ^; w" ]% K if(Parent.IsEmpty())return false; // 目录名称错误; D# w4 ~3 Q& k. j) A4 z. r+ F
bool Ret=true;
9 F; G; z- ]) O% u if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录9 K& R& [, P2 Y1 [. `) L
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
r) A6 @( j! v2 y4 m if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录! K! Y' T T! ~! r) I
if(Ret) // 父目录存在,直接创建目录
" n# s3 K1 i3 h$ S4 H; p {7 c8 O& \" D3 I, Q0 S' B |1 u* z
SECURITY_ATTRIBUTES sa;' g: t% T3 f) s! e9 b
sa.nLength=sizeof(SECURITY_ATTRIBUTES);1 j% k* ]; p; [! @6 {, Y }0 L
sa.lpSecurityDescriptor=NULL;/ C' k! S2 V U/ O$ J
sa.bInheritHandle=0;
2 f5 ^ X1 A& Y$ c" k Ret=CreateDirectory(Path,&sa);
5 P% Y2 x$ }8 U& L8 q, O }0 V% O* }: _8 p; D$ W: V6 U
return Ret;
- N q0 [0 M- J; U}
/ k( ]/ H; o) G) @# v/ l 可以看出基本方法是:9 Y) z- r6 Z7 y* |7 {9 V( Q4 c
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
( V$ `9 _$ @ o. u如果父目录存在,则直接创建目录,否则自我调用创建父目录。 " r/ G& D" m4 Z9 F7 D: B! Q
' |, z z1 j. u& S4 F# a
四、直接删除整个目录:
2 F+ p( T% E& s) K 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: ( J! Y% _* l. j
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) * Z" N# O' f+ m! f* K( Y
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
- U' H# ` d5 x: P; Y如果找到目录,则进行自我调用,即开始递归过程。
" s8 s8 [% e1 j. x1 y" E& h ?如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
0 j) s6 h8 l2 I3 G* Z具体程序代码如下: : ~; c" A& y1 u( G8 Z m/ N
bool DeleteDirectoryEx(const AnsiString & )
P/ S: }& k- {8 G( \" w{
: I% y, N$ y1 |0 R, @4 n- Q5 l if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
0 M; q8 Z; i U2 S2 D int len=P.Length();" X r+ Z7 k% ?5 H3 I
char *Path=P.c_str();2 ?6 ^8 R4 T$ r' X# J
AnsiString Dir=Path;
! s5 I9 i, K! l) l4 ?3 w/ w/ ~. i if(Path[len-1]!='\\')Dir=Dir+'\\';6 E; F3 a) y3 ]" h$ \# t* B$ \
AnsiString Files=Dir+"*.*";
. D- N7 d! T# C2 ]/ S- i& k WIN32_FIND_DATA wfd;
1 H; L, t3 R* {# o6 a HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
7 x) T, p7 ^- |2 `* A, m+ z* i9 B bool Ret=true;
1 G+ E* y6 M2 S3 e) I' e AnsiString Tmp;+ e, N ^: z N, Z/ b
if(hFind!=INVALID_HANDLE_VALUE)
y1 ~ [: }- ?9 b5 v {. \. `6 b2 g8 G) x8 C
bool bFind=true;+ P$ _: c; @- M7 ?4 l) i. M
while(bFind)- V6 z$ W/ `1 w: W' O/ m
{) i) N& o+ l/ ]7 N6 U7 Y1 I
if(wfd.cFileName[0]!='.') // . ..
1 z, c, O" N4 Y$ p* F {# O8 z: z- f7 D P9 U5 q
Tmp=Dir+wfd.cFileName;
* k. B @. ~' f( E8 P" Y! R/ K/ \ if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) m5 U: q" V% D' Z a
{ // 删除所有子目录
% j+ a$ J5 @" @( j+ v7 h9 Q Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);+ k( p7 M F& V
}else
; A3 m j2 G0 I9 C" ~! W { // 删除所有文件4 }4 i- O# R/ g
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);1 a, F7 g/ c% s& ^! ?. M
Ret=Ret&&DeleteFile(Tmp.c_str());. D: J' G1 P& l( V* ?* A
}8 t+ m# W9 n) P
}6 h# w o3 d" o- r! ^/ H% I% R' E5 j. w
bFind=FindNextFile(hFind,&wfd);
( k9 |4 Z$ K6 F* J; o }2 o( n7 {& A* F. n
FindClose(hFind);7 q) f! `/ Y+ H+ V
}$ l3 ]6 l% @2 V3 I
if(Ret)return RemoveDirectory(Path);: v0 n) e% A: @# W! P
return false;6 h$ b( f5 Y3 {2 E% b/ U
} / B$ x8 r. {* p$ I' d5 ~ w4 `/ p. t
完 3 y- M" N, x6 O7 I0 a9 b: G+ _+ u: Z
! G/ l @+ f, e1 C; h |