msdn的文档

来源:互联网 发布:软件架构设计 温昱 编辑:程序博客网 时间:2024/05/17 00:04

深入了解在 Visual Studio .NET 中创建安装例程的基础知识!Vishnu Prasad H 探究了安装项目模板、编辑器、自定义安装程序以及更多内容。然后,他将所有内容放在一起,组成了一个部署数据库应用程序的示例。

与以前相比,现在部署 .NET 应用程序非常简单。现在 xcopy 部署也成为了可能。但是,简单 xcopy 部署还是有限制的。它不能用于分层应用程序部署、配置设置、细化调整等等。将应用程序层与自定义配置集成在一个分布式框架中需要专业、可自定义的部署工具。基于 Microsoft Windows Installer (MSI) 的部署能够完成所有这些任务甚至更多任务。然而,要使其得以运行还需要一些努力。幸运的是,Visual Studio .NET 的安装和部署项目使这变得相当简单。

部署示例

我创建了一个示例应用程序,以演示 MSI 部署在 Visual Studio .NET 中的功能。这是一个非常简单的应用程序,只有一个显示客户的窗体。

该应用程序具有一个 CustomSteps 类库。此库包括一个 SetupDB 组件类,它从 Installer 类继承。该组件类提供了自定义数据库安装。它对 OSQL 进程进行了自定义,并运行该进程以安装和删除数据库。我讲述的重点主要在这个类库。

然后,您就拥有了 Customer 安装,这是一个安装项目。此项目提供了与用户进行接口的组件,可用来捕获诸如服务器名、数据库名、用户 ID 等数据库信息。然后,它会将这些文件复制到目标,并创建数据库、数据库的对象和基础数据。最后,它会在 CustomSteps 类库的帮助下对配置文件中的连接字符串属性进行配置。

返回页首返回页首

创建示例应用程序

即使您打算在阅读本文的同时着手创建该示例,也请您去下载本文附带的源代码。(创建这个基本的 Customer 应用程序没有什么要指出的内容,因为包括它的目的只是为了最终了解安装程序如何运行!)

创建一个空的解决方案,将其命名为 InstallerSample,然后将其添加到下载的 Customer 应用程序中。此应用程序只有一个窗体,其中带有一个显示数据库中客户的数据网格,如图 1 所示。有一个 Resources 文件夹,其中的 Customers.xml 文件配置了一些数据。请注意,该文件的 Build Action 属性被设置为 Content。引入此文件是为了演示这个 Build Action 的重要性。

App.config 具有下面的配置设置。稍后,您将看到安装程序在运行时设置的这个值。

<?xml version="1.0" encoding="utf-8" ?>  <configuration>       <appSettings>          <add key="connectionstring" value=""/>       </appSettings>  </configuration>
返回页首返回页首

创建 CustomSteps 库

此步骤会帮助您处理这个自定义数据库安装。在安装项目中,您可以指定 Custom Actions。此项目的输出就是其中一个 Custom Action 步骤,因此在文件复制到目标之后称之为步骤。

创建一个新的类库 CustomSteps,添加一个新的 Installer 类,然后将其重命名为 SetupDB.cs。在进入实施阶段之前,您必须决定安装数据库的方法。表 1 提供了一些常用方法。

表 1. 数据库部署方法。 模式 说明

备份还原

SQL Server 提供了备份和还原数据库的选项。您可以使用 System.Data.SqlClient 命名空间以及特定的数据库存储过程进行还原。这可能是最简单的技术,但是它具有一些问题(如硬编码的名称)和其他一些配置考虑因素。

SQL-DMO

如果您打算部署基于 VB/COM/SQL 的应用程序,这就是最好的方法之一。SQL-DMO 提供了封装的 MS SQL Server 对象,以便用于管理目的。其中包括创建数据库和其他对象。您可以创建一个带有 DMO 对象的 EXE,DMO 对象会封装创建数据库的操作。

脚本文件

