|
目录处理函数
1 Z" ^, X; n% U n/ p编程语言:C++ Builder 作者:王行舟 , O, `- A) O( u/ r4 o$ m' b
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
3 G# X8 ?: O9 `一、判断目录是否存在:
) l8 K& a5 L& w1 D, m: _* j C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
" X! m0 f) L1 v0 W6 J1 D% x3 U3 U6 P0 Y设char *Dir为带判断的目录
5 i( M; {( Z# B9 obool Exist; // 最后结果,表示目录是否存在
. a, ~$ ^7 F, } s* v4 S' Q8 q' t$ rif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”; e, u# H3 {; e7 ^8 R
WIN32_FIND_DATA wfd; // 查找
$ ~8 q8 V: C% i1 G2 pHANDLE hFind=FindFirstFile(Dir,&wfd); 7 y( E7 g9 N5 K
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
& I& t+ I3 m; _) yelse
6 ?/ N! @& v9 ^6 `; b% E/ P) T{
* T, `3 B l8 B; c% _1 B/ ?+ h; k( | if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录/ }, q" L2 n) u* h, a5 f
Exist=true; // 是目录,目录存在) q% |4 K, r$ E" F5 m
else1 C) Q& W9 c( W4 g
Exist=false; // 是目录,目录不存在3 |: L7 N5 q* O! T( r3 X- ] W+ u) u
FindClose(hFind);
6 P) \5 X* U2 Q5 q" S* T) x1 ?2 o} ) T5 }) A5 u, ~* P1 x; g2 b
二、打开目录选择对话框选择一个目录: ) V/ d' L( l# D' m% O1 c K
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: 0 m) ^# E2 m( X3 z& c
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 5 i+ z2 g; g& o- a D: \: q; H
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL; F/ [3 ~% w/ X$ p- @. c
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
4 n; S7 Z* G7 R8 A1 Z, GLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
6 ^1 n) ]7 l9 b$ i7 m3 u4 KUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
% d6 t1 q" }! I. s* L _BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL , S0 G/ o( J8 ^# Z0 K
LPARAM lParam; // 预定义的对话框传递给回调函数的值$ j! O) I8 O2 V6 @" |
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引
" F( c# d. k6 k/ r可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
6 E. o, _% @1 a: k& q0 D9 u#include <shlobj.h> // 必须包含的头文件# n0 w7 Q6 `; m" q9 [
char SelectedDir[MAX_PATH]; // 最终结果
0 Z+ n. D# o) D1 U$ ~" P3 IBROWSEINFO bi; // 入参 Z$ H0 ~$ i: M" g0 D6 H
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
+ c$ H1 g1 s: C9 G( r- qLPITEMIDLIST ItemID; // 所选目录的系统标志指针 $ o9 o, e! l. z
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
* N. M4 ~1 {: q4 N& W2 h3 Zmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据; a- ` Q6 `- c! d, t0 I
bi.hwndOwner = Application->Handle;
6 r* e7 W9 P# G' f8 T7 jbi.pszDisplayName = FolderName;
* Z+ l3 s( s* T- kbi.lpszTitle = "请选择目录"; // 改成自己希望的# k) U( B' b( M% ]+ U
bi.ulFlags=BIF_RETURNONLYFSDIRS;) F+ E; a$ D' Y
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
, U+ l4 b& a6 D' x, H/ t& u' r" ~if(ItemID)
& @. q3 t: r l/ N2 L9 z5 ]{
& r9 {6 o# Q8 `+ l SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
5 H8 G% Q3 c$ n& B2 ?# m$ F GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
' r/ G* Z! J5 ]8 G% @4 B; k/ p}
3 B& x! P: F. Y' T% A6 a& S( v三、直接建立多级目录: / {" r- X K% L; u3 @$ h' o
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
3 n4 t3 A* C& G3 r/ Ybool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
- E/ p1 @6 \) q ]( V{
. z2 [; U2 m+ O6 A if(P.IsEmpty())return false;
8 Q2 l( Z' d8 h( L; m int len=P.Length();
, s- \0 d6 d+ d* z" v char *Path=P.c_str();
; p' ~4 H' m) m2 y3 a if(Path[len-1]=='\\')
0 d7 s- O' E1 a3 {# L6 m" w {
, k& `) x0 Q# B5 E; D+ r7 R6 A len--;4 _; Q i! O( G1 M
Path[len]='\0';
/ R* h/ V# S3 T } // 删除末尾的"\"5 o6 C- n o3 [6 [
AnsiString Dir=Path;
: J" |9 N8 R- _$ z2 x4 A7 \ // 分开父目录和本身目录名称
3 L6 c% w$ c" A. w AnsiString Parent;
t6 L+ D& ]/ D ]0 h# W for(int i=len-1;i>0;i--)0 D' ^, k1 S. \( ~$ z
{- ^/ U9 B; C! w4 \: m+ V
if(Dir.IsPathDelimiter(i))
( Q/ e5 ~2 o2 t! P! V( W( U$ f {
- ?2 }( c0 f8 u" U Parent=Dir.SubString(0,i);
7 \9 T1 q; a' K6 T break;
& f. u# q! I3 ?+ n3 Q8 [2 Q9 y }5 J# w0 ]( h+ g# G+ K) P% _
}( V" e; r5 d. D$ U+ S' F: x; G0 ?
if(Parent.IsEmpty())return false; // 目录名称错误
: w, n# E' B4 L# ^; l# X: b! C bool Ret=true;/ _6 Y& j1 U% b1 {' h" }! A" @' l- X
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
% i4 D$ g9 _- P2 h+ j) u% I Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在6 X. O0 E: z! G5 q* M
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
N L2 S/ `* ^/ }( G) v/ k% _, m0 [ if(Ret) // 父目录存在,直接创建目录
% _4 x; Q, q% g {
% Y/ h# H j; j1 d* v* J SECURITY_ATTRIBUTES sa;% Q1 C6 r: Z! G* V; E) v$ S% ]8 k
sa.nLength=sizeof(SECURITY_ATTRIBUTES);2 X! {5 J, C V4 g2 o5 u* P1 L
sa.lpSecurityDescriptor=NULL;
$ U4 W' I6 u6 \) n sa.bInheritHandle=0;5 ` @# \ {& q* j4 s! Q$ H8 F
Ret=CreateDirectory(Path,&sa);
8 S. F. ^) n5 \# w- Y( l! F }% ]6 R* s+ a- P9 m3 [
return Ret;
+ W3 _4 X2 F& J: K! {} ' N5 f/ ~# B( f5 L4 Q0 J' Y
可以看出基本方法是:
2 l! n& p8 ]- |; }8 d先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
; P" B+ X# z& P' A* j( _如果父目录存在,则直接创建目录,否则自我调用创建父目录。 5 Z- P0 C% o# K M# i
/ ~& X1 L; ~6 B0 }" X# M
四、直接删除整个目录: , ?1 U0 `: B: U- o5 L7 }/ c% G( @
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
W, _( k: ^5 o6 M; {0 E7 A查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
7 V/ F1 `0 _$ _; C如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 : ~4 p; k# x2 [; i, L
如果找到目录,则进行自我调用,即开始递归过程。 , n+ x* k( W. |" t D( c
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
! Y H+ b, J3 ?1 d& G" c具体程序代码如下:
4 t( k3 d/ f- h+ b6 qbool DeleteDirectoryEx(const AnsiString & )4 X# g/ ~( K. b. i/ I) D
{, t- D" u2 D% c* z8 z w' p$ z
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
/ m5 |9 y- a7 K) C int len=P.Length();
- l8 P+ U- S8 m- t/ Z1 e, ?- ] char *Path=P.c_str();. }# ?7 E. [: ~7 ]
AnsiString Dir=Path;
( v! S( F' G4 S1 B if(Path[len-1]!='\\')Dir=Dir+'\\';" E$ O# e* X' |! G
AnsiString Files=Dir+"*.*";: p' s5 E" b! Q/ p- W% K
WIN32_FIND_DATA wfd;
2 J3 r- ~2 r2 `' V% ~$ S, H HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);+ V$ H0 I) F& n9 `
bool Ret=true;
5 v; K7 x5 v6 m3 C4 u AnsiString Tmp;
: _; K! C7 I$ J4 V! Q if(hFind!=INVALID_HANDLE_VALUE)
% I: |6 F7 A: _8 r# @- { {
2 y: r0 ?8 x& }3 [6 c bool bFind=true;/ T) |, C4 q- _) S
while(bFind)- K& E0 `8 I8 E m/ A/ I& A
{
# k' C2 x3 @/ N1 E. R2 W if(wfd.cFileName[0]!='.') // . ..- x- v) Y4 L- x) Q L
{, G$ N+ S# m3 @9 Y0 W- w( n
Tmp=Dir+wfd.cFileName;
; @ N2 _2 m' X$ s6 e if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
Q, I, S, g4 { { // 删除所有子目录! ~2 b9 r! K9 @$ B1 w% F/ `
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);5 J: H% I" T1 Q/ e$ |( D
}else
' H6 l" K" }6 P" ?7 ?! G6 z { // 删除所有文件& K" G9 |7 _5 Q. Y
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);5 l; g( k! I A: c0 f2 K
Ret=Ret&&DeleteFile(Tmp.c_str());4 Y7 s8 i+ S! a2 c
}( N S' B) f r+ a
}
7 x- o8 u1 I, x: X; j5 y bFind=FindNextFile(hFind,&wfd);0 P7 W, u' V) u7 v( y! T0 U) C
}
1 c5 F0 s4 d) Z+ M: _% _ FindClose(hFind);$ z! n: o: m. `9 D$ v
}
T. {7 o7 x' Z9 t n, [7 n4 O if(Ret)return RemoveDirectory(Path);; N% o' v( L# [7 P
return false;" a, E3 E, x% P& z
}
) }( L7 T& m t; }9 d7 f; o6 W+ s完
, [# z+ M. r+ V+ H/ p8 @: P, X% y% V# b8 S5 }: i' n: h N, q
|