Custom MSI installation

xiaoxiao2021-03-06  25

This page

Deployment example Create a sample application Creating a CustomSTeps library Creating a installation item and installing the Start Condition Editor Skinding

In-depth understanding of the basics of creating installation routines in Visual Studio .NET! Vishnu Prasad h explores the installation project template, editor, custom installer, and more. Then he puts all the content together, constitutes an example of deploying a database application.

It is very simple to deploy .NET application compared to previous than before. The XCopy deployment is now possible. However, simple XCOPY deployment is still limited. It cannot be used for layered applications deployment, configuration settings, refinement adjustments, and more. Integrating application layers with custom configurations in a distributed framework requires professional and customizable deployment tools. Microsoft Windows Installer (MSI) deployment can complete all of these tasks and even more tasks. However, some efforts are needed to make it run. Fortunately, the installation and deployment of Visual Studio .NET makes this fairly simple.

Deployment example

I created a sample application to demonstrate the functionality of MSI deployment in Visual Studio .NET. This is a very simple application, with only one form that shows the customer.

The app has a CustomSTeps class library. This library includes a setupdb component class that inherits from the Installer class. This component class provides a custom database installation. It customizes the OSQL process and runs the process to install and delete the database. The focus of I tell is mainly in this class library.

Then you have a Customer installation, this is a installation item. This project provides components that interface with users, which can be used to capture database information such as server names, database names, user IDs. Then, it will copy these files to the target and create a database, database objects, and basic data. Finally, it will configure the connection string properties in the configuration file with the help of the CustomSteps class library.

Back to top

Create sample application

Even if you intend to create this example while reading this article, please download the source code included with this article. (Creating this basic Customer application does not have something to point out, because it includes its purpose just to finally understand how the installer runs!)

Create an empty solution, name it INSTALLERSAMPLE, then add it to the downloaded Customer application. This app has only one form with a data grid that displays customers in the database, as shown in Figure 1. There is a resources folder, where the Customers.xml file is configured. Note that the build action attribute of the file is set to Content. Introducing this file is to demonstrate the importance of this build action.

App.config has the following configuration settings. Later, you will see this value set at runtime.

Back to top

Create a customsteps library

This step will help you process this custom database installation. In the installation project, you can specify Custom Actions. The output of this project is one of the Custom Action steps, so it is called steps after the file is copied to the target.

Create a new class library CustomSteps, add a new Installer class, then rename it as setupdb.cs. Before entering the implementation phase, you must decide how to install the database. Table 1 provides some common methods. Table 1. Database deployment method. Mode Description Backup Restore SQL Server provides options for backup and restore databases. You can restore using System.Data.sqlclient namespace and a specific data stock process. This may be the easiest technology, but it has some problems (such as hard coding names) and other configuration considerations. SQL-DMO If you plan to deploy VB / COM / SQL-based applications, this is one of the best ways. SQL-DMO provides packaged MS SQL Server objects for administrative purposes. These include creating a database and other objects. You can create an exe with DMO objects, and the DMO object will package the operation of creating a database. Script files This may be the best comprehensive approach because it is usually a best practices for maintaining / managing scripts. You can use "OSQL" to perform these scripts. However, before steering deployment, you must first run a valid SQL script test. In addition, use BCP utilities to migrate a large amount of data in various formats. It is also a good choice. ADO.NET has a good reliability, but will involve a lot of maintenance and encoding work. You can create some C # methods to create a database, object, and use the SqlClient namespace inserted statement.

There is a database in this sample application, which only two tables: TBLCustomer and TBLADDress. In this scenario, assuming that you have decided to use a script file-based deployment. Typically, in the company's development environment, the database and its objects are tightly controlled by DBA, and the developers are required to maintain their own DML statement scripts. When generating, you can run a predefined event to create a SQL file. Therefore, suppose there are four SQL files as follows:

Database.sql - The script used to create a database.

DropDatabase.sql - The script used to delete the database.

Objects.sql - the script of the table and other objects (if needed).

StandingData.sql - DML script of the table.

The name of the database should be specified by the user. The placeholder can then replace the real database name at runtime. In Database.sql / DropDatabase.sql (see files in the download), I use <> as a placeholder. Copy these scripts to a separate subfolder called Scripts in the project. Note that even here, the build action attribute of these script files is still Content.

Alternatively, you can have multiple Objects and StandingData SQL script files; you may want to achieve sufficiently flexible and can handle this situation. Create installationFiles.xml under the Resources subfolder and set the build action property to Embedded Resource. Listing 1 shows the contents of the XML file.

Listing 1. InstallationFiles.xml content.

This XML file can help you identify which files are used to create or delete databases and objects and records. You can add multiple elements below File and use the SQL file name to be executed to configure the name properties.

