Export (0) Print
Expand All
Abortable Thread Pool
The Analytic Hierarchy Process
API Test Automation in .NET
Asynchronous HttpWebRequests, Interface Implementation, and More
Bad Code? FxCop to the Rescue
Basics of .NET Internationalization
Behind the Scenes: Discover the Design Patterns You're Already Using in the .NET Framework
BigInteger, GetFiles, and More
Binary Serialization of DataSets
Building Voice User Interfaces
Can't Commit?: Volatile Resource Managers in .NET Bring Transactions to the Common Type
CLR Inside Out: Base Class Library Performance Tips and Tricks
CLR Inside Out: Ensuring .NET Framework 2.0 Compatibility
CLR Inside Out: Extending System.Diagnostics
CLR Profiler: No Code Can Hide from the Profiling API in the .NET Framework 2.0
Concurrent Affairs: Build a Richer Thread Synchronization Lock
Custom Cultures: Extend Your Code's Global Reach With New Features In The .NET Framework 2.0
Cutting Edge: Collections and Data Binding
Const in C#, Exception Filters, IWin32Window, and More
Creating a Custom Metrics Tool
DataGridView
DataSets vs. Collections
Determining .NET Assembly and Method References
Experimenting with F#
File Copy Progress, Custom Thread Pools
Finalizers, Assembly Names, MethodInfo, and More
Got Directory Services?: New Ways to Manage Active Directory using the .NET Framework 2.0
High Availability: Keep Your Code Running with the Reliability Features of the .NET Framework
How Microsoft Uses Reflection
ICustomTypeDescriptor, Part 2
ICustomTypeDescriptor, Part 1
Iterating NTFS Streams
JIT and Run: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects
Lightweight UI Test Automation with .NET
Low-Level UI Test Automation
Make Your Apps Fly with the New Enterprise Performance Tool
Managed Spy: Deliver The Power Of Spy++ To Windows Forms With Our New Tool
Memory Models: Understand the Impact of Low-Lock Techniques in Multithreaded Apps
Microsoft Java Virtual Machine Update
Microsoft .NET Framework Delivers the Platform for an Integrated, Service-Oriented Web, Part 2
Mini Dump Snapshots and the New SOS
Mutant Power: Create A Simple Mutation Testing System With The .NET Framework
NamedGZipStream, Covariance and Contravariance
.NET Internationalization Utilities
.NET Profiling: Write Profilers With Ease Using High-Level Wrapper Classes
No More Hangs: Advanced Techniques To Avoid And Detect Deadlocks In .NET Apps
The Perfect Host: Create and Host Custom Designers with the .NET Framework 2.0
Phoenix Rising
Scheme Is Love
Security Enhancements in the .NET Framework 2.0
Sepia Tone, StringLogicalComparer, and More
Software Testing Paradoxes
Stay Alert: Use Managed Code To Generate A Secure Audit Trail
Stream Decorator, Single-Instance Apps
StringStream, Methods with Timeouts
SUPERASSERT Goes .NET
Tailor Your Application by Building a Custom Forms Designer with .NET
Test Harness Design Patterns
ThreadPoolPriority, and MethodImplAttribute
ThreadPoolWait and HandleLeakTracker
Three Vital FXCop Rules
A Tidal Wave of Change
To Confirm is Useless, to Undo Divine
Touch All the Bases: Give Your .NET App Brains and Brawn with the Intelligence of Neural Networks
Transactions for Memory
Trustworthy Software
Tune in to Channel 9
UDP Delivers: Take Total Control Of Your Networking With .NET and UDP
UI on the Fly: Use the .NET Framework to Generate and Execute Custom Controls at Run Time
Unexpected Errors in Managed Applications
Unhandled Exceptions and Tracing in the .NET Framework 2.0
Using Combinations to Improve Your Software Test Case Generation
Wandering Code: Write Mobile Agents In .NET To Roam And Interact On Your Network
What Makes Good Code Good?
XML Comments, Late-bound COM, and More
Expand Minimize