这可能是最好的综合性方法,因为它通常都是一种用来维护/管理脚本的最佳做法。您可以使用“OSQL”来执行这些脚本。但是,在转向部署之前,必须先运行一个有效的 SQL 脚本测试。另外,使用 BCP 实用程序以各种格式迁移大量数据通常也是一个很好的选择。

ADO.NET

这个选择的可靠性很好,但是会涉及大量的维护和编码工作。您可以创建一些 C# 方法来创建数据库、对象,并使用 SqlClient 命名空间插入语句。

在该示例应用程序中有一个数据库,它只有两个表:tblCustomer 和 tblAddress。在此方案中,假设您已经决定采用基于脚本文件的部署。通常,在公司开发环境中,数据库及其对象是由 DBA 进行紧密控制的,而要求开发人员来维护自己的 DML 语句脚本。在生成时,可以运行一个预先生成的事件来创建 SQL 文件。因此,假设有四个 SQL 文件,如下所示:

DataBase.sql — 用于创建数据库的脚本。

DropDatabase.sql — 用于删除数据库的脚本。

Objects.sql — 表和其他对象的脚本(如果需要的话)。

StandingData.sql — 表的 DML 脚本。

数据库的名称应由用户指定。然后,占位符可以在运行时替换为真正的数据库名称。在 Database.sql/DropDatabase.sql(请参阅下载中的文件)中,我使用 <> 作为占位符。将这些脚本文件复制到项目中一个名为 Scripts 的单独子文件夹中。请注意,即使在此处,这些脚本文件的 Build Action 属性仍然为 Content。

另外,您可以拥有多个 Objects 和 StandingData SQL 脚本文件;您可能希望该实现足够灵活,可以处理这种情况。在 Resources 子文件夹下创建 InstallationFiles.xml,并将 Build Action 属性设置为 Embedded resource清单 1 显示了该 XML 文件的内容。

清单 1. InstallationFiles.xml 内容。

<?xml version="1.0" encoding="utf-8" ?> <configroot>  <Files>       <DataBase>              <Add>                   <File name="Database.sql"/>              </Add>              <Remove>                   <File name="DropDatabase.sql"/>              </Remove>       </DataBase>       <Objects>              <File name="Objects.sql"/>       </Objects>       <Records>              <File name="StandingData.sql"/>       </Records>  </Files></configroot></code>

该 XML 文件可帮助您在运行时识别哪些文件用于创建或删除数据库及其对象和记录。您可以在 Files 下面添加多个元素,并使用要执行的 SQL 文件名来配置名称属性。

现在,您已经拥有了这些脚本文件,以及一个提供有关每个脚本文件执行哪些操作的信息的 XML 文件。接下来,我要将注意力转移到 SetupDB.cs。它是从 System.Configuration.Install.Installer 继承的,后者是所有自定义安装程序的基类。请记下 Runinstaller(true) 属性;这样可以确保调用此 Custom Action。该 Installer 类通过重写以下四个关键方法来为您提供自定义选项:

Install — 在安装步骤调用。

Commit — 完成安装过程。

Rollback — 将系统还原为安装前的状态。在 Install 步骤失败时调用 Rollback。通常,在重写该方法时,它应该执行与卸载相同的任务。

Uninstall — 删除已经安装的文件,并重置/删除在安装过程中完成的任何配置。如果此步骤失败,通常无法还原到安装前的状态。

另外,还有一些前面提到操作的 OnAfter 和 OnBefore 事件。但在此示例中,我只重写了 Install 和 Uninstall 方法。

首先,我们来讲述 Install 方法。此方法必须执行下列步骤:

1.

捕获并计算用户输入的数据库连接详细信息。

2.

配置 App.Config 文件 connectionstring 设置中的连接字符串。

3.

以合适的顺序运行脚本文件。

首先,将清单 2 中的代码添加到您的 SetupDb.cs 中。

清单 2. Install 方法的代码。

