转:HowTo: Deploy .NET ActiveX Control
From ; http://nikolkos.blogspot.com/2009/08/howto-deploy-net-activex-control.html
这篇技术文章居然国内访问不了,转之分享。
You have developed an ActiveX Control with your favorite .NET language, and you think it's time to deploy it. The most common way is to pack the control to a CAB file and use Internet Explorer for installation (via "Internet Component Download" service). If you are VB6 programmer the first problem you'll face: Visual Studio does not have "Package & Deployment Wizard".
There are some nice posts in the web describing how you can accomplish this. But not everything work as expected (Vista issues, update troubles and etc.) Hereby I'll try to summarize all info I have found and use at the moment... Let's do that step by step.
- Create an ActiveX Control: MyControl.dll
Create a class library project, add a class, implement the control's logic within the class. Mark the class with attributes: ProgId, Guid, ComVisible.
Assume that the library has 1.0.0.0 file and assembly versions. - Create an installation package: MyControl.msi
You can use Visual Studio Setup project to create Windows Installation package (MSI) of your ActiveX Control.
- Add primary output from the class library project.
- Set for the DLL a Register option to vsdrpCOM.
- Add a registry key to HKCR:
"CLSID\{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\InstalledVersion" with default string value: 1,0,0,0. Replace XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX with a GUID of your class in the control (see GuidAttribute you mark the class with). This key is used by Internet Component Download service to detect version of installed ActiveX control.
- Build the project.
More info:
- Deployment Changes in Visual Basic .NET
- How to: Create or Add a Setup Project
- Visual Studio Setup - series of articles by Phil Wilson
- Be aware of the Visual Studio 2008 bug for 64bit target platform - Create a bootstrapper: setup.exe
To ensure that your control will install and run successfully, you must first ensure that all components upon which it is dependent (.NET Framework, third-party components and etc.) are already installed on a target machine. In other words, we need a bootstrapper.
Create a text file named "bootstrapper.proj".
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SDKBootstrapperPath>$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\GenericBootstrapper\3.5@Path)</SDKBootstrapperPath>
</PropertyGroup>
<ItemGroup>
<BootstrapperFile Include="Microsoft.Net.Framework.3.5">
<ProductName>Microsoft .NET Framework 3.5</ProductName>
</BootstrapperFile>
<BootstrapperFile Include="Microsoft.Windows.Installer.3.1">
<ProductName>Windows Installer 3.1</ProductName>
</BootstrapperFile>
</ItemGroup>
<Target Name="Bootstrapper">
<GenerateBootstrapper
ApplicationFile="run.exe"
ApplicationName="MyControl"
BootstrapperItems="@(BootstrapperFile)"
OutputPath="$(TargetDir)"
ComponentsLocation="HomeSite"
ComponentsUrl=""
CopyComponents="false"
Culture="en"
Path="$(SDKBootstrapperPath) "/>
</Target>
</Project>
Build the project with MSBuild:
"C:\Windows\Microsoft.NET\Framework\v3.5\msbuild.exe" /target:Bootstrapper bootstrapper.proj
More Info:
- GenerateBootstrapper Task
- Application Deployment Prerequisites
- Possible issues with the Visual Studio 2008 bootstrapper and how to work around them - Create an executor application: run.exe
To install the MSI on a target machine we'll use msiexec application that belongs to the Windows Installer Service. It seems that in Vista the CAB installer refuses to execute any application that is not included in the CAB file.
So, we will need a small utility that is able to run an external program which path is passed via the command line (if no path is passed it should successfully exit).
C# code sample for the run.exe:
static int Main()
{
// Get command line arguments.
string[] args = Environment.GetCommandLineArgs();
// If no arguments are passed then return.
if (args.Length < 2)
{
return 0;
}
// Get the file name to run.
string fileToRun = args[1];
// Compile command line for the file to run.
var cmdLine = new StringBuilder();
for (int i = 2; i < args.Length; i++)
{
cmdLine.AppendFormat(arg.Contains(" ") ? "\"{0}\" " : "{0} ", arg[i]);
}
// Execute the external file.
var process = Process.Start(fileToRun, cmdLine.ToString());
// Wait the process to complete.
process.WaitForExit();
return process.ExitCode;
} - Create HTM file: MyControl.htm
The HTM file is an HTML description of the ActiveX Control that is used by Internet Component Download service.
<html>
<head>
<title>MyControl.CAB</title>
</head>
<body>
<OBJECT ID="MyControl" CLASSID="CLSID:XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" CODEBASE="MyControl.CAB#version=1,0,0,0"></OBJECT>
</body>
</html>
Replace XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX of CLASSID with a GUID of your class in the ActiveX Control. - Create INF file: MyControl.inf
The information file provides installation instructions that Internet Component Download service uses to download and install the CAB.
[version]
signature="$CHICAGO$"
AdvancedINF=2.0
[Setup Hooks]
bootstrap=bootstrap
install=install
[bootstrap]
run="""%EXTRACT_DIR%\setup.exe"""
[install]
run="""%EXTRACT_DIR%\run.exe""" msiexec.exe /i """%EXTRACT_DIR%\MyControl.msi""" /qb
More Info:
- Introduction to Internet Component Download
- Packaging ActiveX Controls
- About INF File Architecture - Create CAB file: MyControl.cab
Use CABARC utility from CAB SDK to create a cab:
cabarc.exe n MyControl.cab MyControl.inf MyControl.msi run.exe setup.exe
More Info:
- MS CAB SDK - Deploy an updated version of the control
The control is automatically downloaded and installed when the version number is higher than that of the control currently installed on the client computer. So in order to deploy an updated control you must change the assembly and file versions.
You should keep in mind that Windows Installer service uses 3 digits version format. The format of the version is as follows: major.minor.build
The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255. The third field is called the build version or the update version and has a maximum value of 65,535.
For example, you have deployed a control of 1.0.0.0 version. To deploy the updated control you can change its version to 1.0.1.0. Also don't forget to change versions of the MSI and HTM files.
More Info:
- Windows Installer Guide: Patching and Upgrades
- Windows Installer: File Versioning Rules
- .NET Framework Developer's Guide: Assembly Versioning
Now you have MyControl.htm and MyControl.cab - it's all you need to deploy the ActiveX Control.
Some links that were used as sources for implementation ideas:
- Create ActiveX in .NET Step by Step
- ActiveX Control deployment on Vista using MSI and CAB setup hooks
- codebase #Version not updating CAB with .NET control wrapped as COM
P.S.
If you target 32/64-bit platforms you can use the following trick.
- Create separate MSI packages for every platform: MyControl-X64.msi, MyControl-X86.msi.
- Change the [install] hook in the INF file to use a macro in a filename of MSI:
[install]
run="""%EXTRACT_DIR%\run.exe""" msiexec.exe /i """%EXTRACT_DIR%\MyControl-$(Platform).msi""" /qb
- Adjust the run.exe application to detect a platform it executes on and replace the macro with the proper platfrom value.
static int Main()
{
// Get command line arguments.
string[] args = Environment.GetCommandLineArgs();
// If no arguments are passed then return.
if (args.Length < 2)
{
return 0;
}
// Get the file name to run.
string fileToRun = args[1];
// Get platform value to replace macro in command line.
var macroPlatformValue = GetPlatformValue();
// Compile command line for the file to run.
var cmdLine = new StringBuilder();
for (int i = 2; i < args.Length; i++)
{
// Substitute the platform macro.
var arg = Regex.Replace(args[i], @"\$\(platform\)", macroPlatformValue, RegexOptions.IgnoreCase);
cmdLine.AppendFormat(arg.Contains(" ") ? "\"{0}\" " : "{0} ", arg);
}
// Execute the external file.
var process = Process.Start(fileToRun, cmdLine.ToString());
// Wait the process to complete.
process.WaitForExit();
return process.ExitCode;
}
static string GetPlatformValue()
{
string arch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE") ?? "x86";
string arch2 = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432") ?? "x86";
if (arch == "AMD64" || arch2 == "AMD64")
{
return "x64";
}
return arch;
}
Actully, if you generate the MSI file through vs2008 and try to intall it to 64Bit env, you will find the IE64 still cannot work.
A good news is we can use Orca to edit the msi and fix this issue.
Open the MSI file using ORCA, look at the component table. Find component which ComponentId is equal to CLSID in .NET assembly.
Actual Results
The attribute value of the component is 4 (msidbComponentAttributesRegistryKeyPath). That indicates that the component is 32bit only.
http://msdn.microsoft.com/en-us/library/aa368007(VS.85).aspx
Expected Results
The attribute value of the component should be 260 (msidbComponentAttributesRegistryKeyPath | msidbComponentAttributes64bit).
Edit the MSI with ORCA: change 4 to 260. Install the edited MSI - the assembly is registered correctly and COM object can be created by an unmanaged 64bit application.
TAP Code (if applicable)