Sign in to follow this  
Followers 0
archrival

Problem with MsiDeterminePatchSequence

14 posts in this topic

#1 ·  Posted (edited)

I'm trying to implement MsiDeterminePatchSequence and I'm having some trouble. The first 2 .msp files are applicable to my machine, the third is not. I would expect to get ERROR_SUCCESS (0) when just checking the first two, but no matter what I do I get ERROR_PATCH_NO_SEQUENCE (1648).

My implementation:

Func _MsiDeterminePatchSequence($szProductCode, $szUserSid, $dwContext, $cPatchInfo, ByRef $pPatchInfo)
    Local $error = 0
    Local $extended
    Local $return
    If $szUserSid = "" Then
        $sidType = "ptr"
        $szUserSid = ptr(0)
    Else
        $sidType = "wstr"
    EndIf

    Local $uRet = DllCall($msidll, "UINT", "MsiDeterminePatchSequenceW", "wstr", $szProductCode, $sidType, $szUserSid, "DWORD", $dwContext, "UINT", $cPatchInfo, "ptr", DllStructGetPtr($pPatchInfo))
    If @error Then
        $error = @error
    Else
        If IsArray($uRet) Then
            $return = $uRet[0]
        EndIf
    EndIf
    Return SetError($error, $extended, $return)
EndFunc

Calling code:

$xml = _MsiExtractPatchXMLData("onenote.msp")
$xml2 = _MsiExtractPatchXMLData("osc-x-none-en-us.msp")
$xml3 = _MsiExtractPatchXMLData("OLKINTL.msp")

$patchinfo = __MsiPatchSequenceInfo($xml, $MSIPATCH_DATATYPE_XMLBLOB)
$patchinfo2 = __MsiPatchSequenceInfo($xml2, $MSIPATCH_DATATYPE_XMLBLOB)
$patchinfo3 = __MsiPatchSequenceInfo($xml3, $MSIPATCH_DATATYPE_XMLBLOB)

$pPatchInfo = DllStructCreate("ptr pPatchInfo[3]")
DllStructSetData($pPatchInfo, "pPatchInfo", DllStructGetPtr($patchinfo), 1)
DllStructSetData($pPatchInfo, "pPatchInfo", DllStructGetPtr($patchinfo2), 2)
DllStructSetData($pPatchInfo, "pPatchInfo", DllStructGetPtr($patchinfo3), 3)

$ret = _MsiDeterminePatchSequence("{91140000-0012-0000-1000-0000000FF1CE}", "", $MSIINSTALLCONTEXT_MACHINE, 3, $pPatchInfo)
ConsoleWrite($ret)

Function for MSIPATCHSEQUENCEINFO:

Func __MsiPatchSequenceInfo($szPatchData, $ePatchDataType, $dwOrder = -1, $uStatus = 0)
    Local $error = 0
    Local $extended, $return

    Local $pPatchInfo = DllStructCreate("wchar szPatchData[" & StringLen($szPatchData) & "]; int ePatchDataType; DWORD dwOrder; UINT uStatus")
    DllStructSetData($pPatchInfo, "szPatchData", $szPatchData)
    DllStructSetData($pPatchInfo, "ePatchDataType", $ePatchDataType)
    DllStructSetData($pPatchInfo, "dwOrder", $dwOrder)
    DllStructSetData($pPatchInfo, "uStatus", $uStatus)

    Return $pPatchInfo
EndFunc

MsiExtractPatchXMLData Function:

Func _MsiExtractPatchXMLData($szPatchPath, $dwReserved = 0, $pcchXMLData = 65536)
    Local $error = 0
    Local $extended
    Local $szXMLData
    Local $uRet = DllCall($msidll, "UINT", "MsiExtractPatchXMLDataW", "wstr", $szPatchPath, "DWORD", $dwReserved, "wstr", $szXMLData, "DWORD*", $pcchXMLData)
    If @error Then
        $error = @error
    Else
        If IsArray($uRet) And UBound($uRet) = 5 Then
            $extended = $uRet[0]
            $szXMLData = $uRet[3]
        EndIf
    EndIf
    Return SetError($error, $extended, $szXMLData)
EndFunc

I was able to accomplish this in C# using Microsoft.Deployment.WindowsInstaller provided by the WiX project.

