目录处理函数
4 h( p+ f7 p, Y3 k6 V% l3 a4 t' Q7 x编程语言:C++ Builder 作者:王行舟 * U1 d5 Y$ J# E U; T2 l
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。 " ^) ^) Z& l( M
一、判断目录是否存在: 1 R) r$ g& @# ~1 a
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下: . L3 C0 o+ N, {) C8 v
设char *Dir为带判断的目录! w( ^) e+ r2 u$ ?. h
bool Exist; // 最后结果,表示目录是否存在
0 K1 x) B+ ^% ?3 R5 Iif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
5 b# A) w1 y1 u X: f' @+ Z& FWIN32_FIND_DATA wfd; // 查找. P9 g* I5 {& m; i! m
HANDLE hFind=FindFirstFile(Dir,&wfd);
* I6 T. o( M- d8 B: Aif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在
, ^, E( R7 W: melse
( Z) t, n @3 y3 X% h{
0 a7 J# C+ c1 D# q- C( B if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
* e, K: F7 A7 M' M i& }/ p Exist=true; // 是目录,目录存在% _: ]" }6 K' n! X) e
else
' s$ Y0 L$ ]8 m% {/ n Exist=false; // 是目录,目录不存在- ^8 c* d4 r4 ~6 N6 R* G8 y
FindClose(hFind);2 p, u* t) d- c. b, Y0 R/ e; d0 {3 `
}
& _4 C7 b/ o W H3 J# n二、打开目录选择对话框选择一个目录: [6 O, ?" H/ E/ g
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
, G: ~6 H- z* v+ e. ~HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle ' g- G2 _' H: ^6 [, G
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL( u$ c- s* G$ s4 l. m( G6 g+ R
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
8 C! U) M1 d1 Z+ k) P- t! k+ wLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
* d0 }, x; S. n6 [+ _UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
2 x T$ |" U. S$ ^BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
+ `4 b7 u9 g" B% @* j6 _LPARAM lParam; // 预定义的对话框传递给回调函数的值
- D. ~- [2 y; {# Q$ K* cint iImage; // 与所选目录相关联的图标在系统图标集合中的索引 : ]- _& R; d% P& v7 _
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
* Z8 t( G& z9 R#include <shlobj.h> // 必须包含的头文件( J, }; D1 U. K7 e
char SelectedDir[MAX_PATH]; // 最终结果
8 l) M; S/ a' a8 r2 uBROWSEINFO bi; // 入参
) ^9 X- H2 Q# `8 Z2 Nchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
; w, Q8 Y" T3 U2 W2 j' \8 KLPITEMIDLIST ItemID; // 所选目录的系统标志指针 - n9 ^; [5 H6 V3 o
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
" \3 I( \8 m( D7 Z; h R% Kmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据( v* ^7 a5 O; s" C( U; `( i
bi.hwndOwner = Application->Handle;3 s9 {% s; w! \! L4 {9 L
bi.pszDisplayName = FolderName;1 J; P: Y. g, b; e+ K3 [# P* B
bi.lpszTitle = "请选择目录"; // 改成自己希望的
* U& M+ p% Y3 I# kbi.ulFlags=BIF_RETURNONLYFSDIRS;( _6 r. k1 F+ }% g- _
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框* W$ L8 s5 o" i* A
if(ItemID)
- Q }" c) h9 c1 F, V. l{
3 f0 V+ n( n; K5 K' y. N B SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名. Y$ _7 w- x o5 c q
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放: U/ Q( t$ |* ^' a6 Y
}
) O/ S2 E8 ]. V* Q三、直接建立多级目录: 5 @( P* u2 o' ~- l$ h7 W% \# q
Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 8 l8 H. _% I, c- ^) D0 ~
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"( j! A; q, U+ [$ f2 E
{; b& t1 j f/ M3 I8 U! i
if(P.IsEmpty())return false;
w( v/ o$ H6 J: T& ]0 F' b5 C int len=P.Length();
* V D* p6 q& \ Q1 x char *Path=P.c_str();
$ p! `" }! K/ L$ Y1 H" f if(Path[len-1]=='\\')
* l1 D: ]$ Y- g: c4 V# f {( J4 s/ f' H# @0 Q9 I4 j
len--;7 Q: R) k( w/ Y: t2 B" a
Path[len]='\0';
* L: D) M2 l2 K9 q } // 删除末尾的"\"
c2 t& J6 Q6 g' _* v& Z AnsiString Dir=Path;+ L- R1 o1 v2 f! z$ G f; R) F
// 分开父目录和本身目录名称
0 [$ G5 A, l8 V9 B: C3 o# m AnsiString Parent;; Q/ l5 S. Z x b% \
for(int i=len-1;i>0;i--)
0 j6 I" N; M" B2 g0 r3 k {% j3 ]8 `6 k& M
if(Dir.IsPathDelimiter(i))3 H9 p7 p% y# ~% K, s
{
3 b8 e3 [1 E: ~8 K1 {! v Parent=Dir.SubString(0,i);" E; K% B3 ^/ s$ K6 ~) N7 \
break;
% {9 J) ]* ?1 ~4 ?. x5 t B, m }
; i& t4 K+ S8 I0 e3 z }
# @6 {9 v; H T7 ^% H if(Parent.IsEmpty())return false; // 目录名称错误
: g1 M( `# @ U, d3 F' c9 q: d bool Ret=true;# g2 G8 N- Q1 x* i
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录$ O0 a0 |7 }% b; r! ?
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在+ I e6 f" H( X& o6 K
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
6 I6 Y3 P$ `! k if(Ret) // 父目录存在,直接创建目录
2 Q5 x) L$ k" g) y1 Z9 G {4 J3 W9 m, k3 O4 u4 |. h" v
SECURITY_ATTRIBUTES sa;" P8 W( N E. Z' \# O" ~
sa.nLength=sizeof(SECURITY_ATTRIBUTES);* z8 Z7 J |) w
sa.lpSecurityDescriptor=NULL;
4 m8 O2 ]- m5 G sa.bInheritHandle=0;8 Y& U! ?% I8 ?) E) Y* A1 z
Ret=CreateDirectory(Path,&sa);
E% N7 v$ |+ K/ U& p }! M& a( Z% T8 y1 L5 Q
return Ret;
( m& h+ I) h5 `1 e% h8 @+ M. G/ b} ' w1 ?# b C. x; v {
可以看出基本方法是: _& T, M0 i ]: ]5 ?
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; # u2 a W# l: m4 [' L
如果父目录存在,则直接创建目录,否则自我调用创建父目录。 ( n( K. M! T' e9 H# H& p# J: v: P
6 r* M! m8 ^* d1 D N
四、直接删除整个目录:
7 H3 R- K* Q& |6 c 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
8 D/ l% E; _) P/ s查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) % D. K3 e3 z* a
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 ' Z, L" B* j- T k/ r
如果找到目录,则进行自我调用,即开始递归过程。 5 ~+ Y# f! q: P! `* Q% k B8 V
如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 ) Y0 i9 @9 k/ W k; c
具体程序代码如下: 3 k3 T6 z- w- o$ m3 z, J
bool DeleteDirectoryEx(const AnsiString & )
9 [' _/ O f! O& N l8 s4 H$ ?{4 k) n( o9 n# L; C3 p
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白 p& U% Q! N! f7 H4 K; P( j4 c
int len=P.Length();# V% C4 B, n6 x n
char *Path=P.c_str();
- I# b) n0 g$ E' X% L AnsiString Dir=Path;1 n! E+ m& p- [0 Y4 v r9 E% K, `. U
if(Path[len-1]!='\\')Dir=Dir+'\\';
3 p: i: j. b7 m( z5 x AnsiString Files=Dir+"*.*";7 U9 T* H, L2 r( q/ J8 F
WIN32_FIND_DATA wfd;5 a+ J& o, `4 g, v& o: u* q# a
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd); l2 h9 k' [1 [# g% m; u
bool Ret=true;
! G, t) X; u( { AnsiString Tmp;4 O+ m& g: ?, N! @- {
if(hFind!=INVALID_HANDLE_VALUE)9 u8 z3 D: u3 U, N6 {, S6 b/ q
{- ?& u6 A' ]5 d+ S& L' T9 E+ D
bool bFind=true;; k$ J0 ?! e B2 Q* ?! \
while(bFind)
; E3 L) L" \4 U- g, l: h; A5 I8 B% G6 q {
. G. D: z4 _3 V% a# @. a8 b: N if(wfd.cFileName[0]!='.') // . ..6 \" u/ R3 Z2 s% A) g! U" L
{9 [( O3 l; m8 z' g
Tmp=Dir+wfd.cFileName;
0 P$ z! x( g& F* c: P. H if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+ k/ n8 P$ o+ R. d# j& V
{ // 删除所有子目录7 i. X5 T8 y0 Z: `
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false); H1 p h# o) A( L
}else
) w W% E3 I/ v7 }7 W, u; Z4 i { // 删除所有文件
8 k" Y* I5 `- V3 x SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
1 z+ O. b. e8 d. p9 D Ret=Ret&&DeleteFile(Tmp.c_str());
8 h! H, f6 m' G, Y } X f7 Y7 H' u6 r( J
}
: L/ O7 l# E" |7 N. X bFind=FindNextFile(hFind,&wfd);
+ C$ }0 S; } H) h, b1 n! h }, |9 j3 ]* W$ f* v! P# F/ v
FindClose(hFind);" \; L% ]7 }7 r( P
}5 c8 ]1 V* w* Q; F: O- n
if(Ret)return RemoveDirectory(Path);0 o4 f4 |3 j$ e/ X" v+ j# l
return false;
* V' c3 d1 l4 G: v( w% B}
' ~$ z% U: g/ M6 J2 b N完
2 j% o9 O' ^4 t" Q9 M [1 t, u8 Y; p: S: `
|