ASP.NET完整打包卸載更新攻略(By Installshield 2010)


-

前言

前陣子做了一個有關Installshield的OA 打包安裝程序,用的版本Installshield 2010-Premier,具體功能的內容如下:

1、OA采用的是asp.net(C#)開發

2、動態發布到IIS虛擬目錄(采用自定義對話框)

3、附加,分離,刪除數據庫

4、動態修改web.config

5、完美卸載

6、更新包制作

【安裝】首先准備一個發布好的aspnet網站,然后在web.config插入標簽,在app_data文件夾放入數據庫文件。安裝的時候會自動把文件copy到目標機器,在使用dos命令將app_data里面的文件附加到數據庫,根據用戶填寫的數據庫信息替換web.config的標簽……

【更新】更新包的思路也很簡單,在安裝的時候會把用戶填寫的數據庫信息存到注冊表(數據庫服務器,用戶名,密碼,虛擬目錄,安裝路徑),用了這些信息,那么更新的時候直接把文件copy到用戶安裝時選擇的路徑就可以了,如果有數據庫相關的更新,則可以使用dos命令執行數據庫文件(.sql),如果有web.config的更新,則再一次動態替換web.config的標簽即可。

【卸載】網站的卸載就是刪除文件,分離和刪除數據庫,刪除注冊表相關鍵值,刪除虛擬目錄

接下來讓我們一步一步來實現,篇幅可能有點長,請大家 pay patience,Let’s go

一、新建項目

 

選擇All Types下面的InstallScript MSI Project,填寫產品名稱,保存路徑,點擊OK

 


點擊OK后出來這個界面Project Assistant 項目助手,點擊進去可看到有些簡單描述項目的選項

Installation Designer安裝設計,點擊進去可看到產品的信息,安裝腳本,安裝界面等 

切換到Installation  Designer可看到以上界面。

二、填寫產品信息

填寫產品的基本信息(包括產品名稱,安裝語言,產品的安裝版本,產品編碼……)

三、選擇文件源

 

選擇文件源,DefaultComponents下面的files點擊右鍵,選擇Dynamic File Linking選擇文件源(將文件源填充到components,多個components組成一個features)

點擊 New Link彈出Dynamic File Link Settings對話框,點擊Browse選擇文件夾,然后點擊OK,在點擊左邊對話框的確定,則完成文件源的設置

 

定位到Setup Design選項,可看到右邊窗口有DefaultFeatureDefaultComponents

1Feature(功能)可以拆分為多個Components(組件),1N

1Components可以綁定一個文件夾或者文件,1N
 

Defaultfeature右鍵選擇Associate Components,彈出Component的列表,選擇然后點擊OK按鈕則可以將該components添加到feature下面。

四、設置文件夾權限

功能Feature關聯完Component后則可以在Application Data下面的files and folders看到關聯過來的文件夾信息,可以對其進行局部調整。也可以對文件夾進行權限控制,權限設置如下:

選中文件夾,右鍵點擊Properties屬性,則彈出以下屬性窗口

點擊Permissions彈出以下界面

在空白處點擊右鍵選擇New,則彈出以下界面

設置文件夾的權限,點擊OK完成 

 

五、Installshield Script

默認的腳本沒有任何東西,只有一句 #include "ifx.h",必須點擊右邊的安裝函數才出來腳本。

InstallScript腳本的語法類似於C,也類似於VBScript,可以調用VB的代碼。也可以調用dos命令,也可以調用exe

選擇 Before Move Data 階段的函數 OnFirstUIBefore,可出來安裝過程中對話框的代碼,代碼如下:

function OnFirstUIBefore()
    NUMBER nResult, nSetupType, nvSize, nUser;
    STRING szTitle, szMsg, szQuestion, svName, svCompany, szFile;
    STRING szLicenseFile;
    BOOL bCustom, bIgnore1, bIgnore2;
begin    
     //  TO DO: if you want to enable background, window title, and caption bar title                                                                   
    
//  SetTitle( @PRODUCT_NAME, 24, WHITE );                                        
    
//  SetTitle( @PRODUCT_NAME, 0, BACKGROUNDCAPTION );                       
    
//  Enable( FULLWINDOWMODE );                           
    
//  Enable( BACKGROUND );                              
    
//  SetColor(BACKGROUND,RGB (0, 128, 128));                       

    
//  Added in InstallShield 15 - Show an appropriate error message if
    
//  -removeonly is specified and the product is not installed.
     if( REMOVEONLY ) then
        Disable( DIALOGCACHE );
        szMsg = SdLoadString( IDS_IFX_ERROR_PRODUCT_NOT_INSTALLED_UNINST );
           SdSubstituteProductInfo( szMsg );
        MessageBox( szMsg, SEVERE );
        abort;
    endif;
    
    nSetupType = TYPICAL;    

Dlg_SdWelcome:
    szTitle =  "";
    szMsg   =  "";
    nResult = SdWelcome(szTitle, szMsg);
     if (nResult = BACK)  goto Dlg_SdWelcome;
    
    szTitle   =  "";
    svName    =  "";
    svCompany =  "";

Dlg_SdRegisterUser:
    szMsg =  "";
    szTitle =  "";
    nResult = SdRegisterUser( szTitle, szMsg, svName, svCompany );
     if (nResult = BACK)  goto Dlg_SdWelcome;

Dlg_SetupType:
    szTitle =  "";
    szMsg   =  "";
    nResult = SetupType2(szTitle, szMsg,  "", nSetupType,  0);
     if (nResult = BACK) then
         goto Dlg_SdRegisterUser;
     else
        nSetupType = nResult;
         if (nSetupType != CUSTOM) then
            nvSize =  0;
            FeatureCompareSizeRequired(MEDIA, INSTALLDIR, nvSize);
             if (nvSize !=  0) then      
                MessageBox(szSdStr_NotEnoughSpace, WARNING);
                 goto Dlg_SetupType;
            endif;
            bCustom = FALSE;
             goto Dlg_SQL;
         else
            bCustom = TRUE;
        endif;
    endif;    

Dlg_SdAskDestPath:        
    nResult = SdAskDestPath(szTitle, szMsg, INSTALLDIR,  0);
     if (nResult = BACK)  goto Dlg_SetupType;

Dlg_SdFeatureTree: 
    szTitle    =  "";
    szMsg      =  "";
     if (nSetupType = CUSTOM) then
        nResult = SdFeatureTree(szTitle, szMsg, INSTALLDIR,  ""2);
         if (nResult = BACK)  goto Dlg_SdAskDestPath;  
    endif;

Dlg_SQL:
    nResult = OnSQLLogin( nResult );
     if( nResult = BACK ) then
         if (!bCustom) then
             goto Dlg_SetupType;    
         else
             goto Dlg_SdFeatureTree;
        endif;
    endif;

Dlg_SdStartCopy:
    szTitle =  "";
    szMsg   =  "";
    nResult = SdStartCopy2( szTitle, szMsg );            
    
     if (nResult = BACK) then
        goto Dlg_SQL;;
    endif;

     //  Added in IS 2009 - Set appropriate StatusEx static text.
    SetStatusExStaticText( SdLoadString( IDS_IFX_STATUSEX_STATICTEXT_FIRSTUI ) );

     //  setup default status
    Enable(STATUSEX);
 
     return  0;
end;


Dlg_SdWelcome:    歡迎對話框
Dlg_SdRegisterUser   注冊用戶對話框
Dlg_SetupType       安裝類型對話框
Dlg_SdAskDestPath 選擇安裝目錄對話框
Dlg_SdFeatureTree        功能樹對話框
Dlg_SQL   sql相關對話框
Dlg_SdStartCopy 復制文件對話框
這幾個是系統默認的對話框,所有對話框的生命周期基於Setup.rul腳本,也就是說需要在Setup.Rul里面設置對話框的相關腳本信息和調用對話框的構造函數。
系統默認的對話框腳本都包含在#include "ifx.h"頭文件里面,如果是自定義的對話框則【后面會提到】需要引用相關對話框的腳本。

 

若要引用其他的對話框,則要從dialog source里面調出對話框函數

 

六、Dialog對話框

對話框選項位於User Interface(用戶體驗,簡稱UI)下面的Dialog(對話框)選項


鼠標懸停在對話框名稱,右鍵,選擇Edit,可看到對話框的相關信息(布局,控件,屬性……Control Identifier是唯一標識列),可以修改對話框的布局和信息。

Skin則是對話框的皮膚,選中皮膚,點擊Select應用該皮膚。

七、一個完整的ASP.NET打包程序

1、前言

在了解了Installshield 2010 的一些基本設置和熟悉操作界面后,給大家演示一個完整的ASP.NET打包程序,ASP.NET的安裝與部署比較簡單,主要是把網站發布到IIS,附加數據庫,配置數據庫信息(包括數據庫用戶,密碼,服務器),修改web.config配置文件。主要功能有:

     手動選擇安裝目錄

     創建和設置IIS虛擬目錄

     動態附加分離數據庫

     自動修改配置文件

     完美卸載

2、創建IIS虛擬目錄

2.1、自定義創建虛擬目錄對話框

由於Installshield自身沒有操作IIS的功能,那么就要借助外部程序或者windowsAPI,用程序配置 IIS 所用到的“技術”無非是 ADSI 或者 WMI 提供的組件服務程序。可以通過 Windows Host Script 來執行 JScript 或者 VBScript 腳本,也可以在 VB/Delphi 這類快速開發工具開發程序來調用,甚至可以通過瀏覽器中運行的 JavaScript/JScript/VBScript 以及 IIS 運行的 ASP 來調用。因為支持 IDispatch 接口,所以可以后期綁定地通過 CreateObject 或者 GetObject 方式來獲取 ADSI/WMI 的特定接口。那么我們這里就簡單地利用adsi來操作IIS

由於Installshield自身沒有創建虛擬目錄的窗口,那么我們就簡單的自己做一個自定義的窗口,窗口很簡單,就只有一個文本框,用於輸入虛擬目錄的名稱。制作過程如下:

首先先All Dialogs那里右鍵,彈出菜單,選擇New Dialog

新建對話框向導

對話框有多種類型:

Blank Dialog 空對話框,該對話框什么都沒有,連上一步,下一步的按鈕都沒有

NewScriptBasedDialog 普通基於腳本的對話框,帶基本按鈕

NewSkinnableDialog 帶皮膚功能的對話框,帶基本按鈕

如果彈出沖突頁面,直接點擊SkipAll就行了。

添加完皮膚對話框后,界面如上,現在就可以對對話框進行編輯,修改對話框標題,按鈕的文字,字體大小,擺置方式等。最重要的是甚至對話框的Resource identifier,這是對話框的唯一標識列。

那么現在對話框已經添加完成了,那么如何在安裝的過程中顯示該對話框呢? 

每個對話框都有一個構造函數,那么只有調用該對話框的構造函數就行了,接下來請看怎么編寫對話框的構造函數(詳情按F1)。
 

DefineDialog ( szDialogName, hInstance, szDLLName,nDialogID, szDialogID, nReserved, hwndOwner, lMsgLevel ); 這個函數里,最主要的參數就是第四個nDialogID(對話框ID),也就是對話框Resource identifier屬性的值。那么對話框構造函數就可以這樣寫:

    szDialogName =  " SelectVirDialog ";
    hInstance  =  0;
    szDLLName  =  "";
    nSdDialog  = "13001"
    szDialog   =  ""
    hwndParent =  0
    nResult  = DefineDialog (szDialogName, hInstance, szDLLName, nSdDialog, szDialog, 
                             hwndParent, HWND_INSTALL, 
                             DLG_MSG_STANDARD|DLG_CENTERED);    
     if ( nResult = DLG_ERR ) then
       bDone = TRUE;
        return - 1;
    endif;  

 這里設置了一個名字為SelectVirDialog的對話框,對話框ID13001,其他參數可以為空或為0。那么有了構造函數,那么在Setup.rul里面就可以調用構造函數,使用對話框了。

一般為了方便管理,每個對話框都會配置一個對話框的腳本。腳本里面也就是構造函數和點擊按鈕的業務處理

// 選擇虛擬目錄  
Dlg_SdSelectVirtual:
        szTitle= "";
        szMsg= "";
        nResult=SdSelectVirtual(szTitle,szMsg);
        if(nResult=BACK) then
            goto Dlg_SdAskDestPath;   
        endif;
        if(nResult=NEXT && !MAINTENANCE) then
            goto Dlg_SQL;
        endif;

SdSelectVirtual也就是一個構造函數,里面封裝了DefineDialog 函數和業務處理。返回的是按鈕IDBACKNEW都是枚舉值。

對話框其實處於一種死循環狀態,只靠goto語句來跳出循環。具體出來的對話框界面如下:

那么如果獲取用戶輸入的值呢?

跟對話框的原理一樣,每一個控件也是有一個唯一標識列的(Control Identifier

設置控件的值CtrlSetText(szDialogName,1204,"A8");
獲取控件的值CtrlGetText(szDialogName,1204,svVituralDir);
1204是控件的Control Identifier

szDialogName是對話框的名稱,跟DefineDialog第一個參數相對應。

最后一個參數則是設置和獲取填充的值或變量。

  //  Initialize the indicator used to control the while loop. 
    bDone = FALSE; 

     //  Loop until done. 
     while (!bDone)

         //  Display the dialog and return the next dialog event. 
        nId = WaitOnDialog( szDlg);

         //  Respond to the event. 
         switch(nId) 
        
             case DLG_INIT:
                CtrlSetText(szDialogName, 1204, " A8 ");                              
                  //  No initialization is required for this example.   
             case NEXT: 
            nId   = NEXT;
            bDone = TRUE; 
            CtrlGetText(szDialogName, 1204,svVituralDir);
             // 將路徑寫到注冊表
            nRootKey = HKEY_CURRENT_USER;
            szKey =  " Software\\A8 ";
            szClass= ""
             // 更換注冊表根目錄
             if (RegDBSetDefaultRoot (nRootKey) <  0) then  
                MessageBox ( " First call to RegDBSetDefaultRoot failed. ", SEVERE); 
            endif;
             
             // 創建注冊表項   
             if (RegDBKeyExist (szKey) <  0) then 
                 if (RegDBCreateKeyEx (szKey, szClass) <  0) then 
                    MessageBox ( " RegDBCreateKeyEx failed. ", SEVERE); 
                endif; 
            endif;
            
             // 創建鍵值對[虛擬目錄,目標目錄]
            RegDBSetKeyValueEx (szKey,  " VirDir ", REGDB_STRING, svVituralDir,- 1);   
            RegDBSetKeyValueEx (szKey,  " TargetDir ", REGDB_STRING, TARGETDIR,- 1);
            
            nExists=CreateWebSite(svVituralDir);
             if(nExists== 0) then
                nId= 0;
                bDone=FALSE;
             else
                nId=NEXT;
                bDone=TRUE;
            endif;
             case BACK: 
                nId    = BACK;
                bDone = TRUE;  
                
             case DLG_ERR: 
            
                SdError( - 1" MyDefineDialog " );
                nId    = - 1
                bDone  = TRUE;   
                
             case DLG_CLOSE:   
                    SdCloseDlg( hwndDlg, nId, bDone ); 
            default
                 if(SdIsStdButton( nId ) && SdDoStdButton( nId )) then
                    bDone = TRUE;
                endif;
        endswitch; 
    endwhile;

2.2、創建虛擬目錄(使用ADSI

設置好界面,獲取到用戶輸入的虛擬目錄名稱,接下來就是創建虛擬目錄了。

第一步:獲取IISDefault站點

set objW3SVC = CoGetObject("IIS://localhost/W3SVC/1/Root","");

第二步:創建虛擬目錄

set objVirDir=objW3SVC.Create("IISWebVirtualDir",virtrualDir);//virtrualDir是變量
第三步:設置虛擬目錄的屬性

objVirDir.Path = TARGETDIR;   

objVirDir.AccessRead = TRUE;   

objVirDir.AccessScript = TRUE;   

objVirDir.AppCreate(TRUE);   

objVirDir.SetInfo();

詳細代碼請參考SdSelectVirtual.rulCreateWebSite函數

3、填寫數據庫信息

3.1、自定義數據庫對話框

數據庫的對話框也需要自定義,在上面已經介紹過自定義對話框的制作方法,數據庫對話框界面如下:

 

填寫服務器信息,默認是localhost或者.都可以

填寫用戶名,默認一般是sa

填寫密碼,默認是******

3.2、驗證數據庫信息

 

 bDone = FALSE; 

     //  Loop until done. 
     while (!bDone)

         //  Display the dialog and return the next dialog event. 
        nId = WaitOnDialog( szDlg);

         //  Respond to the event. 
         switch(nId) 
        
             case DLG_INIT:
                CtrlSetText(szDialogName,REX_CTRL_ID_SERVER, " localhost ");
                CtrlSetText(szDialogName,REX_CTRL_ID_USER, "");
                CtrlSetText(szDialogName,REX_CTRL_ID_PWD, "");
                  //  No initialization is required for this example.   

             case NEXT: 
            nId   = NEXT;
            bDone = TRUE; 
                CtrlGetText(szDialogName,REX_CTRL_ID_SERVER,svServer);
                CtrlGetText(szDialogName,REX_CTRL_ID_USER,svUser);
                CtrlGetText(szDialogName,REX_CTRL_ID_PWD,svPwd);
                         Server=svServer;
                         User=svUser;
                         Pwd=svPwd; 
              // 判斷數據庫鏈接是否成功(0代表鏈接失敗,1代表鏈接成功) 
             szWaitTxt= " 正在檢查數據庫用戶名和密碼 "
             SdShowMsg (szWaitTxt, TRUE);
             Delay( 2);
             SdShowMsg (szWaitTxt, FALSE); 
              if(DB_CheckConnection(Server, " SQL Server ",User,Pwd)!= 1) then
                nId= 0;
                bDone=FALSE;  
                MessageBox ( " 數據庫用戶名或者密碼錯誤,請重新輸入 ", WARNING);
                CtrlSetText(szDialogName,REX_CTRL_ID_USER, "");
                CtrlSetText(szDialogName,REX_CTRL_ID_PWD, "");
             else
                nId=NEXT;
                bDone=TRUE;     
                 // 將路徑寫到注冊表
                nRootKey = HKEY_CURRENT_USER;
                szKey =  " Software\\A8 ";
                     
                 // 更換注冊表根目錄
                 if (RegDBSetDefaultRoot (nRootKey) <  0) then  
                    MessageBox ( " First call to RegDBSetDefaultRoot failed. ", SEVERE); 
                endif;
                     
                 // 創建注冊表項   
                 if (RegDBKeyExist (szKey) <  0) then 
                     if (RegDBCreateKeyEx (szKey, szClass) <  0) then 
                        MessageBox ( " RegDBCreateKeyEx failed. ", SEVERE); 
                    endif; 
                endif;    
                 // 添加到注冊表 
                RegDBSetKeyValueEx (szKey,  " Server ", REGDB_STRING, Server,- 1);
                RegDBSetKeyValueEx (szKey,  " User ", REGDB_STRING, User,- 1);
                RegDBSetKeyValueEx (szKey,  " Pwd ", REGDB_STRING, Pwd,- 1);
            endif;
            
             case BACK: 
                nId    = BACK;
                bDone = TRUE;  
                
             case DLG_ERR: 
            
                SdError( - 1" MyDefineDialog " );
                nId    = - 1
                bDone  = TRUE;   
                
             case DLG_CLOSE:   
                    SdCloseDlg( hwndDlg, nId, bDone ); 
            default
                 if(SdIsStdButton( nId ) && SdDoStdButton( nId )) then
                    bDone = TRUE;
                endif;
        endswitch; 
    endwhile; 

DB_CheckConnection 這個函數用於驗證當前用戶輸入的數據庫信息是否正確,有關數據庫的相關操作位於database.rul文件。

如果數據庫的信息填寫無誤,那么把數據庫信息寫到注冊表,方便以后升級使用。

4、附加數據庫

在執行復制文件到目標機器后,點擊Finish(完成)按鈕會觸發函數onend

 

// ---------------------------------------------------------------------------
//  OnEnd
//
//  The OnEnd event is called at the end of the setup. This event is not
//  called if the setup is aborted.
// ---------------------------------------------------------------------------
function OnEnd()
begin 
if(!MAINTENANCE)then
// ConfigurateSql();
CreateDataBase(Server,User,Pwd); // Server,User,Pwd為SdCreateSql.rul的全局變量  
endif;
end;

ServerUserPwd都是全局變量,把變量定義到最頂部跟#include同級

#include  " Ifx.h "  
#include  " database.rul "
 
#define REX_DIALOG_ID 13003
#define REX_CTRL_ID_SERVER 1209  // 服務器
#define REX_CTRL_ID_USER 1207  // 用戶名
#define REX_CTRL_ID_PWD 1208  // 密碼

export prototype SdCreateSql( stringstring);  // 構造函數
// prototype CreateDataBase(STRING,STRING,string); // 創建數據庫
prototype AlterConfigure( string); // 修改web.config
string Server,User,Pwd; //全局變量

附加數據庫是調用了dos命令的osql.exe,代碼如下:

// 創建數據庫
function CreateDataBase(svSQLsvr,svSQLusr,svSQLpwd) 
    STRING szCmdLine,szWaitTxt,szCommandLine; 
    begin 
     // A8數據庫
    szWaitTxt= "  正在創建A8數據庫. "
    SdShowMsg (szWaitTxt, TRUE); 
    Delay( 2); 
    szCmdLine =  " /U  "+svSQLusr+ "  /P  "+svSQLpwd+ "  /S  "+svSQLsvr+ "  /Q \"EXEC  sp_attach_db  @dbname  =  N'A8',@filename1  = N' "+TARGETDIR ^ " App_Data\\A8.mdf',@filename2  = N' "+TARGETDIR ^ " App_Data\\A8_log.ldf'\" "
     if (LaunchAppAndWait( " osql.exe ", szCmdLine,LAAW_OPTION_NOWAIT | LAAW_OPTION_HIDDEN) <  0) then 
        MessageBox ( " 數據庫創建失敗!請確您的系統中已安裝 Microsoft SQL Server 2000. 如仍無法解決,請聯系系統供應商! ",SEVERE); 
    endif;  
    SdShowMsg (szWaitTxt,FALSE);  
    szWaitTxt= "  正在優化系統數據庫. "
    SdShowMsg (szWaitTxt, TRUE); 
    Delay( 2); 
    szCmdLine =  " /U  "+svSQLusr+ "  /P  "+svSQLpwd+ "  /S  "+svSQLsvr+ "  /Q \"use A8 ; exec sp_updatestats\" "
     if (LaunchAppAndWait( " osql.exe ", szCmdLine,LAAW_OPTION_NOWAIT | LAAW_OPTION_HIDDEN) <  0) then 
    MessageBox ( " 數據庫優化失敗!您可以在 sql查詢分析器中執行 use dlbj ; exec sp_updatestats 完成! ",SEVERE); 
    endif; 
    SdShowMsg (szWaitTxt,FALSE);  
     // 打開瀏覽器瀏覽制定的網頁    
    szCommandLine = ProgramFilesFolder ^  " Internet Explorer\\iexplore.exe ";
    LaunchAppAndWait(szCommandLine,  " http://localhost/ "+svVituralDir+ " /login/login.aspx ", NOWAIT);   

     // 修改配置文件
    ConfigurateSql(); 
    
end; 

在這里使用了LaunchAppAndWait調用exe文件,詳情請按F1

5、修改Web.Config文件

修改Web.Config文件也是在文件拷貝到目標機器的完成階段實現。

第一步:定標簽

Web.Config文件里為每一個要替換的節點定下一個注釋標簽

  <connectionStrings>
    <!--#constring1#-->
<add name="abc" connectionString="database=abc;server=.;uid=sa;pwd=123" providerName="System.Data.SqlClient"/>
  </connectionStrings>

<!--#constring1#-->則是一個注釋標簽

第二步:定位行數

根據標簽就可以找到該標簽下面那一個節點,代碼看GetLineNum函數

// 從上往下搜索某文件下面的字符串,並返回該字符串所在的行數
function NUMBER GetLineNum(szFileName,szSearchStr,isContinue)
string svReturnLine;
NUMBER nvLineNumber,nvResult;
begin 
    nvResult = FileGrep (szFileName, szSearchStr, svReturnLine, nvLineNumber,isContinue); 
     switch(nvResult) 
             case FILE_NOT_FOUND: 
             //  Report error; then abort. 
            MessageBox( szFileName +  "  not found. ", WARNING); 
            abort; 
         case FILE_LINE_LENGTH: 
             //  Report error; then abort. 
            MessageBox (szFileName +  " lines too long. ", WARNING); 
            abort; 
         case OTHER_FAILURE: 
             //  Report error; then abort. 
            MessageBox (szFileName +  " Unknown failure on call to FileGrep. ", WARNING); 
            abort; 
    endswitch; 
     return (nvLineNumber+ 1);
end ;

第三步:替換該行數據

使用FileInsertLine函數可以替換文件中的某一行。

             // 替換webconfig里面鏈接字符串,使用GetLineNum注意最后一個參數,從頭開始找還是繼續上次往下找
            nvLineNum=GetLineNum(ConFullDir,sConTag1,CONTINUE);
             if(FileInsertLine(ConFullDir,ConString1,nvLineNum,REPLACE)< 0) then
            MessageBox ( " FileInsertLine failed. ", SEVERE); 
            endif; 

6、完美卸載

安裝過程已經完成,接下來看如何完美卸載程序(刪除文件,分離數據庫,刪除虛擬目錄)

選擇Installscript,找到你要卸載的Feature,默認是DefaultFeature,選擇卸載事件,UnInstalling(卸載前)和UnInstalled(卸載后)

第一步:分離數據庫

因為卸載界面已經脫離了安裝的生命周期,那么所有變量都被回收了,要獲取數據庫信息只能從注冊表獲取(安裝的時候已寫進了注冊表)

// 更換注冊表根目錄
     if (RegDBSetDefaultRoot (nRootKey) <  0) then  
        MessageBox ( " First call to RegDBSetDefaultRoot failed. ", INFORMATION); 
    endif;  
    
     // 從注冊表取數據庫和虛擬目錄相關信息
    RgVirDir=GetReg( " VirDir ");
    RgServer=GetReg( " Server ");
    RgUser=GetReg( " User ");
    RgPwd=GetReg( " Pwd ");  
     // 分離A8數據庫   
    szWaitTxt= " 正在分離A8數據庫 ";
    SdShowMsg (szWaitTxt, TRUE);  
    Delay( 2);
    szCmdLine =  " /U  "+RgUser+ "  /P  "+RgPwd+ "  /S  "+RgServer+ "  /Q \"EXEC  sp_detach_db 'A8' ";  
     if(    LaunchAppAndWait( " osql.exe ", szCmdLine,LAAW_OPTION_NOWAIT | LAAW_OPTION_HIDDEN) < 0) then
        MessageBox ( " 數據庫分離失敗!請確您的系統中已安裝 Microsoft SQL Server 2000. 如仍無法解決,請聯系系統供應商! ",SEVERE); 
    endif;
    SdShowMsg (szWaitTxt, FALSE);  

GetReg是一個自定義函數,參數則是注冊表的鍵名

分離數據庫還是使用dos命令下的osql.exe

刪除數據庫文件只能先分離,不然會有數據庫質疑的字樣

第二步:刪除虛擬目錄

  // 刪除虛擬目錄
      set objW3SVC = CoGetObject( " IIS://localhost/W3SVC/1/Root """); // 獲取Default站點   
      if(IsObject(objW3SVC)) then
         if(IsObject( CoGetObject( " IIS://localhost/W3SVC/1/Root/ "+RgVirDir+ "", ""))) then 
                szWaitTxt= " 正在刪除 "+RgVirDir+ " 虛擬目錄 ";   
                Delay( 2);
                SdShowMsg (szWaitTxt, TRUE);      
                objW3SVC.Delete( " IIsWebVirtualDir ",RgVirDir) ;
                SdShowMsg (szWaitTxt, FALSE);      
        endif;
     endif; 

刪除虛擬目錄一樣使用了ADSI

第三步:停止數據庫服務和刪除注冊表鍵值

      // 停止數據庫服務SQLSERVERAGENT,MSSQLSERVER
    szCmdLine =  "  stop SQLSERVERAGENT ";    
     if(LaunchAppAndWait( " sc ", szCmdLine,LAAW_OPTION_NOWAIT | LAAW_OPTION_HIDDEN)< 0) then
       MessageBox ( " 無法停止數據庫服務--SQLSERVERAGENT,請手動關閉該服務 ",SEVERE); 
    endif;
   
    szCmdLine= "  stop MSSQLSERVER ";
     if(LaunchAppAndWait( " sc ", szCmdLine,LAAW_OPTION_NOWAIT | LAAW_OPTION_HIDDEN) < 0) then
        MessageBox ( " 無法停止數據庫服務--MSSQLSERVER,請手動關閉該服務 ",SEVERE); 
    endif;  
     
      // 刪除完刪除注冊表 
     DelReg(KEY);

DelReg是自定義函數,參數是注冊表的鍵

第四步:刪除文件夾和啟動數據庫服務(在UnInstalled卸載后觸發)

 

// ---------------------------------------------------------------------------
//  The UnInstalled event is sent after the feature DefaultFeature
//  is uninstalled.
//  sented after delete defaultFeature
// ---------------------------------------------------------------------------
 
export prototype DefaultFeature_UnInstalled();
function DefaultFeature_UnInstalled() 
string szCmdLine;
begin
     // 刪除后重新啟動數據庫服務--MSSQLSERVER
    szCmdLine= "  start MSSQLSERVER ";
     if(LaunchAppAndWait( " sc ", szCmdLine,LAAW_OPTION_NOWAIT | LAAW_OPTION_HIDDEN) < 0) then
        MessageBox ( " 無法啟動數據庫服務--MSSQLSERVER,請手動啟動該服務 ",SEVERE); 
    endif;            
    
     // 可能刪除不干凈,手動執行刪除文件夾
     if(ExistsDir(TARGETDIR)=EXISTS ) then   
         if(DeleteDir(TARGETDIR,ALLCONTENTS) <  0) then
            MessageBox( " 刪除失敗 ",SEVERE)  ;
        endif;
    endif;
end;

啟動數據庫服務,刪除文件夾。

整個卸載過程完成。

八、更新包制作

1、前言

更新包也是一個獨立的InstallScript MSI Project,只不過相比於安裝包少了一些步驟,更新包的原理就是從注冊表讀出安裝時寫進的信息,如:數據庫服務器,用戶名,密碼,虛擬目錄,安裝路徑。界面略……

直接跳過選擇安裝目錄那個對話框,因為獲取了注冊表的那個安裝路徑了。代碼如下:

Dlg_SdStartCopy:
    szTitle =  "";
    szMsg   =  "";
    nResult = SdStartCopy2( szTitle, szMsg );            
     if (nResult = BACK) then
        goto Dlg_SQL;;
    endif;  
    
     // 獲取注冊表的目標路徑
    TARGETDIR= GetReg( " TargetDir ");
    
     //  Added in IS 2009 - Set appropriate StatusEx static text.
    SetStatusExStaticText( SdLoadString( IDS_IFX_STATUSEX_STATICTEXT_FIRSTUI ) );

     //  setup default status
    Enable(STATUSEX);
 
     return  0;
end;

2、選擇更新文件

方法跟安裝的時候是一樣的

3、修改Product Code,每次更新都要換一個Code,要不會出現(修復,卸載,重裝的操作界面)

4、運行sql語句

假如有更新sql語句,將需要運行的sql語句整理成一個文件

// ---------------------------------------------------------------------------
//  OnEnd
//
//  The OnEnd event is called at the end of the setup. This event is not
//  called if the setup is aborted.
// ---------------------------------------------------------------------------
function OnEnd()  
    STRING szKey, szClass, szMsg, szTitle,szCmdLine,sqlRoot;
     string targetDir,server,user,pwd;
    NUMBER nRootKey;
begin    
if(!MAINTENANCE)then
    targetDir=  GetReg( " TargetDir ");
    server= GetReg( " Server ");
    user= GetReg( " User ");
    pwd= GetReg( " Pwd ");
    sqlRoot= targetDir+ " sqlFile.sql " ;
    LongPathToQuote(sqlRoot ,TRUE);                                   
     if(Is(FILE_EXISTS,sqlRoot)) then
        szCmdLine =  " /U  "+user+ "  /P  "+pwd+ "  /S  "+server+ "  /i  "+sqlRoot+ ""
         if (LaunchAppAndWait( " osql.exe ", szCmdLine,LAAW_OPTION_NOWAIT | LAAW_OPTION_HIDDEN) <  0) then 
            MessageBox ( " 運行sql更新文件時失敗,請聯系供銷商! ",SEVERE); 
        endif;    
     else
         MessageBox ( " 找不到更新的sql文件,請聯系供銷商! ",SEVERE);  
    endif;
endif;    
end;

執行更新的sql語句也是調用dososql.exe文件

5、屏蔽控制面板里添加刪除程序的那個安裝信息



九、結束語

首先感謝Installshield技術交流群(158107742)的群主海洋女神,Kevin,單車,棕橙藍綠……還有其他群里的朋友們。排名不分前后

如果有看不懂或者不明白的可以給我留言或者加qq群跟大家交流交流,如果想了解更專業的Installshield技術,可以閱讀以下的博客:

 

1.論壇http://www.appinstall.cn/

2.入門http://home.cnblogs.com/Cindy_weiwei

3.提高http://home.cnblogs.com/installshield

 

附:由於部分資源文件涉及公司機密,僅能提供自定義對話框和數據庫操作的腳本資源:/Files/magicchaiy/腳本.rar

 

                                                             2012年3月30日15:34:08

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM