Caveats
Before choosing to write a custom action in managed code instead of traditional native C++ code, you should carefully consider the following:
Obviously, it introduces a dependency on the .NET Framework. Your MSI package should probably have a LaunchCondition to check for the presence of the correct version of the .NET Framework before anything else happens.
If the custom action runs at uninstall time, then even the uninstall of your product may fail if the .NET Framework is not present. This means a user could run into a problem if they uninstall the .NET Framework before your product.
A managed custom action should be configured to run against a specific version of the .NET Framework, and that version should match the version your actual product runs against. Allowing the version to "float" to the latest installed .NET Framework is likely to lead to compatibility problems with future versions. The .NET Framework provides side-by-side functionality for good reason -- use it.
How To
A custom action function needs to be declared as public static (aka Public Shared in VB.NET). It takes one parameter which is a Session object, and returns a ActionResult enumeration.
[CustomAction] public static ActionResult MyCustomAction(Session session)
The function must have a CustomActionAttribute, which enables it to be linked to a proxy function. The attribute can take an optional "name" parameter, which is the name of the entrypoint that is exported from the custom action DLL.
Fill in MSI CustomAction table entries just like you would for a normal type 1 native-DLL CA. Managed CAs can also work just as well in deferred, rollback, and commit modes.
If the custom action function throws any kind of Exception that isn't handled internally, then it will be caught by the proxy function. The Exception message and stack trace will be printed to the MSI log if logging is enabled, and the CA will return a failure code.
To be technically correct, any MSI handles obtained should be closed before a custom action function exits -- otherwise a warning gets printed to the log. The handle classes in the managed library (Database, View, Record, SummaryInfo) all implement the IDisposable interface, which makes them easily managed with C#'s using statement. Alternatively, they can be closed in a finally block. As a general rule, methods return new handle objects that should be managed and closed by the user code, while properties only return a reference to a prexisting handle object.
Don't forget to use a CustomAction.config file to specify what version of the .NET Framework the custom action should run against.
See also:
- Sample C# Custom Actions
- Specifying the Runtime Version
- Working with MSI Databases
- Building Managed Custom Actions
- Debugging Managed Custom Actions