Be resilient and support self-repair |
In the lifetime of the typical application, the odds are that something will go wrong. Telling the user what has gone wrong, and how to fix it, is a great feature – but it’s even better if you can do it for them.
If you’ve coded your application defensively and put in lots of good error handling code to enable you to identify problems, why waste all that work simply telling the user what went wrong; if you know what’s wrong why not just fix it? Well, the Microsoft® Windows® Installer provides an API that allows you to do just that. As long as you’ve separated your product in to features and components and are using a Windows Installer .msi package you can use this management API. One of the most important concepts in self repair is that of the keypath. Any one of the resources within a component can be designated as the keypath for that component, typically a file is chosen as the keypath, but a registry value can also be chosen.
The keypath represents two things:
When an application asks the Windows Installer to resolve a path, the Installer performs two checks.
The code sample below illustrates how to use the Windows Installer API to dynamically repair an application in the case of a missing or corrupt DLL. First we call MsiQueryFeatureState to determine if our component’s feature is actually installed, if it’s advertised then we call MsiConfigureFeature to install the feature locally.
If the feature is already installed, then we make a call to MsiLocateComponent to retrieve the path to the component. Once we have the path to the component we try a LoadLibrary on it, if this fails we try and reinstall the component with MsiReinstallFeature and try again. If the reinstall fails, we have a serious problem, so we tell the user and then continue.
//This code uses the installer APIs to locate the DLL if installed, and install it
//if not installed. This way the application can repair itself if needed.
// Product/Feature/Component IDs from MSI Database
CString strProduct = "{3A26942E-532D-11D2-BAD8-00805F9B1139}";// product ID code
CString strFeature = "PriceHistory";// feature ID code
CString strComponent = "{3A269430-532D-11D2-BAD8-00805F9B1139}"; // component ID
_TCHAR strBuf[MAX_PATH];
DWORD dwCount = MAX_PATH;
UINT uiState;
// Find where to load from and load the DLL.
// Use MsiGetComponentPath() when it becomes available
INSTALLSTATE IState = MsiQueryFeatureState(strProduct, strFeature);
switch (IState)
{
case INSTALLSTATE_ADVERTISED :
// The feature is installable on demand
MsiConfigureFeature(strProduct, strFeature, INSTALLSTATE_LOCAL);
// Fall through to the next case
case INSTALLSTATE_LOCAL :
case INSTALLSTATE_SOURCE :
{
// Should be OK to load
// Find path to component Key File
MsiLocateComponent(strComponent, strBuf, &dwCount);
hDLL = LoadLibrary(strBuf);
if (hDLL == NULL)
{
// The feature should be installable but LoadLibrary has failed
// Try using the Installer API to reinstall .
uiState = MsiReinstallFeature(strProduct, strFeature, REINSTALLMODE_FILEMISSING);
if (uiState != ERROR_SUCCESS)
{
AfxMessageBox( "Reinstall failed" );
return;
}
else
{
MsiLocateComponent(strComponent, strBuf, &dwCount);
hDLL = LoadLibrary(strBuf);
if (hDLL == NULL)
{
// Couldn't load after the reinstall.
AfxMessageBox("Price History Feature: Component error");
return;
}
}
}
break;
}
default :
{
// Something is wrong - no point in trying a reinstall
AfxMessageBox("The Price History Feature is not Installable");
return;
}
}
// now use loaded DLL
Installer Function Reference, MsiConfigureFeature, MsiQueryFeatureState, MsiReinstallFeature, Organizing Applications into Components