分享
 
 
 

实现操作过程提示对话框

王朝厨房·作者佚名  2007-01-04
窄屏简体版  字體: |||超大  

---- 在使用Windows95 进行文件拷贝或者删除操作时,您一定见到过那种具有飞

文件动画的操作过程提示对话框。这一功能的加入不仅使我们能够在操作过程当

中随时取消操作,而且也使文件拷贝或者删除操作变得生动活泼。其实,在使

用Visual C++ 进行应用程序设计时,我们也可以使用下述方法在适当位置加入自

己的操作过程提示对话框。

为每一个操作过程提示对话框创建一个对话框类。为了下面叙述方便,我们

只假设应用程序需要一个操作过程提示对话框并以“CModel”作为对应的对

话框类的名字。

使用Visual C++ 提供的资源编辑器编辑提示对话框,比如加入一些文字说明

和动画等。

在CModel 类的头文件(Model.h) 中,加入两个成员变量,

CWnd* m_pParent; // 指向调用该提示对话框的框架类( 或对话框类),即它

的“父类”int m_nID;// 记录该提示对话框的ID 号

以及下面两个成员函数:

CModel(CWnd* pParent = NULL); // 舍弃原有的构造函数,或者把原函数

修改成这种无模式对话框的构造函数

BOOL Create(); // 该函数将调用创建基类的Create() 函数创建对话框

在Model.cpp 文件中,加入相应函数的实现部分。

CModel::CModel(CWnd* pParent /*=NULL*/)

: CDialog(CModel::IDD, pParent)

{

m_pParent=pParent;

m_nID=CModel::IDD;

//{{AFX_DATA_INIT(CModel)

// NOTE: the ClassWizard will add member initialization here

//}}AFX_DATA_INIT

}

BOOL CModel::Create()

{

return CDialog::Create(m_nID,m_pParent);

}

同时按下Ctrl 和W 键或直接单击工具条上的ClassWizard 按钮,打

开ClassWizard 对话框。在类名(Class name) 列表框中选择该提示对话框类,

在Object IDs 列表框中选择该类的类名后,在消息(Messages) 列表框中选

择PostNcDestroy 消息并双击它,这时ClassWizard 就会在该对话框类中加入

一个PostNcDestroy() 函数。该函数将会在对话框窗口消失后,

由OnNcDestroy() 函数调用。因此,可以在该函数中加入一些扫尾工作,例如

数据传送,释放指针空间等。

void CModel::PostNcDestroy()

{

// TODO: Add your specialized code here and/or call the base class

delete this;

CDialog::PostNcDestroy();

}