NativeMethods.cs

Installer.cs

static void Main(string[] args)
{
     string xml = WindowsInstaller.Installer.ExtractPatchXmlData(@"onenote.msp");
     string xml2 = WindowsInstaller.Installer.ExtractPatchXmlData(@"osc-x-none-en-us.msp");
     string xml3 = WindowsInstaller.Installer.ExtractPatchXmlData(@"OLKINTL.msp");
     string[] patches = { xml, xml2, xml3 };
     InapplicablePatchHandler handler = new InapplicablePatchHandler(PatchHandler);
     IList<string> test = WindowsInstaller.Installer.DetermineApplicablePatches("{91140000-0012-0000-1000-0000000FF1CE}", patches, PatchHandler, null, UserContexts.Machine);
}

static void PatchHandler(string patch, Exception ex)
{
     Console.WriteLine(ex.Message);
}

Any help any of you AutoIt experts can provide would be greatly appreciated.

Edited by archrival

Share this post


Link to post
Share on other sites



Looking at the usage of $pPatchInfo in the _MsiDeterminePatchSequence() function, did you mean to use a pointer-to-a-ByRef-pointer. I don't know that it's wrong, it just looks... unusual.

;)


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

Looking at the usage of $pPatchInfo in the _MsiDeterminePatchSequence() function, did you mean to use a pointer-to-a-ByRef-pointer. I don't know that it's wrong, it just looks... unusual.

;)

I agree, but unfortunately removing the ByRef makes no difference, I still get a 1648 back. I'm concerned I'm creating the Struct incorrectly, or perhaps creating an array of structs incorrectly. Is this the way to create an array of structs?

$pPatchInfo = DllStructCreate("ptr pPatchInfo[3]")
DllStructSetData($pPatchInfo, "pPatchInfo", DllStructGetPtr($patchinfo), 1)
DllStructSetData($pPatchInfo, "pPatchInfo", DllStructGetPtr($patchinfo2), 2)
DllStructSetData($pPatchInfo, "pPatchInfo", DllStructGetPtr($patchinfo3), 3)

Share this post


Link to post
Share on other sites

I'm not sure. That's an array of pointers to the three MSIPATCHSEQUENCEINFO structures, which I think makes sense for this, but this is not my expertise.

;)


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

I asked someone smarter than me and this was recommended (to the extent I understood it properly): Treat "array of structures" something like a union. If the basic MSIPATCHSEQUENCEINFO tag is like this:

"wchar szPatchData[x]; int ePatchDataType; DWORD dwOrder; UINT uStatus"

Then an "array" of three of them is:

"wchar szPatchData0[x]; int ePatchDataType0; DWORD dwOrder0; UINT uStatus0; wchar szPatchData1[x]; int ePatchDataType1; DWORD dwOrder1; UINT uStatus1; wchar szPatchData2[x]; int ePatchDataType2; DWORD dwOrder2; UINT uStatus2"
Note I attached and index number to the field names so they can be addressed by name when setting the data.

My very uneducated guess at what that might look like:

; Each parameter is a 1D array of all required values, all with the same number of elements
Global $aMyPatchData[3] = ["", "", ""]
Global $aMyPatchDataType[3] = ["", "", ""]
Global $aMyOrder[3] = ["", "", ""]
Global $aMyStatus[3] = ["", "", ""]

; Put appropriate data in arrays...
; ...

; Create struct and get pointer
$pPointerToArrayOfStructs = __MsiPatchSequenceInfo($aMyPatchData, $aMyPatchDataType, $aMyOrder, $aMyStatus)

; Make DllCall()
$ret = _MsiDeterminePatchSequence("{91140000-0012-0000-1000-0000000FF1CE}", "", $MSIINSTALLCONTEXT_MACHINE, UBound($aMyPatchData), $pPointerToArrayOfStructs)


