How to determine if an MSI patch (.msp file) has been applied using C++?
Ok, so I need to determine if a patch has been applied to an MSI. Lets start with Enumerating the installed products and enumerating all the patches applied for each installed product.
I guess the title should be “How to enumerate installed MSI products and their applied MSP patches.”
I have to do it in C++, which is a drag because it looked like three lines of code in C#, but hey, maybe it isn’t so hard with C++ using .NET as well.
I researched on MSDN, of course. It looks like I need to use this function: MsiGetPatchInfoEx. However, I need to know the MSI GUID in order to use that function, so I might as well learn to use the MsiEnumProducts, MsiGetProductInfo, MsiEnumPatches to match the Product to an MSI Guid and that to a patch.
Creating the Project
- I created a new Project in Visual Studio to test this out. The project type I used was under C++ and is called Win32 Console application.
- I didn’t make any changes to the default code provided: targetver.h, stafx.h, stafx.cpp.
- Make sure you have the Platform SDK installed for the next step.
- I went to the project properties and went to Linker | Inpuut and added to Additional Dependencies the following line:
"$(WindowsSdkDir)Lib\msi.lib"
- I wrote my code.
Learning the Code
So here is what the code I wrote in this little learning project will do:
- Create a list or
vector
to store eachMSIProduct
. - Loop through each installed MSIs using the
MsiEnumProducts
function and for each installed MSI: - Get MSI information using
MSIProductInfo
. - Create an
MSIProduct
object using the information fromMSIProductInfo
and add theMSIProduct
to the list orvector
. - Write to standard output the MSI count (as Id), the MSI name, and the MSI Guid.
- Create a list or
vector
to store eachMSIPatch
. - Check if any patches or MSPs are applied to the MSI and for each patch:
- Get MSP information using
MSIPatchInfoEx
. - Create an
MSIPatch
object using the information fromMSIPatchInfoEx
and add theMSIPatch
to this list orvector
. - Write to standard output the MSP Guid.
Here is my code:
Run.cpp
This file does all the work and has the tmain function. It creates a list or vector
of MSIProduct
objects and then uses MsiEnumProducts and MsiGetProductInfo to create and add each MSIProduct
to the vector
. It also loops through each of the MSIProduct
‘s and find any installed patches. It adds each patch found to the MSIProduct
‘s _Patches
vector
.
// Run.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" #include "Msi.h" #include "MSIProduct.h" // Includes MSIBase.h and MSIPatch.h as well #include <iostream> #include <vector> using namespace std; #define MYSIZE 512 int _tmain(int argc, _TCHAR* argv[]) { // Step 1. Create a list or vector to store each MSIProduct. vector<MSIProduct> *products = new vector<MSIProduct>(); // Step 2. Loop through each installed MSIs using the MsiEnumProducts // function and for each installed MSI: int i = 0; bool foundMoreApps = true; while (foundMoreApps) { DWORD size = MYSIZE; LPTSTR tmpGuid = new TCHAR[MYSIZE]; LPTSTR tmpName = new TCHAR[MYSIZE]; UINT ret1 = MsiEnumProducts(i, tmpGuid); if (ret1 > 0) { foundMoreApps = false; continue; } // Step 3. Get MSI information using MSIProductInfo. UINT ret2 = MsiGetProductInfo(tmpGuid, INSTALLPROPERTY_PRODUCTNAME, tmpName, &size); if (ret2 > 0) { // Todo: Handle failure } // Step 4. Create an MSIProduct object using the information from MSIProductInfo // and add the MSIProduct to the list or vector. products->push_back(MSIProduct()); products->at(i).SetName(tmpName); products->at(i).SetGuid(tmpGuid); // Step 5. Write to standard output the MSI count (as Id), the MSI name, and the MSI Guid. cout << endl; cout << "Id: " << i << endl; wcout << "Product: " << tmpName << endl; wcout << "Guid: " << tmpGuid << endl; cout << "Patches: "; // Step 6. Create a list or vector to store each MSIPatch. vector<MSIPatch> *patches = new vector<MSIPatch>(); products->at(i).SetPatches(patches); // Step 7. Check if any patches or MSPs are applied to the MSI and for each patch: int j = 0; bool foundMorePatches = true; while (foundMorePatches) { DWORD size = MYSIZE; LPTSTR tmpPatchGuid = new TCHAR[MYSIZE]; LPTSTR tmpPatchState = new TCHAR[MYSIZE]; LPTSTR tmpPatchTransforms = new TCHAR[MYSIZE]; UINT retPatch1 = MsiEnumPatches(tmpGuid, j, tmpPatchGuid, tmpPatchTransforms, &size); if (retPatch1 > 0) { cout << "(" << retPatch1 << ") :" << endl; foundMorePatches = false; continue; } // These values correspond to the constants the dwFilter parameter // of MsiEnumPatchesEx uses. // Step 8. Get MSP information using MSIPatchInfoEx. UINT retPatch2 = MsiGetPatchInfoEx(tmpPatchGuid, tmpGuid, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_PATCHSTATE, tmpPatchState, &size); // Returns "1" if this patch is currently applied to the product. // Returns "2" if this patch is superseded by another patch. // Returns "4" if this patch is obsolete. if (retPatch2 > 0) { // Todo: Handle failure } // Step 9. Create an MSIPatch object using the information from MSIPatchInfoEx // and add the MSIPatch to this list or vector. patches->push_back(MSIPatch()); patches->at(j).SetPatchState(tmpPatchState); patches->at(j).SetGuid(tmpPatchGuid); patches->at(j).SetTransforms(tmpPatchTransforms); // Step 9. Write to standard output the MSP Guid. wcout << "\t" << "Patch Guid: " << tmpPatchGuid << endl; j++; } i++; } } [/sourcecode] I did create some simple supporting classes for this: <strong>MSIBase.h</strong> [sourcecode language="cpp"] #pragma once #include "windows.h" class MSIBase { public: // Constructor MSIBase(void); // Destructor virtual ~MSIBase(void); // Accessor functions LPTSTR GetName(); void SetName(LPTSTR inName); LPTSTR GetGuid(); void SetGuid(LPTSTR inGuid); protected: LPTSTR _Guid; LPTSTR _Name; };
MSIBase.cpp
#include "StdAfx.h" #include "MSIBase.h" MSIBase::MSIBase(void) { } MSIBase::~MSIBase(void) { } // Accessor functions LPTSTR MSIBase::GetName() { return _Name; } void MSIBase::SetName(LPTSTR inName) { _Name = inName; } LPTSTR MSIBase::GetGuid() { return _Guid; } void MSIBase::SetGuid(LPTSTR inGuid) { _Guid = inGuid; }
MSIProduct.h
#include "MSIBase.h" #include "MSIPatch.h" #include <vector> #pragma once class MSIProduct : public MSIBase { public: MSIProduct(void); ~MSIProduct(void); std::vector<MSIPatch> GetPatches(); void SetPatches(std::vector<MSIPatch> * inPatches); void AddPatch(MSIPatch inPatch); protected: std::vector<MSIPatch> * _Patches; };
MSIProduct.cpp
#include "StdAfx.h" #include "MSIProduct.h" MSIProduct::MSIProduct(void) { } MSIProduct::~MSIProduct(void) { delete _Patches; } std::vector<MSIPatch> MSIProduct::GetPatches() { return * _Patches; } void MSIProduct::SetPatches(std::vector<MSIPatch> * inPatches) { _Patches = inPatches; } void MSIProduct::AddPatch(MSIPatch inPatch) { _Patches->push_back(inPatch); }
MSIPatch.h
#pragma once #include "MSIBase.h" class MSIPatch : public MSIBase { public: MSIPatch(void); ~MSIPatch(void); LPTSTR GetTransforms(); void SetTransforms(LPTSTR inTransforms); int GetPatchState(); void SetPatchState(int inPatchState); void SetPatchState(LPTSTR inPatchState); protected: LPTSTR _Transforms; int _PatchState; };
MSIPatch.cpp
#include "StdAfx.h" #include "MSIPatch.h" MSIPatch::MSIPatch(void) { } MSIPatch::~MSIPatch(void) { } LPTSTR MSIPatch::GetTransforms() { return _Transforms; } void MSIPatch::SetTransforms(LPTSTR inTransforms) { _Transforms = inTransforms; } int MSIPatch::GetPatchState() { return _PatchState; } void MSIPatch::SetPatchState(int inPatchState) { _PatchState = inPatchState; } void MSIPatch::SetPatchState(LPTSTR inPatchState) { _PatchState = _wtoi(inPatchState); }
Sorry, I am not explaining in more detail, my time is limited.
Note: I found one problem where an application has a ™ in the name. (Skype™ 4.2) and the output doesn’t work well after that.
Copyright ® Rhyous.com – Linking to this article is allowed without permission and as many as ten lines of this article can be used along with this link. Any other use of this article is allowed only by permission of Rhyous.com.