|
目录处理函数 R1 c; o g+ R6 W
编程语言:C++ Builder 作者:王行舟 / E ]' t4 j: I+ g9 b" E
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
5 S! z R+ w1 B1 ^. A) R一、判断目录是否存在: 7 N. g1 o: w2 O2 v4 k
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
9 R- }# L8 W7 S9 q$ Y9 m设char *Dir为带判断的目录; ?7 g9 ]2 ~ |+ Y4 J
bool Exist; // 最后结果,表示目录是否存在4 p) e+ C, m( b8 D% e5 O/ c
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
+ S! x; P0 l6 O% n9 fWIN32_FIND_DATA wfd; // 查找2 n9 d5 r# ? ~" S8 Q% w! q
HANDLE hFind=FindFirstFile(Dir,&wfd);
h# t9 a- i5 g; Pif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
4 {1 v) L, @ [2 Oelse
6 K7 t; r/ J7 a. h9 @, p( e( F{
3 B2 S6 F$ W) f" ~7 G if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录# T1 u# ]% F) Z) l# g/ q' x/ r
Exist=true; // 是目录,目录存在
' c m' B" q3 T% T$ E else
7 l" _! W* w1 v. e0 U) n+ h Exist=false; // 是目录,目录不存在
( o7 T: F) P7 U0 H6 S FindClose(hFind);
* j/ Z. a9 D1 B! k0 m}
, S9 k1 D" q( F" ~二、打开目录选择对话框选择一个目录:
: k/ ]& U6 N- H) P. k' ` 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: - C% H' i4 H) q; z$ X$ e
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 0 ?7 K% j0 b% j6 V$ z3 |
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL; s3 z& Z$ ]8 R1 j5 g2 {
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
/ m/ U% `1 i2 J8 X5 l% ?$ DLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 4 g. {) R j6 o7 p) L
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS ( a% m2 y% @) m8 V9 F5 Q3 a
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
: H6 y W9 C: d2 gLPARAM lParam; // 预定义的对话框传递给回调函数的值, v! x# `: Z: N6 N& A4 v6 Z
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
; m9 v$ Q' x1 E# q+ x! I可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
! ?; V- q1 Q% C7 l4 ^#include <shlobj.h> // 必须包含的头文件
% \- a" @" a- c$ R# r3 p2 w. k7 Xchar SelectedDir[MAX_PATH]; // 最终结果
8 f" I, K' `% i5 d5 ZBROWSEINFO bi; // 入参
% g( d: j. z% `7 G ?# zchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font - r) _9 L) y: [) k3 z* c
LPITEMIDLIST ItemID; // 所选目录的系统标志指针
( L0 g' ^: v5 t$ h- vmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
- J0 X3 A9 f, ~2 e% S3 b/ kmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据6 `& w- J$ l/ Q3 z$ C* c* f: z
bi.hwndOwner = Application->Handle;
8 z- U% W7 p4 l" P/ Z$ dbi.pszDisplayName = FolderName;
/ o4 w) |7 c8 Xbi.lpszTitle = "请选择目录"; // 改成自己希望的
7 x, B! ]9 I, w+ ?bi.ulFlags=BIF_RETURNONLYFSDIRS;& H; @8 ]1 n1 I9 o
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
% ]$ j/ i+ }# ?: p% R3 Wif(ItemID). F& P7 M( f' W0 Y& k$ ]8 m
{1 b3 ?1 C) `' y3 L
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
7 b, {& i9 J* @, q4 H1 \- c$ x GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放0 T; _; Y' m# A# p# X1 U
}
& f" `( Y$ Y2 Z: `三、直接建立多级目录:
- } m* W& }9 H4 v Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 3 V3 W, U- G9 J) q
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false". H1 w* `4 o" ^* [3 R# {
{
* @& U2 @( z; {& p- f& O4 W! p if(P.IsEmpty())return false;! X, w6 o; Y- q
int len=P.Length();1 {: X) A* K$ j" W$ b. o# e) \
char *Path=P.c_str();
' g1 r& l* p: h; @ T, o* i if(Path[len-1]=='\\')! H! e% P2 d r, f9 ^; t
{
4 i) l( G: o( t0 R: x/ y3 @ len--;
" o( l) V y# X$ Y& j8 R Path[len]='\0';' p: k6 C' k1 ?
} // 删除末尾的"\"$ X, |! T9 R+ |
AnsiString Dir=Path;; ^7 i ^; r, W% ?3 ?$ i6 c( ^
// 分开父目录和本身目录名称
2 v2 i" B- @3 `) a& K1 v AnsiString Parent;; _! W4 s1 F0 \. e. c, A
for(int i=len-1;i>0;i--)
/ m6 e2 I6 [7 F/ x3 X {
" i1 r! v4 {; I, O& A& o if(Dir.IsPathDelimiter(i))
9 C' B. U8 D0 m {& q- \$ _. G9 J- I) g7 {
Parent=Dir.SubString(0,i);
- o6 z( J9 Q5 F$ m! P3 m break;8 j; d2 H6 X! @' k; w# f
}
) h' ]+ x, q- R) g& i }
8 r" h' F) p5 { if(Parent.IsEmpty())return false; // 目录名称错误
" _5 J; R3 ^" ~* b bool Ret=true;4 d! V" m4 C" @
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录$ }- V% H$ R9 s2 c/ }. B
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
t& R+ f d. h4 V4 T if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录( H% |1 _- k7 J/ ?9 |
if(Ret) // 父目录存在,直接创建目录+ W, V" u5 X2 L8 @* [
{
/ J( [; l' g* t: V1 u2 a& k( ^* @ SECURITY_ATTRIBUTES sa;! C2 A% `* z9 x$ {* T
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
1 q8 B$ b" r+ c7 B% E/ n sa.lpSecurityDescriptor=NULL;; \+ z, g/ Q9 `' Y4 D
sa.bInheritHandle=0;6 e. G; |3 B+ u3 [5 f5 N
Ret=CreateDirectory(Path,&sa);
; [% c' {$ p3 e3 I; z* K" s }0 H1 P s- C- d* q
return Ret;. \) \6 m0 `. {/ ^
}
/ P) z! N! g7 n5 o' D; n 可以看出基本方法是:
. s% X* W/ s( B2 y; D先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
5 t: j) @1 f$ X( q4 d2 O如果父目录存在,则直接创建目录,否则自我调用创建父目录。 * x5 }* v1 s$ J; {) x
3 s9 {- A6 e5 {& v$ _: C* x
四、直接删除整个目录:
' r- r+ z/ V/ f& K# D" s 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: 8 R* J% w1 S. j
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) 4 _" \0 l, G! W1 b) O
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
, R2 m( K% U( I8 ]4 ?如果找到目录,则进行自我调用,即开始递归过程。 ) d4 r H( O, G7 C
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
' ]- n4 t( X3 U, S: z& @, g具体程序代码如下: * N0 p4 R/ Y* Q% ^
bool DeleteDirectoryEx(const AnsiString & )( G2 k1 i R- y- v8 {9 H& `
{
. m# s* q3 ]8 S$ L% w. Q if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白0 p1 l; J% x0 `/ P
int len=P.Length();
% \2 z* O3 ~/ w+ A* [/ S; q char *Path=P.c_str(); F4 ]% q r# }' t6 F
AnsiString Dir=Path;: ?; F- }7 c0 x
if(Path[len-1]!='\\')Dir=Dir+'\\';+ C. h+ N9 t6 T q9 Z( ^' w9 H
AnsiString Files=Dir+"*.*";
4 z, i4 S1 ~5 U3 Q/ z WIN32_FIND_DATA wfd;/ K8 _, L% h3 D1 F9 h
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);' X# V1 O, ]# p; s7 S+ f& |7 ^) _
bool Ret=true;
1 m. v8 |0 ]2 C AnsiString Tmp;" o* \% f Q" g, B4 `
if(hFind!=INVALID_HANDLE_VALUE)
. t# o9 q8 A' K/ F5 _ {
$ [" W2 w7 V5 n- C+ w0 A bool bFind=true;8 `% w, h( P( f0 @' f9 C
while(bFind)# X' ~6 m6 f( w! i
{
7 |4 S" w4 E/ z if(wfd.cFileName[0]!='.') // . ../ u* c5 \# c7 v( \. I0 F
{7 {6 S: t" h7 |4 J! t+ ]9 o( ]
Tmp=Dir+wfd.cFileName;. ], T/ ?$ ~# W/ y& ^- y: G9 {) T4 s
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
% ^* O' F% I( | { // 删除所有子目录
6 H( [' [2 _3 R$ t. O' q5 M! _8 K2 r U3 o Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
4 t2 G5 s/ k5 E! u% p- J }else1 l0 ^2 }% ~5 \. E d9 c' I$ ~
{ // 删除所有文件
) t( }0 g6 V) C0 l$ d2 v: t SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);* \0 r/ i% ~4 D" _* Z+ [
Ret=Ret&&DeleteFile(Tmp.c_str());4 {0 |# C" m" ~/ a5 c* T) f% k4 i' p
}1 T4 c' i- J! v7 V8 W8 @5 T
}
1 O' Q$ V2 O- f; Y' f" U, V bFind=FindNextFile(hFind,&wfd);8 ~5 _( h# r9 W
}
w1 ?) b9 M2 c: R9 s! X- Q8 [ FindClose(hFind);/ f& i2 c1 [% I
}1 w6 J$ k3 D4 ?0 M& E& [, e
if(Ret)return RemoveDirectory(Path);2 A, @4 F# C* K/ x
return false;
, i) g: H! z% a* a; q# f1 |" ~2 v}
, f; ?" z' I0 p; X$ T: F5 N* v% ^完 * Z4 p4 |8 [8 h! A
% Z+ \% k2 R6 Y) {. y9 `1 |5 N |