|
目录处理函数
3 |# q. y2 ?; g' r: `- ?& J$ A9 m& w编程语言:C++ Builder 作者:王行舟
: v$ f* z3 e* V' z( E: | 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
4 y; T+ j! E) l/ e1 m3 E一、判断目录是否存在:
# H7 @ m( h1 n* E. ?0 M* l) S& A( O5 i C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: , `/ B1 U* U9 ^$ I9 i5 h+ s
设char *Dir为带判断的目录/ d4 Y- f" k9 K4 `, U
bool Exist; // 最后结果,表示目录是否存在- L0 u* Q0 T- u/ u7 [
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
5 _" @1 y1 k& D# k. WWIN32_FIND_DATA wfd; // 查找3 M5 n3 t* @2 P' o8 R" T* A5 Z& s
HANDLE hFind=FindFirstFile(Dir,&wfd); * ]9 d# _2 ^1 e" O+ H% _
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在7 g& f/ d1 K0 v% v* R" t0 s/ q+ G
else
6 l7 U9 Z @1 x9 S, D* U{" J0 u: @3 B: ]$ ~" y
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
1 @- |+ P4 ]7 Q5 q9 B Exist=true; // 是目录,目录存在
$ B' e' o. Y6 L7 `, `' v7 r else+ d: W1 H! H% E
Exist=false; // 是目录,目录不存在
) t! W! h8 S0 w/ c" S% U4 v e% w FindClose(hFind);
; z, e* u/ K9 @; F% r9 K}
2 M/ W: n+ g8 J: B' K# C5 O二、打开目录选择对话框选择一个目录:
% l1 \6 `) P$ i 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: ' P5 U; |8 G1 Z; B3 h
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 5 a. c8 [% v; j3 r1 U }) A
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
9 e; ]5 g# ^7 ^* l& D# ^LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
* k- b1 g: F$ i; W6 |; D! I) aLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 . o% c) A, B! a5 H
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
, p5 s) g; {* U5 ~2 I3 gBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
9 f$ s! n3 P2 m( ?; d; b; D. b6 LLPARAM lParam; // 预定义的对话框传递给回调函数的值
+ v, }( W- U6 [" pint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
4 x/ j' X8 _5 B9 F) _% r. z0 e可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: , v1 p3 f$ w. o m
#include <shlobj.h> // 必须包含的头文件, h+ i( P0 D, |* w* k7 B2 w
char SelectedDir[MAX_PATH]; // 最终结果# I5 U' C; N+ T( o7 q3 R& j
BROWSEINFO bi; // 入参+ M8 H, p' [/ o* D' j, d
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font 2 g$ }! t% q) i
LPITEMIDLIST ItemID; // 所选目录的系统标志指针
$ J3 ? O0 { i' Mmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
6 s" H2 Q' h/ R+ G9 n" {1 T, ?' @memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据/ I9 p9 O5 M) g
bi.hwndOwner = Application->Handle;+ Y/ ]+ Y4 D; v) f" Y) _
bi.pszDisplayName = FolderName;
, Z& U/ E# m! t" F$ i8 x# ubi.lpszTitle = "请选择目录"; // 改成自己希望的" S6 ]% J( c8 L2 y6 \$ s+ Y( F
bi.ulFlags=BIF_RETURNONLYFSDIRS;
9 B; U3 m8 O- x! D, \: PItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
: A V) B: n5 V- V) @if(ItemID)
) }: L; a% A7 v* y6 ^{5 s) j% K h- c7 O
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名1 s8 L: i& Y* F2 X2 C
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放# m g$ M$ w6 ]- X& J
} 4 n2 Z& s. @* q6 ] _+ s/ d
三、直接建立多级目录:
5 e5 K2 h, b' v! a3 H _ Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
. a$ ]* y# `' v8 |+ V* vbool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
1 O7 {$ a" B$ G5 d5 P+ V5 [{
1 W9 K+ E8 T& A4 M8 R. B if(P.IsEmpty())return false;
6 c' P& }# `, n int len=P.Length();6 A# M S% t" f" @# s+ l* O5 K
char *Path=P.c_str();; H2 Y2 ~: L9 C! h2 }" f7 L' @+ I
if(Path[len-1]=='\\'): _2 ]: w7 V: [6 J7 m- e' ^
{& ?3 _; S9 m- ~* k3 f5 e( M0 t% q
len--;
/ p( K: c- y6 z Path[len]='\0';3 }8 p* j" n' d1 M3 [& S
} // 删除末尾的"\"
0 ~& o3 `5 I) u" P/ T AnsiString Dir=Path;
* c! N, q+ \9 l5 R% c3 P+ H // 分开父目录和本身目录名称
6 c4 C: M8 n5 S8 B K' b3 }: C# A# B AnsiString Parent;' v0 T" o2 G# c: ]3 {0 e
for(int i=len-1;i>0;i--)# T/ Y, ?/ W, B! R- W% ~: j
{
7 m: S; v) U; \* {* G5 W* W if(Dir.IsPathDelimiter(i))# {: \0 R5 i* b4 X& w
{% R$ u# |/ I1 ?* D/ L" H
Parent=Dir.SubString(0,i);6 C3 M, f, Q" W8 E' o7 v
break;
, T4 v. U" y9 v4 C }- J% v0 Y. n$ Z6 W1 B7 v" r1 i- @- j" y
}
$ [# v3 }5 S$ a" t. O6 B& Y. W7 N if(Parent.IsEmpty())return false; // 目录名称错误
& t( O) T2 I, t. \6 g$ H7 R bool Ret=true;( `3 w% r% F! S4 e0 V& }
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录& K3 u% c* P3 y3 a$ Y
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
% ^ J- D0 f6 A7 x! p; v if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录2 ], V: l6 k+ O
if(Ret) // 父目录存在,直接创建目录
8 [( _9 A R m" S8 I2 d5 x {
0 D: Y2 Z9 g. n+ z6 I y SECURITY_ATTRIBUTES sa;
7 r" q6 `1 d# e8 l% N sa.nLength=sizeof(SECURITY_ATTRIBUTES);7 N8 M8 h( @ _- [
sa.lpSecurityDescriptor=NULL;
# v3 w+ N" x- T6 ~0 @, C7 L, n sa.bInheritHandle=0;
5 k8 c/ o) @+ t* d6 V) p1 y Ret=CreateDirectory(Path,&sa);
% a- ^8 v2 T/ _" v }
+ z! j: w9 V* [4 a$ M! f return Ret;2 s$ h$ ^& ]: V/ Z6 L6 y; i. X
} 3 ^4 |- _5 h& ?2 Z/ l
可以看出基本方法是:6 [3 j+ ?4 E I8 e! Z8 ~
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; & x, k- q! R3 w9 ]
如果父目录存在,则直接创建目录,否则自我调用创建父目录。 1 ~1 M2 D* E: |& Z( g0 @- x
/ Y, Y" i. K* v) w6 Q. r9 |2 q1 q四、直接删除整个目录:
& `9 @1 r% A: e. h. M6 @ 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: " ?3 Y; J3 H4 g
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) 4 }4 @2 f2 q9 S4 ^$ Q
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 1 c5 c" D- z1 x- ^
如果找到目录,则进行自我调用,即开始递归过程。
- W( p% u( F. Q) x, ?/ ~* _# f9 |2 b0 {+ v如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 ! P* p$ d+ X: N Y
具体程序代码如下:
7 l, A& X' F9 u# f8 u g$ U" p2 obool DeleteDirectoryEx(const AnsiString & )
. B) T- Q0 S9 h0 m/ ` C! x, k) j{+ ] _( Z9 v Y8 a: z# q# e! I1 F
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白4 n) C1 y8 B8 w7 |( ]1 @
int len=P.Length();' M7 t2 f0 R- D& ?
char *Path=P.c_str();
; N2 Z) W3 b4 E) d! f9 p AnsiString Dir=Path;( X7 c1 { ~0 Q
if(Path[len-1]!='\\')Dir=Dir+'\\';( b! ^. v3 Y: Y2 ~, c! O0 v
AnsiString Files=Dir+"*.*";
( Z5 a" H( a, E ]* n4 \8 C WIN32_FIND_DATA wfd;2 \; j/ S/ j: C( W& ?
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
$ w z5 _* ~1 O7 I5 g, ~ bool Ret=true;2 a4 W: o9 H* e
AnsiString Tmp;
; d& c# V- H! F. D( _6 n if(hFind!=INVALID_HANDLE_VALUE)
( K2 T J: q- ]9 k( ^4 A {
3 y3 C3 d6 ?9 R# ? bool bFind=true;2 v0 v+ ^+ z# b9 j# S7 H
while(bFind)
+ ~; E: ]' p4 x: B {
$ w* i" C, h7 L4 o1 ]1 u if(wfd.cFileName[0]!='.') // . ..! O0 {' r8 _# Q' F
{
$ N) ^5 w2 y) V9 s+ O$ A, p, ]' T Tmp=Dir+wfd.cFileName;
) J4 b& Y; d6 z$ ]& Y$ r6 m if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
4 R7 o9 c" q+ N% P. ]: b { // 删除所有子目录
4 {" s# W3 Q7 y& H. i* q/ h Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
. e% C6 U v7 z2 o3 z; f; ? }else$ S7 o; [" u& D( X: \$ }
{ // 删除所有文件+ a$ Z$ j/ a9 ?# g* W" f3 M
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
9 @5 h! A+ B4 b4 ^0 u0 { Ret=Ret&&DeleteFile(Tmp.c_str());3 W- s$ H% k" ^1 z" C" U; [8 w
}
/ F+ W3 ]7 \; Q }+ y0 o6 J1 o. k2 d. C
bFind=FindNextFile(hFind,&wfd);8 c; i6 C2 h" {7 ]6 }
}: e q# `6 i4 J, ?) O
FindClose(hFind);% P) Z9 L, E2 l/ }- L9 @
}1 X5 C; P# o9 f' s7 j5 N& }
if(Ret)return RemoveDirectory(Path);. r. N4 U; U4 P8 S+ a4 @
return false;! P8 N, |3 [8 [9 V2 Q. X4 P) S
}
/ z m9 [' ?* `9 f9 [' D完
2 [& P6 k3 R6 a
* ?* P: \# D8 p) r$ N+ k |