Sign in to follow this  
Followers 0
bluebearr

DllCall - getting Invalid parameter error

4 posts in this topic

I'm trying to write a script to enumerate all of the MSIs installed on a system. I think that I can probably do this with WMI or COM, but I'd like to be able to do it with DllCall because I have another scripting language that I may want to use the function in that doesn't support either of them, and for the learning exercise (ok, I'm a masochist).

The weird thing is, the function _GetMSIInstalledApps() below works if the index $i = 0, but not if it is anything else. After the first value, it returns ERROR_INVALID_PARAMETER.

I've included the URL and some of the text from the MSDN entry concerning this function. Any help is appreciated; I'd really like to not have to struggle so much with DLL calls.

#Include <String.au3>
#include <Array.au3>
global $ret
$aApps = _GetMSIInstalledApps()
If StringLeft($aApps[0][0], 5) == "ERROR" Then
    MsgBox(0, "_GetMSIInstalledApps Error", "Error : " & $aApps[0][0] & " (" & $aApps[0][1] & ")")
    Exit
EndIf
$fileH = FileOpen(@ScriptDir & "\InstalledApps.txt", 2)
For $j = 1 To $aApps[0][0]
    FileWriteLine($fileH, $aApps[$j][0] & @TAB & $aApps[$j][1])
Next

; ==============
;  FUNCTIONS
; ==============

Func _GetMSIInstalledApps()
    ; Enumerate MSIs
    Const $ERROR_SUCCESS = 0 
    Dim $i = 0, $sProducts[1][2]
    $ret = DllCall("msi.dll", "int", "MsiEnumProductsA", "int", $i, "str", '')
    _ArrayDisplay($ret, "Debug: DllCall Return")
    While $ret[0] = $ERROR_SUCCESS
        MsgBox(0, 'Debug _GetMSIInstalledApps', 'Current Code = ' & $ret[2])
        $i = $i
        ReDim $sProducts[$i + 1][2]
        $sProducts[$i][0] = RegRead('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' & $ret[2], 'DisplayName')
        $sProducts[$i][1] = $ret[2]
        $ret = DllCall("msi.dll", "int", "MsiEnumProductsA", "int", $i, "str", '')
        _ArrayDisplay($ret, "Debug: DllCall Return")
    WEnd
    $retC = _GetReturnCode($ret[0])
    If $retC <> "ERROR_NO_MORE_ITEMS" Then
        ;MsgBox(0, "_GetMSIInstalledApps Error", "Error : " & $retC & " (" & $ret[0] & ")" & @LF & "$i = " & $i)
        Dim $errs[1][2]
        $errs[0][0] = $retC
        $errs[0][1] = $ret[0]
        Return $errs
    Else
        $sProducts[0][0] = $i
        Return $sProducts
    EndIf
EndFunc

Func _GetReturnCode($iCode)
    ; Translate the error code into error text
    Dim $sMsg = ''
    $sErrTexts = StringSplit("ERROR_BAD_CONFIGURATION,ERROR_INVALID_PARAMETER," _
        & "ERROR_NO_MORE_ITEMS,ERROR_NOT_ENOUGH_MEMORY,ERROR_SUCCESS", ",")
    $iErrCodes = StringSplit("1610,87,259,8,0", ",")
    For $i = 1 To $iErrCodes[0]
        If $iCode = $iErrCodes[$i] Then
            $sMsg = $sErrTexts[$i]
        EndIf
    Next
    If $sMsg = '' Then $sMsg = "UNKNOWN_ERROR"
    Return $sMsg
EndFunc

;~ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msienumproducts.asp
;~ MsiEnumProducts

;~ The MsiEnumProducts function enumerates through all the products currently advertised or installed. 
;~ Both per-user and per-machine installations and advertisements are enumerated.

;~ UINT MsiEnumProducts(
;~   DWORD iProductIndex,
;~   LPTSTR lpProductBuf
;~ );

;~ Parameters

;~ iProductIndex
;~     [in] Specifies the index of the product to retrieve. This parameter should be zero for the first call 
;~ to the MsiEnumProducts function and then incremented for subsequent calls. Because products are not ordered, 
;~ any new product has an arbitrary index. This means that the function can return products in any order.
;~ lpProductBuf
;~     [out] Pointer to a buffer that receives the product code. This buffer must be 39 characters long. 
;~ The first 38 characters are for the GUID, and the last character is for the terminating null character.