Choosing Between ClickOnce and Windows Installer

 

Michael Sanford
Xambi Solutions, Inc.

January 2005

Summary: Michael Sanford takes an in-depth look at the .NET Framework 2.0 ClickOnce technology and compares it with the existing Windows Installer technology. Michael concludes by making some recommendations on when to use each technology. (15 printed pages)

Contents

Introduction
Windows Installer Overview
Products, Features, and Components
Windows Installer Features
ClickOnce Overview
Key Differences
How to Choose?
Use Both Windows Installer and ClickOnce
Conclusion

Introduction

One of the most exciting features demo'ed at the 2004 PDC was the new ClickOnce deployment technology. With an impressive feature set, ClickOnce is certain to be a popular technique for deploying applications, but what about Windows Installer? In this article, we'll delve into the features of ClickOnce and highlight the key differences between the two technologies along the way. Finally, we'll provide some guidance on when each of these deployment technologies should be used.

Windows Installer Overview

Microsoft first introduced Windows Installer in 1999 in response to customer feedback about the challenges faced when deploying applications. Specifically, they pointed out the following weaknesses in existing installation technologies:

  • They failed to consistently and reliably manage shared resources such as COM Components, ActiveX controls, and so on. This issue is commonly referred as "DLL Hell".
  • They failed to provide standard customization capabilities.
  • They did not provide a way for an application to repair or maintain itself once it was installed.
  • They failed to provide a mechanism to allow parts of an application to be installed on demand.
  • They failed to provide a consistent mechanism for the installation to interact with the operating system.

Windows Installer is a system-level component included in all operating systems starting with Windows 2000. Where traditional installation technologies relied on proprietary scripts and file formats, Windows Installer uses an open, database-like file format to describe an application, its resources, and the operations required to successfully install it. The internal structure of the database format, including its standard table and column definitions, can be found in the Windows Installer section of the Platform SDK. The latest version of the SDK can be found at http://www.microsoft.com/msdownload/platformsdk/sdkupdate/.

Windows Installer packages are stored in a file with an "msi" file extension. While the format of these files is a proprietary implementation based on OLE structured storage, the content of an msi file is easily accessible using tools provided with the Platform SDK. The primary msi editing tool provided by the Platform SDK is code-named "Orca". As seen in Figure 1 below, the logical structure of an msi file is quite similar to that of a relational database.

Figure 1. Viewing the contents of an msi file with Orca

In addition to the tools provided by the Platform SDK, there are several third-party tools available that dramatically ease the task of msi authoring, and administration. A few of the major vendors are:

Products, Features, and Components

Windows Installer packages rely on a logical structure of Products, Features, and Components to divide an application into cohesive units of functionality. Features are the lowest-level entity with which a user can interact with the installation, while components are the lowest-level entity that the installation developer works with. For example, a graphics application might consist of two features: a core application feature that installs the application's executable (.exe), and a clip-art feature that installs an optional collection of clipart. The core feature of our fictitious installation might be comprised of two components. The first component might act as a container of the application's primary executable file and the registry entries it depends on to start up properly. The second component of the core feature might include a shared .dll and the COM registration information that must be recorded in the registry for the COM sub-system to recognize it.

Windows Installer Features

