#include "windows.h" #include "msi.h" #include "msiquery.h" #include "shlwapi.h" #include "tchar.h" #include "strsafe.h" #include "winreg.h" #include "winver.h" #define STATUS_INSTALLED 0 #define STATUS_FILE_NOT_FOUND 2 #define STATUS_ERROR 3 #define STATUS_NEWER_VERSION_INSTALLED 4 #define STATUS_NOT_INSTALLED 5 #define STATUS_OLDER_VERSION_INSTALLED 6 INT DetermineInstallationStatus(LPTSTR lpszPackagePath); BOOL IsWindowsInstallerInstalled(); INT VerCompare(LPTSTR, LPTSTR); INT _tmain(int argc, TCHAR *argv[]) { INT iExitCode; TCHAR *rgCodes[] = { TEXT("Exact version installed"), TEXT("General failure"), TEXT("Path to MSI package not found or not accessible"), TEXT("Error determining package status"), TEXT("Newer version installed"), TEXT("No version installed"), TEXT("Older version installed") }; if (argc != 2 || (argc == 2 && _tcscmp(argv[1], TEXT("/?")) == 0)) { INT i; printf("Determines if a given Windows Installer package is installed.\n\n"); printf("msicheck package\n\n"); printf("Exit Codes:\n"); for (i = 0; i < 7; ++i) { _tprintf(TEXT("\t%i\t%s\n"), i, rgCodes[i]); } return EXIT_FAILURE; } // Initialize COM since we will be using MSI if (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) != S_OK) { CoUninitialize(); return EXIT_FAILURE; } if (!IsWindowsInstallerInstalled()) { return EXIT_FAILURE; } iExitCode = DetermineInstallationStatus(argv[1]); CoUninitialize(); _tprintf(TEXT("%i - %s\n"), iExitCode, rgCodes[iExitCode]); return iExitCode; } INT DetermineInstallationStatus(LPTSTR lpszPackagePath) { INT iVerCompareResult; MSIHANDLE hProduct; TCHAR *lpszProductCodeCandidate; TCHAR *lpszProductVerCandidate; TCHAR *lpszProductVerExisting; DWORD dwPropertyBuff; INT iStatusCode; if (!PathFileExists(lpszPackagePath)) { return STATUS_FILE_NOT_FOUND; } iStatusCode = 0; // If we don't do this, the Windows Installer UI will pop up briefly when // we open the MSI file for inspection. MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); if (MsiOpenPackageEx( lpszPackagePath, MSIOPENPACKAGEFLAGS_IGNOREMACHINESTATE, &hProduct) == ERROR_SUCCESS) { // Read the product code from the candidate MSI package. dwPropertyBuff = 0; MsiGetProperty(hProduct, TEXT("ProductCode"), TEXT(""), &dwPropertyBuff); dwPropertyBuff += 1; lpszProductCodeCandidate = malloc(dwPropertyBuff * sizeof(TCHAR)); MsiGetProperty(hProduct, TEXT("ProductCode"), lpszProductCodeCandidate, &dwPropertyBuff); // Read the product version from the candidate MSI package. dwPropertyBuff = 0; MsiGetProperty(hProduct, TEXT("ProductVersion"), TEXT(""), &dwPropertyBuff); dwPropertyBuff += 1; lpszProductVerCandidate = malloc(dwPropertyBuff * sizeof(TCHAR)); MsiGetProperty(hProduct, TEXT("ProductVersion"), lpszProductVerCandidate, &dwPropertyBuff); // Close the handle, we're done futzing with it. MsiCloseHandle(hProduct); // Now let's find out if an installation with that product code is already // installed. if (MsiOpenProduct(lpszProductCodeCandidate, &hProduct) == ERROR_SUCCESS) { // OK, something with that product code is already installed. Let's // get the version of the installed product. dwPropertyBuff = 0; MsiGetProperty(hProduct, TEXT("ProductVersion"), TEXT(""), &dwPropertyBuff); dwPropertyBuff += 1; lpszProductVerExisting = malloc(dwPropertyBuff * sizeof(TCHAR)); MsiGetProperty(hProduct, TEXT("ProductVersion"), lpszProductVerExisting, &dwPropertyBuff); // Close the handle to the existing product package, we're done with it. MsiCloseHandle(hProduct); // Compare the version of the existing, installed product with the // candidate one. iVerCompareResult = VerCompare(lpszProductVerExisting, lpszProductVerCandidate); switch (iVerCompareResult) { case 1: iStatusCode = STATUS_OLDER_VERSION_INSTALLED; break; case 0: iStatusCode = STATUS_NEWER_VERSION_INSTALLED; break; case 2: iStatusCode = STATUS_INSTALLED; break; default: iStatusCode = STATUS_ERROR; break; } free(lpszProductVerExisting); } else { iStatusCode = STATUS_NOT_INSTALLED; } free(lpszProductVerCandidate); free(lpszProductCodeCandidate); } else { iStatusCode = STATUS_ERROR; } return iStatusCode; } BOOL IsWindowsInstallerInstalled() { DWORD dwHandle, dwLen; VS_FIXEDFILEINFO *pFileInfo; UINT fileInfoLen; BOOL installed; LPCTSTR lptstrFilename; LPVOID lpData; WORD wMajorVersion, wMinorVersion; installed = FALSE; lptstrFilename = TEXT("MSI.DLL"); dwLen = GetFileVersionInfoSize(lptstrFilename, &dwHandle); if (dwLen) { lpData = (LPTSTR)malloc(dwLen); if (GetFileVersionInfo(lptstrFilename, dwHandle, dwLen, lpData)) { if (VerQueryValue(lpData, TEXT("\\"), (LPVOID *)&pFileInfo, &fileInfoLen)) { wMajorVersion = HIWORD(pFileInfo->dwFileVersionMS); wMinorVersion = LOWORD(pFileInfo->dwFileVersionMS); installed = (wMajorVersion > 4) || (wMajorVersion == 4 && wMinorVersion == 5); } } free(lpData); } return installed; } INT VerCompare(LPTSTR lpszLeft, LPTSTR lpszRight) { TCHAR *pchLeftNext, *pchRightNext; TCHAR *piLeftCur, *piRightCur; INT piLeftCurNum, piRightCurNum; INT iResult; TCHAR rgSeps[] = TEXT("."); iResult = 0; piLeftCur = wcstok_s(lpszLeft, rgSeps, &pchLeftNext); piRightCur = wcstok_s(lpszRight, rgSeps, &pchRightNext); while (piLeftCur != NULL && piRightCur != NULL) { piLeftCurNum = _wtoi(piLeftCur); piRightCurNum = _wtoi(piRightCur); if (piLeftCurNum == piRightCurNum) { piLeftCur = wcstok_s(NULL, rgSeps, &pchLeftNext); piRightCur = wcstok_s(NULL, rgSeps, &pchRightNext); } else { if (piLeftCurNum > piRightCurNum) { iResult = 1; } else { iResult = -1; } break; } } return iResult; }