New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolve issues 4366 and 4420 for better patch performance #69
Changes from 3 commits
70aef0d
26de2c5
fff8cce
6110aa5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
struct POSSIBLE_TARGETPRODUCT | ||
{ | ||
WCHAR wzProductCode[39]; | ||
LPWSTR pszLocalPackage; | ||
MSIINSTALLCONTEXT context; | ||
}; | ||
|
||
|
@@ -131,8 +132,8 @@ extern "C" HRESULT MspEngineDetectInitialize( | |
AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages."); | ||
|
||
HRESULT hr = S_OK; | ||
POSSIBLE_TARGETPRODUCT* rgPossibleTargetProductCodes = NULL; | ||
DWORD cPossibleTargetProductCodes = 0; | ||
POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL; | ||
DWORD cPossibleTargetProducts = 0; | ||
|
||
#ifdef DEBUG | ||
// All patch info should be initialized to zero. | ||
|
@@ -146,18 +147,28 @@ extern "C" HRESULT MspEngineDetectInitialize( | |
|
||
// Figure out which product codes to target on the machine. In the worst case all products on the machine | ||
// will be returned. | ||
hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProductCodes, &cPossibleTargetProductCodes); | ||
hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts); | ||
ExitOnFailure(hr, "Failed to get possible target product codes."); | ||
|
||
// Loop through possible target products, testing the collective patch applicability against each product in | ||
// the appropriate context. Store the result with the appropriate patch package. | ||
for (UINT iSearch = 0; iSearch < cPossibleTargetProductCodes; ++iSearch) | ||
for (UINT iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch) | ||
{ | ||
POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProductCodes + iSearch; | ||
const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch; | ||
|
||
LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context)); | ||
|
||
hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo); | ||
if (pPossibleTargetProduct->pszLocalPackage) | ||
{ | ||
// Ignores current machine state to determine just patch applicability. | ||
// Superseded and obsolesced patches will be planned separately. | ||
hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo); | ||
} | ||
else | ||
{ | ||
hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo); | ||
} | ||
|
||
if (SUCCEEDED(hr)) | ||
{ | ||
for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) | ||
|
@@ -183,7 +194,14 @@ extern "C" HRESULT MspEngineDetectInitialize( | |
} | ||
|
||
LExit: | ||
ReleaseMem(rgPossibleTargetProductCodes); | ||
if (rgPossibleTargetProducts) | ||
{ | ||
for (DWORD i = 0; i < cPossibleTargetProducts; ++i) | ||
{ | ||
ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage); | ||
} | ||
MemFree(rgPossibleTargetProducts); | ||
} | ||
|
||
return hr; | ||
} | ||
|
@@ -620,6 +638,7 @@ static HRESULT GetPossibleTargetProductCodes( | |
{ | ||
HRESULT hr = S_OK; | ||
STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL; | ||
BOOL fCheckAll = FALSE; | ||
WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1]; | ||
|
||
// Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up | ||
|
@@ -642,10 +661,8 @@ static HRESULT GetPossibleTargetProductCodes( | |
hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); | ||
ExitOnFailure(hr, "Failed to add product code to possible target product codes."); | ||
} | ||
else // must be an upgrade code. | ||
else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type) | ||
{ | ||
Assert(BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type); | ||
|
||
// Enumerate all unique related products to the target upgrade code. | ||
for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) | ||
{ | ||
|
@@ -670,9 +687,22 @@ static HRESULT GetPossibleTargetProductCodes( | |
} | ||
ExitOnFailure1(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode); | ||
} | ||
else | ||
{ | ||
// The element does not target a specific product. | ||
fCheckAll = TRUE; | ||
|
||
break; | ||
} | ||
} | ||
} | ||
else // one more more patches didn't target specific products so we'll search everything on the machine. | ||
else | ||
{ | ||
fCheckAll = TRUE; | ||
} | ||
|
||
// One or more of the patches do not target a specific product so search everything on the machine. | ||
if (fCheckAll) | ||
{ | ||
for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) | ||
{ | ||
|
@@ -686,7 +716,7 @@ static HRESULT GetPossibleTargetProductCodes( | |
} | ||
else if (E_BADCONFIGURATION == hr) | ||
{ | ||
// Skip product's with bad configuration and continue. | ||
// Skip products with bad configuration and continue. | ||
LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); | ||
|
||
hr = S_OK; | ||
|
@@ -715,6 +745,7 @@ static HRESULT AddPossibleTargetProduct( | |
) | ||
{ | ||
HRESULT hr = S_OK; | ||
LPWSTR pszLocalPackage = NULL; | ||
|
||
// Only add this possible target code if we haven't queried for it already. | ||
if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode)) | ||
|
@@ -736,15 +767,32 @@ static HRESULT AddPossibleTargetProduct( | |
hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3); | ||
ExitOnFailure(hr, "Failed to grow array of possible target products."); | ||
|
||
hr = ::StringCchCopyW((*prgPossibleTargetProducts)[*pcPossibleTargetProducts].wzProductCode, countof(prgPossibleTargetProducts[*pcPossibleTargetProducts]->wzProductCode), wzPossibleTargetProductCode); | ||
POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts; | ||
|
||
hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode); | ||
ExitOnFailure(hr, "Failed to copy possible target product code."); | ||
|
||
// Attempt to get the local package path so we can more quickly determine patch applicability later. | ||
hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage); | ||
if (SUCCEEDED(hr)) | ||
{ | ||
pPossibleTargetProduct->pszLocalPackage = pszLocalPackage; | ||
pszLocalPackage = NULL; | ||
} | ||
else | ||
{ | ||
// Will instead call MsiDeterminePatchSequence later. | ||
hr = S_OK; | ||
} | ||
|
||
(*prgPossibleTargetProducts)[*pcPossibleTargetProducts].context = context; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could use your new pPossibleTargetProduct here. |
||
|
||
++(*pcPossibleTargetProducts); | ||
} | ||
|
||
LExit: | ||
ReleaseStr(pszLocalPackage); | ||
|
||
return hr; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -410,6 +410,14 @@ The following table describes the parameters that are specific to the <b>Light</ | |
Specifies that the linker should suppress processing the data in the MsiAssembly table. This is equivalent to the -sma switch in light.exe.</td> | ||
</tr> | ||
|
||
<tr> | ||
<td><b>SuppressPatchSequenceData</b></td> | ||
|
||
<td>Optional <b>boolean</b> parameter.<br /> | ||
<br /> | ||
Specifies that the linker should suppress patch sequence data in patch XML to decrease bundle size and increase patch applicability performance (patch packages themselves are not modified).</td> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no documented downside. Is there any downside? (i.e., this is just opt-in to new, better but changed behavior?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pretty much, yeah. When I made the change for VSU certain people were nervous that it was necessary despite plenty of evidence to the contrary, so I made it opt-in. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool. We should look at making it the default in v4.0. |
||
</tr> | ||
|
||
<tr> | ||
<td><b>SuppressPdbOutput</b></td> | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -171,7 +171,12 @@ typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)( | |
__in DWORD cPatchInfo, | ||
__in PMSIPATCHSEQUENCEINFOW pPatchInfo | ||
); | ||
typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)( | ||
typedef UINT(WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on context, it looks like there should be a space between UINT and (WINAPI). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think VS did that automatically. I've fixed it for an upcoming commit. |
||
__in_z LPCWSTR wzProductPackagePath, | ||
__in DWORD cPatchInfo, | ||
__in PMSIPATCHSEQUENCEINFOW pPatchInfo | ||
); | ||
typedef UINT(WINAPI *PFN_MSIINSTALLPRODUCTW)( | ||
__in LPCWSTR szPackagePath, | ||
__in_opt LPCWSTR szCommandLine | ||
); | ||
|
@@ -300,6 +305,11 @@ HRESULT DAPI WiuDeterminePatchSequence( | |
__in PMSIPATCHSEQUENCEINFOW pPatchInfo, | ||
__in DWORD cPatchInfo | ||
); | ||
HRESULT DAPI WiuDetermineApplicablePatches( | ||
__in_z LPCWSTR wzProductPackagePath, | ||
__in PMSIPATCHSEQUENCEINFOW pPatchInfo, | ||
__in DWORD cPatchInfo | ||
); | ||
HRESULT DAPI WiuEnumProducts( | ||
__in DWORD iProductIndex, | ||
__out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelate | |
|
||
// MSI 3.0+ | ||
static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL; | ||
static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL; | ||
static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL; | ||
static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL; | ||
static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL; | ||
|
@@ -46,6 +47,7 @@ static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; | |
|
||
static HMODULE vhMsiDll = NULL; | ||
static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; | ||
static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; | ||
static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL; | ||
static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; | ||
static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; | ||
|
@@ -127,7 +129,7 @@ void UninitializeMessageData( | |
|
||
|
||
/******************************************************************** | ||
WiuInitialize - initializes wioutil | ||
WiuInitialize - initializes wiutil | ||
|
||
*********************************************************************/ | ||
extern "C" HRESULT DAPI WiuInitialize( | ||
|
@@ -148,6 +150,12 @@ extern "C" HRESULT DAPI WiuInitialize( | |
vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary; | ||
} | ||
|
||
vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast<PFN_MSIDETERMINEAPPLICABLEPATCHESW>(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW")); | ||
if (NULL == vpfnMsiDetermineApplicablePatchesW) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WiX style says to use '!' instead of 'NULL == '. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well aware, but it's consistent with the other code before and after. |
||
{ | ||
vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary; | ||
} | ||
|
||
vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast<PFN_MSIENUMPRODUCTSEXW>(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW")); | ||
if (NULL == vpfnMsiEnumProductsExW) | ||
{ | ||
|
@@ -202,6 +210,7 @@ extern "C" void DAPI WiuUninitialize( | |
vpfnMsiGetProductInfoExWFromLibrary = NULL; | ||
vpfnMsiGetPatchInfoExWFromLibrary = NULL; | ||
vpfnMsiEnumProductsExWFromLibrary = NULL; | ||
vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; | ||
vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; | ||
vpfnMsiSourceListAddSourceExWFromLibrary = NULL; | ||
} | ||
|
@@ -530,6 +539,28 @@ extern "C" HRESULT DAPI WiuDeterminePatchSequence( | |
} | ||
|
||
|
||
extern "C" HRESULT DAPI WiuDetermineApplicablePatches( | ||
__in_z LPCWSTR wzProductPackagePath, | ||
__in PMSIPATCHSEQUENCEINFOW pPatchInfo, | ||
__in DWORD cPatchInfo | ||
) | ||
{ | ||
HRESULT hr = S_OK; | ||
DWORD er = ERROR_SUCCESS; | ||
|
||
if (!vpfnMsiDetermineApplicablePatchesW) | ||
{ | ||
ExitFunction1(hr = E_NOTIMPL); | ||
} | ||
|
||
er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); | ||
ExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); | ||
|
||
LExit: | ||
return hr; | ||
} | ||
|
||
|
||
extern "C" HRESULT DAPI WiuEnumProducts( | ||
__in DWORD iProductIndex, | ||
__out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WiX style says DWORD instead of UINT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already existed that way, but I'll change it anyway. To be frank, though, UINTs are used interspersed throughout the codebase and the WiX style docs I know about make no recommendation as to whether DWORDs or UINTs are used. DWORDs are used more commonly, but are not a requirement of which I'm aware.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, there are style docs? I'm just going off the pull request comments I've seen in the last 6 months.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Internally there was. Couldn't find anything externally. Perhaps I should adapt what Rob wrote years back and get it posted to the 'site' repo.