Now, you already have these script files, as well as an XML file that provides information about what operations to perform each script file. Next, I want to transfer attention to setupdb.cs. It is inherited from system.configuration.install.installer, the latter is the base class of all custom installer. Please write down the runinstaller (true) attribute; make sure this Custom Action is called. The Installer class provides custom options for you by overriding the following four key ways:

INSTALL - calls in installation steps.

Commit - Complete the installation process.

Rollback - Reduces the system to the state before installation. Call Rollback when the install step fails. Typically, it should be performed with the same task as uninstalling when rewriting the method.

Uninstall - Remove the installed file and reset / delete any configuration completed during the installation process. If this step fails, it is usually not possible to restore the state before the installation.

In addition, there are some ONAFTERs and ONBEFORE events that are mentioned earlier. But in this example, I only renounce the install and uninstall methods.

First, let's describe the install method. This method must perform the following steps:

1.

Capture and calculate the database connection details entered by the user.

2.

Configure the connection string in the app.config file Connectionstring settings.

3.

Run the script file in a suitable order.

First, add the code in Listing 2 to your setupdb.cs.

Listing 2. The code of the Install method.

Public Override Void Install

System.collections.idictionary Stateaver

{

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 value

For (int int intKeys = 0; intKeys

{

IF (Keys [INTKEYS]. Equals ("Database")))

StateSaver.Add ("Database",

Parameters [keys [intKeys]]. TOSTRING ());

Else IF (Keys [INTKEYS]. Equals ("Server")) {Stateaver.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 ());

Cn.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 uncompete ...

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.createdTabase ();

DBINSTALL.CREATEOBJECTS ();

Dbinstall.createRecords ();

}

}

Catch (Installexception INST)

{

Throw (INST);

Catch (Exception Generic)

{

EventLog.WriteEntry ("setupdb", generic.message,

EventLoGENTRYTYPE.ERROR);

Throw new installexception (generic.message);

}

}

In Listing 2, please pay attention to the signature of the method. It has a idictionary parameter called StateSaver. It maintains the information required for this installer during install, commit, rollback, etc. Initially, this value is NULL. Only when you call Base.Install (StateSaver), the IDictionary object reflects the current state of the process. Therefore, each rewriting method needs to call the base class to initialize StateSaver. If you perform multiple custom installations in each step, this is very important. The next step is to get the parameters passed through the Custom Actions property CustomActionData.

StringDictionary parameters = context.parameters;

This context property is very important because it returns System.Configuration.install.installContext. Even before executing the install method, the installer has set the Context property of the installer. Table 2 points out some important members of InstallContext.

Table 2. Some important members of InstallContext. Members Description LogMessage (String) Record any message in the specified logger. The path to the log is specified in the constructor of the installer. The name / value parameters of the log file can be passed to the custom installer. Parameters returns StringDictionary that contains name / value. This is an important property that will return the command line name / value pair in CustomActionData. ISPARAMETRUE (String) If the specified parameter is set, return true, if no setting is not set, return.

The first part of the code in Listing 2 retrieves different key value pairs and set them into StateSaver. These include database values ​​entered by the user, such as server name, database name, user ID, password, and mountable target directory. Of course, there are some crucial in these details when you want to uninstall. You need the database name that will be deleted during the uninstall process, its connection string, etc. Therefore, in order to keep the installed state, all of these StateSaver values ​​are stored in a file called * .installState by the installer. After installation, you can view the file in the folder. Use the file in this way so that you can avoid the registry. In addition, when you build a real installer, it is best to choose to use StateAver. If a user deletes this file after installation, the installer encounters problems during the uninstallation process.

The second part of the list 2 uses the value input by the user to check the database connection. If the user does not enter a user ID, this step is tested for a trusted connection. This is not a simple way to identify an integrated mode verification selection or hybrid mode verification. However, the restrictions in the existing user interface editor make it possible. In the end, this section also creates a connection string for the database.

The third part of the list 2 can set the connection string in the application configuration file. This section loads the configuration file as an XML document to traverse the Connectionstring node and set this value. Please note how the path to the file is put together.

The fourth part of Listing 2 actually creates this database by delegating the task of the run script to another class: DatabaseInstaller.

Copy DatabaseInstaller.cs (sample files in the download) to your project. DatabaseInstaller has two constructor to deliver database details entered by the user. In addition, it has a private constructor, as shown in Listing 3. Listing 3. Load installationFiles.xml from embedded resources.

XmlDocument config = null;

Private DatabaseInstaller ()

{

System.io.Stream Stream =

System.Reflection.Assembly.getexecutingAssembly ().

GetmanifestResourceStream

"Customsteps.Resources.IstallationFiles.xml");

