By Josh Stechnij
InstallShield Suite installers provide a fairly robust condition system implementation, allowing for building conditions in a much more flexible manner than InstallShield prerequisites previously allowed. The Suite bootstrap uses conditions to determine what mode to run in, whether features should be selected by default, determining if a package is installed or can be allowed to install, etc. Conditions can be grouped in any combination of “none” (not), “any” (or), or “all” (and) groups. While the built-in condition types allow for checking a variety of situations (file exists; files with certain version, date/time, or contents; registry key/value exists; specific registry value data; etc.), some scenarios cannot be covered by these built-in types. For example, if the existence of a web site in IIS (or some other web server) needs to be determined, the built-in conditions cannot check for this type of system resource. In order to provide flexibility, an extension mechanism is provided by the Suite bootstrap to allow for custom conditions to be implemented through a C/C++ DLL.
Implementing an extension condition requires the following:
- A namespace definition in setup.xml to define the resource that implements the condition
- A resource definition in setup.xml to contain the extension DLL
- One or more elements in a setup.xml condition block to describe the condition
- A C/C++ DLL that implements the condition functionality and interfaces with a Suite installer
Authoring Extension Conditions
Built-in conditions are structured as follows in the setup.xml file that drives the Suite bootstrap:
<GroupType>
<ConditionType Attribute1="…" Attribute2="…" />
</GroupType>
A condition type is defined by the element name of the condition, and any parameters for the condition are provided through attribute name/value pairs. Extension conditions follow the same semantic definition but use XML namespace definitions to provide some additional information. The root setup element contains a namespace definition to indicate that a resource DLL implements the extension condition validation and evaluation functionality:
<setup xmlns="installshield/2012/bootstrap" xmlns:testext="testext.dll" SuiteId="…">
In the above example setup element, the ‘testext’ namespace definition indicates testext.dll is a resource DLL that will implement any conditions residing in the ‘testext’ namespace. An extension condition can then be defined; it is implemented by testext.dll as follows:
<testext:TestExtensionCondition Attribute1="value1" Attribute2="value2" />
The above extension condition defines a condition, TestExtensionCondition, and indicates this is implemented by the DLL defined by the ‘testext’ namespace defined in the setup element. The extension name (TestExtentionCondition) also defines the base names of the entry points the extension DLL must implement and export. The attributes are arbitrary names whose values can be retrieved when the bootstrap calls the extension DLL to validate and evaluate the condition.
A resource entry in setup.xml is the last item required to define an extension condition. Resources are normally streamed into a setup.exe at build time so that no external files are required to launch the setup. To define a resource for the extension DLL, a resource element in the Resources group is defined:
<Resources>
<Resource Source="PathToResourceFile" Namespace="testext.dll" />
</Resources>
Note: in a Suite project file, there are multiple ‘resources’ groups. One group is defined for the user interface and another is defined for support files needed to run the setup. To define an extension DLL resource, ensure the new resource element is added to the correct group. To simplify this process, add the extension DLL to the Language Independent node in the Support Files view and then edit the project file in a text/XML editor to add the Namespace attribute to the resource element that the IDE created.
The resource element defines that this DLL is streamed into setup.exe with the name testext.dll. The Namespace attribute links the resource to the XML namespace defined for the extension DLL in the setup element.
Implementing the Extension DLL
With the setup.xml authored to use an extension DLL, the entry points in the DLL need to be authored. The Suite bootstrap expects two entry points per condition, one to validate the condition and one to evaluate the condition. Both entry points are based on the name of the extension condition. In the above setup.xml example, the extension condition is named TestExtensionCondition. The DLL implementing this condition must export the following entry points:
TestExtensionCondition_Validate
TestExtensionCondition_Evaluate
These functions should be defined as follows:
HRESULT __stdcall TestExtensionCondition_Validate(IDispatch *pCondition);
HRESULT __stdcall TestExtensionCondition_Evaluate(IDispatch*pCondition, VARIANT_BOOL *pbEvalResult);
The validation entry point is called while the bootstrap is parsing setup.xml conditions and provides an opportunity for the extension condition to ensure the condition attributes defined by setup.xml are suitable for the condition to evaluate in a predictable manner. This entry point is called any time an extension condition of this type is found in setup.xml. Returning S_OK from this entry point indicates to the Suite that the condition is valid. Returning a failed HRESULT status will cause the Suite to abort with an error for the condition that was last validated.
The evaluation entry point is called at any time an extension condition of this type is referenced by setup.xml. The evaluation is allowed to perform any action necessary to evaluate the condition** to a true or false result. The result is returned in the form of a VARIANT_BOOL in the pbEvalResult parameter. A result of VARIANT_TRUE is treated as a true result and a result of VARIANT_FALSE is treated as a false result. A success status (S_OK) should be returned from an evaluation function. A failure status will currently abort the evaluation of the current condition block being evaluated but will not cause the setup to abort.
** While any action is allowed by the bootstrap, extension conditions run with the privileges of the Suite setup that they are called from. Some actions may require administrator privileges (such as reading IIS 7.x configuration data). In such cases, the setup would need to be run with or be manifested to require administrator privileges for the condition to access needed data successfully.
The IDispatch interface passed to the validation and evaluation entry points implements the ISuiteExtension interface. A pointer to an ISuiteExtension can be obtained by calling the QueryInterface method on the IDispatch interface. The ISuiteExtension interface allows the condition functions to access the attribute parameters defined for the extension condition in setup.xml. Note that each condition in setup.xml will receive a different interface pointer that is specific to each condition instance. Therefore, the interface pointer passed to the entry point function should always be used and should not be saved across calls to the extension DLL.
The interface is currently defined as follows:
interface ISuiteExtension : IDispatch {
[propget, id(1), helpstring("Attribute")]
HRESULT Attribute([in]BSTR bstrName, [out, retval]BSTR *bstrValue);
[id(2), helpstring("method LogInfo")]
HRESULT LogInfo([in]BSTR bstr);
[propget, id(3), helpstring("Property")]
HRESULT Property([in]BSTR bstrName, [out, retval]BSTR *bstrValue);
[propput, id(3), helpstring("Property")]
HRESULT Property([in]BSTR bstrName, [in]BSTR bstrValue);
[id(4), helpstring("FormatProperty")]
HRESULT FormatProperty([in]BSTR bstrValue, [out, retval]BSTR *bstrReturnValue);
[id(5), helpstring("ResolveString")]
HRESULT ResolveString([in]BSTR bstrStringId, [out, retval]BSTR *bstrReturnValue);
};
The following methods are provided by ISuiteExtension for use by an extension DLL:
HRESULT get_Attribute([in]BSTR bstrName, [out, retval]BSTR *bstrValue);
This method can be used to retrieve the value of an attribute for the current condition instance. The attribute name is passed in bstrName and its corresponding value is returned in bstrValue.
HRESULT LogInfo([in]BSTR bstr);
This method can be used to write any information needed to the setup Suite debug log that may be useful for debugging or other informational purposes. The bstr parameter contains the string to be written to the log.
HRESULT get_Property([in]BSTR bstrName, [out, retval]BSTR *bstrValue);
This method can be used to retrieve the value of any property currently defined in the running Suite. Properties that are not defined will return an empty value. The bstrName parameter specifies the name of the property to obtain the value for. The value for the property will be returned in the bstrValue parameter.
HRESULT put_Property([in]BSTR bstrName, [in]BSTR bstrValue);
This method can be used to set the value of a new property or change the value of an existing property in the currently running Suite. Passing an empty value effectively deletes the property. The bstrName parameter specifies the name of the property to set. The bstrValue parameter specifies the value to set for the specified property.
HRESULT FormatProperty([in]BSTR bstrValue, [out, retval]BSTR *bstrReturnValue);
This method can be used to format a string that contains embedded property references (in the form ‘[PROPERTYNAME]’) in the string provided in the bstrValue parameter. The formatted value is returned in the bstrReturnValue parameter. Note that only one level of property references is formatted. If one property’s formatted value contains another property reference, this method will not format the second property.
HRESULT ResolveString([in]BSTR bstrStringId, [out, retval]BSTR *bstrReturnValue);
This method can be used to resolve a Suite string identifier into the corresponding string value for the currently running Suite installation in the currently selected UI language. The bstrStringId parameter specifies the string identifier to resolve, and the resolved string is returned in bstrReturnValue. If no such string identifier exists, the returned string is empty.
Building an Extension DLL
An extension condition DLL can be built with any recent version of Visual C++ or any tool/language that supports COM and DLL exports. When any extension DLL is built with Visual C++, the interface definition for the ISuiteExtension interface can be obtained by using #import to import the type library provided with the setup Suite executable starting with InstallShield 2012 SP1. The following statement can be used to import the type library in a VC++ project (such as in stdafx.h):
#import "C:\Program Files\InstallShield\2012\Redist\Language Independent\i386\SetupSuite.exe" no_namespace raw_interfaces_only named_guids
Note that if InstallShield is installed to a different location, the path in the #import statement above needs to be adjusted to the correct location.
For a sample extension condition, download the SuiteExtensionSample.zip file. This file contains a sample extension DLL project created with Visual Studio 2008 and an InstallShield Suite project that uses the extension DLL for an extension condition in the eligibility condition of a package in the Suite project. Note that to build the DLL project, Visual Studio 2008 or newer with ATL support is required. (Express editions of VC++ do not include ATL support.) The #import path to the setup Suite type library in stdafx.h may also need to be adjusted for the project to build correctly.
Suite support is available in InstallShield 2012 Premier Edition. For a list of additional Premier-only features, visit the InstallShield Editions page.