Windows Installer is a complex technology with a significant number of customization and configuration capabilities. Exploring all the features of Windows Installer is beyond the scope of this article. Instead, we'll take a quick look at some of the more significant features.

  • Programmability
    Windows Installer provides a robust set of APIs and a complete Automation Interface that allow applications to interact with Windows Installer, both as a part of the authoring process and as part of the installation and maintenance process.
  • Customization
    The runtime behavior of an installation package can be customized in a number of different ways. For example, an installation can be developed with support for setting specific options on the command line, or a special Transform File can be created that actually modifies the contents of the msi file at installation time, thereby changing the behavior of the installation. The Platform SDK and most third-party tool vendors provide support for creating Transform Files.
  • Install On Demand
    Windows Installer supports a concept known as advertising, which allows individual parts of an application (or the entire application) to be installed only when the user needs it. Through deep integration with the operating system, Windows Installer is able to detect when an application, or a specific feature of an application, is needed. For example, if an application is installed that is capable of editing .foo files, Windows Installer will check to be sure the app is installed and install it if needed when the user double-clicks a .foo file in Windows Explorer.
  • Resiliency
    Similar to the way Windows Installer is able to install an application or feature on demand, it is also able to repair an application as the user interacts with it. For example, when a user causes an application to be run by clicking a Start Menu shortcut, or double-clicking an associated file, Windows Installer is able to intercept the request and verify that the appropriate elements of the application are properly installed. If a problem is detected, a repair will occur before the application is run.
  • Transactional Installation
    Much of the inner workings of Windows Installer is devoted to ensuring that running an install won't adversely affect your system if something critical fails during the installation. Windows Installer accomplishes this by creating an internal "script" of exactly what must be done to properly install the application. If something goes wrong during the installation, the script can essentially reverse any actions that have been taken, thereby leaving the system in its original state.
  • Source Resiliency
    An important aspect of the ability of Windows Installer to repair an application after it has been installed is that at times, it will need access to a copy of the original installation source. For example, if Windows Installer determines that a critical file is missing or corrupted, it needs to get a clean copy of that file. Source Resiliency is a mechanism that allows Windows Installer to search in multiple locations to find the original installation and then to prompt the user for the media if it cannot be found at any other location. Source locations can be the local file system, a network share, or an Internet URL. Windows Installer 3.0 has also enhanced these capabilities, allowing administrators to more easily manage source locations.
  • Upgrades and Patching
    A typical product lifecycle does not end after the application has been successfully installed. As nice as that would be, the reality is that most applications need to be updated or patched to deploy fixes or new features. Windows Installer supports the creation and distribution of byte-level patches for deploying hotfixes and upgrades for distributing more comprehensive application updates. Windows Installer 3.0 has introduced significant improvements to patching by supporting patch sequencing and the ability to uninstall patches.
  • Managed Environment Support
    In large corporate environments, systems are often locked down to prevent users from installing rogue applications and putting the integrity of that system at risk. Windows Installer includes several features that allow certain msi-based installs to be "blessed" by the System Administrator to run in this type of locked-down environment.

ClickOnce Overview

ClickOnce is a brand new deployment technology that ships as a part of the .NET Framework version 2.0 and was first demo'ed at PDC 2004 in Los Angeles. ClickOnce is focused on bringing the simplicity of Web application deployment to smart client applications, while providing a secure runtime environment. To deploy an application with ClickOnce, you simply need to place the application files on a Web server, file share, or the local file system and provide the user with a link to the application manifest.

If you are developing with Visual Studio 2005, publishing an application with ClickOnce is a trivial task, thanks to the Publish Wizard. To access the Publish Wizard, simply right-click the project name in the Solution Explorer, then select Publish from the context menu. Alternatively, you can access the Wizard from the Publish tab of the Project Properties dialog box.

Figure 2. Visual Studio 2005 Publishing Wizard

A ClickOnce deployment is controlled through the use of two XML manifest files: a deployment manifest and an application manifest.

Deployment Manifest

The deployment manifest is used to describe the application's deployment. It includes the location of the application manifest, files described in the application manifest, and the version of the most current release that clients should be running.

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1
    assembly.adaptive.xsd" manifestVersion="1.0"
    xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:schemas-
    microsoft-com:asm.v2" xmlns:asmv1="urn:schemas-microsoft-
    com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
    xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity name="My Test App.application" version="1.0.0.20"
    publicKeyToken="7726c4d654f5bf83" language="neutral"
    processorArchitecture="msil" xmlns="urn:schemas-microsoft
    -com:asm.v1" />
<description asmv2:publisher="Xambi Solutions" asmv2:product="My Test
    App" asmv2:supportUrl="http://michael
    -dev2/WindowsApplication1/Support.htm" xmlns="urn:schemas
    -microsoft-com:asm.v1" />