public override void Install (   System.Collections.IDictionary stateSaver ){bool TrustedConnection=false;base.Install(stateSaver);try{ if(this.Context!=null)    {StringDictionary parameters = Context.Parameters;    string[] keys =new string[parameters.Count];    parameters.Keys.CopyTo(keys,0);   // Set the StateServer collection values  for(int intKeys=0;intKeys<keys.Length;intKeys++)  {    if(keys[intKeys].Equals("database"))      stateSaver.Add("database",        parameters[keys[intKeys]].ToString());    else if(keys[intKeys].Equals("server"))      {stateSaver.Add("server",        parameters[keys[intKeys]].ToString());}    else if(keys[intKeys].Equals("username"))      {stateSaver.Add("username",        parameters[keys[intKeys]].ToString());}    else if(keys[intKeys].Equals("password"))      {stateSaver.Add("password",        parameters[keys[intKeys]].ToString());}    else if(keys[intKeys].Equals("target"))      {stateSaver.Add("target",        parameters[keys[intKeys]].ToString());}  }  // Evaluate connectionstring based on input  // can encrypt here...  string connectionstring= "Data Source=" +        stateSaver["server"].ToString() ;  connectionstring+= ";Initial Catalog=" +       stateSaver["database"].ToString() ;  if(stateSaver["username"]!=null &&     stateSaver["username"].ToString().Length!=0)  {    // Here you need to test the connection...    // and proceed    SqlConnection conn =       new SqlConnection( "Data Source=" +         stateSaver["server"].ToString() +         ";Initial Catalog=master;User Id=" +         stateSaver["username"].ToString() +         ";Password=" +         stateSaver["password"].ToString());    conn.Open();    conn.Close();    conn.Dispose();    connectionstring += ";User ID=" +       stateSaver["username"].ToString() ;    connectionstring += ";Password=" +       stateSaver["password"].ToString() ;  }  else{    // Here you need to test the connection...    // and proceed.    SqlConnection conn =      new SqlConnection( "Data Source=" +       stateSaver["server"].ToString()+       ";Initial Catalog=master;trusted_connection=yes");    conn.Open();    conn.Close();    conn.Dispose();    TrustedConnection=true;    stateSaver.Add("trustedconnection",true);    connectionstring+=";Trusted_connection=yes";}  // Set the Config file's connection string  XmlDocument doc = new XmlDocument();  doc.Load(stateSaver["target"].ToString()+    @"bin/Customer.exe.config");  XmlNode connectionNode =      doc.SelectSingleNode(    @"configuration/appSettings/" +    @"add[@key='connectionstring']");  if(connectionNode!=null)  {    connectionNode.Attributes["value"].Value =       connectionstring;    doc.Save( stateSaver["target"].ToString()+       @"bin/Customer.exe.config");    EventLog.WriteEntry("SetupDB",      "Configuration file processed...");   }  else  { // This error will ensure installation     // is uncomplete...    throw new       InstallException(      "Configuration file had no node " +      "declared for connection string.");   }  // Run the scripts  DataBaseInstaller dbInstall =null;  if(TrustedConnection)  {    dbInstall= new     DataBaseInstaller(stateSaver["server"].ToString(),      stateSaver["database"].ToString(),      stateSaver["target"].ToString());  }  else{    dbInstall= new       DataBaseInstaller(      stateSaver["server"].ToString(),      stateSaver["database"].ToString(),      stateSaver["username"].ToString(),      stateSaver["password"].ToString(),      stateSaver["target"].ToString());  }  dbInstall.CreateDataBase();  dbInstall.CreateObjects();  dbInstall.CreateRecords();  }}catch(InstallException inst){  throw (inst);}catch(Exception generic){  EventLog.WriteEntry("SetupDB",generic.Message,    EventLogEntryType.Error) ;  throw new InstallException(generic.Message);}}

在清单 2 中,请注意方法的签名。它有一个名为 stateSaver 的 IDictionary 参数。它会在 Install、Commit、Rollback 等过程中保持该安装程序所需的信息。最初,此值为 null。只有当您调用 base.Install(StateSaver) 时,IDictionary 对象才会反映过程的当前状态。因此每个重写方法都需要调用基类,以便初始化 stateSaver。如果您在各个步骤中执行多个自定义安装程序,那么这就变得非常重要。接下来的步骤是获取通过 Custom Actions 属性 CustomActionData 传递的参数。

StringDictionary parameters = Context.Parameters;

这个 Context 属性非常重要,因为它可返回 System.Configuration.Install.InstallContext。甚至在执行 Install 方法之前,该安装程序就已经设置了安装程序的 Context 属性。表 2 指出了 InstallContext 的一些重要成员。

表 2. InstallContext 的一些重要成员。 成员 说明

LogMessage(string)

记录指定记录器中的任何消息。该日志的路径是在安装程序的构造函数中指定的。日志文件的名称/值参数可以传递到自定义安装程序中。

Parameters

返回包含名称/值对的 StringDictionary。这是一个重要属性,它将返回 CustomActionData 中的命令行名称/值对。

isParameterTrue(string)

如果设置了指定的参数,则返回真,如果没有设置,则返回假。

清单 2 中的第一部分代码会检索不同的键值对,并将它们设置到 stateSaver 中。其中包括用户输入的数据库值,如服务器名、数据库名、用户 ID、密码和安装的目标目录。当然,在您想要卸载时,这些详细信息中有一些是至关重要的。您需要将在卸载过程中删除的数据库名称、它的连接字符串等。因此,为了保持安装的状态,所有这些 stateSaver 值都由安装程序储存在一个名为 *.installstate 的文件中。安装后,您可以在文件夹中查看该文件。按照这种方式使用文件使您能够避开注册表。另外,当您构建真正的安装程序时,最好明智地选择使用 stateSaver。如果某个用户在安装后删除了此文件,安装程序在在卸载过程中就会遇到问题。

清单 2 中的第二部分代码使用用户输入的值来检查数据库连接。如果用户没有输入用户 ID,此步骤就会对一个可信连接进行测试。这不是一个用来辨别集成模式验证选择或混合模式验证选择的简便方式。但是,现有用户界面编辑器中的限制又使得必须这样做。最终,此部分还会为数据库创建连接字符串。

清单 2 中的第三部分代码可在应用程序配置文件中设置连接字符串。此部分会将配置文件加载为一个 XML 文档,遍历到 connectionstring 节点,并设置该值。请注意将该文件的路径放在一起的方式。

清单 2 中的第四部分代码通过将运行脚本的任务委托给另一个类,来实际创建该数据库: DatabaseInstaller。

将 DatabaseInstaller.cs(下载资料中的示例文件)复制到您的项目中。DatabaseInstaller 具有两个构造函数来传递用户输入的数据库详细信息。另外,它还有一个私有构造函数,如清单 3 所示。

清单 3. 从嵌入式资源加载 InstallationFiles.xml。

XmlDocument config=null;private DataBaseInstaller(){  System.IO.Stream stream =     System.Reflection.Assembly.GetExecutingAssembly().    GetManifestResourceStream(    "CustomSteps.Resources.InstallationFiles.xml");  config=new XmlDocument();  config.Load(stream);}

此构造函数会加载包含有关这些脚本文件详细信息的 InstallationFiles.xml 文件。您还记得吧,您已经将此文件的 Build Action 显式设置为 Embedded resource。之后,您就拥有了封装创建数据库功能的各种成员。表 3 提供了有关上述内容的一些详细信息。

表 3. DatabaseInstaller 的成员。 成员 说明

CreateDataBase()

调用以创建数据库的公共方法。

DropDataBase()

删除数据库的公共方法。

CreateObjects()

创建对象(如表、存储过程等)的公共方法。

CreateRecords()

在各种表中创建任何基础数据或配置数据。

GetFullPath

返回带有脚本文件完整路径的字符串的私有方法。它采用该脚本文件的名称。

PopulateDatabaseNamePlaceHolder

使用实际的数据库名称替换脚本文件中的占位符的私有方法。

GetCommonProcessArguments

创建参数的私有方法,这些参数将传递到 OSQL 以便创建数据库。

ExecuteScripts

对于一个给定的 XPath,此私有方法会获取脚本文件的文件名,并使用 OSQL 对它们进行递归执行。这是由 CreateObjects 和 CreateRecords 使用的。

现在我将主要讲述 CreateDatabase() 方法,因为其他大多数方法所遵循的实现都是类似的。清单 4 提供了该方法的主要代码。

清单 4. 数据库创建的代码片段。

ProcessStartInfo processInfo =  new ProcessStartInfo("osql.exe");processInfo.WindowStyle=ProcessWindowStyle.Normal;// Get the name of the file from the assembly's // embedded resource.if(config !=null){  fileName = config.SelectSingleNode(    "configroot/Files/DataBase/Add/File").    Attributes["name"].Value;}else{  //Customized message..  throw new InstallException(    "Configuration for database file " +    "creation missing.");}//Get argumentsprocessInfo.Arguments=  GetCommonProcessArguments(fileName,"master");EventLog.WriteEntry("DatabaseInstaller",  processInfo.Arguments);PopulateDatabaseNamePlaceHolder(  GetFullPath(fileName));Process osql = Process.Start(processInfo);//Wait till it is done...osql.WaitForExit();EventLog.WriteEntry(   "DatabaseInstaller","Database created..");osql.Dispose();return true;

ProcessStartInfo 可用于对您将要启动的进程(OSQL)进行更好的控制。它为您提供了一种用于设置参数、控制窗口样式等的简便方法。然后,它可以与清单 4 中所示的进程 结合使用。该进程启动之后,您可以等待它退出,也可以使用 WaitForExit 方法来控制退出时间。另外,您在构造函数中使用初始化的 XmlDocument 来选择一个预定义的节点,您在其中存储了数据库脚本文件的名称。然后,您将参数构建的过程委托给一个特定的方法 (GetCommonProcessArguments),同时传递了该脚本文件的名称以及要从中运行的数据库上下文的名称。在数据库创建过程中,您可以将 master 作为数据库传递,原因是您的应用程序数据库正处于创建过程中。下一行代码(方法 PopulateDatabaseNamePlaceHolder)使用用户提供的实际名称来替换数据库名称的占位符。

请注意,无论何时,只要存在 try、catch 代码块或者发生了自定义异常,就会引发一个 InstallException。它的类型为 SystemException,该安装程序甚至会在 Commit、Rollback 和 Uninstall 阶段引发这种类型的异常。较好的做法是使用 EventLog 或自定义安装日志文件来跟踪并记录该错误。您可以使用 Context 属性来创建自定义日志。

您已经为数据库创建成功自定义了安装!

使用相似的方式,您可以重写 Installer 类的 Uninstall 方法,然后复制附带示例中的代码。此时您会看到,当用户确认时,删除数据库的任务就委托给 DatabaseInstaller 类。在接下来的步骤中,您将创建一个安装项目,并使用它进行部署。

返回页首返回页首

创建安装项目并进行安装

最后,您要准备创建安装项目了。表 4 说明了 VS.NET 提供的各种部署方式(另请参见图 2)。

表 4. 模板选项。 模板 说明

Setup

生成 MSI。通常在生成基于 Windows 的应用程序时使用。但是,您可以对其进行自定义,以满足任何形式的部署需要。

WebSetup

为 Web 部署创建虚拟目录。还可以使用此模板进行自定义。

Merge Module

它可帮助您将组件与其他应用程序共享,如 .NET Framework、MSDE 等。

Cab

输出一个压缩包 (.cab) 文件以便于分发。

对于该示例应用程序,请选择一个安装项目模板,并将其命名为 Customer Setup。

Visual Studio 提供了多种编辑器,用于创建安装应用程序。我将对其中的一些编辑器及其用法进行探究。

图 3 显示的是文件系统编辑器。对于要传递到部署环境中的项目、文件等,您可以在这里添加来自它们的输出。您还可以选择专门的文件夹作为您文件的目标,如 System 文件夹、Program Files 文件夹等。

在 Application 文件夹下创建两个子文件夹,将其分别命名为 bin 和 Install。然后选择 bin 文件夹,右键单击,并选择“添加”|“项目输出”,以获得如图 4 所示的对话框。

在组合框中选择“Customer”项目,然后选择 Primary outputContent files。主输出表示项目输出,内容文件将包含标记为 Build Action 内容的文件。这就是 Build Action 属性非常关键的位置。您还可以复制源文件,但是此示例无需进行复制。

转至 Install 文件夹,以及 CustomSteps 中的主文件和内容文件。

现在您已经完成了向部署添加提交内容的过程。下一步就是设置用于安装的用户界面。

用户界面编辑器默认情况下,您可以在用户界面编辑器中看到一组界面,如 target location 等。但是,对于这个应用程序,您需要捕获四个参数:服务器名、数据库名、用户 ID 和密码。右键单击“Start”部分,然后右键单击“Add Dialog”。选择为您提供四个参数的 Textboxes (A) 选项,并按图 5 所示确定它的位置。此时,您可以配置表 5 中所示的属性。 表 5. Textboxes (A) 对话框的重要属性。

属性 说明

BannerText

要为该屏幕显示的标志。

Edit1Label

第一个文本框的标签。设置为 Server Name。查找其他标签属性,并相应地进行设置。

Edit1Property

该属性非常重要:此处提供的名称要在 Custom Actions 中访问。将该属性设置为 SERVER_NAME。同样地,为示例中显示的其他属性设置 DATABASE_NAMEUSER_NAMEPASSWORD

Edit1Value

要设置为默认值的值。您可以指定可用的默认属性。例如,[ComputerName] 会将此文本框的默认值设置为用户计算机的名称。请检查示例应用程序中设置的值,以了解更多信息。

Edit1Visible

此值有助于您控制文本框的可见性。对于此示例,所有属性均为真。

用户界面编辑器还有一些别的内容。您可以研究它的其他各种对话、它们的属性,以及一些可用于真正扩展您的自定义的默认 Windows 属性。但是,对于此示例,我就需要这些,所以我要转而说明启动条件编辑器了。

返回页首返回页首

启动条件编辑器

在某些情况下,您的部署要迎合特定的运行库,如 .NET Framework、MSDE、MDAC 版本等。如果您没有什么先决系统要求,则没有要指出的内容,继续进行安装就可以了。启动条件编辑器可帮助您完成此任务。在此示例中,除了现有的默认“.NET Framework”条件之外,我还要添加一个条件。单击启动条件编辑器,然后右键单击“Search Target Machine”部分。在这里,您可以评估某个特定的条件。假设此安装程序是面对 Windows OS 4 和更高版本的。尽管可以使用默认的 Windows 条件,但是我要为您讲述如何创建自己的条件。选择 Add file registry search,然后按照图 6 所示设置这些值。

Property 会为您的搜索评估指定一个名称。RegKey 是要搜索的目标关键字,Root 则是要搜索的注册表树的根。这些设置会搜索 Value 集合的 RegKey 路径(例如 CurrentVersion 值名称)。此搜索会返回它在 CurrentVersion 值名称中找到的任何值。

右键单击“Launch Conditions”,然后添加一个新条件。图 7 提供了此条件的设置。请注意 Condition 属性。此属性应该计算为真,安装才能继续。在这里,您提供了 WINDOWSVERSION>= "4.0"。WINDOWSVERSION 是注册表搜索条件的名称。如果返回假,则安装程序会显示 Message 属性中配置的错误消息,并中止安装。您可以按照这种方式配置各种条件,以使您的部署具有条件。

在下一部分中,您将看到如何使用自定义生成的 CustomSteps 安装程序。

自定义操作编辑器您可以添加属于部署一部分的任何 EXE、脚本文件或 DLL 文件。在此示例中,您需要添加的只是 CustomSteps 类库。您可以在 Install 阶段添加此类库。为此,请右键单击“Install”部分,然后选择“Add Custom Action”。然后,遍历 Applications 文件夹/Install,然后选择主输出,如图 8 所示。将该步骤重命名为 Install database

图 9 显示了为该 Custom Action 配置的属性。这些属性中最重要的是 Condition 和 CustomActionData。Condition 可确保此安装程序只有在满足特定条件时才运行。CustomActionData 则是向 CustomAction EXE/DLL 传递参数。

在此示例中,我按照下列方式设置了这些属性:

/server=[SERVER_NAME] /target="[TARGETDIR]/" /database=[DATABASE_NAME] /username=[USER_NAME] /password=[PASSWORD] /version=[WINDOWSVERSION]

请注意,设置 Windows 默认属性(如 TARGETDIR)与设置用户界面编辑器或启动条件编辑器中您自己创建的属性(如 SERVER_NAME 和 WINDOWSVERSION)之间的区别。但是,通用格式为 /name=[value],后面紧跟反斜杠,具体取决于属性。另外,请注意 InstallerClass 属性值。此处传递的参数将用作 Context.Parameters。您可以看到这些参数在 CustomSteps 组件中是如何使用的。当您运行该安装程序时,自定义安装程序的 Install 方法会在复制了所有部署文件之后被调用。

您又按照类似方法添加了 Uninstall 阶段的 Custom Action,并将其重命名为 Uninstall database。但是,这不需要任何参数。必需的参数将使用 stateSaver 集合提供。这就是为什么在安装过程中创建的 *.installstate 文件非常重要的原因了。您可以在 Install 文件夹中看到该文件。stateSaver 是使用此文件初始化的,因此建议您在安装过程中向 stateSaver 添加卸载操作所需的所有这些值。

根据您的方案,还可以使用其他一些编辑器,如文件类型编辑器和注册表编辑器。但是,对于此示例,并不需要这些编辑器。

您现在就可以进行安装了。生成解决方案和安装程序项目,然后进行安装和卸载,以此进行测试。

返回页首返回页首

小结

对于可以使用 VS.NET 中的安装项目来实现实施的高级概念来说,此处讲述的这个过程只是冰山一角。很重要的一点是,要根据您自己的应用程序要求来分析和自定义您的部署。在此示例中,您看到了文件的 Build Action 属性的用法。

请尝试减少注册表项的使用,并处理好最差的情况。请对于所有可能的方案情况(如已删除的了 *.installstate 文件)测试您的安装程序,并尝试以尽可能好的方式来处理这些方案。

另外,在此示例中,我从来没有检查过活动数据库连接。这在卸载过程中是非常重要的。对于应用程序的卸载的重视程度应该给予与安装一样重要的重视程度,因为您肯定会要再次重新安装该应用程序。

如果现在的 VS.NET 安装编辑器有限制的话,那么这个限制也会存在于用户界面编辑器中。我相信对于将来版本的 VS.NET 来说,最佳的解决方案是为开发人员提供一个可在设计时添加自己的用户界面的选项,并且带有用于对话框的条件选项。但是,目前您仅限于了 Custom Actions。

异常处理也非常重要,因为总是应该在回滚/卸载之后保持安装前的状态。

不要对于目标运行库时环境做作任何假设,。而是始终总是要使用启动条件来验证是否存在您需要的每项内容是否存在。

最后,请一定要生成适合于您的应用程序的安装程序。此过程值得您花费项目时间和成本。

Download 410PRASAD.ZIP

要了解有关 Hardcore Visual Studio 和 Pinnacle Publishing 的更多信息,请访问它们的网站 http://www.pinpub.com/

注:这不是 Microsoft Corporation 的网站。Microsoft 对该网站的内容不承担任何责任。

本文摘自 Hardcore Visual Studio复制于 的 2004 年 10 月刊份的 Hardcore Visual Studio。版权所有 2004,Pinnacle Publishing, Inc.(除非另行说明)。保留所有权利。Hardcore Visual Studio 是 Pinnacle Publishing, Inc. 独立发行的产品。未经 Pinnacle Publishing, Inc. 事先同意,不得以任何形式使用或复制本文的任何部分(主要评论文章和回顾评论中的简短引用除外)。要联系 Pinnacle Publishing, Inc.,请致电 1-800-788-1900。

转到原英文页面

原创粉丝点击