在要调用提示对话框类的类的头文件中,先包含(#include)CModel 类的头文

件,再声明一个指向CModel 类的对象的指针,如m_Dlg,并在该类的构造函数

中,加入“m_Dlg = NULL;”一句。然后,在打开和关闭提示对话框的函数中加

入如下一段程序:

if (m_Dlg==NULL) {//如果当前没用提示对话框在活动,就创建一个

m_Dlg = new CModel(this);

m_Dlg->Create();

GetDlgItem(IDC_EXPORT)->EnableWindow(FALSE);

}

else//否则就激活它

m_Dlg->SetActiveWindow();

另外,再在要关闭提示对话框的地方,加入如下语句:

m_Dlg->DestroyWindow();

m_Dlg=NULL;

---- 至此,您已经拥有了自己的过程操作提示对话框。不过,它还不具有动画和

随时取消操作的功能。您不妨尝试着加入这些功能。另外,笔者也曾尝试过用下面

介绍的方法实现过程操作提示对话框。两种方法比较,可谓各有千秋。如果您希望

上面设计的过程提示对话框能够被多个应用程序共享,那么最好把提示对话框作

为独立的进程来调用。但是,当您还希望在提示对话框与调用者之间传输数据的

话,似乎这一部分介绍的实现方法更简洁且更有效。

应用进程实现对其他应用程序的调用

---- 在我们设计的应用程序中,很可能会用到其他应用程序来完成某一特定功

能。例如,当我们为了便于数据的传输而对诸多文件进行压缩和解压缩时,一种作

法是我们自己设计一个这样的压缩/ 解压缩程序,然后以动态链接库(DLL) 或者函

数库的形式由主应用程序调用。但更方便而且高效的作法是利用现有的这方面的

优秀软件,比如ARJ.EXE,并以进程的形式调用它,再在适当时候关闭它。下面将以

上面所述为例,具体介绍后一种方法的实现过程。

在需要调用ARJ.EXE 进行压缩/ 解压缩的类中,创建一个成员函数,不妨称

作CreateBat(),其作用是生成一个批处理文件。由该批处理文件调

用ARJ.EXE,并给出具体压缩/ 解压缩参数。之后,再利用MS-DOS 的DIR 命令生

成一个临时文件,以作为压缩/ 解压缩工作完成的标志。

void CMyCompress:: CreateBat(CString BatPath,CString ArjPath,

CString BatName,CString ArjFileName,

CString TempPath,CString ExitFlag,BOOL out)

{

LPTSTR lpBuffer;

UINT uSize;

HANDLE hHeap;

uSize=(GetCurrentDirectory(0,NULL))*sizeof(TCHAR);

hHeap=GetProcessHeap();

lpBuffer=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,uSize);

GetCurrentDirectory(uSize,lpBuffer);

//得知当前目录信息,以便根据需要变换目录

if (lpBuffer!=BatPath) //diferent dir

SetCurrentDirectory(BatPath);

CStdioFile f;

CFileException e;

if (!f.Open( BatName, CFile::modeCreate|CFile::modeWrite, &e))

//以BatName的内容创建一个批处理文件

{

AfxMessageBox("不能创建文件"+BatName);

return ;

}

char density[6];

sprintf(density,"%d",mTotalBytes);

---- //mTotalBytes 是由其他函数设定的变量,用于记录用于拷入或拷出文件

的磁盘所具有的最大可用空间

CString Density=density;

CString string;

if (out)//说明是生成做压缩工作的批处理文件

string="arj a -v"+Density;

else //说明是生成做解压缩工作的批处理文件

string="arj e -v"+Density;

string+=" ..\\"+ArjPath+"\\"+ArjFileName+" ";

if (out)

string=string+"..\\"+TempPath+"\\*.* -y -jm\n";

else

string=string+"..\\"+TempPath+"\\ -y -jm\n";

f.WriteString(string);

string="dir >"+ExitFlag+"\n";

f.WriteString(string);

f.Close();

SetCurrentDirectory(lpBuffer);//回复到原来的目录下

}

---- 该函数执行后,将生成一个批处理文件,内容大致是:

---- ARJ A -V1440 压缩后文件的路径名+ 文件名被压缩文件的路径名+ 文件名

-Y -JM

---- DIR > 临时文件名

---- 或者是:

---- ARJ E -V1440 被解压缩文件的路径名+ 文件名解压缩后文件的路径名+ 文

件名-Y -JM

---- DIR > 临时文件名

在需要调用ARJ.EXE 进行压缩/ 解压缩的类中,再创建一个成员函数,不妨称

作RunBat(),其作用是创建和执行进程来运行上述所生成的批处理文件,并

在适当时候撤消进程。

void CMyCompress::RunBat(CString

BatPath,CString fileName,CString ExitFlag)

{

CString lpApplicationName=BatPath+"\\"+fileName;

// 进程执行的应用程序的完全路径名

STARTUPINFO StartupInfo;// 创建进程所需的信息结构变量

GetStartupInfo(&StartupInfo);

StartupInfo.lpReserved=NULL;

StartupInfo.lpDesktop=NULL;

StartupInfo.lpTitle=NULL;

StartupInfo.dwX=0;

StartupInfo.dwY=0;

StartupInfo.dwXSize=200;

StartupInfo.dwYSize=300;

StartupInfo.dwXCountChars=500;

StartupInfo.dwYCountChars=500;

StartupInfo.dwFlags=STARTF_USESHOWWINDOW;

StartupInfo.wShowWindow=SW_HIDE;

// 说明进程将以隐藏的方式在后台执行

StartupInfo.cbReserved2=0;

StartupInfo.lpReserved2=NULL;

StartupInfo.hStdInput=stdin;

StartupInfo.hStdOutput=stdout;

StartupInfo.hStdError=stderr;

LPTSTR lpBuffer;

UINT uSize;

HANDLE hHeap;

uSize=(GetCurrentDirectory(0,NULL))*sizeof(TCHAR);

hHeap=GetProcessHeap();

lpBuffer=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,uSize);

