目录处理函数 ( q: f' W, _' C' T
编程语言:C++ Builder 作者:王行舟 K' x" |1 r" D5 R8 Q
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 - Z. B) ~8 }( `8 G& X! c
一、判断目录是否存在:
* e4 L+ U7 Z( m5 k _ C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: : B9 |/ b" P" g, T
设char *Dir为带判断的目录# p/ f: d. y8 m( ^2 _5 v! A6 \. y% a6 l
bool Exist; // 最后结果,表示目录是否存在
9 g) F$ L" ^7 tif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
A% p4 A% d' n; e$ wWIN32_FIND_DATA wfd; // 查找
) l( Z1 s G9 t' I9 S! P @HANDLE hFind=FindFirstFile(Dir,&wfd);
4 L A/ q' A- w! sif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
! b' l( e2 N4 x, W8 J% nelse9 U: J* ?! y1 M: R
{
' k- R; F7 E4 j' w) U7 _: T if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
/ Q6 E% V6 R% d3 U& z Exist=true; // 是目录,目录存在
( Q9 c3 w) i: Q; N; e1 x else8 v3 I; }$ m& c8 X9 C9 J
Exist=false; // 是目录,目录不存在
9 ~! a- o6 Z. W FindClose(hFind); b5 i3 t& A5 B) C( F& z
}
" G( [2 Z% |& R" h# c2 E e0 C$ y二、打开目录选择对话框选择一个目录: 2 P/ V2 z4 i3 I" s g5 O' ?2 `
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: % T) A8 G1 b# Q: R J7 n& K( o
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle l/ j# g; ?9 X: U) T7 B! s
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
) _ j W5 k$ s! e9 ALPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
& n) _0 x6 s7 Y! M3 O" S* u9 m* ALPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
5 c6 B0 n* ]$ V! oUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS % F/ K9 Z7 A9 e: S& B `3 q
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
+ l6 a6 E; z" ]4 C, _9 d6 @LPARAM lParam; // 预定义的对话框传递给回调函数的值: U8 V' l3 M0 r/ D
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
0 L# J# ~; l+ l% b) h* J5 D- d可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: 1 z- e. K: U @) ^0 `, u7 Y
#include <shlobj.h> // 必须包含的头文件
7 b- U: Z4 E4 b: j9 {7 E9 u' w" Nchar SelectedDir[MAX_PATH]; // 最终结果 j, x- f# t( f3 L5 b
BROWSEINFO bi; // 入参8 T+ C' V% j6 z
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
/ m0 q: C: b, C' w# K; @LPITEMIDLIST ItemID; // 所选目录的系统标志指针 ( z3 r! u$ h5 [# T& U5 D/ [
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
: z# e1 D" ^! d# F, V5 qmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
6 W0 |$ w. F+ c3 m+ v* ~& fbi.hwndOwner = Application->Handle;
- N$ F0 H5 d/ B _0 N) }5 m! P" Zbi.pszDisplayName = FolderName;. l( h( a1 Y- A+ j0 q j
bi.lpszTitle = "请选择目录"; // 改成自己希望的
* x$ y/ k. B6 {1 a- i) [( ^bi.ulFlags=BIF_RETURNONLYFSDIRS;6 Z" P; @8 B* E0 Y5 Q7 T' N* H
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框6 ^4 f3 v" L# z! [$ Y
if(ItemID)
7 R* F) V4 T" M0 X5 c# k* F{/ `4 B3 H4 [3 I( P
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名! ~3 e; q+ D( d' \
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放1 ^. L& s: c1 U# \/ ` B& I
}
" K* V" t& @% ?( H三、直接建立多级目录:
- ?- Y4 M ^/ K9 T' | Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 8 c! M3 A9 S2 J6 [9 E
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"2 `) A; g4 e, |# G; m
{0 N9 ~' o, M0 o
if(P.IsEmpty())return false;
& T- {+ z6 b6 h& W( K7 c0 ` int len=P.Length();
9 a* A- d6 e. H2 \ q: F char *Path=P.c_str();/ m( f( d8 ~% v5 ^! n5 Q# B
if(Path[len-1]=='\\')
/ `5 E& a1 L9 q9 z {6 X7 `! D# M3 c1 D# J
len--; { ^4 G: l" q H- c* g8 a
Path[len]='\0';
6 I4 V% z% i* H- u0 X$ v } // 删除末尾的"\"6 o# A3 c) e6 t8 Z
AnsiString Dir=Path;/ F4 s) @5 u; ^1 N
// 分开父目录和本身目录名称
2 H8 C) N# O# _/ ? AnsiString Parent;- ~( B8 G! X, K# a8 P2 `
for(int i=len-1;i>0;i--)
5 l+ M, \' u' ~0 E; d. o! u( S9 C( i" S- A {7 L# ], B, ?0 x
if(Dir.IsPathDelimiter(i))$ A9 N( k* q' M; k
{
$ K& y" }% J( p( Z Parent=Dir.SubString(0,i);
3 C3 q$ z1 z% _5 w break;
& \; F" @5 y8 r, { }) j9 z, e, P6 L6 I! p6 E
}! {6 _, f J3 X1 H( u, Y2 |; a
if(Parent.IsEmpty())return false; // 目录名称错误
, J& r, \: Q4 g) x# e4 F7 h bool Ret=true;
) `/ f3 Y! r- ]/ W if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
6 J: g# K r' l/ E1 c+ v! X, p; J Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在" |$ i/ R! ?2 k
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
0 C' O5 k: L% a( ?+ w if(Ret) // 父目录存在,直接创建目录
. k2 s, X2 t' S' ?, U {
7 Z0 |1 w0 Y* q' a3 h+ N& V9 ?& d SECURITY_ATTRIBUTES sa;, z' I! r# |. P3 s3 s
sa.nLength=sizeof(SECURITY_ATTRIBUTES);- u$ G5 t1 o! W; T
sa.lpSecurityDescriptor=NULL;
0 \# v3 V2 i) U, B1 z- `7 u sa.bInheritHandle=0;
) [7 f" Q r1 Q Ret=CreateDirectory(Path,&sa);6 C! |) F; x* z2 B
}
9 \9 l p6 E- }& M: t! E9 F! ] return Ret;
' Q1 N: i" u5 r}
a; Q. ~2 G1 z5 x6 W4 @; t* @ 可以看出基本方法是:/ s s7 r8 J0 ]& G ~$ J6 n3 U0 T+ D
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
& G1 N( R/ e, q& i# v) y$ P如果父目录存在,则直接创建目录,否则自我调用创建父目录。
# W( j3 m, K0 R& y4 @" y
0 A8 G, N% g- G四、直接删除整个目录: % w3 H! u! t3 e5 b, x3 A+ J- Z7 ]# q
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
) N1 X# U' l3 ]# f& A查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) 7 U3 y, E7 d V% e3 V
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
: q1 k% P" V' l# |! ^. J如果找到目录,则进行自我调用,即开始递归过程。 : e! I2 {% W* }
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
9 C+ v- G B4 ~: i! f具体程序代码如下:
0 q. i! W. u, r( g/ O3 `9 M2 \4 ybool DeleteDirectoryEx(const AnsiString & )
9 ?) h5 H) k) b% c{% N3 h- V. m' B8 y0 t2 ?; C! W" B4 M
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
+ r! [$ c/ z8 m int len=P.Length();! ?8 t% X) J! i! q# b: ^
char *Path=P.c_str();, ]$ E( W' C2 J+ ~
AnsiString Dir=Path;
: s8 P* i7 Y- Y if(Path[len-1]!='\\')Dir=Dir+'\\';& G _) W3 Y, k0 s' r, h/ ?
AnsiString Files=Dir+"*.*";
4 v+ F1 `" i8 o8 H- }* b9 A WIN32_FIND_DATA wfd;
$ @7 Z. i/ ^6 Y% H HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);5 j* F% |: I9 J4 V2 [, U! y8 w2 L9 F
bool Ret=true;
! o1 f) T: Q$ i$ Z4 J AnsiString Tmp;( b2 B6 {, d" t$ m% S3 ~* I
if(hFind!=INVALID_HANDLE_VALUE)
" S; `& l8 c2 {; I/ z, b {- @9 v2 S" T+ ?. I3 c& L
bool bFind=true;' l$ e) X2 k- i9 |' D2 M' Q/ {# @9 Z
while(bFind)
: |! ^- p: G" @' O4 C: } {
" Z! y& h" J3 N! y0 E; E6 r" H if(wfd.cFileName[0]!='.') // . ..
( Y- a. ]! C3 R: W' ^ {
\" i- U* {8 J/ E2 f5 l& o Tmp=Dir+wfd.cFileName;
% S* P2 z, \! F: K) p if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)8 b9 X6 C; C& L* j, V8 d
{ // 删除所有子目录& }' N3 |& r/ N3 r2 U! {& ]* u; v+ I
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
8 T8 q4 Y4 e9 E0 G Y$ g }else$ E/ h ~/ @& U3 l' u
{ // 删除所有文件
# U, Q( J8 `5 b& W' p SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);- X. g# H) F3 N& t) b' c
Ret=Ret&&DeleteFile(Tmp.c_str());
R* q3 a0 J9 h( c) \/ f }
8 ]8 z4 v6 x8 o! E7 U6 A$ d } y( G7 Y' Q( ]4 P
bFind=FindNextFile(hFind,&wfd);- P5 K$ g8 H& }+ }7 A6 j/ J
}) b* k" `$ P/ g; H( J" t# s
FindClose(hFind);; l6 e4 W X6 t3 J( f" D
}" D6 C$ m* U' [0 G, @7 _8 l$ t7 \
if(Ret)return RemoveDirectory(Path);
( K; d" U$ x% b% a) u return false;
- j1 E; I5 l- N. F; ?} # f! V5 G2 {2 ~( }$ _1 r; y
完 . v7 [) o9 S" _4 P
5 T' J' D5 `& t9 S3 m3 P
|