<deployment install="true">
<subscription>
<update>
<expiration maximumAge="3" unit="days" />
</update>
</subscription>
<deploymentProvider
    codebase="http://michael_dev2/WindowsApplication1/My%20Test%20App
    .application" />
</deployment>
<dependency>
<dependentAssembly codebase="My Test App_1.0.0.20\My Test 
    App.exe.manifest" size="4908">
<assemblyIdentity name="My Test App.exe" version="1.0.0.20"
    publicKeyToken="7726c4d654f5bf83" language="neutral" 
    processorArchitecture="msil" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft
    -com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<dsig:DigestValue>GANTD3FaR5KJiqnEluecM05wtss=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency> 
<Signature Id="StrongNameSignature" 
    xmlns="http://www.w3.org/2000/09/xmldsig#" />
<!-- Details Omitted for Brevity -->
</asmv1:assembly>

Application Manifest

The application manifest is used to describe the application, assemblies, files, resources, and permissions required for the application to function properly. Additionally, the application manifest also dictates the location where updates will be located.

<?xml version="1.0" encoding="utf-8"?> <asmv1:assembly 
    manifestVersion="1.0" xsi:schemaLocation="urn:schemas-microsoft
    -com:asm.v1 assembly.adaptive.xsd"
    xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:schemas
    -microsoft-com:asm.v2" xmlns:asmv1="urn:schemas-microsoft
    -com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <asmv1:assemblyIdentity name="My Test App.exe" version="1.0.0.20"
    publicKeyToken="7726c4d654f5bf83" language="neutral"
    processorArchitecture="msil" type="win32" />
<asmv2:configuration configFile="My Test App.exe.config"
    xmlns="urn:schemas-microsoft-com:asm.v1" />
<entryPoint>
<assemblyIdentity name="My Test App" version="1.0.0.0"
    publicKeyToken="7726C4D654F5BF83" language="neutral"
    processorArchitecture="msil" />
<commandLine file="My Test App.exe" parameters="" />
</entryPoint>
<trustInfo>
<security>
<applicationRequestMinimum>
<PermissionSet class="System.Security.PermissionSet" version="1"
    ID="Custom">
<IPermission class="System.Security.Permissions.EnvironmentPermission,
    mscorlib, Version=2.0.3600.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> <IPermission class="System.Security.Permissions.FileDialogPermission,
    mscorlib, Version=2.0.3600.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089" version="1" Access="Open" /> <IPermission
    class="System.Security.Permissions.IsolatedStorageFilePermission,
    mscorlib, Version=2.0.3600.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089" version="1"
    Allowed="DomainIsolationByUser" UserQuota="10240" />
<IPermission class="System.Security.Permissions.SecurityPermission,
    mscorlib, Version=2.0.3600.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089" version="1" Flags="Execution" /> <IPermission class="System.Security.Permissions.UIPermission, mscorlib,
    Version=2.0.3600.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089" version="1"
    Window="SafeTopLevelWindows" Clipboard="OwnClipboard" /> <IPermission class="System.Windows.Forms.WebBrowserPermission, System,
    Version=2.0.3600.0, Culture=neutral,
    PublicKeyToken=b77a5c561934e089" version="1" Level="Restricted" /> <IPermission class="System.Drawing.Printing.PrintingPermission,
    System.Drawing, Version=2.0.3600.0, Culture=neutral,
    PublicKeyToken=b03f5f7f11d50a3a" version="1" Level="SafePrinting"/> </PermissionSet>
<defaultAssemblyRequest permissionSetReference="Custom" /> </applicationRequestMinimum>
</security>
</trustInfo>
<dependency>
<dependentAssembly codebase="My Test App.exe" size="12288"> <assemblyIdentity name="My Test App" version="1.0.0.0"
    publicKeyToken="7726C4D654F5BF83" language="neutral"
    processorArchitecture="msil" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft
    -com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <dsig:DigestValue>xx4Nai4Nr7Bp5R7xtyqO8gAVsSk=</dsig:DigestValue> </hash>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly preRequisite="true">