GetCurrentDirectory(uSize,lpBuffer);

// 得知当前目录信息,以便根据需要变换目录

if (lpBuffer!=BatPath) //diferent dir

SetCurrentDirectory(BatPath);

// 创建进程

if (CreateProcess(lpApplicationName,NULL,NULL,

NULL,FALSE,CREATE_DEFAULT_ERROR_MODE,

NULL,NULL,&StartupInfo,&pro_info))

{

MSG Message;

DeleteFile(ExitFlag);

SetTimer(1,100,NULL);// 设置计时器

Search=TRUE;

while(Search) {

if (::PeekMessage(&Message,NULL,0,0,PM_REMOVE)) {

::TranslateMessage(&Message);

::DispatchMessage(&Message);

}

}

// 进程结束前后的处理工作

DWORDExitCode;

if (!GetExitCodeProcess(pro_info.hProcess,&ExitCode))

AfxMessageBox("GetExitCodeProcess is Failed!");

if (!TerminateProcess(pro_info.hProcess,(UINT)ExitCode))

// 终止进程

AfxMessageBox("TerminateProcess is Failed!");

if (!CloseHandle(pro_info.hProcess))

// 释放被终止进程的句柄

AfxMessageBox("CloseHandle is Failed!");

KillTimer(1);// 撤消计时器

}

else AfxMessageBox("Process Is Not Created!");

SetCurrentDirectory(lpBuffer);// 回复到原来的目录下

}

同时按下Ctrl 和W 键或直接单击工具条上的ClassWizard 按钮,打

开ClassWizard 对话框。在类名(Class name) 列表框中选择需要调用ARJ.EXE

进行压缩/ 解压缩的类,在Object IDs 列表框中选择该类的类名后,在消

息(Messages) 列表框中选择WM_TIMER 消息并双击它,这时ClassWizard 就会

在该类中加入一个OnTimer() 函数。该函数将以一定的时间间隔检查压缩/ 解

压缩程序是否已经执行完毕,即检查作为标志的临时文件是否已经存在,并

及时修改状态变量“Search”,以便通知RunBat() 函数结束进程。

void CMyCompress::OnTimer(UINT nIDEvent)

{

// TODO: Add your message handler code here and/or call default

CFile file;

CFileException Error;

if (file.Open(ExitFlag,CFile::modeRead,&Error)) {

Search=FALSE;

file.Close();

}

}

自编删除目录及其下属文件的函数

---- 高版本的MS-DOS 和Windows 95 都提供了一个可以删除一个或多个目录及其

下属文件和目录的命令,即DeleteTree 命令。然而,无论在MFC 类库还是在Win32 函

数库中,都没有相应的函数与之对应。这样,当我们在自己设计的应用程序中需要

用到DeleteTree 的功能时,自然想到的方法是通过进程调用或者系统调用的方

式( 正如上面部分所述的那样) 调用MD-DOS 或Windows 95 下的DeleteTree 命令。然

而,Win32 函数库已经为我们提供了多种用于文件和目录操作的函数,利用它们不

难设计出自己的DeleteTree() 函数。

---- 读者读到这里,也许会感到有些疑惑,为什么第六部分强调进程调用优于自

我设计的函数,而这一部分又反了过来?是的,在通常情况下,调用应用程序内部

的函数比使用进程或者调用外部函数更灵活并且可以提高执行效率,也便于修

改。所以,象DeleteTree() 这样的功能,利用现有的函数并不难实现,自然就最好

通过内部函数的方式来完成。然而,象设计一个压缩/ 解压缩这样的函数的工作

量,并不比通过进程调用来使用现成品的开销更合算,因为它至少需要我们了解

压缩/ 解压缩的复杂算法,而且调试和维护它也需要一定代价。于是,这个时候,还

