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

2023-10-29 10:48

本文主要是介绍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技术,可以阅读以下的博客:

这篇关于ASP.NET完整打包卸载更新攻略(By Installshield 2010)(转载)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/300125

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

AI行业应用(不定期更新)

ChatPDF 可以让你上传一个 PDF 文件,然后针对这个 PDF 进行小结和提问。你可以把各种各样你要研究的分析报告交给它,快速获取到想要知道的信息。https://www.chatpdf.com/

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get