CONFIG = New XmLDocument ();

Config.Load (stream);

}

This constructor loads the installationFiles.xml file containing the details of these script file details. You still remember, you have explicitly set this file to Embedded Resource. After that, you have a variety of members of the package to create database functions. Table 3 provides some detailed information about the above.

Table 3. Members of DatabaseInstaller. Members Description CREATEDATABASE () Tes to create a public approach to the database. DropDatabase () Delete the public method of the database. CreateObjects () Create an object (such as a table, stored procedure, etc.) public approach. CreateRecords () Create any basic data or configuration data in various tables. GetFullPath returns a private way to string with a script file full path. It uses the name of this script file. PopulateDatabaseNamePlaceHold uses the actual database name to replace the private method of the placeholder in the script file. GetcommonProcessArguments creates a private way of the parameter, which will pass to OSQL to create a database. ExecuteScripts For a given XPath, this private method gets the file name of the script file and uses OSQL to recursively execute them. This is used by CreateObjects and CreateRecords.

Now I will mainly tell the Createdatabase () method, as the implementation followed by most other methods is similar. Listing 4 provides the main code of the method.

Listing 4. Code snippet created by the database.

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 / data / add / file").

Attributes ["name"]. Value;

}

Else {

// Customized Message ..

Throw new installexception

"Configuration for Database File"

"CREATION MISSING.");

}

// Get Arguments

ProcessInfo.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 can be used to better control the process (OSQL) you want to start. It provides you with a simplicity of setting parameters, control window styles, etc. It can then be used in conjunction with the processes shown in Listing 4. After the process starts, you can wait for it to exit, or use the waitforExit method to control exit time. In addition, you use the initialized XMLDocument in the constructor to select a predefined node, where you store the name of the database script file. Then, you delegate the process built into a specific method (GetcommonProcessarguments), which passes the name of the script file and the name of the database you want to run from it. During the database creation, you can transfer Master as a database because your application database is in the creation process. The next line code (Method PopulateDatabaseNamePlaceHold) uses the actual name provided by the user to replace the placeholder of the database name.

Please note that whenever you have a try, catch code block or a custom exception occurs, an installexception will be thrown. Its type is SystemException, which will even trigger this type of exception in the CommTe, Rollback, and Uninstall phases. Preferred approach is to use EventLog or custom installation log files to track and log this error. You can use the context property to create a custom log.

You have created a successful custom installation for the database!

Using a similar way, you can rewrite the Uninstall method of the Installer class and then copy the code included in the example. You will see this, when the user confirms, delete the database's task is commissioned to the DatabaseInstaller class. In the next step, you will create a installation item and use it to deploy.

Back to top

Create an installation item and install

Finally, you have to prepare to create a installation item. Table 4 illustrates various deployment methods provided by VS.NET (see also Figure 2).

Table 4. Template options. Template Description Setup Generates MSI. Usually used when generating Windows-based applications. However, you can customize them to meet any form of deployment needs. WebSetUp Creates a virtual directory for the web deployment. You can also use this template to customize. Merge Module It helps you share components with other applications, such as .NET Framework, MSDE, etc. CAB outputs a compressed package (.cab) file to make it distribute.

For this example application, select a installation project template and name it Customer Setup.

Visual Studio provides a variety of editors for creating an installation application. I will explore some of these editors and their usage.

Figure 3 shows the file system editor. For items, files, files to be delivered to the deployment environment, you can add output from them here. You can also choose a dedicated folder as your file, such as the System folder, Program Files folder, etc. Create two subfolders under the Application folder, named bin and install, respectively. Then select the bin folder, right-click, and select Add "|" Project Output "to obtain the dialog shown in Figure 4.

Select the "Customer" item in the combo box and select Primary Output and Content Files. The main output represents the item output, and the content file will contain a file tagged as build action content. This is a very critical location of the build action attribute. You can also copy the source file, but this example does not need to be copied.

Go to the Install folder, and the main files and content files in CustomSteps.

Now you have completed the process of adding submission to deployment. The next step is to set the user interface for installation.

User Interface Editor By default, you can see a set of interfaces such as Target Location in the User Interface Editor. However, for this application, you need to capture four parameters: server name, database name, user ID and password. Right click on the "START" section and right-click Add Dialog. Choose the TextBoxes (A) option for the four parameters and determine its location as shown in Figure 5. At this point, you can configure the properties shown in Table 5. Table 5. Important properties of the TextBoxes (a) dialog box.

