archrival Posted September 25, 2010 Share Posted September 25, 2010 (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) EndFuncCalling 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 EndFuncMsiExtractPatchXMLData 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) EndFuncI was able to accomplish this in C# using Microsoft.Deployment.WindowsInstaller provided by the WiX project.NativeMethods.csInstaller.csstatic 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 September 25, 2010 by archrival Link to comment Share on other sites More sharing options...
PsaltyDS Posted September 25, 2010 Share Posted September 25, 2010 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 Link to comment Share on other sites More sharing options...
archrival Posted September 27, 2010 Author Share Posted September 27, 2010 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) Link to comment Share on other sites More sharing options...
PsaltyDS Posted September 27, 2010 Share Posted September 27, 2010 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 Link to comment Share on other sites More sharing options...
PsaltyDS Posted September 28, 2010 Share Posted September 28, 2010 (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: expandcollapse popup; 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 September 28, 2010 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 Link to comment Share on other sites More sharing options...
trancexx Posted September 28, 2010 Share Posted September 28, 2010 (edited) Until smart people do, here's what I would try:expandcollapse popup#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 September 28, 2010 by trancexx ♡♡♡ . eMyvnE Link to comment Share on other sites More sharing options...
PsaltyDS Posted September 28, 2010 Share Posted September 28, 2010 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 Link to comment Share on other sites More sharing options...
archrival Posted October 4, 2010 Author Share Posted October 4, 2010 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!... Link to comment Share on other sites More sharing options...
archrival Posted October 4, 2010 Author Share Posted October 4, 2010 By the way, it appears returning a struct from a function does not work correctly and this was part of my problem. Link to comment Share on other sites More sharing options...
trancexx Posted October 4, 2010 Share Posted October 4, 2010 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 Link to comment Share on other sites More sharing options...
archrival Posted October 14, 2010 Author Share Posted October 14, 2010 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. Link to comment Share on other sites More sharing options...
trancexx Posted October 15, 2010 Share Posted October 15, 2010 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 Link to comment Share on other sites More sharing options...
archrival Posted October 15, 2010 Author Share Posted October 15, 2010 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. Link to comment Share on other sites More sharing options...
trancexx Posted October 15, 2010 Share Posted October 15, 2010 (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 October 15, 2010 by trancexx ♡♡♡ . eMyvnE Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now