Jump to content

DllCall - getting Invalid parameter error


Recommended Posts

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.
Link to comment
Share on other sites

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.

 

Link to comment
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
 Share

  • Recently Browsing   0 members

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