|
目录处理函数
! n) P4 v6 \9 d' O编程语言:C++ Builder 作者:王行舟 ' [% D7 a/ j+ E
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 1 Y( e5 e$ c* m+ `8 {
一、判断目录是否存在:
" i3 u% k+ |7 Z C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: 2 Q" t2 x/ i9 E0 R& L V) J9 R9 V
设char *Dir为带判断的目录% N' `! j6 l- M8 R& [1 E
bool Exist; // 最后结果,表示目录是否存在
$ y5 x5 Y. C$ A% g8 S* [+ M0 Pif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”5 x3 x4 G* h( l) Q) x
WIN32_FIND_DATA wfd; // 查找# Y/ w4 s, Q2 c; H" W4 @8 a
HANDLE hFind=FindFirstFile(Dir,&wfd); 8 \+ y: y8 V8 C$ B0 o4 d6 J
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
$ [- c6 r' O+ V. n4 ielse3 Q) `6 [5 x! L* G8 _8 t+ M6 y `3 C
{0 W5 x/ N* Q' i6 Y; }, B
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录, |4 z: x A! @7 ]: ]% D A
Exist=true; // 是目录,目录存在- k$ h: W) H- ~; J. H7 I
else
7 x. b2 H4 @8 D. L2 r4 h9 m Exist=false; // 是目录,目录不存在: @4 R4 A8 t* n( h5 I6 y& b
FindClose(hFind);
% g& _* [/ X0 Y# Q) _8 B( E- E4 ~}
% T8 w( K7 R* [# E+ Q6 r/ [/ l' y二、打开目录选择对话框选择一个目录: ' i" p; n! u" y t @
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: ) Q- @+ M& M, ]
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
! ~4 { i# M) w/ a6 ]LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL R* E) }$ M0 c4 ~, N1 j
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 . k+ c: N4 `2 v6 [+ _# x% c
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
6 g3 j s2 a' m0 {1 J8 Q6 {5 zUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
* R7 `6 Y3 N$ Y+ ?- ]: {8 r0 b) MBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
3 g4 t I8 B4 b6 yLPARAM lParam; // 预定义的对话框传递给回调函数的值, P, V. W; {3 X$ e) Z! ?+ x3 \# }/ M
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
$ V+ R+ U% A1 J2 K可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: " g. ]) } |& ~5 c3 p
#include <shlobj.h> // 必须包含的头文件; I o8 Q$ q( B9 {' s
char SelectedDir[MAX_PATH]; // 最终结果- U& T5 ]1 o$ A& i( S/ O
BROWSEINFO bi; // 入参4 h( N6 c2 D1 _2 ^# n
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
. e& x/ R5 D! ]8 E, F9 |LPITEMIDLIST ItemID; // 所选目录的系统标志指针 5 X% b- J/ {. n& @6 w" |
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果2 v& k+ c" [8 s, E* P) C
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据/ o/ a- E+ I# |$ w7 p5 @6 j0 Z5 B
bi.hwndOwner = Application->Handle;0 ?2 G: p# j# q. ], w- G8 e
bi.pszDisplayName = FolderName;3 R% A2 r/ d5 E9 d
bi.lpszTitle = "请选择目录"; // 改成自己希望的
# A/ F0 O, _' @5 a, N5 o, }4 @1 q' Pbi.ulFlags=BIF_RETURNONLYFSDIRS;2 f- r2 k2 w6 S% r' m
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
( [+ \8 k: X2 e9 P2 p2 ]+ x1 n jif(ItemID)
E2 @7 M& Y _{9 ~/ o8 v0 d5 |5 s8 Q) e; A
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名- z+ v1 l( a" W; g% Q
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
- j' A6 g4 o, `7 _% ^3 \}
5 I* f2 @! r& e. m8 T+ n5 A三、直接建立多级目录: 5 i4 k w X* R. } R8 G6 G
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 7 i3 b% j, a3 M1 q& `1 k
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"7 G* l" n$ D* D( l; T) W" x, T
{
W9 l; D; C5 F9 m9 d if(P.IsEmpty())return false;
; s4 o! `2 }, E/ M0 M" E int len=P.Length();
8 @$ Q2 i' t* A' S T; [ char *Path=P.c_str();
3 G W+ j* Y+ F0 ~: Z2 y) s if(Path[len-1]=='\\')4 @: _& F5 O% @& J
{7 U3 @7 G) Y' U& Z! w( |7 v
len--;
3 S$ D% a6 U/ A2 x" j1 ^$ u1 u Path[len]='\0';3 x# ^( J, ~; n- a$ e0 U
} // 删除末尾的"\"
) g8 l2 I& G/ @6 a" [8 R AnsiString Dir=Path;; L) _( V# H6 E
// 分开父目录和本身目录名称
1 O# g8 f$ F, L0 i# \$ l+ K AnsiString Parent;
1 R( N) ?* V7 f ]1 {! K; @" D. m for(int i=len-1;i>0;i--)
$ F1 P7 @" _. E% j8 A% N {- a1 x! c- r9 E8 _# ?
if(Dir.IsPathDelimiter(i))
, i d4 W/ B9 C# a" f3 v/ K& v {
; s- n" A, |2 x3 K# f' \- s Parent=Dir.SubString(0,i);
6 e7 |* n( L K- C" d6 q break;
- a9 m; k5 W' e; ^! k }
) m# Q9 B3 a+ a1 _3 Q; \8 @ }$ C4 }; A+ P9 D& P
if(Parent.IsEmpty())return false; // 目录名称错误% z+ F1 W: D! J+ |! U: g' w# a( y
bool Ret=true;$ V) [9 s% |5 d: ?
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录+ d7 n8 w1 j" S0 K+ X
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
2 v$ v- R$ l+ B0 g if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录: F2 n6 {9 s9 y( p/ o
if(Ret) // 父目录存在,直接创建目录3 L+ I: Z" E0 Q g( p. k( t3 A
{
( k" O @; \: F0 s0 ?. Z+ x SECURITY_ATTRIBUTES sa;
p- V$ r8 |2 v- A: O! \/ ]7 p sa.nLength=sizeof(SECURITY_ATTRIBUTES);
; Z) O5 v ]! O+ s sa.lpSecurityDescriptor=NULL;
# D/ d2 a h( T' k sa.bInheritHandle=0;
& u9 l: j) S) _4 i9 s Ret=CreateDirectory(Path,&sa);, c8 z8 F; H) Q4 H; P
}: l5 _6 y' M( z- ^% p) E
return Ret;
1 ]/ I2 ]4 a' d6 l4 k2 n} 7 I2 J6 H) {0 _8 H: E& ~
可以看出基本方法是:3 @$ t) x/ [, q0 Q% V! \- O
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; $ P! J. a! b( I% T
如果父目录存在,则直接创建目录,否则自我调用创建父目录。 6 u n$ h/ }1 p( \7 s* y
. c9 S( n. @2 w3 w' o: I( `
四、直接删除整个目录:
" d2 ?3 } M, B3 n 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: * B- ^4 i2 b+ |! S0 v
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
/ P$ U" V" a! O" \. C O如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
% p# n" \/ P4 l D5 Q9 ~如果找到目录,则进行自我调用,即开始递归过程。 * S O6 k: D0 ]: u1 i; W1 |
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
* i7 w& r& _! N' y3 F. H5 f; n具体程序代码如下: 0 S3 v) D2 V& T) G0 g" l( L
bool DeleteDirectoryEx(const AnsiString & ), X! I( \* T1 N* w$ i4 ~7 Q
{" B( a9 w1 D) U* E6 V: b
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白% Y7 B! p- |6 m+ I
int len=P.Length();
& q, Z: N+ i- P4 ^6 y! p char *Path=P.c_str();4 A9 u2 |9 h' m5 Q' E, Y& D
AnsiString Dir=Path;
! t) I% b. J( {+ y if(Path[len-1]!='\\')Dir=Dir+'\\';
% h N8 M6 ]: _0 C' O AnsiString Files=Dir+"*.*";
4 R9 Q F. t/ e4 M& @9 k WIN32_FIND_DATA wfd;7 b5 e1 t4 j+ ] v0 Q
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
8 m/ c7 _- w) [! H* c bool Ret=true;
% w2 `2 e: z* @6 c AnsiString Tmp;
6 @; [" A: W: d. w if(hFind!=INVALID_HANDLE_VALUE)
9 a/ ?" q" o% e8 x6 e {
6 y" K- v" J9 H& t& h- b, y; h4 h bool bFind=true;
, I) F& N/ F: A2 f. \% L while(bFind)
9 G, A) T( u# o3 `5 t0 p- K4 {8 F {( o7 q7 E" z. w/ [$ N5 k7 T
if(wfd.cFileName[0]!='.') // . ..8 o7 b3 O( r) C% K) j1 m& j
{
- T: j5 N3 M6 l Tmp=Dir+wfd.cFileName;4 j& Q/ E# k9 ^' F1 j7 H4 [
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2 k: C, j; J+ a- d { // 删除所有子目录) L9 v: w Q, N3 e! M) A6 A! K
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
- d6 G! t6 l7 J) Q# D3 ~, y }else8 Q* w u r0 m+ ^7 G% k* L
{ // 删除所有文件
, O0 ^1 ^3 @/ [4 k% V2 N$ R( j SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);) u' x) p' N }4 b Z% o/ i5 t
Ret=Ret&&DeleteFile(Tmp.c_str());
. T x( n* r$ n }
( K0 I" ^# p7 X8 P( g }; x; P a/ e1 t6 s
bFind=FindNextFile(hFind,&wfd);9 s5 B) Q: U0 L9 B; o2 b" D1 m
}
- x+ J; ^, B+ N FindClose(hFind);9 c- _5 V* s' S9 |8 e
}
+ g( l! s6 |5 l if(Ret)return RemoveDirectory(Path);
: m7 ]; U4 v T3 P8 o: W return false;6 [4 }6 h% x/ N" z
} R4 s% I# E2 V& J V6 {$ R
完
! A. F! w7 M+ d5 Y
% B" `/ E o8 j" N |