Func __MsiPatchSequenceInfo($aszPatchData, $aePatchDataType, $adwOrder, $auStatus)
    ; Create tag for struct
    Local $tagMsiPatchSequenceInfo = ""
    For $n = 0 To UBound($aszPatchData) - 1
        $tagMsiPatchSequenceInfo &= "wchar szPatchData" & $n & "[" & StringLen($aszPatchData[$n]) & "]; int ePatchDataType" & $n & "; DWORD dwOrder" & $n & "; UINT uStatus" & $n & ";"
    Next
    StringTrimRight($tagMsiPatchSequenceInfo, 1) ; trim trailing semicolon
    
    ; Create struct
    Local $tPatchInfo = DllStructCreate($tagMsiPatchSequenceInfo)
    
    ; Copy Data from input arrays to struct
    For $n = 0 To UBound($aszPatchData) - 1
        DllStructSetData($tPatchInfo, "szPatchData" & $n, $aszPatchData[$n])
        DllStructSetData($tPatchInfo, "ePatchDataType" & $n, $aePatchDataType[$n])
        DllStructSetData($tPatchInfo, "dwOrder" & $n, $adwOrder[$n])
        DllStructSetData($tPatchInfo, "uStatus" & $n, $auStatus[$n])
    Next

    ; Create pointer
    Global $pPatchInfo = DllStructGetPtr($tPatchInfo)
    Return $pPatchInfo
EndFunc

Again, I'm not the expert and hopefully smart people will step in to ridicule/correct my impression.

;)

P.S. God help you if you try to make this work on both 32-bit and 64-bit platforms with the same script and keep the boundaries straight! :)

Edited by PsaltyDS

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Until smart people do, here's what I would try:

#AutoIt3Wrapper_UseX64=n

Global Const $MSIPATCH_DATATYPE_XMLBLOB = 2
Global Const $MSIINSTALLCONTEXT_MACHINE = 4
Global Const $hMSIDLL = DllOpen("msi.dll")

Global $aArrayOfData[3] = ["onenote.msp", "osc-x-none-en-us.msp", "OLKINTL.msp"]



Local $tagArrayMSIPATCHSEQUENCEINFO
Local $tMSIPATCHSEQUENCEINFO = "ptr PatchData; int PatchDataType; int Order; dword Status;"

Local $aArrayOfXMLData[UBound($aArrayOfData)]

For $i = 0 To UBound($aArrayOfData) - 1
    $aArrayOfXMLData[$i] = _MsiExtractPatchXMLData($aArrayOfData[$i])
    $tagArrayMSIPATCHSEQUENCEINFO &= $tMSIPATCHSEQUENCEINFO
Next

Local $tArrayMSIPATCHSEQUENCEINFO = DllStructCreate($tagArrayMSIPATCHSEQUENCEINFO)
Local $tPatchData[UBound($aArrayOfXMLData)]

