|
目录处理函数 / g* v7 X$ P- }
编程语言:C++ Builder 作者:王行舟
3 O- u/ y9 t, s9 K 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
0 }+ z9 Q( F0 I. D1 z一、判断目录是否存在: ! L! Q4 p) ?9 v# |2 _
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: & v* I2 l$ t# v2 L! T
设char *Dir为带判断的目录- T2 O }) C' K. e |9 B
bool Exist; // 最后结果,表示目录是否存在
5 z2 N3 X: h) c @if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”1 E2 z, H/ F' S
WIN32_FIND_DATA wfd; // 查找
! a$ W+ _7 O$ w! P' F$ X! BHANDLE hFind=FindFirstFile(Dir,&wfd); 2 `) p9 g% [1 K, H1 O8 J; U* J
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
. m+ K2 M* F2 ]7 `else
/ ^. s w$ Q* z: P0 _: [4 Q{
6 p1 Y4 k8 W# r3 w N if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录9 n* u* {- k- b, _4 f
Exist=true; // 是目录,目录存在
& t5 D. ^0 }) [ else
/ f2 h; @% _6 x0 r, d4 D9 i Exist=false; // 是目录,目录不存在
6 ~" h! |. F% x2 s FindClose(hFind);
* n F& }) @6 a/ T: ^} + P( V' p8 g K; K
二、打开目录选择对话框选择一个目录: % [2 D! j& Y! @- {$ v" w
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: * ~& n, o' |0 m! q
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
; D& ]1 K$ h" R! _. |- ALPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
& {/ _0 L( B8 \! V6 X- nLPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
, y6 g7 R( c$ Z! s" Y' y# ]LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
9 e Q6 ~, c3 ~+ tUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
p. Q: ^1 n$ wBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL 7 d: a/ @; E! B7 ~0 R, H
LPARAM lParam; // 预定义的对话框传递给回调函数的值
9 [5 {+ N6 i4 ~int iImage; // 与所选目录相关联的图标在系统图标集合中的索引 0 C5 Y6 ^6 k1 @6 A2 H
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
; Z7 T4 [8 B. ^6 M6 y+ P6 o#include <shlobj.h> // 必须包含的头文件- t" a+ o* X0 ]% d
char SelectedDir[MAX_PATH]; // 最终结果$ z4 @ Z( }* }3 L3 c
BROWSEINFO bi; // 入参
% G' s7 [$ ]. `& K" \6 {/ e. Cchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font - @& q! f! I" t3 i) m/ L
LPITEMIDLIST ItemID; // 所选目录的系统标志指针 8 H Y# ~ p6 _6 z
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
/ C3 ^/ ^& m* g& Z5 C2 Wmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
4 d0 l# F7 Y. _" }bi.hwndOwner = Application->Handle;
2 i% @( @8 L" B* ]bi.pszDisplayName = FolderName;' i. g' g7 N- I1 }, D4 I9 U. x
bi.lpszTitle = "请选择目录"; // 改成自己希望的
4 K, {( W- O3 T D* L- `bi.ulFlags=BIF_RETURNONLYFSDIRS;
5 B8 i1 E/ D( }/ U. GItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
( @, C+ J1 h' l! e6 @if(ItemID)
# U; j1 ]* B( Q/ e{
. V/ b+ l. w6 x SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
1 k# Y. w: ^) D1 s GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放" W, X7 |. ~1 n$ v5 r- z
}
* V" C' g/ _/ ^6 B* E' P三、直接建立多级目录:
& }$ I6 C1 X8 ~: G3 R+ M Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
: S0 x7 h! P: z4 K, Lbool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
1 ]9 c% ^) D" c3 e: O{
: W4 f& h( U' S# \( _+ G4 O6 w; d if(P.IsEmpty())return false;( J& s5 J) U4 A% A7 ?, k
int len=P.Length();1 n; ^% }- ?, K9 S0 K
char *Path=P.c_str();
2 i R7 e9 t/ O; _' c4 ` if(Path[len-1]=='\\')
* Z8 s7 e7 a- [. ]. F, E1 ` {- }& ]3 q3 g9 i9 W
len--;
# v8 ]2 d: a& Z0 `% ?! m Path[len]='\0';
b# I1 T- [0 U9 X9 T+ G } // 删除末尾的"\"& Q$ J1 G3 o+ L0 g# V
AnsiString Dir=Path;
9 W1 p% b" I6 Z9 w; H+ C- x // 分开父目录和本身目录名称7 r8 P8 d& x2 F$ U- E. A
AnsiString Parent;& x! U9 A# W" E$ r
for(int i=len-1;i>0;i--)) l$ o4 y1 n! C Q
{
* I+ ^) R1 R+ v0 {, L1 D) W1 |+ P if(Dir.IsPathDelimiter(i))
! P9 {, S' } L7 K7 ?% [# `; \ {; m E/ K6 @- O9 j) X5 J2 Q
Parent=Dir.SubString(0,i);2 G6 n+ W' P; }7 ^
break;6 R+ ?' W K8 f: Z: |
}
4 @' m3 V8 W9 A0 @- r- @ }
5 J7 S, o) B* [ k if(Parent.IsEmpty())return false; // 目录名称错误! M7 i/ R9 K3 h6 X2 Y- r
bool Ret=true;+ d! l9 T7 o0 \8 N- ~
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
% m# H- I. x$ ^; n Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
. w- H( h3 Z' j6 Z9 p- ` if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录8 T1 D0 h* F5 B5 ]8 p
if(Ret) // 父目录存在,直接创建目录) A: Q8 y/ x \% {1 m- Z9 {' n
{
; ]* x: J& ]6 g SECURITY_ATTRIBUTES sa;! [4 D3 p2 g: H/ b$ I4 j
sa.nLength=sizeof(SECURITY_ATTRIBUTES);, @9 k6 r" T& v7 y% Q( a0 v* w
sa.lpSecurityDescriptor=NULL;
& T( J$ p; A8 U" E) X f, e- F$ G# Y sa.bInheritHandle=0;
1 e+ a6 {, W) e! n* Y Ret=CreateDirectory(Path,&sa);
( f5 l" S8 i9 j$ b& b- y }
" u* b5 l& A/ q return Ret;
4 }4 v% f2 Q* s} ( n6 q* {7 X9 _ ]8 l
可以看出基本方法是:
( N" q# i S* ]& C) w* t; m. [先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
: x2 {2 h/ t* B4 X1 ^ b1 H如果父目录存在,则直接创建目录,否则自我调用创建父目录。 6 k- h0 w: D! r, f# W
" F; V+ Q G# D& k9 o# I四、直接删除整个目录:
# P, e* L; ?* n4 P: S) Y 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是: 4 \1 U5 j. B& [) b' J
查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) - y+ P" r% Q+ ^+ h! m6 |4 L% U- D' }
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
. H1 N2 r+ t# V& ~1 b. r( t- S如果找到目录,则进行自我调用,即开始递归过程。
; F5 s r* r4 ^) ^+ M# H/ Q如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
4 I; Z: ]% d5 \, a具体程序代码如下:
/ d3 h5 U/ a! L/ x/ Ibool DeleteDirectoryEx(const AnsiString & )2 q- T6 d' Q% l2 c
{" L1 o7 r! K8 _
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白1 Y+ I8 H/ l% W5 l) i* m6 z
int len=P.Length();/ m3 C/ u( Y7 j; P( j1 s0 j
char *Path=P.c_str();0 |6 U, A4 g0 r u2 X' \$ t4 k
AnsiString Dir=Path;( j. }( n6 B! x/ x+ [, V" r& T
if(Path[len-1]!='\\')Dir=Dir+'\\';
3 w$ F' r& j; V' Z' Z AnsiString Files=Dir+"*.*";# F9 Y) M3 c4 C( h) U* }
WIN32_FIND_DATA wfd;
: J& Z0 _' G( u" A HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
) K; w7 z% L, E- j- z. @ bool Ret=true;' E/ r7 S6 U( ~ x' b! D
AnsiString Tmp;7 M9 u: K/ }8 T* w; \
if(hFind!=INVALID_HANDLE_VALUE)
7 e" A- ?, [2 {4 G# e& n. |8 u {
, c. O; F; K3 ], h bool bFind=true;
2 K$ W3 I f* V8 q# G while(bFind)4 E) t, n, `1 G; N3 d+ y
{
' Y$ Y5 y9 D; t, V9 G if(wfd.cFileName[0]!='.') // . ..
/ [! c" H U( f6 E, \! H7 ` {& u D, J, [' t0 d; p) s9 A
Tmp=Dir+wfd.cFileName;5 ?( b; Y2 L C" q3 _+ u
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); h' j4 }- E# G8 R$ T
{ // 删除所有子目录4 G) b5 U4 N7 h# O
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);* o: n! \# r, O! \" n. F
}else
4 q$ K B. Z c1 [/ p$ d { // 删除所有文件; H* g( a, t; l9 k
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);9 v4 _# l5 x0 H* m& t' j& A
Ret=Ret&&DeleteFile(Tmp.c_str());
0 h+ @* a. X! V, I. [$ M }
+ w: p& ], S6 ~ }& }8 D$ @ a& ?/ X! L9 F; h
bFind=FindNextFile(hFind,&wfd);
1 H+ m+ S7 t' K" D }
% q$ v/ H; {. G4 K FindClose(hFind);% T; F) e0 V8 j! o* S
}
# h* J j6 w, E! m, x if(Ret)return RemoveDirectory(Path);# z/ c p# F1 v1 `7 ]2 r% ?
return false;
5 o4 b4 t' q5 U} : \& d* d/ k2 i6 v+ g
完 Q4 m4 ~8 l K' s7 a, H/ z8 ~
6 [( I. r8 N; d: E/ t
|