Property Description Bannertext The flag is displayed for the screen. Edit1Label's label of the first text box. Set to Server Name. Find other tag properties and set it accordingly. Edit1Property This property is very important: the name provided here is to access in Custom Actions. Set this property to Server_name. Similarly, setting Database_Name, User_Name, and Password for other attributes displayed in the example. Edit1Value is set to the value of the default value. You can specify the available default properties. For example, [ComputerName] sets the default value of this text box to the name of the user's computer. Please check the values ​​set in the sample application for more information. Edit1Visible This value helps you control the visibility of the text box. For this example, all properties are true.

The user interface editor has some other content. You can study its other dialogues, their properties, and some of the default Windows properties available to truly extend your custom. However, for this example, I need this, so I want to turn to the start condition editor.

Back to top

Start condition editor

In some cases, your deployment is to meet a specific runtime, such as .NET Framework, MSDE, MDAC version, and more. If you don't have any prerequisites, there is no need to point out, and you can continue to install. Start condition editor helps you complete this task. In this example, in addition to the existing default ".NET Framework" criteria, I have to add a condition. Click the Start Condition Editor, then right-click the "Search Target Machine" section. Here you can assess a particular condition. Suppose this installer is facing Windows OS 4 and higher. Although the default Windows condition can be used, I want to tell you how to create your own conditions. Select Add File Registry Search and set these values ​​as shown in Figure 6.

Property specifies a name for your search assessment. RegKey is the target keyword to search. Root is the root of the registered tree to search. These settings searches for the RegKey path of the Value collection (for example, the CurrentVersion value name). This search returns any values ​​that it found in the CurrentVersion value name. Right-click "Launch Conditions" and add a new condition. Figure 7 provides settings for this condition. Note the construct property. This property should be calculated as true and installation can continue. Here, you provide WindowsVersion> = "4.0". WindowsVersion is the name of the registry search criteria. If returned, the installer displays the error message configured in the Message property and aborts the installation. You can configure a variety of conditions in this way so that your deployment is condition.

In the next section, you will see how to use custom generated CustomSTeps installations.

Custom Action Editor You can add any exe, script files, or DLL files belonging to a part. In this example, you need to add just a CustomSteps class library. You can add this library at the Install phase. To do this, right-click the "Install" section and select "Add Custom Action". Then, traverse the Applications folder / install, then select the main output, as shown in Figure 8. Rename this step as Install Database.

Figure 9 shows the properties configured for the Custom Action. The most important of these properties is Condition and CustomActionData. Condition ensures that this installer runs only when a specific condition is met. CustomActionData passes parameters to the CustomAction Exe / DLL.

In this example, I set these properties as follows:

/ server = [server_name] / target = "[targetdir] /"

/ Database = [Database_name] / username = [user_name]

/ password = [password] / version = [windowsversion]

Note that setting Windows default properties (such as targetdir) and the difference between the properties (such as server_name and windowsvers) created in the settings user interface editor or initiating condition editor. However, the general format is / name = [value], followed by the backslash, depending on the attribute. Also, note the installerclass attribute value. The parameters transmitted here will be used as Context.Parameters. You can see how these parameters are used in the CustomSteps component. When you run the installer, the custom installer's Install method will be called after copying all deployment files.

You add a Custom Action in the UNInstall phase according to a similar approach and rename it Uninstall Database. However, this does not require any parameters. The required parameters will be provided using the StateSaver collection. This is why the * .installState file created during the installation process is very important. You can see this file in the Install folder. StateSaver is initialized using this file, so it is recommended that you add all of these values ​​you need to add uninstall operations to StateSaver during installation.

According to your solution, you can also use some other editor, such as the File Type Editor and Registry Editor. However, these editors are not required for this example. You can install it now. Generate a solution and installer project, then install and uninstall to test it.

Back to top

summary

The process described here is only the horn of the iceberg. It is important to analyze and customize your deployment based on your own application requirements. In this example, you see the usage of the Build Action property of the file.

Try to reduce the use of registry keys and handle the worst case. Please test your installer for all possible scenarios (if you have deleted * .installState files) and try to process these solutions as possible.

In addition, in this example, I have never checked the active database connection. This is very important during the uninstallation process. The emphasis on the application's uninstallation should give the same degree of importance as an installation, because you will definitely reinstall the application again.

If the current VS.NET installation editor is limited, then this limit exists in the user interface editor. I believe that for future versions of VS.NET, the best solution is to provide developers with an option that adds its own user interface when design and has a conditional option for dialog. However, you are currently only only Custom Actions.

Abnormal treatment is also very important, because always keep the state before installing after rollback / uninstallation.

Don't make any assumptions for the target runtime,. Instead, always use the start condition to verify that there is existence of each of the content you need.

Finally, be sure to generate an installer suitable for your application. This process deserves your project time and cost.

转载请注明原文地址:https://www.9cbs.com/read-42933.html

New Post(0)