|
目录处理函数
2 O Q! o+ t- ^8 T( b3 e* ^& o编程语言:C++ Builder 作者:王行舟
% H' L9 y: B; F$ a$ q8 j7 G! N 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 $ y. v# m4 W" I. @; U' J
一、判断目录是否存在: , }/ I+ D7 E5 Z4 q# ^
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: / j1 n) w- o" }% b# R0 Y9 O" }
设char *Dir为带判断的目录8 M! D( D) W$ t1 l7 G. S
bool Exist; // 最后结果,表示目录是否存在 l# m. [9 v+ |# x( U- s
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
9 a: p4 b3 q4 h: }" p" U5 k( qWIN32_FIND_DATA wfd; // 查找
, r% \. F& K/ g: O _- AHANDLE hFind=FindFirstFile(Dir,&wfd); 5 W/ f2 K4 H5 x' C0 \5 g
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在2 l Y D" _$ O
else. b' u5 I1 y) |7 Z% \ ]: f2 k& q
{
4 J |; L' a# h: }4 ` if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
" J+ X H) I2 t9 E! {9 ~6 O Exist=true; // 是目录,目录存在
* Y" J. O, Y/ f& | else
4 K+ k( ?& R+ C9 h# U, g Exist=false; // 是目录,目录不存在
- w, v' W& L3 O. A0 T, b FindClose(hFind);) U# D! E# Z8 c; c* k" w4 r
} 8 D; }, L" x& G/ U6 G6 H
二、打开目录选择对话框选择一个目录:
, U3 f& G1 Z% N% s 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: & C8 u1 [. ?6 ?9 v. {+ N( ~8 Y, l5 c
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle
) Z8 e, b* b: n9 y" r5 CLPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL) Z% m$ a1 c( w
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 - F& b, u; b. n; X7 @
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 - s/ K# n1 @: @1 u/ A
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
' D2 r% p2 ?9 [- b/ g; X$ W: Y; kBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
8 |0 M8 Q1 ^/ J6 Y) l9 B+ `) S- J+ [% rLPARAM lParam; // 预定义的对话框传递给回调函数的值
. Y; f2 u8 t" ]int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
3 l! ]0 @8 e- F D, `/ y- D, y$ C8 N可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: S8 X& O" E1 O
#include <shlobj.h> // 必须包含的头文件
% n2 @. R" X7 z: x. Echar SelectedDir[MAX_PATH]; // 最终结果9 L( u" K1 W4 O6 o3 `
BROWSEINFO bi; // 入参
n8 a, C0 |8 f4 Y# f5 schar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
* A1 Z; t; w9 jLPITEMIDLIST ItemID; // 所选目录的系统标志指针
3 c9 I+ a$ e- X( H. hmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果2 O1 R6 v! k; |6 f, T" \/ b( o
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
8 t' |8 O7 l1 a) N/ Wbi.hwndOwner = Application->Handle;
' [' M6 N1 W2 M7 g- ]2 hbi.pszDisplayName = FolderName;0 x4 [5 k+ \% f2 u
bi.lpszTitle = "请选择目录"; // 改成自己希望的5 w9 k! b! R- i; k
bi.ulFlags=BIF_RETURNONLYFSDIRS; r# y. V2 p, y6 j. t
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框% |. L& |$ l# s! R% {4 I
if(ItemID)
5 W. ?1 e! Y, `$ W' N+ G{
( ?! t" W, T- M6 d/ m% }0 R4 r1 A SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名, q2 ?% y% R5 E+ R
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放+ v0 L" V( L1 X( z3 F
}
% |/ P% w0 S: S: F, J- Y0 ^三、直接建立多级目录:
2 J" X0 Y: z8 h3 y8 p; R: L2 n' U Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 + \8 o$ p, s |4 Y9 A0 Z m
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
. I" D! I1 v7 A" E{! `2 x- \* g* b' W- z! Y+ ]
if(P.IsEmpty())return false;; T9 B2 S& b, P; F9 b
int len=P.Length();: u4 o; A' g6 X% O) l
char *Path=P.c_str();
- N+ |+ n' ~/ @7 r+ k0 N if(Path[len-1]=='\\')4 Z( g0 M8 p! `; s5 L& L9 N
{6 x4 S$ o, z* [' _2 T/ [, I3 G
len--;
' c- Y. e) S8 L8 S Path[len]='\0';
8 J( |# h* M$ `* g4 C. G& a. E1 }6 ~3 R } // 删除末尾的"\"
. t# Q- K5 m2 l( ] AnsiString Dir=Path;2 G6 g; T) ]4 _4 f( i
// 分开父目录和本身目录名称
" e- E* \! E4 b1 `5 f AnsiString Parent;
+ u8 J6 B) o8 p+ c V+ @! n for(int i=len-1;i>0;i--)2 x, S5 c9 w# @6 X
{
4 c s9 h |0 x* ]# r! j0 F if(Dir.IsPathDelimiter(i))6 X/ V' W* t E9 h! Z" D- H
{
6 N3 R6 V2 x* y. Y' @ Parent=Dir.SubString(0,i);
, x' I- J, `! U, L: y break;( E2 Y# z7 h" N5 s
} R: v7 E) J1 K4 E" h
}
3 ~1 s6 d' ]! W! ]% l( V# t if(Parent.IsEmpty())return false; // 目录名称错误
8 G4 ]( \1 u. R _' v |( s. n$ V bool Ret=true;( w7 `# a# ~( V& k. G0 H/ v: }
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
2 B: e$ R" K- Q" m3 c% L, J4 A Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
- S# j8 h& g2 K y( Z if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录! W- @8 l+ Y7 G- l0 z8 e7 `
if(Ret) // 父目录存在,直接创建目录$ c3 h3 [1 d. i. `
{) ]/ C' I! L4 _2 d* r
SECURITY_ATTRIBUTES sa;& d6 r% F; L' a% F
sa.nLength=sizeof(SECURITY_ATTRIBUTES); m) X# i( U! \& I
sa.lpSecurityDescriptor=NULL;9 y- h3 s8 H" _2 I1 u1 I% y3 p
sa.bInheritHandle=0;3 n/ y5 W; w* h$ O6 D8 D0 \
Ret=CreateDirectory(Path,&sa);
! b z+ W2 O( o5 Q; b }- @0 B4 j2 e8 E7 e
return Ret;
8 `: ~5 U* Q7 C9 Z}
: O, _8 c2 e) g8 G- ^, s! z# y( ?0 h 可以看出基本方法是:
; g4 _3 K3 M2 @, E" i; u8 q先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
. }! K# B! g$ X如果父目录存在,则直接创建目录,否则自我调用创建父目录。
( C$ z' n1 ~' [# T& A
, m6 W4 u7 o- R9 W5 K" s- e5 C0 d+ I四、直接删除整个目录:
( q j6 @/ y$ [& n; Y! _ 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
7 L! o/ h# g/ V2 U. Q' r/ |; p查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) . }+ c' B4 T. w& [+ t5 b7 m% ]
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 ) U( A2 g; l2 J
如果找到目录,则进行自我调用,即开始递归过程。 # N# A E" j3 U$ u! U, G( {0 ~
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
! S8 Q' n( {9 b+ |; X/ _/ Y具体程序代码如下:
* S! g( k5 A* P6 v/ ^bool DeleteDirectoryEx(const AnsiString & )/ ?+ T4 @3 }( b7 s( k0 r
{
$ _6 ]3 E( h# U5 k/ _- d if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
4 W- i" R0 o3 m5 h, S5 Z) q int len=P.Length();
" ^6 a6 r; \4 E( Y' J6 X char *Path=P.c_str();
8 o; @' W* s3 i8 ?0 ?1 e AnsiString Dir=Path;
# R" j7 Q: Y) l, c% z ~ if(Path[len-1]!='\\')Dir=Dir+'\\';
( d4 L$ n4 J7 H0 N AnsiString Files=Dir+"*.*";0 k* |: y/ p& e
WIN32_FIND_DATA wfd;1 q9 {/ L. L9 B5 n
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
# k- z, x- v4 u! Z, ]0 { bool Ret=true;. h3 k5 c& s0 r' D& _! F* o/ D3 w; g
AnsiString Tmp;( B$ t% F2 ^* ^0 U1 n' q, K
if(hFind!=INVALID_HANDLE_VALUE)
# j" S9 p9 K/ ? {
4 R+ b6 T1 k+ g9 H0 C3 ^5 f bool bFind=true;
* K( l% i6 x; P1 {# M/ e0 E0 ?( e1 n while(bFind)
, ]* ]7 ?# _ a0 Q' n. c {3 e3 ]) J- N, `
if(wfd.cFileName[0]!='.') // . ..
7 V) o0 J( \8 x+ i4 B [ {5 R+ Z; P& {; N3 d$ g& [
Tmp=Dir+wfd.cFileName;
0 F% [8 \0 \" x: F. w+ ` if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
6 ?& N% Z- D3 P) F1 K9 u5 M7 I3 F { // 删除所有子目录* _$ b3 x' |, U9 D& f4 r) D
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);: b _: |4 N/ k! Y
}else
( I+ @3 r. z1 [* L/ b { // 删除所有文件
4 T9 q2 Q& B2 o- H0 u7 C8 Y+ T- P SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);) N) p% u7 i+ p8 d4 J$ c; G5 e
Ret=Ret&&DeleteFile(Tmp.c_str());
3 J, r) I+ [- U9 _1 U% W }5 z6 ^; B0 ?/ z$ N
}! V, N7 M+ t, g& Y9 s5 O
bFind=FindNextFile(hFind,&wfd);! K0 Y% F$ |9 ^ |: h* d
}) ~* l8 `1 O' I# H2 B- W D
FindClose(hFind);/ ]9 B( U1 R) p4 z. W
}; C: @* d9 v8 z
if(Ret)return RemoveDirectory(Path);* K/ h+ F4 @# b$ R# g# K* B
return false;1 M& Q& l ^% M
}
4 _6 s/ u( p9 O1 W' P7 b完
4 U z$ x% u: K8 t( l- V% Y8 L- }$ ^5 w
|