For $i = 0 To UBound($aArrayOfXMLData) - 1
    $tPatchData[$i] = DllStructCreate("wchar[" & StringLen($aArrayOfXMLData[$i]) + 1 & "]")
    DllStructSetData($tPatchData[$i], 1, $aArrayOfXMLData[$i])
    DllStructSetData($tArrayMSIPATCHSEQUENCEINFO, 1 + $i * 4, DllStructGetPtr($tPatchData[$i])) ; PatchData
    DllStructSetData($tArrayMSIPATCHSEQUENCEINFO, 2 + $i * 4, $MSIPATCH_DATATYPE_XMLBLOB) ; PatchDataType
    DllStructSetData($tArrayMSIPATCHSEQUENCEINFO, 3 + $i * 4, -1) ; Order
    DllStructSetData($tArrayMSIPATCHSEQUENCEINFO, 4 + $i * 4, 0) ; Status (redundant if it's 0)
Next

Local $pArrayMSIPATCHSEQUENCEINFO = DllStructGetPtr($tArrayMSIPATCHSEQUENCEINFO)

Local $fRet = _MsiDeterminePatchSequence("{91140000-0012-0000-1000-0000000FF1CE}", "", $MSIINSTALLCONTEXT_MACHINE, UBound($aArrayOfData), $pArrayMSIPATCHSEQUENCEINFO)

;**********************************print*******************************************
ConsoleWrite("!Sucess = " & $fRet & ", returned thru @extended = " & @extended & @CRLF)
ConsoleWrite("----------------------------------------------------------" & @CRLF)
For $i = 0 To 4 * (UBound($aArrayOfData) - 1) Step 4
    ConsoleWrite($aArrayOfData[$i / 4] & @CRLF)
    ConsoleWrite(@TAB & "PatchData = " & DllStructGetData(DllStructCreate("wchar[256]", DllStructGetData($tArrayMSIPATCHSEQUENCEINFO, $i + 1)), 1) & @CRLF)
    ConsoleWrite(@TAB & "PatchDataType = " & DllStructGetData($tArrayMSIPATCHSEQUENCEINFO, $i + 2) & @CRLF)
    ConsoleWrite(@TAB & "Order = " & DllStructGetData($tArrayMSIPATCHSEQUENCEINFO, $i + 3) & @CRLF)
    ConsoleWrite(@TAB & "Status = " & DllStructGetData($tArrayMSIPATCHSEQUENCEINFO, $i + 4) & @CRLF)
    ConsoleWrite("----------------------------------------------------------" & @CRLF)
Next
;**********************************print*******************************************




;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Func _MsiExtractPatchXMLData($sPatchPath)
    Local $aCall = DllCall($hMSIDLL, "dword", "MsiExtractPatchXMLDataW", _
            "wstr", $sPatchPath, _
            "dword", 0, _
            "wstr", "", _
            "dword*", 65536)
    If @error Or $aCall[0] Then Return SetError(1, 0, "")
    Return $aCall[3]
EndFunc   ;==>_MsiExtractPatchXMLData

Func _MsiDeterminePatchSequence($sProductCode, $vUserSid, $iContext, $iPatchInfo, $pPatchInfo)
    Local $sSidType = "ptr"
    If $vUserSid Then $sSidType = "wstr"
    Local $aCall = DllCall($hMSIDLL, "dword", "MsiDeterminePatchSequenceW", _
            "wstr", $sProductCode, _
            $sSidType, $vUserSid, _
            "dword", $iContext, _
            "dword", $iPatchInfo, _
            "ptr", $pPatchInfo)
    If @error Then Return SetError(1, 0, False)
    Return SetExtended($aCall[0], $aCall[0] = 0)
EndFunc   ;==>_MsiDeterminePatchSequence
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Doh! ;)

I kind of forgot you could just hit the elements by number without worrying about the names.

Sigh...

Thanks, trancexx!

:)


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

Thanks! This works great! In my situation I am guaranteed to only have 1 struct required so I created a wrapper DLL around the function that worked, but this is preferred. Thanks again!

...

Share this post


Link to post
Share on other sites

By the way, it appears returning a struct from a function does not work correctly and this was part of my problem.

Share this post


Link to post
Share on other sites

By the way, it appears returning a struct from a function does not work correctly and this was part of my problem.

Are you implying that AutoIt did something wrong or you?

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Are you implying that AutoIt did something wrong or you?

I'm not sure what is going wrong, but when I created the struct in a function and then returned the struct, the first 4 bytes of the XML in the struct were changed.

Share this post


Link to post
Share on other sites

I'm not sure what is going wrong, but when I created the struct in a function and then returned the struct, the first 4 bytes of the XML in the struct were changed.

Then it's you.

My guess would be that you declared it as local variable. Then returned it from function. In that case there is no dllstruct any more. What you try to access is some corrupted memory space. This was expected (to me) considering your coding technique.

If I'm wrong... ahh well.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Then it's you.

My guess would be that you declared it as local variable. Then returned it from function. In that case there is no dllstruct any more. What you try to access is some corrupted memory space. This was expected (to me) considering your coding technique.

If I'm wrong... ahh well.

That makes sense, but it makes me ask this: should I never declare the return value as Local in a function?

I'm not tied to any particular coding style, in fact I'm just trying to make sure memory is freed properly and what I'm doing now doesn't seem to be doing the trick.

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

That makes sense, but it makes me ask this: should I never declare the return value as Local in a function?

I'm not tied to any particular coding style, in fact I'm just trying to make sure memory is freed properly and what I'm doing now doesn't seem to be doing the trick.

I'm reading what I wrote in the post you quoted and I see I could be misinterpreted.

Show me the code that's not working as you would expect and I'll show you what's wrong with it. And reduce the size of the code to a minimum, not to lose focus on stuff that aren't important. Can you do that?

edit: double negations are always confusing. This one too. ;)

Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0