|
目录处理函数
+ y& ]. g# F: O) G/ Y" Y编程语言:C++ Builder 作者:王行舟 + L5 y7 g. r) o6 m" a. Y
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 - o8 v* _- Y* E5 f
一、判断目录是否存在: % F) _1 `% o; d# y6 J" V
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
+ |# i; L* k5 T3 C+ W设char *Dir为带判断的目录
$ b7 ^" o, L+ W5 jbool Exist; // 最后结果,表示目录是否存在, Y6 h: I: t/ ]& X
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
# T8 e+ G4 U7 R8 n5 v8 kWIN32_FIND_DATA wfd; // 查找
( m4 @9 Z% r: h8 P6 O, L$ VHANDLE hFind=FindFirstFile(Dir,&wfd); + u+ d9 O: U5 ~8 D: L$ R
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在7 W3 H1 p/ R( e; J4 I" M. w
else6 @2 H6 d* B6 w! w
{3 r4 ~% g, Y8 f; c( G% L$ l
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
]4 @' t( V4 l/ _5 q$ ?3 N Exist=true; // 是目录,目录存在
% Z2 Q0 T" v& @# j, L8 \- K else
* r" W* {) ]8 B2 k# @3 N% n Z3 U! e Exist=false; // 是目录,目录不存在: l$ c4 u4 ` [5 C8 q
FindClose(hFind);
- Y" S! Q k3 ^; b B5 `7 A' F}
) h6 [* M, _ w- L. W- l; n8 b二、打开目录选择对话框选择一个目录:
/ S4 h4 B, E. r 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
5 o) `( _ a# D; M& s+ o8 QHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
" C# H6 v' t# WLPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL; T6 z1 {: B& w2 B, G4 _
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
q9 H# w4 Y, _) t y/ _LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
( W# h5 i+ l4 F4 s. B8 [% VUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
: P1 _) L4 F( k5 s/ Y. ABFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL 3 x7 o+ T2 d+ [( a" u% w& D( R- z
LPARAM lParam; // 预定义的对话框传递给回调函数的值
4 ~5 j8 |6 I' t ~int iImage; // 与所选目录相关联的图标在系统图标集合中的索引 . Y) g+ R$ u# z- f3 A9 o* n- v
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: ! V1 }0 p4 D2 u$ h4 O, u' [. | Q
#include <shlobj.h> // 必须包含的头文件8 I8 F- g D& c# {8 `
char SelectedDir[MAX_PATH]; // 最终结果
3 r- Q, X- c# B, `( W( @BROWSEINFO bi; // 入参
5 q$ ]0 ]) r3 r3 uchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font / L3 k- V. p9 i* _5 U
LPITEMIDLIST ItemID; // 所选目录的系统标志指针
! B* A$ w2 {8 A, ememset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
% ~ Y7 v& p+ }3 F- l% Wmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
7 d$ z) h& ^8 S0 Z6 b. \bi.hwndOwner = Application->Handle;
' k- i+ z: N& P0 Pbi.pszDisplayName = FolderName;
1 x: n, P+ l w! G+ mbi.lpszTitle = "请选择目录"; // 改成自己希望的
: E7 ^6 d- o2 v# c. M" R- xbi.ulFlags=BIF_RETURNONLYFSDIRS;
7 B/ ^7 Q) s9 XItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
5 F& ]8 I$ w9 e, O, f2 Tif(ItemID): G+ {9 F I5 d
{
) T/ p9 D7 Z: h! `4 r SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名, A$ N r# q2 d$ `6 h
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
9 o: V$ K$ u" a, e- g# Y" t}
! l" e" b& D( I三、直接建立多级目录:
8 H7 L# _# @& ^* u0 l" }" N" u Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 7 B- W, z9 H% q/ u5 d
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"" n& L4 J( f1 c" V# @
{- v) R6 s& t- Q5 a3 i, H& ]
if(P.IsEmpty())return false;
. b* T3 I9 _8 m& _9 e- A4 w5 _2 p int len=P.Length();
* `+ o3 u' L/ J% d char *Path=P.c_str();4 L: E6 [, U5 a3 Y1 {1 E) \6 ~$ l- _" i
if(Path[len-1]=='\\')
5 |& `+ e3 J7 g0 P5 m {
" P) Y# j$ Q* a2 M. B len--;) A: v5 V5 G2 u9 H$ q
Path[len]='\0';8 D( v+ G/ _4 B- q& E
} // 删除末尾的"\"7 o% S, @7 E6 U' X( s1 P
AnsiString Dir=Path;
& d. p: b: m+ {% u" F // 分开父目录和本身目录名称4 `( |4 G2 Z& B& W& Y
AnsiString Parent;* f- {) r; {8 \5 ?
for(int i=len-1;i>0;i--)
0 s8 J! m( R- F5 m# D2 {- D9 S {/ d6 G9 D- ?' _1 K5 ]
if(Dir.IsPathDelimiter(i)). `* {( \2 L9 _
{5 A9 e- A4 o* Y
Parent=Dir.SubString(0,i);
4 H( y) c/ K' p' { break;
1 `5 b9 \8 ?7 ^* r0 A2 O4 P) i }) n& J1 i; q& O
}7 k- H& {* x" }2 g8 p* [
if(Parent.IsEmpty())return false; // 目录名称错误) H* U7 ~- G4 V/ n$ t/ K. j+ D
bool Ret=true;5 p5 I0 X& n# h' A9 k, m. n" P
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
# w2 y; }- `5 p' Z3 b5 s$ u! g# t Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
# V5 f7 [3 ]7 E9 U if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
2 w2 N( U" {$ b9 m if(Ret) // 父目录存在,直接创建目录9 H* Y" I: G) |6 T5 l
{/ }5 Y1 U2 W0 j2 {4 r3 @
SECURITY_ATTRIBUTES sa;+ E7 l8 Y" p3 |# H9 s/ c
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
) w Q8 o8 w2 T5 G6 A2 h1 h sa.lpSecurityDescriptor=NULL;$ i1 p8 i+ ^7 }" F4 U+ i' N
sa.bInheritHandle=0;
1 |/ A! D( R- z2 B$ ^# w% n Ret=CreateDirectory(Path,&sa);
7 l7 T* T; x' w& o, H }
3 v9 p6 @* Z( z return Ret;( k7 e1 O7 I" e' G0 M# N
} , m- Q1 `) K3 r. E
可以看出基本方法是:" |0 d; R5 |+ N7 |9 a, x
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; . C% S$ `5 q+ N% z7 t" r
如果父目录存在,则直接创建目录,否则自我调用创建父目录。 9 n& c- F; {, a h' B: v/ {( K) H/ R
$ |8 d2 E4 [$ F# O: J
四、直接删除整个目录:
6 T; p1 O6 Q% z& D+ a { 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
- l9 Z; P6 W" P4 j查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) 6 t3 g* }: C4 ~" @2 s5 \4 z/ @. \0 G4 x
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
" P- I3 s5 I* |1 s* F9 Z5 H如果找到目录,则进行自我调用,即开始递归过程。 * s) A/ }5 O, I( @1 `) D6 F W
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 - J; G% c. p5 _+ T( |
具体程序代码如下: - h: S* D" T" ~: d
bool DeleteDirectoryEx(const AnsiString & ), a$ E7 P$ T2 k, }- ~# R9 O
{
4 j5 Y. G; Z7 I if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白9 k, d9 C% G2 v/ u: I; [- v% y
int len=P.Length();
* @! l: c# K& p! _# d& e char *Path=P.c_str();
/ Z+ g6 D5 m, e# X8 `; \. f AnsiString Dir=Path;$ O8 i0 W4 J) }: X8 |
if(Path[len-1]!='\\')Dir=Dir+'\\';3 h( f9 A- R. ]' J, g; n
AnsiString Files=Dir+"*.*";
& S$ c3 o3 [7 L- L WIN32_FIND_DATA wfd;
3 p3 T" t6 e) y( J. \# A" W* c' y HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
0 D: }5 I3 c, {& r bool Ret=true;
$ z/ x% z+ e- Z6 L AnsiString Tmp;
& ~3 y" D p5 _% u F% a if(hFind!=INVALID_HANDLE_VALUE)
7 T& w. ?/ u9 {. H, {$ r0 [, n {+ I# h& ]5 m& {1 S2 y+ K. S/ X. P
bool bFind=true;* g8 E# r0 Q* I. a4 J- Y
while(bFind)
* c4 ~7 b# j1 R* v {' [2 K7 W2 ?) r8 u
if(wfd.cFileName[0]!='.') // . ..9 M+ g- d% F& c+ g( u( Z
{
# O5 f' \( ^' Q/ y5 } Tmp=Dir+wfd.cFileName;
' \( {6 `+ s% n, a9 w) e9 I9 k if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)- r) x5 C* E% W( U1 A, |" U
{ // 删除所有子目录
) A6 x K! A" w Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false); V6 [. c& ? F5 }; O3 s
}else/ I/ d3 R Y. \; f5 ]4 Y z \
{ // 删除所有文件
$ M$ m9 Q, U" Z3 i& x% a SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);5 ^: i" n9 {( N1 V
Ret=Ret&&DeleteFile(Tmp.c_str());
8 u8 Q- D R2 a9 p; T% O! Q8 T8 ~ }# B5 F p9 r Z Z/ F* j8 N
}
" ]9 \/ w2 G S9 H# K; p1 d% V0 ]2 ^( W bFind=FindNextFile(hFind,&wfd);7 w4 N, @: ]* h8 V$ ?7 \# O, a
}3 ?) w! O( J) Y7 s
FindClose(hFind);4 e t7 |7 j% `1 b* z" e+ F
}
( W% L7 n% O$ {! v; ~1 a* e if(Ret)return RemoveDirectory(Path);/ r8 W+ B. m2 {5 W3 l w! l" t$ Z' [. d7 x
return false;
7 s/ x2 M1 L& F( u}
a& V5 |+ O/ `; q7 [' E完
- C* h" I; c: K/ H! y% J9 P, l# q) F5 D- X; o5 u# I/ E: H* V7 h$ z
|