|
目录处理函数
! C8 a6 t8 O7 T; L9 N3 X$ d: H编程语言:C++ Builder 作者:王行舟 7 @4 L+ ?3 G1 J9 X$ n& f# [+ u" h
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 + ?7 N% `0 z( P! D' c- F
一、判断目录是否存在: 9 G2 S- ?/ t5 b
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
& {! q0 ?' R: m: @! y I' U6 Y设char *Dir为带判断的目录
0 h2 a4 A3 R( Fbool Exist; // 最后结果,表示目录是否存在3 U6 Q7 C7 C1 y" O
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”, Z% i* P4 ?3 T
WIN32_FIND_DATA wfd; // 查找9 x0 [9 j8 ]( l5 w- {4 y( d
HANDLE hFind=FindFirstFile(Dir,&wfd);
0 ~/ i$ E2 ]$ ]8 b( Aif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
6 \# @' X8 ?6 b- y$ j9 \) a5 }2 h; Velse8 |% I- w3 F4 E+ c1 R4 j
{% }1 ]* A9 r7 K! [/ U% u) R9 R
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
, [" f0 f# [0 R) A Exist=true; // 是目录,目录存在
$ C% ?+ ~/ u7 F- C. U& k" y8 @0 J9 H, U else
$ D: w0 o* g/ H% T; I) x. c Exist=false; // 是目录,目录不存在, I9 h* R# h z7 F5 ^% _: W
FindClose(hFind);/ k! D' N0 Z. c/ }) N3 `) o
} % A) p5 u, F% `- a8 X$ B+ q
二、打开目录选择对话框选择一个目录:
8 @) y# { ?# a 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: 0 f: I1 h1 {" B8 G# `
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 5 @, _; s! b# e1 }# i: \
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL" |" k! ?" Q8 e" G
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 5 m6 I: p' _/ Q$ m" h6 g; d) P
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 # N9 a8 D3 _7 G" I
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS ; W2 n% @5 Z2 y$ [
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
# J. U6 l4 R9 l! g6 pLPARAM lParam; // 预定义的对话框传递给回调函数的值, ]9 W5 G" C8 ?4 y5 V8 Y6 X, ~5 ]* N
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
# p, q5 m3 \9 y- w0 G c9 z, M可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
( T( r9 z/ R: ~$ I5 g#include <shlobj.h> // 必须包含的头文件) \# l+ V( o* }8 B; A) O
char SelectedDir[MAX_PATH]; // 最终结果& n% N$ F- U2 `5 v, L
BROWSEINFO bi; // 入参
' M# E+ o, ?# |" B% N; bchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
* y7 o0 ^9 ~/ W: X1 iLPITEMIDLIST ItemID; // 所选目录的系统标志指针
( N; W+ X! l7 n5 qmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果. Y+ M0 Q) ?+ Q* o( J/ p
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据/ C0 {. {8 S' o6 u" U
bi.hwndOwner = Application->Handle;
3 H" D9 W8 X+ H: u( |bi.pszDisplayName = FolderName;2 T6 X+ I) X/ c) S
bi.lpszTitle = "请选择目录"; // 改成自己希望的: D1 v2 W! [, H* u8 a
bi.ulFlags=BIF_RETURNONLYFSDIRS;
. u0 t2 A6 q0 m$ V {* bItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框3 b1 h' O+ y; y- d) @- r
if(ItemID)1 \0 w! P4 o4 a; r
{& O( L: ~; H; A3 J" a
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
$ S7 t! S$ f" X% j, `/ m9 r- s GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
6 I$ j. A& L+ x t}
7 Z& U) }2 P; C7 I% s三、直接建立多级目录: i: V+ K z1 v: m p- q
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 4 N' V& Y3 o6 h" _; y0 M
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"- ?) I6 Q$ ]" y) t# ~: @
{5 ~$ ^- ~) n) h8 |, ^" l: L5 R: ~6 J- k
if(P.IsEmpty())return false;
3 _) k5 j g5 }0 F0 w int len=P.Length();
6 I) M4 W P% S( w0 ~8 x# e$ ~0 b char *Path=P.c_str();. |8 s7 D/ I& |% ~" s
if(Path[len-1]=='\\')
* N8 U9 t# ~* r/ u, T {
9 Q* [( i0 O& S i# K! |/ d len--;
) S- r. Z6 i- _( d+ ~! H) H Path[len]='\0';. U3 E2 Z7 |# A! u
} // 删除末尾的"\"
' L0 F2 Q4 Z R1 p" { AnsiString Dir=Path;
7 u: v3 f9 n$ A" j( P // 分开父目录和本身目录名称
* v" R( u% Y# N+ h- K AnsiString Parent;3 `1 @3 J/ v" _8 z( ^
for(int i=len-1;i>0;i--)# ?! i/ [3 B, H7 u
{! i+ z% j+ B# c* {1 I
if(Dir.IsPathDelimiter(i)). L6 _5 @, j H2 ~' |
{
W8 H" g1 l9 c5 v! y) m0 Y7 z; B Parent=Dir.SubString(0,i);
5 A8 D! g$ T; V& K- j9 T# X+ u! e( B' j break;" f* ^0 a M( l4 ]5 z
}
3 a% n7 \9 K- s9 O4 f, t* ` }; e! j# O, x/ g9 H8 m
if(Parent.IsEmpty())return false; // 目录名称错误" M2 |% Y, l6 x7 \
bool Ret=true;
- z8 H8 S) O ?( ]: b if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
& D$ D4 U K% c$ j Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
% p; v/ d, k) r/ h2 f) c if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
2 Z& v1 c4 W' N( l1 G7 E if(Ret) // 父目录存在,直接创建目录
- K* o& ~4 y" t4 N4 O, |) d; _ {
" ^! M+ w* s3 p- c# _4 U$ E SECURITY_ATTRIBUTES sa;5 k& `( i3 _3 Z; P/ [; |
sa.nLength=sizeof(SECURITY_ATTRIBUTES);5 S( S- T) U4 P( f& |! _
sa.lpSecurityDescriptor=NULL;, D1 E: g% `6 d+ r* n t H% M
sa.bInheritHandle=0; |' X" A/ P* t% B% S5 k. a
Ret=CreateDirectory(Path,&sa);9 Z% K& h3 X; E" O' a& K4 s. @( T: ^
}
; R. Z4 b4 @# b1 a return Ret;
: a+ {$ j( p& | ~, F} " _5 m) B5 `! k8 l
可以看出基本方法是: c- ?. \- f9 q6 ~6 I
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
" m5 k. q$ @; c: Q, D) d0 V如果父目录存在,则直接创建目录,否则自我调用创建父目录。 # P) Z- d( k4 S6 x3 V3 w- g
' H" R, S9 \ t, }
四、直接删除整个目录:
1 H. O: i D; Q- M 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
5 X1 w" V7 c% `/ X( E* O' E查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
6 K R: D) z+ P3 h如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
" n1 t& m+ Z7 E6 `) R如果找到目录,则进行自我调用,即开始递归过程。 # k" |* K2 ?: l# d+ ~! S& o7 \
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
- j+ V' I5 ^9 F6 e# b具体程序代码如下:
) g( V2 q( d( I) b p; dbool DeleteDirectoryEx(const AnsiString & )# W. n d r/ x3 r3 ] ]
{
: u( I8 E. T1 I+ z; \ if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
1 z' x0 T* C. @, g0 S int len=P.Length();
' O; V5 p1 p: J; W9 X2 {: I char *Path=P.c_str();' G3 T% D; ]* V: D% \; B
AnsiString Dir=Path;
3 x8 o2 t7 Q7 ^0 x if(Path[len-1]!='\\')Dir=Dir+'\\';
; U+ J" F% V& {$ q8 v, t# b AnsiString Files=Dir+"*.*";4 t" V9 _* p* _5 e0 r
WIN32_FIND_DATA wfd;9 \& }3 v+ u; z' {
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
2 ]8 @; @5 C! A$ d2 ]4 w- A bool Ret=true;2 q U1 w5 h$ Q. B; h2 P
AnsiString Tmp;! p2 h) z4 H# |, i6 e, b
if(hFind!=INVALID_HANDLE_VALUE)
( c) K" b' X( i {# C. K3 v8 h- \& \( t
bool bFind=true;; j$ j2 k4 H1 g1 b6 `# d
while(bFind)
: x. g; h" f+ ` {
* F8 B8 ~! H0 a3 | if(wfd.cFileName[0]!='.') // . ..
0 M% S8 h) i$ j8 i+ X# g {& E6 [* |2 T* ?* m! u/ }
Tmp=Dir+wfd.cFileName;& n/ N4 w$ F* e" a! s" R( O
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)" P+ k @4 a1 X- ~* J1 f8 W
{ // 删除所有子目录
9 K2 H4 h' Z4 N& _8 q# ?% a Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
0 ?+ |5 z6 R# b, ^ }else
. A3 U, s% r6 i, ^7 z# j# h7 \* O9 a- h { // 删除所有文件( E6 s% r6 d, I0 U+ h
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
! C2 P) ]$ P6 s: b0 ]+ N Ret=Ret&&DeleteFile(Tmp.c_str());
9 q) `4 s. B% C* }" J" h }6 }+ V, O) t: p/ ~% F
}
7 b* b; @9 I4 {( E8 `7 l bFind=FindNextFile(hFind,&wfd);
. x- n. I; N, ` } |" Q- s- |0 V U
FindClose(hFind);% p. i8 s o6 v
}
8 t8 }/ {. T3 u1 f! O if(Ret)return RemoveDirectory(Path);6 B: Q2 r l W3 @2 n U+ U
return false;
6 U' r# t# l0 X: \' ?: l; t1 e) e}
7 A6 T' m, }9 O8 Z" b& F; g完
! c1 k8 J& T; @+ \- N& k1 c) L- s( J, S
|