[This is preliminary documentation and subject to change.]
This following simple application shows how to use many Microsoft® Windows® installer service functions. The sample application takes the package of an installed application at the command line and prints all of the features and components associated with the application.
#include <windows.h>
#include <msi.h>
#include <stdio.h>
#include <stdlib.h>
void ProcessError(UINT uiError,LPTSTR szFunctionName);
#define PATH_BUFFER_SIZE 10 // beginning buffer size for path
#define GUID_LENGTH 39 // includes terminating null
int main(int argc,char *argv[])
{
DWORD iFeatureIndex=0; // index for enumerating features
DWORD iComponentIndex=0; // index for enumerating components
DWORD iProductIndex=0; // index for enumerating products
LPTSTR lpProductBuf; // buffer for product code, string GUID
LPTSTR lpComponentBuf; // buffer for component code, GUID
LPTSTR lpFeatureBuf; // buffer for feature
LPTSTR lpParentBuf; // buffer for parent of feature
LPTSTR lpPathBuf; // the path to the databases
LPTSTR lpTempProductBuf;
MSIHANDLE hProduct; // returned product handle, must be
// closed
INSTALLSTATE isState; // the install state returned by
// MsiGetComponentPath
UINT uiError,uiError2; // variable to receive errors
DWORD cchProductBufLength; // number of characters in lpProductBuf
DWORD cchComponentBufLength;// number of characters in lpComponentBuf
DWORD cchPathBufLength; // number of characters in PathBuf
// allocating space for variables
cchComponentBufLength=GUID_LENGTH;
cchProductBufLength=GUID_LENGTH;
cchPathBufLength=PATH_BUFFER_SIZE;
lpFeatureBuf=(LPTSTR) calloc(MAX_FEATURE_CHARS,sizeof(char));
lpParentBuf=(LPTSTR) calloc(MAX_FEATURE_CHARS,sizeof(char));
lpPathBuf=(LPTSTR) calloc(PATH_BUFFER_SIZE,sizeof(char));
lpProductBuf=(LPTSTR) calloc(GUID_LENGTH,sizeof(char));
lpComponentBuf=(LPTSTR) calloc(GUID_LENGTH,sizeof(char));
lpTempProductBuf=(LPTSTR) calloc(GUID_LENGTH,sizeof(char));
/* determine the current path so that the user doesn't
have to type it in at the command line */
cchPathBufLength=GetCurrentDirectory(
cchPathBufLength,
lpPathBuf);
/* If the path variable isn't long enough for the path plus the package name, reallocate and try again */
if(cchPathBufLength>PATH_BUFFER_SIZE+1+strlen(argv[1])){
realloc(lpPathBuf,cchPathBufLength);
if(cchPathBufLength>PATH_BUFFER_SIZE)
GetCurrentDirectory(cchPathBufLength,lpPathBuf);
}
strcat(lpPathBuf,"\\");
strcat(lpPathBuf,argv[1]);
printf ("%s\n",lpPathBuf);
// open the package
uiError=MsiOpenPackage(lpPathBuf,&hProduct);
// check for errors
if (uiError!=ERROR_SUCCESS){
ProcessError(uiError,"MsiOpenPackage");
return uiError;
}
else
printf("Package opened\n");
/* Many functions require the product code, so you obtain it from
the ProductCode property. After this function call, the package
is no longer used. All further information is obtained from the
configuration database */
uiError=MsiGetProductProperty(hProduct,
"ProductCode",
lpProductBuf,
&cchProductBufLength);
/* Here you make sure that the product code is properly obtained.
If there isn't enough room in the variable, you reallocate and try
again. A while loop is used to ensure that MsiGetProductProperty
finally does return ERROR_SUCCESS */
while (uiError!=ERROR_SUCCESS){
if (ERROR_MORE_DATA==uiError){
cchProductBufLength++;
realloc(&lpProductBuf,cchProductBufLength);
uiError=MsiGetProductProperty(hProduct,
"ProductCode",
lpProductBuf,
&cchProductBufLength);
}
else{
ProcessError(uiError,"MsiGetProductProperty");
return uiError;
}
}
printf("Product code obtained %s\n",lpProductBuf);
// Here you enumerate all of the features associated with the product
do{
uiError=MsiEnumFeatures(lpProductBuf,
iFeatureIndex,
lpFeatureBuf,
lpParentBuf);
if(ERROR_SUCCESS==uiError){
printf("Feature: %s\n Parent: %s\n",lpFeatureBuf,lpParentBuf);
iFeatureIndex++;
}
} while (ERROR_SUCCESS==uiError);
if(uiError!=ERROR_NO_MORE_ITEMS){
ProcessError(uiError,"MsiEnumFeatures");
return uiError;
}
/* This section enumerates all of the installed components of a
product and prints a path to their locations. This is more
complicated than enumerating the features. MsiEnumComponents
enumerates all of the components present on a particular computer,
regardless of associated product. You are only interested in the
components associated with the product given by the product code you
obtained above. Thus for each component enumerated, you enumerate the
products that use it. If the product matches the product you are
interested in, you find the path and print it.
You can use MsiLocateComponent instead of MsiGetComponentPath. In
fact MsiLocateComponent doesn't require the product code so you could
eliminate the MsiEnumClients call and list all of the components.
However, it is possible for two products to use the same component in
different locations. Without the product code, it is not possible for
the installer to know which path to return. For this reason, it is
recommended that you use MsiGetComponentPath instead of
MsiLocateComponent. */
do{
uiError=MsiEnumComponents(iComponentIndex,lpComponentBuf);
if (ERROR_SUCCESS==uiError){
iProductIndex=0;
do{
uiError2=MsiEnumClients(lpComponentBuf,
iProductIndex,
lpTempProductBuf);
if (!(uiError2==ERROR_SUCCESS||
uiError2==ERROR_NO_MORE_ITEMS)){
ProcessError(uiError2,"MsiEnumClients");
return uiError2;
}
iProductIndex++;
if(0==strcmp(lpTempProductBuf,lpProductBuf)){
uiError2=ERROR_NO_MORE_ITEMS;
isState=MsiGetComponentPath(lpProductBuf,
lpComponentBuf,
lpPathBuf,
&cchPathBufLength);
while (INSTALLSTATE_MOREDATA==isState){
lpPathBuf=realloc(lpPathBuf,
sizeof(char)*cchPathBufLength);
isState=MsiGetComponentPath(lpProductBuf,
lpComponentBuf,
lpPathBuf,
&cchPathBufLength);
}
switch (isState){
case INSTALLSTATE_LOCAL:
printf("Locally installed\n");
break;
case INSTALLSTATE_SOURCE:
printf("Installed from source\n");
break;
case INSTALLSTATE_SOURCEABSENT:
printf("Source not available\n");
break;
case INSTALLSTATE_DEFAULT:
printf("Default install state\n");
break;
default:
printf("installstate number:%d\n",isState);
break;
}
printf("Component: %s\nPath: %s\n",lpComponentBuf,lpPathBuf);
}
}while(ERROR_SUCCESS==uiError2);
}
iComponentIndex++;
} while (ERROR_SUCCESS==uiError);
if(uiError!=ERROR_NO_MORE_ITEMS){
ProcessError(uiError,"MsiEnumComponents");
return uiError;
}
MsiCloseHandle(hProduct);
return 0;
}
void ProcessError(UINT uiError,LPTSTR szFunctionName){
printf("An error occurred in %s:",szFunctionName);
switch (uiError){
case ERROR_BAD_CONFIGURATION:
printf("The configuration information is corrupt.\n");
break;
case ERROR_INSTALL_FAILURE:
printf("The product could not be opened.\n");
break;
case ERROR_INVALID_HANDLE:
printf("An invalid handle was passed to the function.\n");
break;
case ERROR_INVALID_PARAMETER:
printf("An invalid parameter was passed.\n");
break;
case ERROR_UNKNOWN_PRODUCT:
printf("The specified product is unknown.\n");
break;
case ERROR_ACCESS_DENIED:
printf("Access is denied.\n");
break;
default:
printf("Error number: %d\n",uiError);
break;
}
return;
}