I’ve heard several people ask how to conditionally select a package based on the target machine’s system language. While InstallShield 2012 didn’t include a way to do this with a few mouse clicks, it does allow us to create our own extension condition. Extension conditions can perform any check desired, including randomly returning TRUE or FALSE results (though I would never suggest it). In this article I will walk through using Visual C++ 2010 Express with InstallShield 2012 SP1 to add a set of conditions that check the target machine’s language settings.
I start by creating a new Win32 project, called LangCond, and I select DLL for the application type. Because I want minimal run-time dependencies, I edit the properties under Code Generation, changing Runtime Library to /MTd and /MT instead of /MDd and /MD. Then so the proper functions are exported, I create a module definition file LangCond.def and add it to my Linker Input properties by setting Module Definition File to LangCond.def. Finally it’s time to talk C++. Following the instructions on Extension Conditions, I import COM information from setupsuite.exe and create six functions:
HRESULT __stdcall User_Validate(IDispatch *pCondition);
HRESULT __stdcall User_Evaluate(IDispatch *pCondition, VARIANT_BOOL *pbResult);
HRESULT __stdcall System_Validate(IDispatch *pCondition);
HRESULT __stdcall System_Evaluate(IDispatch *pCondition, VARIANT_BOOL *pbResult);
HRESULT __stdcall OS_Validate(IDispatch *pCondition);
HRESULT __stdcall OS_Evaluate(IDispatch *pCondition, VARIANT_BOOL *pbResult);
I want these conditions to respectively allow a match of a match of a target machine’s user locale (User), its system locale (System), or the default UI language (OS). Since we expect to support versions of Windows before Windows Vista, we will accept Locale IDs (LCIDs) instead of Language or Culture Names. Thus the condition will take an LCID attribute, where exactly one LCID must be specified. If it matches, the condition will yield a TRUE result; if it doesn’t match, it will yield a FALSE result.
Let’s implement this in LangCond.cpp. Each kind_Validate call should verify the LCID attribute is an integer. Correspondingly each kind_Evaluate call should retrieve the current setting corresponding to the kind of condition (using GetUserDefaultLCID, GetSystemDefaultLCID, or GetSystemDefaultUILanguage), and then verify if the one listed in the LCID attribute matches. We will use wcstoul to implement both checks. To avoid repeating too much code, each validation will call a helper function ValidateLCID; each evaluation will call MatchLCID.
Finally let’s wire it up. Start a new suite project and add a package with an eligibility condition. Also add LangCond.dll as a support file. Then save and close the project, and edit it in your favorite XML editor. Add namespace attribute xmlns:language="LangCond.dll" to the root setup element, attribute Namespace="LangCond.dll" to the Resources/Resource element referencing LangCond.dll, and element <language:User LCID="1033"/> to the eligibility condition on the package. Once you build this suite, the package will run only on machines where the user has selected the US English locale. Swap 1033 out for 1041 to run only on a Japanese locale, or swap the whole language:User element for a language:System element to run only on machines that match the system locale. Or combine both conditions under an <Any> group to run on systems where the user is likely to understand the selected language, under the assumption that both the user and system languages that are selected on target systems reflect languages the user understands.
The attached LangCond.zip file contains the sample source code described here, as well as a built copy of LangCond.dll and a sample suite project. In the sample project, there are three packages. One will run only on a machine with US English settings. The second will run on a machine with Japanese settings. The third will run on either.
You can use the language condition in any condition field, such as in an exit condition to allow installation on a specific locale, or in feature conditions to configure the default selection state of language-related subfeatures. The latter is useful in a feature tree that looks like this (with conditions referenced):
- English: <Any><language:System LCID="1033"/><language:User LCID="1033"/></Any>
- French: <Any><language:System LCID="1036"/><language:User LCID="1036"/></Any>
- German: <Any><language:System LCID="1031"/><language:User LCID="1031"/></Any>
Note that support for extension conditions is available in InstallShield 2012 SP1. To obtain SP1, see Q201298: InstallShield 2012 Service Pack 1.