<assemblyIdentity name="Microsoft-Windows-CLRCoreComp"
    version="2.0.3600.0" />
</dependentAssembly>
</dependency>
<file name="My Test App.exe.config" size="1222">
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft
    -com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<dsig:DigestValue>BdHEVcmEBWqRZGitWdDZ/vAGGmQ=</dsig:DigestValue> </hash>
</file>
<Signature Id="StrongNameSignature"
    xmlns="http://www.w3.org/2000/09/xmldsig#">
<!-- Details Omitted for Brevity -->
</Signature>
</asmv1:assembly> 

After the application and deployment manifests have been created, they simply need to be copied to the deployment location, along with the required application files. It is important to note that the deployment manifest need not be stored in the same location as the application manifest. For example, the deployment manifest can be shipped on a physical disc. When activated, the deployment manifest will tell the ClickOnce engine where to find the application manifest. This is an important feature of ClickOnce, as it allows you to ensure that users get the latest version of your application from the very first install, rather than installing a version from disc, then immediately requiring an update.

When the user launches your application, ClickOnce will install or update your application according to the settings contained in your deployment manifest. If your app requires elevated permissions, the user will be asked to grant the required permissions:

Figure 3. The user is queried about required permissions

Clicking the More Information link in the lower-right corner of this dialog will result in the informational dialog shown below.

Figure 4. More information about the security warning

And finally, once the application is up and running, we can see in the title bar where the app was deployed from and a special icon overlaying the applications icon. When we hover our mouse pointer over this icon, we can see a special message indicating the security context in which the application is running.

Figure 5. Informational message about the security environment

Note   When writing .NET applications that may run in a secure environment, it is very important that security exceptions be explicitly handled to ensure that the application remains stable and that the user understands what has happened when a security exception occurs.

Key Differences

Now that I've taken a quick look at features of both Windows Installer and ClickOnce, you will note that each technology was quite different goals. To further illustrate this point, consider the following matrix:

Task ClickOnce Windows Installer
Install Files X X
Create Shortcuts X X
Associate File Extensions X X
Install Services   X
Install to GAC   X
Manage ODBC   X
Manage COM+   X
Write to Registry   X
Advertising   X
Self-Repair   X
File/Folder/Registry Permissions   X
Install-time User Interaction   X
Install for All Users   X
Custom Actions at Install/Uninstall   X
Installation Conditions/System Interrogation   X
Auto-Update and Scheduling X  
Forced Updates X  
Security Sandboxing X  
Download/Install Assemblies on Demand X  
Rollback to Previous Version X  

As I mentioned earlier, each of these technologies was designed and developed with a completely different set of goals. Neither is intended to replace the other. ClickOnce offers a very compelling set of features, most of which are not found in Windows Installer technology. These features are specifically targeted at developers building .NET Framework-based smart client applications that don't have sophisticated configuration requirements. Windows Installer is a much broader deployment technology that is inherently designed to be extensible and able to handle any deployment challenge, including .NET applications.

How to Choose?

When it comes to your choice in deployment technologies, you don't need to limit yourself to just one option. The key is to choose the right tool for the right job. While there is no single rule or simple answer, there are some general guidelines you can use to help make the best decision for your specific needs.

  • Does the application install any COM components?
  • Does the application require registering any components for COM-Interop?
  • Does the application install any services?
  • Does the application have to install to a specific location or to the Global Assembly Cache (GAC)?
  • Does the application have any components that are conditionally installed, based on the operating system or runtime environment?
  • Does the application require user input at installation time?
  • Does the application require configuration of system-level services such as Active Directory or COM+?
  • After the application is installed, does it create files, write to the registry, or affect the system in some way that would leave resources behind when the application is removed?

If you answered yes to any of these questions, then today Windows Installer is the best choice for your needs. However, if you don't need to address the scenarios described in the list above, then ClickOnce is an excellent candidate for your deployment solution. If you want to leverage the distinct benefits provided by ClickOnce, then understanding the capabilities of ClickOnce early in your application design process is critical. Deploying an early version of an application with ClickOnce, but then belatedly realizing a need to move to Windows Installer, would create a difficult upgrade path that can be avoided through careful up-front planning.

