|
目录处理函数 0 O8 Q7 @: y3 o+ F7 E! L& [& p
编程语言:C++ Builder 作者:王行舟 ' h* p4 ~# D. \/ j0 \/ }; K
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 8 M$ y. l4 i! C! D1 d" h) ~
一、判断目录是否存在: 2 C8 w/ Z. X, N8 `1 q9 ?, D0 }" [* c
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: ; t5 X2 z: S9 V2 r' C* r5 }; v, L
设char *Dir为带判断的目录2 L- X# I) {* h/ R- s$ C
bool Exist; // 最后结果,表示目录是否存在/ V* D$ _2 c0 B
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”* O' {5 C1 x. I7 p4 Y+ Z
WIN32_FIND_DATA wfd; // 查找' d* E* N" K3 C3 {; x8 }% {# v
HANDLE hFind=FindFirstFile(Dir,&wfd);
R% x0 \$ I" N8 L# T. L8 yif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
3 U/ n8 ^! x8 ^; v6 telse' @1 @+ N! q! U- ]
{
- j' V" y0 u. S8 H6 M- E if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录 Z9 \ l4 A/ K& I6 }% |
Exist=true; // 是目录,目录存在
! Y5 f/ U0 s8 C; R( ~ else
( J E" A: Z; k; Z- y* w Exist=false; // 是目录,目录不存在/ m) F% |1 H& k3 G- K6 r6 T5 M
FindClose(hFind);
: q. g1 H F) w1 D/ Q; N} - r4 V) m: |0 L- |+ u
二、打开目录选择对话框选择一个目录: ( ]( o0 u" D- o9 l
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: , I5 I% u p/ z# N: y
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle ' c& Y; B7 `; q- l6 y7 z6 M
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
+ h+ s# Q) M" l- N, o! t! gLPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 7 |5 B9 _4 h( M1 U* D( N, G
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 # Z- T1 _& L, h) |7 n
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS ! U% S% w* g, T, ~+ d
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL / ]2 U0 A' D! s% z
LPARAM lParam; // 预定义的对话框传递给回调函数的值
1 a4 a5 N B: O4 G4 @int iImage; // 与所选目录相关联的图标在系统图标集合中的索引 - Q( ?6 {8 Q3 t, w' V- N0 R- ^2 P X
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
5 @! \( m! _/ x& b _#include <shlobj.h> // 必须包含的头文件! p5 a( p0 T6 L3 s7 {
char SelectedDir[MAX_PATH]; // 最终结果
0 ]/ E" L D) fBROWSEINFO bi; // 入参' C6 N- A V& G- \! J+ `, x
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
+ u* M% U8 p4 Q* [ RLPITEMIDLIST ItemID; // 所选目录的系统标志指针
$ D# b. h! A. z/ Y8 f9 vmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果! t5 J- g! m% Y7 _5 ]4 G
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据( N1 D% k) B' t
bi.hwndOwner = Application->Handle;
( \4 ^( |' ]: N, n* r6 x3 gbi.pszDisplayName = FolderName;. E8 h1 J" ?& _
bi.lpszTitle = "请选择目录"; // 改成自己希望的
% `* i2 }0 p! ]bi.ulFlags=BIF_RETURNONLYFSDIRS;
h9 h8 p1 o, eItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框$ C9 M; J+ j7 q7 F
if(ItemID)% p0 Z( H& b# L
{
4 n6 B* i3 t- N% r3 ?$ S3 z! Y SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
5 g5 a: K. e0 R$ R8 Z GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
- ?( J1 f. [9 v! J, ?! H}
9 H1 a' W! N$ ^1 Z# H& }三、直接建立多级目录:
! B! y f8 L/ ^" |3 G Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 % A# W# y. f. m, D
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"2 m/ a4 ~ D M( ~/ @7 C
{
! Z' C% X2 B% h if(P.IsEmpty())return false;7 F/ w: ^1 v. q& b4 O8 T6 m' w
int len=P.Length();+ `* n( @% Z, X5 V% e
char *Path=P.c_str();) t2 ^' e) o# F8 z" w
if(Path[len-1]=='\\'), D9 G6 T! T6 T
{
4 r8 a0 n7 R1 k len--;
& l$ N- f$ n0 I F Path[len]='\0';
+ B5 M: k+ B' s' j v. Y5 B8 w7 w5 e } // 删除末尾的"\"
* u8 C) u$ u; q! J# Z6 n AnsiString Dir=Path;
* L5 b$ j3 _: u( N; J // 分开父目录和本身目录名称
% d3 p. T" R$ t5 U. a AnsiString Parent;
G& L3 e, F" R [! }- K' a8 D& D for(int i=len-1;i>0;i--)
! |7 A1 p7 ~5 |& b" w {3 h1 e& p4 i1 Y0 \8 Q
if(Dir.IsPathDelimiter(i)) Q: d& K. w% ^- {. g! h+ H
{
$ C0 f! y' Q! U' q* c- f* B( I3 A4 \ Parent=Dir.SubString(0,i);5 W7 h/ r% u/ b3 Z @
break;
+ x+ i' F( X- O0 z, D }
4 W( l. S3 U& d# C: L& \7 W' h8 r }
$ g* G: B' m# P8 @5 Q if(Parent.IsEmpty())return false; // 目录名称错误
2 H: ^9 b, ?9 X6 T bool Ret=true;
* g( H! X6 Z2 ?6 o# _ if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录+ n/ t7 z/ y- a3 k+ m
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
2 T( K6 X5 e) ? if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录& v4 V7 }* W% c a n! P: C
if(Ret) // 父目录存在,直接创建目录
' r: K8 E5 E% n0 Q {7 b, ?$ _: ^3 _/ U7 X3 z
SECURITY_ATTRIBUTES sa;" G; I1 ~' `% ^7 s4 f
sa.nLength=sizeof(SECURITY_ATTRIBUTES);4 i+ p7 ~/ n' p
sa.lpSecurityDescriptor=NULL;
6 a/ n) r* ?7 U) K sa.bInheritHandle=0;) s+ W# k8 x1 I) |2 ]' g
Ret=CreateDirectory(Path,&sa);
+ n6 J U8 ?& A& l9 V& r; n }
% O* u% E- \8 W( x& ?3 u2 M return Ret;
' ]& y# ?2 x0 n% p1 G}
* l+ i, k. w- o. E/ }+ ~1 N 可以看出基本方法是:. v9 D# E4 V, I0 V' {
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
; i! ^) {" @) W7 ~如果父目录存在,则直接创建目录,否则自我调用创建父目录。 ; {1 A/ g3 l0 O6 q8 W' H
9 v# K; k, F& B4 l' q! A
四、直接删除整个目录:
: Z2 c g/ Q5 v- M _0 g 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
" T; Q+ l0 Q5 {4 l7 W查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
. s6 I9 N( Z( D% |, q如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 3 P1 `2 g2 D, f0 o! e; s
如果找到目录,则进行自我调用,即开始递归过程。
. O- p9 j9 @( M1 t0 u0 Q如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 ; D% Z3 S9 R1 o( o5 h
具体程序代码如下: 0 ~) v* |: t, g; g# a3 y' ]1 i
bool DeleteDirectoryEx(const AnsiString & )6 @6 L5 b, q' |5 ^* U+ C
{4 O+ ~4 h, X- A% i5 D: \
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
& M% g7 w. F9 u4 D3 S, G int len=P.Length();0 b6 T2 T: a$ P/ K
char *Path=P.c_str();
( ]' Z+ u1 u" W5 h8 c# t. g7 c4 x AnsiString Dir=Path;
# ?- f2 f' [( ^1 o2 \% K if(Path[len-1]!='\\')Dir=Dir+'\\';
, @# N/ A& i9 L* H2 d AnsiString Files=Dir+"*.*";7 K! H* }( v3 f2 x2 q5 `
WIN32_FIND_DATA wfd;8 @3 e- v. U; @0 \; |: R+ O, l
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
5 d W3 z4 p* o bool Ret=true;: x O4 x; @. |
AnsiString Tmp;5 h1 G9 N: [1 m: y: v
if(hFind!=INVALID_HANDLE_VALUE)
( Y6 H, Y, B, i6 X7 i {
$ ~) m. l4 _3 {# m' k( `. C K bool bFind=true;0 B7 m1 X: ]; X( C* z5 s
while(bFind)1 l1 w6 b1 r8 n3 v7 T( u
{
& f' ^" L) ? a! B! H0 r if(wfd.cFileName[0]!='.') // . ..6 h4 K: s" c5 ]/ e; B
{; ^+ _! `8 J/ u' R
Tmp=Dir+wfd.cFileName;
+ C9 H- R& @6 y2 \) E1 F if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
5 ]8 f6 i# b2 \! J: \/ p9 P: ~ { // 删除所有子目录& `( n9 H6 I# M; L8 r
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
: h) u& U7 S, t* Y }else/ b) Y: b1 F! F8 n( ~* _5 I
{ // 删除所有文件9 ?: E' F+ {7 v, B! f m' T0 @
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);, O" ]. ? r& ~2 J; C& C
Ret=Ret&&DeleteFile(Tmp.c_str());$ K9 Z6 M" C7 B: B1 V! i
}& N0 ]0 S0 I5 k% _
}( F8 @; C" e, V" T3 [
bFind=FindNextFile(hFind,&wfd);, f3 `9 Z, k* q' u; p$ q2 g% y
}0 Z8 A) }* {0 @( ~
FindClose(hFind);- i S6 }3 ?* l% T$ ?; y5 |
}
. r+ v- U9 i1 U. T if(Ret)return RemoveDirectory(Path);
2 e/ v- a8 O) I0 t& b/ H) M; A3 ~& T return false;/ Z/ O* C% B3 Q1 h' m; A
}
Z0 ^* H) A! E" r5 }3 d完
! ?3 {8 `# m/ P, E4 t2 n& ~( |+ g' _
. c; |4 F. |$ c5 M" I; r" X( X |