|
目录处理函数
% w/ _, q1 j9 ?3 X, v编程语言:C++ Builder 作者:王行舟
$ ^. |/ m$ Z$ F+ T' Z) F 在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
/ p& @% h' a& Z/ F% F一、判断目录是否存在:
; q$ O- y, t `& t# s! ?( J' D8 c C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
; n" ^! m* A) c5 F3 }4 a设char *Dir为带判断的目录
; w6 [9 L+ _: l y6 Y+ u. Hbool Exist; // 最后结果,表示目录是否存在4 {% a' i H( U5 |8 g2 e U* F% W
if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
3 P+ f7 i" d/ NWIN32_FIND_DATA wfd; // 查找
- |2 B( p6 f! ?, v( LHANDLE hFind=FindFirstFile(Dir,&wfd);
5 S/ P a: |4 f3 M$ T2 ?" hif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在* Z. m, k ^& Y" l
else) `# H! M/ h1 B8 c' w! J0 z
{2 {4 U5 l& m# @) e" H" p1 F
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录
3 Q$ V2 G- t0 |0 R+ K$ u Exist=true; // 是目录,目录存在! \* A& R, C: ^0 n/ g$ Q
else
2 C6 {4 k) {: P0 r2 z7 e {- G8 P Exist=false; // 是目录,目录不存在0 T4 R. V' f7 o) _ Q
FindClose(hFind);# Q) F; h1 @' A/ v# u6 O. V
}
7 H2 \1 Q9 z- @8 u: V& t: S二、打开目录选择对话框选择一个目录: & l W$ ]; w6 k9 g \9 f7 Q: R
大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:
3 r* F* i, m2 H8 F, _* VHWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle 9 G: M8 s! _& }6 w( |$ ?) F
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL6 T0 S2 o' ~; C; p* B! n3 M
LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 2 T4 c( r3 T D: }& N. j
LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 6 B9 k2 u* h% ~: f0 R* N0 K2 y/ f
UINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS 6 i# u3 X( _4 C3 H* _" y) Q8 B
BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
6 V) m2 `/ d( Z% LLPARAM lParam; // 预定义的对话框传递给回调函数的值1 C. e& q. @6 p U8 F/ E5 t
int iImage; // 与所选目录相关联的图标在系统图标集合中的索引 * J* I8 T! z: d, y1 l- i+ ?4 n3 L f
可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: 9 [5 r/ j9 }- p7 O9 V- ]# w* r
#include <shlobj.h> // 必须包含的头文件
7 O( X# i7 J7 c5 X; H) K4 L1 lchar SelectedDir[MAX_PATH]; // 最终结果
1 `( X3 i: |2 L( N/ iBROWSEINFO bi; // 入参+ M& L9 b5 X! g* ~1 z
char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font % m5 a, V* E$ n" g
LPITEMIDLIST ItemID; // 所选目录的系统标志指针
# y/ U! Z7 c+ k! s$ Nmemset(SelectedDir, 0, MAX_PATH); // 初始化最终结果2 c' @: ~! _" i: Q/ Y* H
memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据
1 `# E7 c* ]/ c) |$ L! x vbi.hwndOwner = Application->Handle;7 y/ v& W- Y: |1 ?, B9 ]8 b I
bi.pszDisplayName = FolderName;
# w: B, t* O/ e8 bbi.lpszTitle = "请选择目录"; // 改成自己希望的
' v6 x9 h" o8 zbi.ulFlags=BIF_RETURNONLYFSDIRS;/ N! D) Y) n/ z% e# G4 i6 w
ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
! {0 A( I$ h Q/ w6 Cif(ItemID)$ Z+ D5 S# O. @! `9 J+ _# ^% J% U+ V
{" v9 b. \2 w# {! w" c) } T
SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名& I/ u9 W! K! [& w# c
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
% ~! |( z! M7 W}
3 [. ^* w* [! T- N% J# C4 X) o三、直接建立多级目录:
- u5 u2 }+ r' r8 s# U7 _ Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。
1 j8 {! Z0 Z+ K# M H# x+ ?8 Q$ Wbool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
8 G# ~: V7 h* h8 T4 c' P' b{
- ]' l, x3 [' \, ]2 s# n if(P.IsEmpty())return false;
7 J0 C7 z2 [) F! i! X: f: S6 G int len=P.Length();1 ]* U9 L* |9 P
char *Path=P.c_str();: Q H: ]8 ^9 G3 m- P- G
if(Path[len-1]=='\\')" f! m) l2 l$ X* g/ P+ |5 {0 k/ i
{
1 n- @* D, e* ]6 V/ D2 U- ]4 k& v len--;
# Y3 s8 N! S3 t1 w# n+ Z Path[len]='\0';
5 x6 x8 |6 _0 x# V/ d. K* \0 @ } // 删除末尾的"\"1 Y0 C# B4 x) `4 a
AnsiString Dir=Path;
1 ^- g8 y+ ]6 i3 A) D8 P- @; E2 X // 分开父目录和本身目录名称7 [ Z2 p* `- W- P7 G2 K
AnsiString Parent;9 a2 {/ i8 ?% X9 {- g9 E4 P- {$ A9 @4 M
for(int i=len-1;i>0;i--)$ m3 Y7 s4 `/ D5 M* t
{
! N5 b( _/ E8 S# @( V if(Dir.IsPathDelimiter(i))2 x- @5 u6 D( O$ B
{) ? B* d2 ~6 B2 m* t1 H, R1 Y6 ^
Parent=Dir.SubString(0,i);: T$ A' d9 x' v. u
break;
, O6 Z) b7 C4 }/ o1 u }
- E; T3 R- D E: a( l/ @ }9 Q X8 E/ m3 p. S3 p0 e5 m6 r
if(Parent.IsEmpty())return false; // 目录名称错误
" V5 ~3 Z% l1 u2 c0 Y4 c% h$ e2 [ bool Ret=true;2 m% Q0 r' h( _! R2 {# |( U ]3 B
if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
0 Z0 T5 ?. l. ` R Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在0 e% B; R! P' T+ N9 R5 D7 U
if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录 B: v& x, {4 s; n. m
if(Ret) // 父目录存在,直接创建目录4 R; {3 s% M* M( [ j% Z4 D: {. a' [
{' U3 W, p" i5 N7 N# s$ \% _# J. @
SECURITY_ATTRIBUTES sa;- T7 E) k1 F; |& Z% t( N3 Y
sa.nLength=sizeof(SECURITY_ATTRIBUTES);1 a% K. }, U; E k3 w9 `
sa.lpSecurityDescriptor=NULL;; _; I2 L w: Y
sa.bInheritHandle=0;& l0 B2 n1 P* z7 R8 Q
Ret=CreateDirectory(Path,&sa);
* ~0 P c* {5 ]+ {3 e }. `; }" e3 L7 h) \9 e' G" J
return Ret;
9 n# L9 D0 {. _' @3 z4 \( `}
1 F9 J4 a4 d4 _0 ?" ` t# T 可以看出基本方法是:- f- ?$ K9 `8 r
先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计;
+ o/ E( K& Z0 t! M5 S$ Z/ F' R! @如果父目录存在,则直接创建目录,否则自我调用创建父目录。
' M7 E: R& i6 h8 E) \0 P1 w9 b+ R% i* Y+ j. X4 K# i
四、直接删除整个目录: : d2 o& @+ S2 Q% r6 j
在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
; s e8 R2 p# m& L1 {, T查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) , `! Y# ~2 _6 Q: _. x0 t7 l
如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 6 H+ Q3 c( A1 i
如果找到目录,则进行自我调用,即开始递归过程。
# h+ }9 p) k# E: d3 [ p如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
! G8 {2 D" q; z( z% e# p具体程序代码如下:
4 v# F1 C7 Q) j2 U7 j: ubool DeleteDirectoryEx(const AnsiString & ); y6 t$ f9 U1 X2 W* q4 L
{
& l$ w' d4 g8 m1 e0 y, p if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白
; P" @) |3 \4 v* R4 u5 D int len=P.Length();- t' P1 z& z4 p) p8 |5 P
char *Path=P.c_str(); f) d! r+ q% Z" w' b
AnsiString Dir=Path;* I% L1 M3 V- T: Y8 ^3 X
if(Path[len-1]!='\\')Dir=Dir+'\\';
; F# K3 J0 y1 |5 J: O+ |% w8 l AnsiString Files=Dir+"*.*";8 ~# y9 n: d T/ }4 v7 M+ X+ V. f
WIN32_FIND_DATA wfd;
# M, K" \* h+ q2 Z* X HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);9 Z8 r5 I" H' l, ?
bool Ret=true;
2 E2 [& N5 _6 t: ~ AnsiString Tmp;& ?6 e* S+ t- q# D
if(hFind!=INVALID_HANDLE_VALUE)+ ^+ k& y0 z; {5 M
{
G2 X( w) @3 p- n( e bool bFind=true;$ p7 Y, b9 j0 \2 k
while(bFind)
6 G6 j9 ~# z2 C; v {1 n% O9 J( z) m
if(wfd.cFileName[0]!='.') // . ..
1 m- `! f4 ?+ W' a/ R. q8 |5 ? {" x" q( O6 z' _' ?
Tmp=Dir+wfd.cFileName;6 i" {0 D) l+ I- n, f$ C
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)1 p, I( R: V0 c8 b8 Q& l+ l
{ // 删除所有子目录
# e: H( Z0 x' N7 U0 B Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);: I, \; X- B# [0 a1 {. p
}else
% S% f/ ]3 _! j7 X- m6 ]5 E { // 删除所有文件. I7 f! c& ]% Z9 G& U
SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);
, t/ ^6 M3 ?& x( U/ k% O9 q Ret=Ret&&DeleteFile(Tmp.c_str());
" P2 u) y% f+ u% G }* i1 M9 c! U1 v# {
}
# D/ I9 T$ T& `, B bFind=FindNextFile(hFind,&wfd);4 l' M3 P$ |$ Y1 m1 w. D
}
, d7 l2 \% J/ B9 h" ?" u FindClose(hFind);+ S0 j/ S, k4 {; Y
}
% P! s; R1 `. {7 I' O& C! S if(Ret)return RemoveDirectory(Path);8 z7 @# P' T6 Y- \: }8 n# p0 k
return false;
6 e1 l$ M- k4 `$ @} $ x+ f3 Z* E( u2 r
完
! K% \( P4 ^- T* S7 p! L% I. |! F6 y- c( q( C Y6 Y7 `
|