Jump to content

Recommended Posts

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
Posted

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
Posted

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)
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
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

Posted

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!

...

Posted

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

  • 2 weeks later...
Posted

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.

Posted

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

Posted

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.

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

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...