是采用“拿来主义”为好。

---- 下面,给出我自己设计的DeleteTree() 函数,仅供参考。

BOOL DeleteTree(CString DirName)

{ //成功:返回TRUE;否则,返回FALSE

BOOL Result;

Result=PreRemoveDirectory(DirName)

&& RemoveDirectory(DirName);

return Result;

}

BOOL PreRemoveDirectory(CString DirName)

{//成功:返回TRUE;否则,返回FALSE

LPTSTR lpBuffer;

UINT uSize;

CString fileName;

HANDLE hHeap;

BOOL result;

HANDLE hFindFile;

WIN32_FIND_DATA FindFileData;

uSize=(GetCurrentDirectory(0,NULL))*sizeof(TCHAR);

hHeap=GetProcessHeap();

lpBuffer=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,uSize);

GetCurrentDirectory(uSize,lpBuffer);

if (lpBuffer!=DirName) {//调整当前目录

SetCurrentDirectory(DirName);

}

hFindFile=FindFirstFile("*.*",&FindFileData);

CString tFile;

if (hFindFile!=INVALID_HANDLE_VALUE) {

do {

tFile=FindFileData.cFileName;

if ((tFile==".")||(tFile=="..")) continue;

if (FindFileData.dwFileAttributes==

FILE_ATTRIBUTE_DIRECTORY){

if (DirName[DirName.GetLength()-1]!=’\\’)

PreRemoveDirectory(DirName+’\\’+tFile);

else

PreRemoveDirectory(DirName+tFile);

if (!RemoveDirectory(tFile))

result=FALSE;

else

result=TRUE;

}

else

if (!DeleteFile(tFile)) result=FALSE;

else result=TRUE;

}

while (FindNextFile(hFindFile,&FindFileData));

FindClose(hFindFile);

}

else {

SetCurrentDirectory(lpBuffer);

return FALSE;

}

SetCurrentDirectory(lpBuffer); //回复到原来的目录下

return result;

}

如何得到并修改各驱动器的信息

---- 在设计和文件输入/ 输出有关的应用程序时,我们很可能在输入/ 输出文件

前,需要了解一下源驱动器或者目标驱动器的各项信息,比如是否有磁盘在软驱

中,它是否已打开写保护,以及现有磁盘的容量等。遗憾的是,MFC 类库中没有提供

支持这些功能的类,所以我们只能通过Win32 提供的函数来完成我们的要求。下

面,我根据自己的编程实践,通过几段程序,来说明如何利用Win32 提供的函数实

现对驱动器的操作。读者可以根据自己的需要,把介绍的函数稍加修改后,即可插

入到自己设计的应用程序中去。

下面程序的功能是搜索计算机中所有驱动器,选择出其中软盘驱动器的驱动

器号,依次加入到一个下拉列表框中。

void FindDriverInfo()

{

CComboBox* Driver=(CComboBox*)GetDlgItem(IDC_DRIVER);

DWORD dwNumBytesForDriveStrings;

HANDLE hHeap;

LPSTR lp;

CString strLogdrive;

int nNumDrives=0, nDriveNum;

dwNumBytesForDriveStrings=GetLogicalDriveStrings(0,NULL)

*sizeof(TCHAR);//实际存储驱动器号的字符串长度

if (dwNumBytesForDriveStrings!=0) {

hHeap=GetProcessHeap();

lp=(LPSTR)HeapAlloc(hHeap,HEAP_ZERO_MEMORY,

dwNumBytesForDriveStrings);//

GetLogicalDriveStrings(HeapSize(hHeap,0,lp),lp);

StringBox.SetSize(dwNumBytesForDriveStrings/sizeof(TCHAR)+1);

while (*lp!=0) {

if (GetDriveType(lp)==DRIVE_REMOVABLE){

Driver->AddString(lp);

StringBox[nNumDrives]=lp;

nNumDrives++;

}

lp=_tcschr(lp,0)+1;

}

}

else AfxMessageBox("Can’t Use The Function GetLogicalDriveStrings!");

}

下面介绍的EmptyDiskSpace() 函数主要负责清空指定驱动器中的磁盘,同时

