|
目录处理函数 % b) `' `$ e4 u
编程语言:C++ Builder 作者:王行舟
3 w1 ?1 \" n( K. ?. ] 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
C. ~% G6 C, t一、判断目录是否存在: 5 w' y) L) I& |4 h1 |2 O" _
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: h5 D/ V( r# b8 C
设char *Dir为带判断的目录
( m1 u( W2 G7 Xbool Exist; // 最后结果,表示目录是否存在
! n6 }& h/ Y* o( X% _if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
# E+ v; I, E; c9 X$ k6 G. N: i& cWIN32_FIND_DATA wfd; // 查找
5 I+ }9 j1 {8 h+ sHANDLE hFind=FindFirstFile(Dir,&wfd);
" x0 T/ g3 s( ]9 F# Aif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在( A% ~8 `5 e& H6 n+ k- E4 z3 q
else
- d/ y7 }* E7 R- F) x{
$ D" `% y, [- n" ?% I- b9 r if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
/ s, I( Y( b; F! a& o+ g: S Exist=true; // 是目录,目录存在
" e( ~7 z# O- A. m else! a; U. b# Z' Y$ k6 L- w6 U
Exist=false; // 是目录,目录不存在
: j- P1 ?. Z! F9 ^0 t1 ~ FindClose(hFind);
) T2 b# _4 J4 y; H0 _5 N% I} ( G: A& w& k% [1 P8 T/ h
二、打开目录选择对话框选择一个目录:
; s5 L" j- u; ~8 v' g$ F 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
5 _* S2 x) O z- a# p& J LHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 5 Y* t3 Y0 x: b! C' d U
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
+ O- f7 y' v9 xLPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 8 E- f+ g* q) n" E! J
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 5 x2 a# U2 m0 V& c: i7 x6 J; K
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
% L6 Y* V# @; Q& D9 RBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
) L3 l! U2 F8 @) @2 kLPARAM lParam; // 预定义的对话框传递给回调函数的值8 J4 T8 I: Z6 t3 k' X
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
7 B/ L4 y9 k% V3 ]可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
% V' D/ m" H. B#include <shlobj.h> // 必须包含的头文件7 x( m4 V! B" Z! e8 r
char SelectedDir[MAX_PATH]; // 最终结果# O6 C6 k: Z9 v1 n7 e P7 e; q
BROWSEINFO bi; // 入参# f( ?/ ^. V- Q8 k4 z- X( O
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font 3 r* ~) O. _" N) H8 T
LPITEMIDLIST ItemID; // 所选目录的系统标志指针 ( D4 u, H& c$ n2 _
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果5 f* ^( J) N+ P4 o0 \; M
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
% l3 f7 K8 s" B. H, xbi.hwndOwner = Application->Handle;
: f+ M4 O4 d- kbi.pszDisplayName = FolderName;
3 I; e0 i4 B+ g& F, {" k h* xbi.lpszTitle = "请选择目录"; // 改成自己希望的7 `7 x) b* _5 O
bi.ulFlags=BIF_RETURNONLYFSDIRS;
* J5 U! J9 x3 N# q" qItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
! J, g9 r5 u, `( vif(ItemID)
0 q8 w) r* b# C: J/ d{2 f9 B5 t/ l4 k; Q; j# v
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
8 n" d4 A8 Y: I. }+ d2 | GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放$ ^- Y: Q7 a$ ]- O& `+ o
} 2 j2 E3 a& @! Y9 k7 [+ G
三、直接建立多级目录: ! B7 r$ @$ q* p- }
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
9 a" X8 ~1 {1 A w6 x/ gbool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"; {3 T: v7 {! |$ [. x5 U$ Z" n+ o4 } F
{/ Z" ^5 `/ P x ^8 P
if(P.IsEmpty())return false;
. i- _0 P( H8 u; \ b2 F: v. r" u int len=P.Length();
# n3 ?, Z8 R; b n* q char *Path=P.c_str();
0 d) s& O, ~5 F3 l' W if(Path[len-1]=='\\')8 E/ U+ L9 N ^! t5 u
{0 N7 g' n5 x: t$ q' S9 R
len--;
* {1 \4 G/ I- ]3 a8 s Path[len]='\0';( R' Z B2 K1 [- i# x
} // 删除末尾的"\"* q6 O3 T5 D4 \+ T6 Z2 i
AnsiString Dir=Path;3 o2 ]% ?5 H# o
// 分开父目录和本身目录名称5 c! f" X, D4 b! ]$ t" Y
AnsiString Parent;
6 g o/ E( _; |: t h7 h for(int i=len-1;i>0;i--)
4 p6 E9 U3 c) U! }5 F {
. A9 {5 t w& ?5 B1 y* Z: g7 T if(Dir.IsPathDelimiter(i))
8 h/ i1 e* ]) j0 [1 m+ e { H4 y( J2 r& ?( F: o( p
Parent=Dir.SubString(0,i);9 p9 ?3 Q5 @/ s
break;, n8 m% h+ i7 T- Q- G8 p
}; Z5 Y; q$ A. \! T: U
}
# V7 P$ r) U, }. I2 x8 H if(Parent.IsEmpty())return false; // 目录名称错误
# P" u4 m$ z2 \ bool Ret=true;
: x1 L. \, ?; Y2 q8 n if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录& ~- v3 [" {- J# D+ o' \
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在/ s4 P& T/ B/ y$ I4 |
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录+ h# O+ d5 W8 B2 Z8 m3 g
if(Ret) // 父目录存在,直接创建目录
: g b- L) ]/ i) @( c {
) M6 ]- Q5 s. ^- C' L6 O% K" } SECURITY_ATTRIBUTES sa; j0 l& t, `: @$ y }: u3 W* ^
sa.nLength=sizeof(SECURITY_ATTRIBUTES);; o$ w! ] r: ~2 ?2 ~, g- Q8 @
sa.lpSecurityDescriptor=NULL;
! W+ s0 I* F# N+ t sa.bInheritHandle=0;" Q2 g: H1 o0 T6 T; ~
Ret=CreateDirectory(Path,&sa);
1 r+ ^" W2 T0 X; c }
+ J h& \& ?) F/ e9 S! H( n return Ret;
$ X) l: g* i% T* d8 o9 J. l} * `& `( M9 f5 V) r9 v2 Q) ?
可以看出基本方法是:& j3 d0 }: G+ B
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
" e" g: E: v0 n7 a9 F如果父目录存在,则直接创建目录,否则自我调用创建父目录。 * H" C* U, m/ R% N- K
9 b, i+ O9 z$ H8 V! p# s, W1 d
四、直接删除整个目录: 7 B G: n# _* v! v
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
1 I t$ A% X! X, E8 q查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) . j$ C, {, \0 ]6 X* z
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 8 ^& ?) i) v" Q! j9 H: @
如果找到目录,则进行自我调用,即开始递归过程。
1 k. H% C4 H: p4 _ }8 L. d2 c4 z) I如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 # J+ K, c/ f# E9 Z: ~% b4 Z
具体程序代码如下: 9 D5 e5 B6 }! ?
bool DeleteDirectoryEx(const AnsiString & )
! w( p, d- D' k$ ~{
; R- X3 L3 D# @8 G if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
1 E: f% ]" L- t# _' X* V int len=P.Length();
0 f1 q5 o4 b, |2 t" ~! z# c char *Path=P.c_str();+ v% G9 @4 y# R7 o
AnsiString Dir=Path;
6 [/ M. A3 O( ^+ d if(Path[len-1]!='\\')Dir=Dir+'\\';
- k- E4 k9 h' ^) d AnsiString Files=Dir+"*.*";
# W# k, z/ Z: g G) E1 x6 x( y1 b$ n WIN32_FIND_DATA wfd;
# w5 Q: N- }1 c HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
. b, a9 i( E* O% t/ m bool Ret=true;
' _( b2 |) Y) q& a" |# l5 \. B: Q AnsiString Tmp;
# w/ l Q) g0 R if(hFind!=INVALID_HANDLE_VALUE)9 z0 ]9 K! H6 c( Z# R! t2 W
{
+ u8 G& X) e- Z% T5 I* K4 U* U$ x- @ bool bFind=true;- J9 t3 {/ S o) ~1 w: _( i
while(bFind) h0 I) D( R1 O1 ^9 A* I
{2 l$ B; K6 U$ g' ]( F4 L4 D2 A
if(wfd.cFileName[0]!='.') // . ../ X0 e+ B9 _ J) a! w. E
{
+ B9 Y0 E/ ]+ d$ B, q8 O Tmp=Dir+wfd.cFileName;* C- p3 w0 i3 ]4 J
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) Q' T% P* ?! K# P6 E$ S
{ // 删除所有子目录
% k2 K* Z& w+ [+ ?. a9 _+ U Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);1 t6 f/ `* q! W7 K7 a" E6 @2 j
}else7 m9 e; B6 ?& p1 m# \; E) K
{ // 删除所有文件, t1 ?% e' [8 J3 {1 a$ t+ G8 y# ]3 ?
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);! C$ t. X- Y3 i- O6 t
Ret=Ret&&DeleteFile(Tmp.c_str());4 E. N2 S. ^, p4 a( C
}
7 @- H+ U. Z, A }1 z3 Y! d5 h. g2 a6 {! t
bFind=FindNextFile(hFind,&wfd);4 l% a# a& V* ?0 g H
}
; b- c; J3 m1 m8 K# ?) C FindClose(hFind);% f% Y9 N$ L* n
}
3 h% x9 a+ z: q9 E if(Ret)return RemoveDirectory(Path);
- U* X$ f+ ^" x# ]: r# s% Y return false;
$ W& o+ O, Z6 _/ ?& F- P}
! ]' y% {! Y7 p, h9 j0 [完 6 m' K2 X+ i, ], G) `
3 v$ t3 ]" i" {9 e
|