|
目录处理函数
' {) f! x+ O9 s3 W编程语言:C++ Builder 作者:王行舟 . Q, {6 c+ {! B# n- r4 o" }
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
& l; ]7 _' K( ^2 w( [0 K2 Y7 B: l一、判断目录是否存在:
) K- o' `2 x0 ~! B' L C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
0 F8 T* _3 f0 @. y9 R" R; A设char *Dir为带判断的目录
3 v$ d; i0 J1 Z; } d; K1 Obool Exist; // 最后结果,表示目录是否存在
# [' j% y+ Y1 k! r d% zif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
/ k8 a0 q2 e7 d9 ~: T) J1 NWIN32_FIND_DATA wfd; // 查找
, l+ v6 J6 N. z$ ~HANDLE hFind=FindFirstFile(Dir,&wfd);
$ i+ P. B% _8 m! K+ o: uif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在: \3 x" f7 P* b) N- J
else
( }( r/ S! |3 ?/ G5 n6 ?: `( S% \{
+ G% m: O8 N" z/ h! e4 j if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
+ B9 n5 h" ~* a Exist=true; // 是目录,目录存在& V$ E+ \5 @! B# `6 ^# i1 Q0 e
else
' n/ z0 @ @! z V. l0 m: J Exist=false; // 是目录,目录不存在& f/ r- U" N/ X3 J5 ?1 M
FindClose(hFind);, o! F/ S( k* ]5 G: f! z
} ' J: u; Z, D1 n: V0 F# u" R
二、打开目录选择对话框选择一个目录: # R7 @. M7 D# L0 y
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: ; I8 G3 Q0 q- S6 D2 h) c
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 7 _7 ]* l2 N' r) z* M( Q4 C
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
+ ~7 T7 u* I% ^8 lLPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 % H; g! V$ y3 L7 f2 j$ @4 B7 G* Z
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
' n5 }/ D0 P0 [8 G3 TUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
, {" T$ v) s; p) F! i& xBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL 3 ~' a; a- R3 J% l
LPARAM lParam; // 预定义的对话框传递给回调函数的值
+ q- ]9 ^! r& Sint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
3 C4 X! Z9 C' _- | ?- x) s可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: : u1 @4 o9 @3 Z! b _* [! F' D
#include <shlobj.h> // 必须包含的头文件0 n& r' M3 {9 a4 `/ n- |
char SelectedDir[MAX_PATH]; // 最终结果2 c G1 U3 Y" K! R9 x) M& {
BROWSEINFO bi; // 入参
/ J. I$ { Z+ n3 i& e' Gchar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
: ]$ d S) }# J, a, PLPITEMIDLIST ItemID; // 所选目录的系统标志指针 $ ^0 b, N# ]; X& G) C" G
memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果4 U& i3 d* x I6 R8 a9 B
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
1 ?8 `; |& B6 D; q/ J3 Lbi.hwndOwner = Application->Handle;
8 G+ d- r. R% T- xbi.pszDisplayName = FolderName;: M, O* P1 i6 C( d. m+ E0 q
bi.lpszTitle = "请选择目录"; // 改成自己希望的
* J8 A" m( Q& z' `bi.ulFlags=BIF_RETURNONLYFSDIRS;1 m5 K) M1 L! y' [& X- G8 L+ l
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
& m+ e' {' t0 V' g, J Pif(ItemID)
8 U+ ]0 d. N j- X3 o9 H{
, I/ a7 ]- G; a' q9 B SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名
; }( l& {9 L# c0 R: |$ _9 R5 G GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
' R$ C+ n& W2 ~' i% J2 z} % {2 }+ x$ K4 e6 g* V2 B
三、直接建立多级目录:
- a& c" X a$ { Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 ' E% u/ K; u* z# E) Z
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
$ y7 s8 S# [$ T0 z9 x! k{4 s/ _3 u6 e; {
if(P.IsEmpty())return false;
4 O! J* b. x; Y" g9 H5 Q0 G9 [: T4 y int len=P.Length();5 w s/ C7 E; b; t9 I
char *Path=P.c_str();
: a* y! E6 L8 A3 ^% }2 F! J5 g1 ] if(Path[len-1]=='\\')
3 a5 n2 ?# _, G' M {
, m3 I" s: Y5 |/ }' D3 {! w len--;
6 Y' g1 Y/ G# Q1 |: N6 Q Path[len]='\0';. g( h4 @/ q/ u6 Q+ w
} // 删除末尾的"\"
- B: t9 h- P# @- q AnsiString Dir=Path;) a/ b* [4 M5 k v/ k
// 分开父目录和本身目录名称 K' T# P5 s8 v8 z+ ?; X6 v
AnsiString Parent;. i& A! `9 Q7 S! ]7 v6 P9 A+ n ?3 W
for(int i=len-1;i>0;i--)% u& O8 b& K* T9 P& {, C5 X( U
{3 O" N l! B' J- {
if(Dir.IsPathDelimiter(i))9 \% K* u- l+ P$ c" f
{
7 q% o. W9 i0 Q- _1 g( a9 N8 w" m Parent=Dir.SubString(0,i);$ A# F/ T' |3 d7 j F9 j, k% L, l# U
break;
5 T1 K6 W1 _; v/ p- U }
0 s% z$ b3 B6 q x: v8 a }2 Y9 |& k7 { D8 [
if(Parent.IsEmpty())return false; // 目录名称错误
- t B0 K5 r7 x' J# `- A* O bool Ret=true;) q# w0 T: S2 r
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录- P8 G. E; T5 u& G. i6 l/ _
Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在9 x; v* A) P* f: F* O: q' x
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录
1 X& m) ^" `* Q) ]: L if(Ret) // 父目录存在,直接创建目录
1 o" C! b5 J2 ^3 G' b {
! M. h: E; l R$ Z" @ SECURITY_ATTRIBUTES sa;- [+ A+ @: d/ n; S
sa.nLength=sizeof(SECURITY_ATTRIBUTES);4 L% Q! j- |, b, D& Q+ L* z7 d @
sa.lpSecurityDescriptor=NULL;
5 b5 I9 \0 o/ T! l sa.bInheritHandle=0;
6 G# q5 n: P. t3 W* M# O Ret=CreateDirectory(Path,&sa);! E2 ?* s, b. f9 p3 k+ K5 t! d( Z
}6 |# D) J t) t0 [5 G8 N9 [
return Ret;
0 [0 g2 q+ T' v$ w& E. ~" _5 I}
8 Y) f' m/ I/ U2 e1 ^+ ~ 可以看出基本方法是:
$ T# q: N c/ }% d) y: U先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
1 M& ^1 O) [$ y. W: A如果父目录存在,则直接创建目录,否则自我调用创建父目录。
2 y& I, L. E' [; }7 U
' F' F) e# [5 \4 Y$ v) X) i四、直接删除整个目录:
1 J9 ^8 w ]! |% G4 b0 X 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
/ b) s- V) F n; J6 f查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
& |9 h- Z( O( w4 t) ?% L) U如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。
9 E- u. l- n( i$ A如果找到目录,则进行自我调用,即开始递归过程。
" s+ f8 b( D% j+ }- |' h如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 l+ H8 X1 W m0 s0 a1 o+ E
具体程序代码如下: 7 ` @6 s5 \+ g1 O* t. V) k* [, V+ b' K
bool DeleteDirectoryEx(const AnsiString & )
8 [" O- P0 }4 E/ G5 n+ m$ j9 |/ K{8 @5 {! b8 a* W) {8 {8 j* V
if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白% V c( l) ]9 a! }5 ?5 L0 u0 x2 M
int len=P.Length();6 r% P* S4 E; W
char *Path=P.c_str();
9 ~0 Q* K4 y9 A% d& n0 [ AnsiString Dir=Path;
* D2 O7 r3 w. B. e2 {8 K if(Path[len-1]!='\\')Dir=Dir+'\\';& j) K2 R5 t* S# N! `2 L
AnsiString Files=Dir+"*.*";. H3 E+ o6 t% S$ N3 B/ w2 L& a
WIN32_FIND_DATA wfd;
/ a. \) j! P' P/ U4 A HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);
5 [3 F" A# X: W; `$ Z& |9 \ bool Ret=true;
7 s* G. f, U$ g8 }0 i AnsiString Tmp;
5 T1 \+ N5 H' }, [) \ if(hFind!=INVALID_HANDLE_VALUE)
1 B) b7 Y9 g; N$ E$ z$ n3 v {! F' P+ n& e2 K8 B* K& A1 b
bool bFind=true;
* T5 M, B O" z. I& C* Z" ^ while(bFind)7 M8 C; Q8 D: z( t8 I2 E
{
; e2 Z( f( q1 w4 h if(wfd.cFileName[0]!='.') // . ..2 x5 y$ t. i, r$ v
{
$ S0 h" A$ c5 }; y t8 H Tmp=Dir+wfd.cFileName;( U* L8 Y( d9 f- G4 V
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)& Q* |3 {' g" i6 J! t `; y
{ // 删除所有子目录0 m$ R" Z& _5 M0 Z+ N6 N8 Z
Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
- I% ]1 D: K b5 z }else) G4 O8 `- m' V3 u+ _
{ // 删除所有文件- a, w+ R0 @# t* H( R( e
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
3 i9 D( W0 z) F4 `' x Ret=Ret&&DeleteFile(Tmp.c_str());
9 z R. D2 b* d" m, ]; [+ \ }
* |5 C2 l o6 X$ O! @ }
/ [2 f* u2 D6 p! |! f- O6 o/ N9 \ bFind=FindNextFile(hFind,&wfd);+ Q O3 N& \; ?
}& j% \0 W5 d5 d
FindClose(hFind);; l* _5 L2 x( ~( N* Q' ^5 Q
}7 i3 a3 }4 N, Y! R& U1 a
if(Ret)return RemoveDirectory(Path); V: Q/ _8 h4 g, P7 d+ G6 E% Z
return false;
) z; W$ o/ s( c: C) W} . x3 U! ~5 {9 J7 H7 f8 @( Y& V& C
完
( w3 |/ a4 a/ F5 K$ S! ~) P0 j: `* C3 m& }' t# N- I* M. j
|