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

  1. 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.
  2. I didn’t make any changes to the default code provided: targetver.h, stafx.h, stafx.cpp.
  3. Make sure you have the Platform SDK installed for the next step.
  4. I went to the project properties and went to Linker | Inpuut and added to Additional Dependencies the following line:
    1
    "$(WindowsSdkDir)Lib\msi.lib"
  5. I wrote my code.

Learning the Code
So here is what the code I wrote in this little learning project will do:

  1. Create a list or vector to store each MSIProduct.
  2. Loop through each installed MSIs using the MsiEnumProducts function and for each installed MSI:
  3. Get MSI information using MSIProductInfo.
  4. Create an MSIProduct object using the information from MSIProductInfo and add the MSIProduct to the list or vector.
  5. Write to standard output the MSI count (as Id), the MSI name, and the MSI Guid.
  6. Create a list or vector to store each MSIPatch.
  7. Check if any patches or MSPs are applied to the MSI and for each patch:
  8. Get MSP information using MSIPatchInfoEx.
  9. Create an MSIPatch object using the information from MSIPatchInfoEx and add the MSIPatch to this list or vector.
  10. 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.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// 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++;
    }
}
&#91;/sourcecode&#93;
 
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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
#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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
#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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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.

9 Comments

  1. koreanamericandaily.com

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  2. yacht.rit.edu says:

    yacht.rit.edu

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  3. carnegiecouncil.org says:

    carnegiecouncil.org

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  4. Australian Kids Clothing Size Chart says:

    Australian Kids Clothing Size Chart

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  5. mybedshop.com

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  6. just click Storenvy.com

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  7. diy.com projects says:

    diy.com projects

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  8. sofas says:

    sofas

    How to determine if an MSI patch (.msp file) has been applied using C++? | Rhyous

  9. [...] already have a C++ example of doing this, but that isn’t much help when you have to do it in C#. So it is good to know how to do this [...]

Leave a Reply

How to post code in comments?