1. 程式人生 > >InstallShield安裝新版本時解除安裝老版本

InstallShield安裝新版本時解除安裝老版本

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指令碼完成安裝新版本前解除安裝老版本的方法, 如果大家有其他好的方法,請多多指教。