;~ Return Values
;~ Value    Meaning
;~ ERROR_BAD_CONFIGURATION  The configuration data is corrupt.
;~ ERROR_INVALID_PARAMETER  An invalid parameter was passed to the function.
;~ ERROR_NO_MORE_ITEMS  There are no products to return.
;~ ERROR_NOT_ENOUGH_MEMORY  The system does not have enough memory to complete the operation. Available with Windows Server 2003 family.
;~ ERROR_SUCCESS    A value was enumerated.

BlueBearrOddly enough, this is what I do for fun.

Share this post


Link to post
Share on other sites



When making multiple calls to MsiEnumProducts to enumerate all of the products, each call should be made from the same thread.


SciTE for AutoItDirections for Submitting Standard UDFs

 

Don't argue with an idiot; people watching may not be able to tell the difference.

 

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

Edit: changed for loop for While loop

#Include <String.au3>
#include <Array.au3>
Global $Debugit = 0
Global $ERROR_BAD_CONFIGURATION = 1610
Global $ERROR_INVALID_PARAMETER = 87
Global $ERROR_NO_MORE_ITEMS = 259
Global $ERROR_NOT_ENOUGH_MEMORY = 8
Global $ERROR_SUCCESS = 0
$dll = DllOpen("msi.dll")
Global $ret, $aApps[1][2], $x = 0
While 1
    _GetMSIInstalledApps($dll, $x, $aApps)
    If @error Then ExitLoop
    $x += 1
WEnd
Switch @error
    Case $ERROR_BAD_CONFIGURATION
        ConsoleWrite("ERROR_BAD_CONFIGURATION" & @LF)
        Exit
    Case $ERROR_INVALID_PARAMETER
        ConsoleWrite("$ERROR_INVALID_PARAMETER" & @LF)
        Exit
    Case $ERROR_NO_MORE_ITEMS
        ConsoleWrite("$ERROR_NO_MORE_ITEMS" & @LF)
    Case $ERROR_NOT_ENOUGH_MEMORY
        ConsoleWrite("$ERROR_NOT_ENOUGH_MEMORY" & @LF)
        Exit
EndSwitch

$fileH = FileOpen(@ScriptDir & "\InstalledApps.txt", 2)
For $j = 1 To $aApps[0][0]
    FileWriteLine($fileH, $aApps[$j][0] & @TAB & $aApps[$j][1])
Next

; ==============
;  FUNCTIONS
; ==============

Func _GetMSIInstalledApps(ByRef $dll, ByRef $i_index, ByRef $aApps)
    ; Enumerate MSIs
    $ret = DllCall($dll, "int", "MsiEnumProducts", "int", $i_index, "str", "")
    If $ret[0] <> 0 Then Return SetError($ret[0], $ret[0], $ret[0])
    ReDim $aApps[UBound($aApps) + 1][2]
    $aApps[0][0] = UBound($aApps) - 1
    $aApps[UBound($aApps) - 1][0] = RegRead('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' & $ret[2], 'DisplayName')
    $aApps[UBound($aApps) - 1][1] = $ret[2]
    If $Debugit Then _DebugPrint("Debug: DllCall Return:" & @LF & "--> " & $ret[0] & @LF & "--> " & $ret[1] & @LF & "--> " & $ret[2])
EndFunc   ;==>_GetMSIInstalledApps

Func OnAutoItExit()
    DllClose($dll)
EndFunc   ;==>OnAutoItExit


Func _DebugPrint($s_text)
    $s_text = StringReplace($s_text, @LF, @LF & "-->")
    ConsoleWrite("!===========================================================" & @LF & _
            "!===========================================================" & @LF & _
            "-->" & $s_text & @LF & _
            "!===========================================================" & @LF)
EndFunc   ;==>_DebugPrint
Edited by gafrost

SciTE for AutoItDirections for Submitting Standard UDFs

 

Don't argue with an idiot; people watching may not be able to tell the difference.

 

Share this post


Link to post
Share on other sites

Thank you, very nice!


BlueBearrOddly enough, this is what I do for fun.

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