When I previously did some updates to the Java Installers for Apache Maven, I noticed that, when I was testing the two versions, it often didn’t update the Environment variables.
At first, I put it down to the fact that the variables for one version where there and maybe they get in the way of the versions for the next. I checked to see if there was a way to force it to override the values and was initially puzzled by the information – until I realised that this is actually a common problem.
It seems that a WiX installer will happily update the environment variables – but that nothing else knows it’s been done until you tell them. You can tell them in any of several ways, including:
- Log Out and back in again or worse, reboot! Tedious, yet depressingly popular.
- Use the Environment Variables system dialog to “nudge” (i.e. edit and save any of them – without necessarily changing the value) a change. Annoying and a little fiddly for many.
- Use the Windows API call
SendMessageTimeoutto broadcast a
WM_SETTINGCHANGEto the system during the installation.
Having verified the problem using the 2nd approach (much less trouble than the first) I set out to add the third method to the WiX Installers.
A WiX Customer Action
So, I needed to add a Custom Action to the WiX projects and that necessitated writing in a Programming Language instead of XML. I’m using Visual Studio 2012 with the Votive (WiX development) plugin and that offers several project templates for writing Custom Actions.
Whilst I used to be quite handy with C++, I’ve been a Java and C# softie for a few years and more recently that has been confined to my hobby time. So initially I tried to write a C# custom action.
Right click (context menu) on the Solution in the Solution Explorer in Visual Studio and select Add->New Project… The example below shows the C++ Custom Action Project selected – but I did try the C# version first.
The C# approach proved problematic – first having to hook in a Windows API call that wasn’t covered in the .NET libraries (necessitating lots of messy definitions), and then finding that for some reason it didn’t appear to work anyway.
The final blow came when I read one article pointing out that using C# in WiX requires that you link in the .NET framework and thus create much larger MSI files (not sure of the validity or details, but it had the desired effect).
I’ve included the code in case anyone has any bright ideas about where I’ve gone wrong…
public class CustomActions
public enum SendMessageTimeoutFlags : uint
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
out UIntPtr lpdwResult);
//private const int HWND_BROADCAST = 0xffff;
public const uint WM_WININICHANGE = 0x001A;
public const uint WM_SETTINGCHANGE = WM_WININICHANGE;
public const int MSG_TIMEOUT = 15000;
public static ActionResult RefreshEnvironmentVariables(Session session)
IntPtr HWND_BROADCAST = new IntPtr(0xffff);
string ENVIRONMENT = "Environment";
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, UIntPtr.Zero,
SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, MSG_TIMEOUT, out RESULT);
//SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, NULL);
session.Log("Completed RefreshEnvironmentVariables: result=" + RESULT.ToString() );
Faced with a long list of problems and disappointments, I bit the bullet and created myself a new C++ Custom Action project. The template handled most of the work – although I had a few issues because the version of Votive was for an older (2010) version of Visual Studio and I had to identify and fix a few path issues to the various headers and libs.
All that remained was to rename the sample
CustomAction method to
RefreshEnvironmentVariables and add the highlighted code, below:
UINT __stdcall RefreshEnvironmentVariables(MSIHANDLE hInstall)
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "RefreshEnvironmentVariables");
ExitOnFailure(hr, "Failed to initialize");
// Send out the Settings Changed message - Once using ANSII...
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, NULL);
// ...and once using UniCode (because Windows 8 likes it that way).
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
Having written and compiled (successfully, of course) the Custom Action it then needs to be added as a project dependency to both WiX projects.
Finally, it needs to be added to the WiX code using the following lines:
<!-- Include the Custom Actions library - currently just to send notification of Environment changes. -->
<Binary Id="SetupCustomActionsCPP.dll" SourceFile="..\SetupCustomActionsCPP\bin\$(var.Configuration)\SetupCustomActionsCPP.dll" />
<!-- Define the custom action to Refresh Environment Variables. -->
<!-- Now schedule the custom action to happen after InstallFinalize. -->
<Custom Action="RefreshEnvironmentVariables" After="InstallFinalize"/>
On final change was to replace the “%M2%” value being added to the
PATH variable with the actual directory path (
"[INSTALLDIR]bin") just as used to define
M2. This was because, when I checked the variables created after the above changes, it had failed to expand the variable – meaning that Maven was not on the path.
These changes appear to have worked – though while testing it I had the remaining problem of it being ignored occasionally. In other words, it worked some times, but not others.
Does Windows get bored?