InstallShield安装新的打包程序时,默认会提示下面的对话框,大意是说已经安装了产品的另一个版本,要用户手动去卸载老版本,不是很友好。
下面是对InstallShield在安装新版本时自动卸载老版本的一些尝试,主要是Install Script程序的编写。
1.首先要将Installation Designer–》Upgrades–》Prepare Setup For Upgrade Scenarios–》Upgrade Windows Installer Setup–》common下的Small/Minor Upgrade Settings选择为Don’t prompt user,just install the upgrade。这样不会出现上面的提示框,继续下面的卸载和安装。
2.下一步是要对OnResumeUI事件进行响应,主要是OnResumeUIBefore和OnResumeUIAfter。
3.在OnResumeUIBefore中完成老版本的卸载,脚本如下:
//---------------------------------------------------------------------------
// OnResumeUIBefore
//
// The OnResumeUIBefore event is called when end user runs installation that
// is performing a resumed install. Usually this happens by specifying
// a property like REINSTALL or ADDLOCAL at the command line when the
// product is already installed. After this function returns,
// ComponentTransferData is called to perform file transfer.
//---------------------------------------------------------------------------
function OnResumeUIBefore()
string szfilename, szmsg1, szmsg2;
int nresult;
STRING szDir, szIcon;
begin
nresult = MessageBox("新版本安装之前将卸载旧版本,是否继续?", MB_YESNO);
if(nresult = IDNO)then
abort;
endif;
szfilename = UNINSTALL_STRING +" /UNINSTALL /hide_progress";
nresult = StrFind(szfilename,".exe");
if nresult >=0 then
StrSub(szmsg1,szfilename,0,nresult + 4);
StrSub(szmsg2,szfilename,nresult + 4,200);
LongPathToQuote(szmsg1, FALSE );
LongPathToQuote(szmsg2, FALSE );
//szfilename = "\"" + szmsg1 + "\"" +szmsg2;
endif;
if(LaunchAppAndWait(szmsg1, szmsg2, LAAW_OPTION_WAIT_INCL_CHILD | LAAW_OPTION_WAIT) < 0 )then
MessageBox("旧版本卸载失败", MB_OK);
abort;
else
WaitForApplication(LAAW_PROCESS_INFORMATION.hProcess, LAAW_PROCESS_INFORMATION.dwProcessId, INFINITE, LAAW_OPTION_WAIT_INCL_CHILD | LAAW_OPTION_WAIT);
endif;
szIcon = DesktopFolder;
DeleteFolderIcon(szIcon, "AliProbe");
szDir = INSTALLDIR + "AliProbe V1.1";
DeleteDir(szDir, ALLCONTENTS);
end;
需要注意的地方是,在调用LaunchAppAndWait方法等待老版本卸载完成的时候,一定要加上LAAW_OPTION_WAIT_INCL_CHILD ,主要是因为卸载的过程是Windows Install启动卸载程序,所以一定要等待Windows Install的子进程结束,否则将不能等待。
4.在OnResumeUIAfter中完成新版本的安装,脚本如下:
//---------------------------------------------------------------------------
// OnResumeUIAfter
//
// The OnResumeUIBefore event is called when end user runs installation that is
// performing a resumed install. Usually this happens by specifying a property
// like REINSTALL or ADDLOCAL at the command line when the product is already
// installed. In the handler, installation usually displays UI that will
// inform end user that Reinstallation has been completed successfully.
//---------------------------------------------------------------------------
function OnResumeUIAfter()
STRING noUse;
NUMBER szProcessId;
NUMBER nvProcessHandle; // Handle to a process
NUMBER nvModuleHandle; // Handle to a process module
STRING szModuleName; // Module filename
NUMBER nvBytesRequired;
begin
szProcessId = GetCurrentProcessId();
if szProcessId != 0 then
if ProcessRunning(noUse, szProcessId, szModuleName) = TRUE then
LaunchApp(szModuleName, "/hide_progress");
else
MessageBox("自动安装新版本遇到问题,请再次运行安装文件完成安装。", MB_OK);
endif;
else
MessageBox("自动安装新版本遇到问题,请再次运行安装文件完成安装。", MB_OK);
endif;
end;
这里是通过GetCurrentProcessId得到当前进程的Id,并调用ProcessRunning获取当前进程模块的完整路径。
5.ProcessRunning的脚本如下:
//
//
// Function: _Process_Running
//
// Description: Determines if the specified process is running in memory.
//
// Parameters: szAppName - Name of the application to check.
// nvFindProcessId - ID of the process to find.
// szFindModName - Name of the Module of the found process
//
// Returns: TRUE - The process is running.
// FALSE - The process is not running.
//
//
function BOOL ProcessRunning(szAppName, nvFindProcessId, szFindModName)
BOOL bvRunning; // Process is running
NUMBER nvProcessIDs(512); // Array of process IDs
NUMBER nvBytesReturned; // Number of bytes returned in process ID array
NUMBER nvProcesses; // Number of processes running
NUMBER nvIndex; // Loop index
NUMBER nvProcessHandle; // Handle to a process
NUMBER nvModuleHandle; // Handle to a process module
NUMBER nvBytesRequired; // Number of bytes required to store values
POINTER pvProcessIDs; // Pointer to process ID array
STRING svModuleName; // Module name
STRING svFileName; // Module filename
begin
// The psapi.dll reads the Windows NT performance database. The DLL
// is part of the Win32 SDK.
if UseDLL(WINSYSDIR ^ PSAPI_FILE) < 0 then
// Could not load psapi.dll.
MessageBox("ERROR: Could not load [" + WINSYSDIR ^ PSAPI_FILE +
"].", SEVERE);
return FALSE;
endif;
// Get the PIDs of all currently running processes.
pvProcessIDs = ArrayToPointer(nvProcessIDs);
EnumProcesses(pvProcessIDs, 512, nvBytesReturned);
// Determine the number of process IDs retrieved. Each process ID
// is PROCESSID_LENGTH bytes.
nvProcesses = nvBytesReturned / PROCESSID_LENGTH;
// Get the executable associated with each process, and check if
// its filename matches the one passed to the function.
for nvIndex = 1 to nvProcesses
// Get a handle to the process.
nvProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, 0, nvProcessIDs(nvIndex));
if nvProcessHandle != 0 then
// Get a handle to the first module in the process, which
// should be the executable.
if EnumProcessModules(nvProcessHandle, nvModuleHandle,
PROCESSID_LENGTH, nvBytesRequired) != 0 then
// Get the path of the module.
if GetModuleFileNameExA(nvProcessHandle, nvModuleHandle,
svModuleName, SizeOf(svModuleName)) != 0 then
// Extract the filename (without an extension) from
// the path.
//Add by QianShi at 2010.6.23
//Get module name by process id.
if nvProcessIDs(nvIndex) = nvFindProcessId then
szFindModName = svModuleName;
bvRunning = TRUE;
goto ProcessRunningEnd;
endif;
ParsePath(svFileName, svModuleName, FILENAME_ONLY);
if StrCompare(svFileName, szAppName) = 0 then
// The process module matches the application
// name passed to the function.
bvRunning = TRUE;
goto ProcessRunningEnd;
endif;
endif;
endif;
endif;
endfor;
ProcessRunningEnd:
if UnUseDLL(PSAPI_FILE) < 0 then
MessageBox("ERROR: Could not unload [" + WINSYSDIR ^ PSAPI_FILE +
"].", SEVERE);
return FALSE;
endif;
return bvRunning;
end;
程序的功能主要是枚举当前的进程Id,并通过Id匹配来得到Id对应的Module name,本函数同样可以判断某个name的进程是否正在运行。
以上就是本人对用IS脚本完成安装新版本前卸载老版本的方法, 如果大家有其他好的方法,请多多指教。