它还负责记录指定驱动器中磁盘的容量,并得到该磁盘的序列号。在该函数

中,还将调用第七部分提到的PreRemoveDirectory() 函数,来完成清空工作。

BOOL EmptyDiskSpace(CString Driver)

{

BOOL result=TRUE;

DWORDSectorsPerCluster; // address of sectors per cluster

DWORDBytesPerSector; // address of bytes per sector

DWORDNumberOfFreeClusters; // address of number of free clusters

DWORDTotalNumberOfClusters;

DWORDTotalBytes;

DWORDFreeBytes;

int bContinue=1;

char DiskVolumeSerialNumber[30];

//存储驱动器内当前磁盘的序列号

LPCTSTRlpRootPathName;

// address of root directory of the file system

LPTSTRlpVolumeNameBuffer=new char[12];

// address of name of the volume

DWORDnVolumeNameSize=12;

// length of lpVolumeNameBuffer

DWORD VolumeSerialNumber;

// address of volume serial number

DWORD MaximumComponentLength;

// address of system’s maximum filename length

DWORD FileSystemFlags;

// address of file system flags

LPTSTRlpFileSystemNameBuffer=new char[10];

// address of name of file system

DWORDnFileSystemNameSize=10;

// length of lpFileSystemNameBuffer

lpRootPathName=Driver;

while (1){

if (GetDiskFreeSpace(Driver, &SectorsPerCluster,

&BytesPerSector, &NumberOfFreeClusters,

&TotalNumberOfClusters))

{//驱动器中有磁盘

TotalBytes=SectorsPerCluster*BytesPerSector

*TotalNumberOfClusters;//磁盘总容量

FreeBytes=SectorsPerCluster*BytesPerSector

*NumberOfFreeClusters;//磁盘空闲空间容量

GetVolumeInformation(lpRootPathName,

lpVolumeNameBuffer, nVolumeNameSize,

&VolumeSerialNumber,

&MaximumComponentLength,

&FileSystemFlags,

lpFileSystemNameBuffer, nFileSystemNameSize);

sprintf(DiskVolumeSerialNumber,"%X",VolumeSerialNumber);

//得到驱动器内当前磁盘的序列号

SetmTotalBytes(TotalBytes/1024);//存储指定驱动器中磁盘的容量

if (TotalBytes!=FreeBytes){//当磁盘总容量不等于空闲空间容量时,

应该执行清空操作

while (bContinue) {

if ((bContinue==2)||(MessageBox

("在驱动器"+m_Driver+"中的磁盘尚存有数据.

\n您愿意让系统为您删除它们吗?",

"提问",MB_YESNO|MB_ICONQUESTION)==IDYES))

if (!PreRemoveDirectory(Driver))//无法执行清空操作

if (MessageBox("因某种原因系统无法删除

在驱动器"+m_Driver+"中的磁盘上的数据.

\n请检查磁盘是否没有关闭写保护.

\n您愿意再试一次吗?",

"问题",MB_YESNO|MB_ICONERROR)==IDYES) {

bContinue=2;

continue;

}

else {

bContinue=0;

result=FALSE;

}

else {

MessageBox("成功删除磁盘上的数据!",

"提示信息",MB_OK|MB_ICONINFORMATION);

bContinue=0;

result=TRUE;

}

else {//THE FIRST IF’S ELSE

bContinue=0;

result=FALSE;

}

}

}

else result=TRUE;

break;

}

else {

if (MessageBox("没有磁盘在驱动器"+m_Driver+"中.

\n您愿意插入一张磁盘再来一次吗?",

"问题",MB_YESNO|MB_ICONASTERISK)==IDYES) continue;

else break;

}

}//END OF WHILE

return result;

}

在MS-DOS 和Windows95 中,磁盘卷标最多由11 个字符组成,并且字母的大小写

不加区分。当需要设定指定驱动器中磁盘的卷标时,只要调用Win32

的SetVolumeLabel() 函数即可,并在第一个参数中指明磁盘所在的驱动器

号,在第二个参数中指明新的卷标号。例如,SetVolumeLabel(DriverNum,

NewVolumeLabel)。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有