Problem mit VBS Übersetzung in AutoIt

  • Guten Abend liebe Leute,

    Ich würde gerne via WMI überprüfen, ob ein bestimmtes MSI Product (Property "ProductCode") bereits auf der Maschine installiert ist und welcher Wert das Property "ALLUSERS" hat.

    Dazu habe ich nachfolgendes Script erstellt. Aber es dauert im günstigsten Fall 7,8 im ungünstigsten Fall 9,8 Sec. :sleeping:
    Evtl. ist es ja möglich diese Routine zu optimieren? :huh:

    [autoit]


    $COMPUTERNAME = EnvGet ("COMPUTERNAME")
    InstalledProduct()
    Exit

    [/autoit][autoit][/autoit][autoit]

    Func InstalledProduct()

    [/autoit][autoit][/autoit][autoit]

    Local $ProductCode, $ProductName ,$Val
    Local $objItems
    Local $objWMI

    [/autoit][autoit][/autoit][autoit]

    $wbemFlagReturnImmediately = 0x10
    $wbemFlagForwardOnly = 0x20

    [/autoit][autoit][/autoit][autoit]

    $objWMI = ObjGet("winmgmts:\\" & $COMPUTERNAME & "\root\CIMV2")
    ;$objItems = $objWMI.ExecQuery('Select * from Win32_Property Where Name = {2C1B3E20-EE87-4E70-A0CD-5FCFD0F2D9B4}', 'WQL', $wbemFlagReturnImmediately + $wbemFlagForwardOnly)
    $objItems = $objWMI.ExecQuery('Select * from Win32_Property', 'WQL', $wbemFlagReturnImmediately + $wbemFlagForwardOnly)
    If IsObj($objItems) Then
    For $objItem In $objItems
    $Caption = $objItem.Caption
    $ProductCode = $objItem.ProductCode
    $Val = $objItem.Value
    If $ProductCode = "{2C1B3E20-EE87-4E70-A0CD-5FCFD0F2D9B4}" And $Caption = "ALLUSERS" Then ConsoleWrite("> Product Code : " & $ProductCode & @CRLF & "> ALLUSERS State : " & $Val & @CRLF)
    Next
    EndIf
    EndFunc

    [/autoit]

    Auf der anderen Seite habe ich ein VBS. Evtl. kann man ja das in AutoIt umsetzen?
    Hier wird das ProductEx Object verwendet. Um dies korrekt verwenden zu können muss bei Euch der Windows Installer 3.1 installiert sein.
    Ist ja bei mir der Fall, allerdings funktioniert das ansteuern dieses Object in AutoIt bei mir nicht.

    [autoit]


    Set oMSI = CreateObject("WindowsInstaller.Installer")

    [/autoit][autoit][/autoit][autoit]

    Function IsProductInstalled(strProductCode)
    Dim oProducts
    Dim strTemp
    IsProductInstalled = False
    On Error Resume Next
    Err.Clear
    Set oProducts = oMsi.ProductsEx(strProductCode,"",4) ' list only Products that installed in machine context
    If Err.Number <> 0 Then
    On Error Goto 0
    ' Your Windows Installer Version does not support the ProductsEx property!
    Set oProducts = oMsi.Products
    For Each strTemp In oProducts
    If UCase(strTemp) = UCase(strProductCode) Then
    IsProductInstalled = True
    End If
    Next
    Else
    On Error Goto 0
    If oProducts.Count > 0 Then
    IsProductInstalled = True
    Else
    Set oProducts = oMsi.ProductsEx(strProductCode,"s-1-1-0",1+2+4) ' list all products
    If oProducts.Count > 0 Then
    ' WARNING: The Product is not installed using ALLUSERS = 1
    End If
    End If
    End If
    Set oProducts = Nothing
    End Function

    [/autoit]

    Im VBS ist es vertretbar schnell.

    Kann mir jemand helfen? Ich sitze jetzt schon 1 Woche daran. ;(

    Viele Grüße,
    Axel

    --
    Mit freundlichen Grüßen,
    Axel Stoppa

    Einmal editiert, zuletzt von MCTAST245 (29. Juni 2009 um 09:49)

  • Hallo,

    du könntest auch mittels einer Schleife den Uninstall-Zweig in der Registry nach dem Produkt durchsuchen lassen...

    Hier ist ein Beispiel: Apple QuickTime 7.5.5 (zugegebenermaßen ist der Code nicht sehr gut lesbar...)

    Der Einfachheit halber, kannst du aber auch diese Funktion testen, die ich inzwischen verwende (die Basis habe ich entweder hier oder im englischen Forum gefunden):

    Spoiler anzeigen
    [autoit]

    ;===============================================================================
    ; Description: Uninstalls the version of a software identified by the DisplayName key in the registry. (Works only for MSI packages)
    ; Syntax; RemoveMSISoftware($s_Name)
    ; Parameters: $s_Name - The display name of the software to remove.
    ;===============================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func _RemoveMSISoftware($s_Name)

    MsgBox(0,"Uninstallation message", "Check for old Version")
    $num = 0
    $fin = 0
    Do
    $num = $num + 1
    $key = RegEnumKey("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", $num)
    If @error <> 0 Then
    $fin = 1
    Else
    $displayname = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" & $key, "DisplayName")
    If StringInStr($displayname, $s_Name) Then
    $UninstallString = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" & $key, "UninstallString")
    $DisplayVersion = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" & $key, "DisplayVersion")
    MsgBox(0,"Uninstallation message", "Uninstall " & $displayname & " (" & $DisplayVersion & ")")
    $UninstallString = StringUpper($UninstallString)
    $UninstallString = StringReplace($UninstallString, "/I", "/X ") & " /qn ALLUSERS=1 REBOOT=ReallySuppress"
    $code = RunWait($UninstallString, $WorkDir, @SW_HIDE)
    Sleep(10000)
    If $code Then
    If $code = 3010 Then
    MsgBox(0,"Uninstallation message", "Successfull uninstallation, a reboot of the workstation is requested.")
    Else
    MsgBox(0,"Uninstallation message", "Error during uninstallation, the error code is:." & $code)
    SetError(4)
    Return;
    EndIf
    Else
    MsgBox(0,"Uninstallation message", "Successfull uninstallation.")
    EndIf

    ; now check if uninstall ok - uninstall key deleted and decrement instance number ($num)
    $displayname = RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" & $key, "DisplayName")
    If $displayname = "" Then $num = $num - 1
    EndIf
    EndIf
    Until $fin
    EndFunc ;==>_RemoveMSISoftware

    [/autoit]

    mfg
    Axel

    There exist 10 different kind of people on earth.
    Those who understand binary, and those who don't.

  • Hallo Axel (Namensvetter) :huh:

    vielen Dank für deine Antwort.
    Jedoch ist eine Aufgabe meines Scriptes herauszufinden, ob das installierte Product mit dem Property ALLUSERS=1 installiert wurde oder nicht.
    Darum verwende ich auch das WMI Object "Win32_Property". Da kann ich den Status auslesen.
    Leider ist dein Script in meiner Situation unbrauchbar, sorry.

    Aber vielen Dank für deine Mühe. ;)

    Viele Grüße,
    Axel

    --
    Mit freundlichen Grüßen,
    Axel Stoppa

  • Hallo Bugfix,

    uuupps. Hab ich übersehen. :S

    Set oMSI = CreateObject("WindowsInstaller.Installer")

    Schon mal vielen Dank für deine Mühe.

    Viele Grüße,
    Axel

    --
    Mit freundlichen Grüßen,
    Axel Stoppa

    Einmal editiert, zuletzt von MCTAST245 (29. Juni 2009 um 10:25)

  • So?

    Spoiler anzeigen
    [autoit]

    MsgBox(0, "Installiert?", _IsProductInstalled("{54E3707F-808E-4fd4-95C9-15D1AB077E5D}"))

    [/autoit] [autoit][/autoit] [autoit]

    Func _IsProductInstalled($ProdCode)
    Local $oMSI = ObjCreate("WindowsInstaller.Installer")
    For $elements In $oMSI.Products
    If $elements = $ProdCode Then Return True
    Next
    Return False
    EndFunc ;==>_IsProductInstalled

    [/autoit]

    EDIT: Sorry hab das mit den ALL USERS noch nicht!

    • Offizieller Beitrag

    Probier mal:

    [autoit]


    Local $strProductCode = '{1234ABCD-AAAA-BBBB-CCCC-567890ABCDEF}' ; <== dein Produkt-Code
    Local $oMSI = ObjCreate("WindowsInstaller.Installer")
    Local $oProducts = $oMsi.Products
    Local $IsInstalled = 0
    For $ProductID In $oProducts
    If StringUpper($ProductID) == StringUpper($strProductCode) Then
    $IsInstalled = 1
    ExitLoop
    EndIf
    Next
    If Not $IsInstalled Then Exit MsgBox(0, '', 'Produkt ist nicht installiert')

    [/autoit][autoit][/autoit][autoit]

    $oProducts = $oMsi.ProductsEx($strProductCode,"s-1-1-0",1+2+4)
    If $oProducts.Count > 0 Then MsgBox(0, 'WARNING', 'The Product is not installed using ALLUSERS = 1')

    [/autoit]
  • Hallo Bugfix,

    danke für dein Script.
    Bis Zeile 12 ist alles ok. Super. Er filtert genau den ProductCode heraus und gibt über die Variable $IsInstalled wieder, ob das MSI bereits installiert ist oder nicht.

    Aber der Rest funktioniert nicht.
    Ich habe das Script mit einem installiertem MSI ProductCode, welches ich selber repaketiert habe, versucht.
    Daher weiß ich, dass bei der Installation das MSI Property ALLUSERS=1 war.

    Es kommt immer die Message Box, da der Wert von $oProducts.Count immer 1 ist, egal ob das MSI mit ALLUSERS=1 oder ALLUSERS=2 installiert wurde.

    Habe aber gerade einen interessanten Link gefunden.
    http://msdn.microsoft.com/en-gb/library/aa369461(VS.85).aspx
    und
    http://msdn.microsoft.com/en-gb/library/aa371120(VS.85).aspx

    Evtl. kannst du ja etwas damit anfangen.

    Schönen Abend,
    Axel

    --
    Mit freundlichen Grüßen,
    Axel Stoppa

    • Offizieller Beitrag

    Hi,
    dann sollte man vielleicht das ganze Objekt-Gedöns weglassen :D und mit der DLL arbeiten.

    Ich habe mich mal dran versucht und die Objekteigenschaft ProductsEx in den Dll-Funktionsaufruf MsiEnumProductsExW umgesetzt. Sollte (wenn es denn klappt) auch deutlich schneller sein.
    Kleiner Aufruf an progandy: Schau bitte mal drüber, ob das OK ist.
    Habs noch nicht getestet. Und bin im Moment auch zu müde. :P
    Ich hänge mal die OriginalInfo zu dieser Funktion mit an.
    Es werden noch die Werte für einige Konstanten benötigt, aber da kannst du ja selber mal schauen. Nimm es als Arbeitsansatz. ;)

    Func MsiEnumProductsEx
    [autoit]

    Func MsiEnumProductsEx($strProductCode, $strUserSid, $dwContext, $dwIndex)
    Local $szProductCode = DllStructCreate('wchar[' & StringLen($strProductCode)+1 & ']')
    Local $szUserSid = DllStructCreate('wchar[' & StringLen($strUserSid)+1 & ']')
    Local $pdwInstalledContext = DllStructCreate('int')
    Local $szSid = DllStructCreate('wchar[128]')
    Local $pcchSid = DllStructCreate('int')
    DllStructSetData($szProductCode, 1, $strProductCode)
    DllStructSetData($szUserSid, 1, $strUserSid)
    DllStructSetData($pdwInstalledContext, 1, $dwContext)
    Local $ret = DllCall('Msi.dll', 'uint', 'MsiEnumProductsExW', _
    'ptr', DllStructGetPtr($szProductCode), _
    'ptr', DllStructGetPtr($szUserSid), _
    'dword', $dwContext, _
    'dword', $dwIndex, _
    'wchar', 'szInstalledProductCode[40]', _
    'ptr', DllStructGetPtr($pdwInstalledContext), _
    'ptr', DllStructGetPtr($szSid), _
    'ptr', DllStructGetPtr($pcchSid))
    Local $aOut[4]
    $aOut[0] = $ret[5] ; ProductCode GUID
    $aOut[1] = DllStructGetData($pdwInstalledContext, 1); can be MSIINSTALLCONTEXT_USERMANAGED, MSIINSTALLCONTEXT_USERUNMANAGED, MSIINSTALLCONTEXT_MACHINE
    $aOut[2] = DllStructGetData($szSid, 1) ; string SID of the account under which this product instance exists
    $aOut[3] = DllStructGetData($pcchSid, 1) ; number of TCHAR in the szSid buffer
    Return $aOut
    EndFunc

    [/autoit]


    Und hier die Infos aus der Beschreibung zur Dll:

    Spoiler anzeigen
    [autoit]

    #cs

    [/autoit] [autoit][/autoit] [autoit]

    UINT MsiEnumProductsEx(
    __in_opt LPCTSTR szProductCode,
    __in LPCTSTR szUserSid,
    __in DWORD dwContext,
    __in DWORD dwIndex,
    __out_opt TCHAR szInstalledProductCode[39],
    __out_opt MSIINSTALLCONTEXT *pdwInstalledContext,
    __out_opt LPTSTR szSid,
    __inout_opt LPDWORD pcchSid

    [/autoit] [autoit][/autoit] [autoit]

    szProductCode [in, optional]
    ProductCode GUID of the product to be enumerated. Only instances of products within the scope of the context specified by the
    szUserSid and dwContext parameters are enumerated. This parameter can be set to NULL to enumerate all products in the
    specified context.

    [/autoit] [autoit][/autoit] [autoit]

    szUserSid [in]
    Null-terminated string that specifies a security identifier (SID) that restricts the context of enumeration.
    The special SID string s-1-1-0 (Everyone) specifies enumeration across all users in the system.
    A SID value other than s-1-1-0 is considered a user-SID and restricts enumeration to the current user or any user in the system.
    This parameter can be set to null to restrict the enumeration scope to the current user.
    SID Meaning
    type

    [/autoit] [autoit][/autoit] [autoit]

    NULL Specifies the currently logged-on user.

    [/autoit] [autoit][/autoit] [autoit]

    User SID Specifies enumeration for a particular user in the system. An example of user SID is
    "S-1-3-64-2415071341-1358098788-3127455600-2561".

    [/autoit] [autoit][/autoit] [autoit]

    s-1-1-0 Specifies enumeration across all users in the system.

    [/autoit] [autoit][/autoit] [autoit]

    Note:
    The special SID string s-1-5-18 (System) cannot be used to enumerate products or patches installed as per-machine.
    When dwContext is set to MSIINSTALLCONTEXT_MACHINE only, szUserSid must be null.

    [/autoit] [autoit][/autoit] [autoit]

    dwContext [in]
    Restricts the enumeration to a context. This parameter can be any one or a combination of the values shown in the following table.

    [/autoit] [autoit][/autoit] [autoit]

    Context Meaning
    MSIINSTALLCONTEXT_USERMANAGED Enumeration extended to all per–user–managed installations for the users specified by szUserSid.
    An invalid SID returns no items.

    [/autoit] [autoit][/autoit] [autoit]

    MSIINSTALLCONTEXT_USERUNMANAGED Enumeration extended to all per–user–unmanaged installations for the users specified by szUserSid.
    An invalid SID returns no items.

    [/autoit] [autoit][/autoit] [autoit]

    MSIINSTALLCONTEXT_MACHINE Enumeration extended to all per-machine installations. When dwInstallContext is set
    to MSIINSTALLCONTEXT_MACHINE only, the szUserSID parameter must be null.

    [/autoit] [autoit][/autoit] [autoit]

    dwIndex [in]
    Specifies the index of the product to retrieve. This parameter must be zero for the first call to the MsiEnumProductsEx function
    and then incremented for subsequent calls. The index should be incremented, only if the previous call has returned ERROR_SUCCESS.
    Because products are not ordered, any new product has an arbitrary index. This means that the function can return products in any
    order.

    [/autoit] [autoit][/autoit] [autoit]

    szInstalledProductCode [out, optional]
    Null-terminated string of TCHAR that gives the ProductCode GUID of the product instance being enumerated. This parameter can be
    null.

    [/autoit] [autoit][/autoit] [autoit]

    pdwInstalledContext [out, optional]
    Returns the context of the product instance being enumerated. The output value can be MSIINSTALLCONTEXT_USERMANAGED,
    MSIINSTALLCONTEXT_USERUNMANAGED, or MSIINSTALLCONTEXT_MACHINE. This parameter can be null.

    [/autoit] [autoit][/autoit] [autoit]

    szSid [out, optional]
    An output buffer that receives the string SID of the account under which this product instance exists. This buffer returns an
    empty string for an instance installed in a per-machine context.
    This buffer should be large enough to contain the SID. If the buffer is too small, the function returns ERROR_MORE_DATA (0x000000EA)
    and sets *pcchSid to the number of TCHAR in the SID, not including the terminating NULL character.
    If szSid is set to NULL and pcchSid is set to a valid pointer, the function returns ERROR_SUCCESS (0x00000000) and sets
    *pcchSid to the number of TCHAR in the value, not including the terminating NULL. The function can then be called again to
    retrieve the value, with the szSid buffer large enough to contain *pcchSid + 1 characters.
    If szSid and pcchSid are both set to NULL, the function returns ERROR_SUCCESS if the value exists, without retrieving the value.

    [/autoit] [autoit][/autoit] [autoit]

    pcchSid [in, out, optional]
    When calling the function, this parameter should be a pointer to a variable that specifies the number of TCHAR in the
    szSid buffer. When the function returns, this parameter is set to the size of the requested value whether or not the function
    copies the value into the specified buffer. The size is returned as the number of TCHAR in the requested value, not including
    the terminating null character.
    This parameter can be set to NULL only if szSid is also NULL, otherwise the function returns ERROR_INVALID_PARAMETER (0x00000057).
    #ce

    [/autoit]
  • Guten Morgen BugFix,

    hoffe du hast usgeschlafen. ;)

    Wow, das ist ein tolles Script.
    Ich habe dem Script jetz einfach nur die Zeilen

    [autoit]


    $strProductCode='{8EDD2192-A0FF-485D-862F-C692DE378B9B}'
    $strUserSid='s-1-1-0'
    $dwContext='MSIINSTALLCONTEXT_MACHINE'
    $dwIndex='0'

    [/autoit][autoit][/autoit][autoit]

    MsiEnumProductsEx($strProductCode, $strUserSid, $dwContext, $dwIndex)

    [/autoit]


    Vorangestellt.

    Ist das so korrekt? Denn ich bekomme beim ausführen einen Fehler. F:\MsiEnumProductsEx.au3 (30) : ==> Subscript used with non-Array variable.:
    Evtl. habe ich mir das ja zu einfach vorgestellt. Ehrlich gesagt verstehe ich das Ganze nicht 100%tig.

    Viele Grüße,
    Axel

    --
    Mit freundlichen Grüßen,
    Axel Stoppa

    • Offizieller Beitrag

    Hi,
    nur ganz kurz bevor ich losfahr:
    MSIINSTALLCONTEXT_MACHINE ist eine Konstante, da muß noch der Wert zu rausgesucht werden.
    Ich bin mir auch nicht ganz sicher ob alles rund ist. Im Moment hab ich nur keine Zeit weiter zu probieren. Evtl. komme ich heute Abend oder morgen nochmal dazu.

  • Hallo BugFix,

    nur keine Hektik.
    Ich finde es echt Klasse das du mir hilfst. :rock:
    Ohne die Hilfe würde ich ganz schön im Regen stehen. :huh:
    Vielen Dank auch auch an alle anderen Leute die sich beteiligen. :thumbup:

    Schönen Tag.

    Viele grüße,
    Axel

    --
    Mit freundlichen Grüßen,
    Axel Stoppa