Use Both Windows Installer and ClickOnce

Yes, that's right! You heard me correctly. Now that I've built a case for how different these two technologies are in their implementation, I am going to shake up your world by telling how you can use them together.

Windows Installer provides advanced capabilities for interacting with the user during an installation. Oftentimes, this is a critical step in the deployment process. The ability of an installation to interrogate the target system to ensure that it meets the minimum requirements of the application is critical. Additionally, most applications tend to have some interaction with the operating environment. This may include writing log files to disk, storing data in the registry, etc. A well-behaved application needs the opportunity to clean up this application-specific data when it is removed from the system. All of these are key deployment concepts missing from the ClickOnce paradigm.

In contrast, Windows Installer has an update and patching story, but it's not nearly as sleek and easy to manage as that of ClickOnce.

With a little thought, planning, and ingenuity, we can leverage both technologies to create a deployment model with the best of both worlds.

Designing the Windows Installer Setup

The basic concept here is to create an outer install based on Windows Installer technology. This outer install takes on the responsibility of inspecting and interrogating the system before the app is installed. It can interact with the user and gather and store configuration information, install shared assemblies to the GAC, and so forth. At uninstall time, it takes on the responsibility of cleaning up resources that ordinarily would have been left behind by our application. For example, it can uninstall files, delete log files create at runtime, uninstall services, remove assemblies from the GAC, and so on.

Plugging our ClickOnce application into our install is again a fairly simple task. Since ClickOnce apps can be activated via an Internet or intranet URL, we can simply create a shortcut on the target system that points to the application manifest. To accomplish this, we can use Windows Installer to create a .url file on the fly at install time. A .url file is a special type of shortcut that complies with the standard .ini file format.

[InternetShortcut]
URL=http://www.myserver.com/myapp/v1_0/myapp.application

Using Windows Installer to create this INI file, we only need to add a single row to the IniFile table.

Column Name Value Notes
IniFile URLShortcut1 Primary key
FileName My App.url The name of the .url file. The main part of this name is what the user will see. In this case, the shortcut will display "My App".
DirProperty MyShortcutFolder The primary key name of a directory defined in the install. This identifies the location where the shortcut will be created.
Section InternetShortcut The main section being written into the ini file.
Key URL Key portion of the key/value pair.
Value http://www.myserver.com/myapp/v1_0/myapp.application The actual URL to the application manifest.
Action 0 A value of 0 indicates that the value should be created or updated if it already exists.
Component MyComponent A reference to the component that is responsible for the install/uninstall of our shortcut.

A well-designed implementation of this technique will require further modifications to our msi file to ensure our installation is accomplishing all the tasks required by our application, but I hope this has provided you a glimpse of what is possible when you leverage the best parts of each deployment technology.

Conclusion

For applications that aren't restricted by their functional limitations, ClickOnce is an excellent deployment technology that provides valuable benefits and an updating model that was previously available only to Web-based applications. For applications with more sophisticated requirements, Windows Installer remains the deployment technology of choice. Each of these deployment technologies shares the common goal of reliably installing your application, but the similarities end there. You can be sure that the capabilities of ClickOnce will grow in the future, but you can be equally sure that Windows Installer is here to stay. In the meantime, get creative and learn to love them both!

 

About the Author

Michael Sanford is President and Chief Software Architect for Xambi Solutions (http://www.xambi.com). Prior to forming Xambi, Michael was President and CEO of ActiveInstall Corporation, which was acquired by Zero G Software. ActiveInstall achieved notoriety for its Windows Installer Authoring solutions. Michael is a Microsoft Certified Solution Developer (MCSD), Microsoft Certified Systems Engineer (MCSE), and a Windows Installer MVP. You can read Michael's Blog at http://msmvps.com/michael.

Show:
© 2014 Microsoft