自动安装程序的实现算法和源代码
李安东
关键字: 自解压 自动安装程序
假如我的程序要调用一个setup.exe程序,自动安装一个软件,完成安装后再把临时文件全部删除,应怎样实现呢?虽然很简单,但有一个问题需要解决,就是如何判断何时已经安装完成了呢?当然可以用
//Wait for until it terminated:
while(GetExitCodeProcess(newinfo.hProcess,&dwExitCode)&&
dwExitCode==STILL_ACTIVE);
来等待setup.exe运行结束,但是问题可能并不这么简单,常常是setup.exe又调用了别的子进程(例如_delis和inst5176什么的),而setup.exe退出后,子进程并未退出,即任务仍未完成。因此这时删除临时文件和文件夹仍然会导致安装失败和删除文件失败。(我判断早期的WinRAR创建的TempMode自解压文件,在启动setup.ex后安装之所以会失败,可能就是因为判断错误,即在未完成安装时就把临时文件删除了。)
这个问题可以按如下方法解决(供参考):
1、用系统函数CreateEvent()创建一个事件hEvent;
2、启动释放在临时目录(比如C:/WINDOWS/TEMP/MYTEMP)下的setup.exe后,然后执行如下语句:
//Wait for the self-extract process exit:
::ResetEvent(hEvent);
while(::WaitForSingleObject(hEvent,500)==WAIT_TIMEOUT)
{ IsExit(); }
即先将事件hEvent复位到无信号状态,并循环调用IsExit()函数;
3、在IsExit()函数中列举系统中所有进程:
(1)、调用系统函数CreateToolhelp32Snapshot()并指定TH32CS_SNAPPROCESS参数,获取一个系统中所有进程的列表(snapshot);
(2)、调用系统函数Process32First()获取第一个进程的信息;
(3)、循环调用系统函数Process32Next()获取其余进程的信息。
上述函数调用中有一个参数lppe是一个PROCESSENTRY32类型的结构。lppe.th32ProcessID参数包含了获取的进程标识符;lppe.szExeFile为该进程的可执行文件路径和名称。
因此在上述处理过程中,每次获取lppe后均判断lppe.szExeFile 中的路径是否是安装程序所在的临时目录,如果不存在这样的进程,则说明安装已经完成,则调用SetEvent()函数,将hEvent事件设置为有信号,从而使第二步中的循环结束;
4、关闭事件句柄,删除安装程序的所有临时文件和文件夹(例如MYTEMP),完成安装。
注意:在调用列举进程的函数时必须添加#include <Tlhelp32.h>指令。
下面是示例代码(已调试通过):
// MySfx.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
// Foward declarations of functions included in this code module:
BOOL InitInstance(HINSTANCE, int);
void RemoveThem(char *strPath);
void IsExit();
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// Perform application initialization:
return InitInstance (hInstance, nCmdShow);
}
//
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
#if defined(_DEBUG)
#define THISFILE_LENGTH 159785
#else
#define THISFILE_LENGTH 28672
#endif
char sPath[256];
HANDLE hEvent;
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
char sModule[256],sTemFile[256];
//Gets temporary directory:
::GetTempPath(255,sPath);
strcat(sPath,"Mytemp");
::CreateDirectory(sPath,NULL);
strcpy(sTemFile,sPath);
strcat(sTemFile,"//Sfx.exe");
::GetModuleFileName(NULL,sModule,255);
//Opens the module file:
HANDLE hFile=::CreateFile(sModule,GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hFile)return 0;
//Creates the temprory file:
HANDLE hFileTemp=::CreateFile(sTemFile,GENERIC_WRITE|GENERIC_READ,
0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hFileTemp)
{
::CloseHandle(hFile); return 0;
}
::SetFilePointer(hFile,THISFILE_LENGTH,NULL,FILE_BEGIN);
//Now begin to read and write:
while(TRUE)
{
BYTE buf[40*1024];
DWORD dwNumberOfBytesRead;
if(::ReadFile(hFile,buf,40*1024,&dwNumberOfBytesRead,NULL)==0)
break;
DWORD dwNumberOfBytesWritten;
if(dwNumberOfBytesRead>0)
if(!::WriteFile(hFileTemp,buf,dwNumberOfBytesRead,
&dwNumberOfBytesWritten,NULL))break;
if(dwNumberOfBytesRead<40*1024)break;
}//while(TRUE)
::CloseHandle(hFile);
::CloseHandle(hFileTemp);
//Prepare to extract files and setup the application:
//Creates a auto-reset event object:
hEvent=::CreateEvent(
NULL, // SD
FALSE, // reset type
FALSE, // initial state
NULL // object name
);
//Executes self-extract file to extract files:
STARTUPINFO info;
PROCESS_INFORMATION newinfo;
::GetStartupInfo(&info);
::CreateProcess(sTemFile,NULL,NULL,NULL,FALSE,
CREATE_DEFAULT_ERROR_MODE,NULL,sPath,&info,&newinfo);
//Wait for the self-extract process exit:
::ResetEvent(hEvent);
while(::WaitForSingleObject(hEvent,500)==WAIT_TIMEOUT)
{
IsExit();
}
//Executes setup:
strcpy(sTemFile,sPath);
strcat(sTemFile,"//Setup.exe");
::CreateProcess(sTemFile,NULL,NULL,NULL,FALSE,
CREATE_DEFAULT_ERROR_MODE|CREATE_NO_WINDOW,
NULL,sPath,&info,&newinfo);
//Wait for setup process and other started by it exit:
::ResetEvent(hEvent);
while(::WaitForSingleObject(hEvent,500)==WAIT_TIMEOUT)
{
IsExit();
}
::CloseHandle(hEvent);
//Remove tempary files and folders:
RemoveThem(sPath);
return FALSE;
}
void RemoveThem(char *strPath)
{
char strTemFile[256];
strcpy(strTemFile,strPath);
strcat(strTemFile,"//*.*");
WIN32_FIND_DATA FindFileData;
HANDLE hFind=FindFirstFile(strTemFile,&FindFileData);
if(hFind!=INVALID_HANDLE_VALUE)
while(TRUE)
{
if(FindFileData.cFileName[0]=='.')
{
if(!FindNextFile(hFind,&FindFileData))break;
continue;
}
strcpy(strTemFile,strPath);
strcat(strTemFile,"//");
strcat(strTemFile,FindFileData.cFileName);
if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
RemoveThem(strTemFile);//recursive call if it's a subdirectory.
else ::DeleteFile(strTemFile);//Delete it if it's a file.
if(!FindNextFile(hFind,&FindFileData))break;
}
::CloseHandle(hFind);
::RemoveDirectory(strPath);
}
void IsExit()
{
//Enumerate current processes:
//This process don't exit until the processes belonged to setup are all terminated:
HANDLE hSnapshot;
PROCESSENTRY32 pe;
pe.dwSize=sizeof(pe);
BOOL blExist=FALSE;
size_t len=strlen(sPath);
hSnapshot=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(hSnapshot<0) goto L1;
if(::Process32First(hSnapshot,&pe)==FALSE)
{
::CloseHandle(hSnapshot); goto L1;
}
if(_strnicmp(sPath,pe.szExeFile,len)==0)
blExist=TRUE;
while(blExist==FALSE && ::Process32Next(hSnapshot,&pe))
{
if(_strnicmp(sPath,pe.szExeFile,len)==0)
{
blExist=TRUE; break;
}
}
::CloseHandle(hSnapshot);
L1: if(blExist==FALSE) ::SetEvent(hEvent);
}
本文的意图不是要开发一个工具软件(因为市面上已有此类工具),其主要目的是想与有兴趣的朋友一起切磋一下实现思路,说不定对某位朋友也许会有一点帮助(如需要完整代码可来信索取)。
*****************************************************************
附注:
1、感谢朋友们的鼓励,因为要源码的朋友较多,现在请您直接到下面去下载源代码:
2、使用Sleep()函数确实更加简便,谢谢高手指点。