COM / OLE Objekte - Fehler 80020003, Mitglied nicht gefunden

  • Hallo Zusammen,

    versuche gerade mit AutoIt eine ERP-Anwendung per OLE / COM Schnittstelle zu automatisieren.

    Bisher wurde dies immer über Excel-VBA bzw. VBScript durchgeführt, was auch funktioniert.

    Nun bin ich begeisterter AutoIt-Fan und versuche die VBA- bzw. VB-Scripte auf AutoIt umzustellen.

    Dabei tritt das seltsame Phänomen auf, dass aus AutoIt heraus nicht alle Methoden des Anwendungs-Objektes funktionieren, während es diesbezüglich mit VBA/VBS keinerlei Probleme gibt.

    Im folgenden habe ich mal ein Beispiel-Skript aufgelistet:


    Local $oHWP
    Local $wert
    Local $i

    $oHWP = ObjCreate ("HWP.Anwendung")

    if ($oHWP.Angemeldet = 0) Then ; funktioniert 
    $oHWP.Anmelden ("USER","PASSWORD") ; liefert Fehler "COM-Error 80020003, Mitglied nicht gefunden" 
    endif

    $wert = $oHWP.Version ; funktioniert (Version der Software abfragen)

    $wert = $oHWP.Table("ADR").Anzahl ; funktioniert (Anzahl der Adressen abfragen)

    $oHWP.Table("ADR").ErsterSatz ; liefert Fehler "COM-Error 80020003, Mitglied nicht gefunden"

    For $i = 1 to $wert 
    MsgBox (0, "Information", $oHWP.Table("ADR").Feld("Nummer")) ' funktioniert 
    $oHWP.Table("ADR").NaechsterSatz ' funktioniert 
    Next

    $oHWP = 0

    Exit

    Die Fehlercodes wurden über das Standard-Verfahren ($oMyError = ObjEvent("AutoIt.Error","MyErrFunc")) ermittelt, das aus Gründen der Übersichtlichkeit im obigen Code weggelassen wurde.

    Nochmals der Hinweis: Alle oben als nicht funktionierende Methoden gekennzeichneten Aufrufe in AutoIt funktionieren ohne Probleme unter VBA / VBS in der o.a. Syntax.

    Der Hersteller der Software behauptet nun natürlich, wenn es unter VBA / VBS ohne Fehler funktioniert, so ist seine Schnittstelle in Ordnung und es liegt an AutoIt.

    Soweit ich in Erfahrung bringen konnte, verwendet der Hersteller für die COM Schnittstelle das sog. "late binding" Verfahren.

    Nun die Frage:
    Gibt es irgendwelche Besonderheiten zu beachten bzw. Tricks, um aus AutoIt heraus OLE-/COM-Objekte mit "late binding" aufzurufen?

    Wäre für Eure Hilfe sehr dankbar.

    Gruss, Frank

    Einmal editiert, zuletzt von kfl42ai (1. Juni 2010 um 20:10)

  • Der Herstlerr der Software behaupptet nun natürlich, wenn es unter VBA / VBS ohne Fehler funktioniert, so ist seine Schnittstelle in Ordnung

    damit hat er recht => Beweis ist erbracht.

    Um welche Software handelt es sich denn :?:

  • Hallo AutoBert,

    es handelt sich um die Software HWP des Herstellers Sage (eine Software für Handwerks-Unternehmen). Programmiert ist sie in Delphi und für die COM-Schnittstelle wird das sog. late-binding verwendet.

    Gruss, Frank

  • Gibts dazu eine Doku? Bzw. poste mal das vbs Skript dazu.
    Weil da was von "Table" steht: Autoit kann mit Aufrufen die ein Array zurückgeben leider nichts anfangen.

    Das hier sollte man wahrscheinlich auch vermeiden:

    [autoit]

    $wert = $oHWP.Table("ADR").Anzahl ; funktioniert (Anzahl der Adressen abfragen)

    [/autoit]


    besser so:

    [autoit]


    $wert = $oHWP.Table("ADR")
    $ret=$wert.Anzahl

    [/autoit]
  • In VBA sieht das Script in etwa so aus (und funktioniert problemlos):

    Dim oHWP
    Dim wert
    Dim i

    Set oHWP = CreateObject ("HWP.Anwendung")

    if (oHWP.Angemeldet = 0) Then ' prüfen, ob schon in Anwendung angemeldet
    oHWP.Anmelden "USER","PASSWORD" ' in Anwendung anmelden  
    end if

    wert = oHWP.Version

    wert = oHWP.Table("ADR").Anzahl ' gibt die Anzahl der Datensätze der Tabelle "ADR" als Ganzzahl zurück

    oHWP.Table("ADR").ErsterSatz ' geht auf den ersten Satz der Tabelle "ADR"

    For i = 1 to wert 
    MsgBox (oHWP.Table("ADR").Feld("Nummer")) ' gibt den Inhalt des Feldes "Nummer" im aktuellen Datensatz der Tabelle "ADR" als String zurück 
    oHWP.Table("ADR").NaechsterSatz ' geht zum nächsten Datensatz der Tabelle "ADR"
    Next

    Set oHWP = Nothing


    Das obige Beispiel ist sozusagen das 1:1 Äquivalent zum AutoIt Script und funktioniert ohne Probleme bzw. Fehler.

    AutoIt scheint also irgendwie ein Problem beim Aufruf div. Methoden zu haben, während es andere Methoden desselben Objektes ohne Probleme aufrufen kann.

  • autoBert,

    sowohl die Variante :

    $wert = $oHWP.Table("ADR").Anzahl

    als auch die Variante :

    $oHWP_ADR = $oHWP.Table("ADR")
    $wert = $oHWP_ADR.Anzahl

    funktionieren einwandfrei.

    das Problem ist vielmehr, das z.B.

    $oHWP.Table("ADR").NaechsterSatz

    einwandfrei funktioniert, während der Aufruf von

    $oHWP.Table("ADR").ErsterSatz

    fehlschlägt, obwohl beide Methoden für das Objekt definiert sind und unter VBA / VBS funktionieren.

    Selbst der Aufruf einer Methode des Hauptobjektes $oHWP funktionert nicht:

    $oHWP.Anmelden ("USER", "PASSWORD")

    Andere Methoden des Hauptobjektes hingegen funktionieren.

  • Puh schwierig ohne die Software und eine Doku.
    Sicher das die Zugangsdaten auch stimmen? $oHWP.Anmelden ("USER", "PASSWORD")
    Das Aufruf funktioniert doch oder? (kein request to objekt failed oder ähnliches?)

  • nuts,

    eine voll funktionsfähige Demoversion kann ich Dir auf DVD zur Verfügung stellen.

    P.S.:
    Beim Statement "$oHWP.Anmelden ("USER", "PASSWORD")" sind nicht korrektes USER/PASSWORD das Problem, sondern dass die Methode gar nicht erst ausführt wird, obwohl sie sich einwandfrei unter VBA etc. ausführen lässt. Es wird sofort der Fehler "COM error 80020003 - Mitglied nicht gefunden" gemeldet. Die Methode gibt es jedoch definitiv und auch mit der angegebenen Syntax, sonst würde sie ja auch nicht unter VBA etc. funktionieren.
    Ähnlich seltsam ist ja, dass das Statement "$oHWP.Table("ADR").NaechsterSatz" funktioniert, während "$oHWP.Table("ADR").ErsterSatz" ebenfalls den Fehler 80020003 meldet, obwohl wiederum die Syntax bei beiden korrekt ist und unter VBA etc. problemlos funktioniert.

    Gruss, Frank

  • Nach einigen weiteren Tests mit dem COM-Interface ist mir eine Besonderheit / Regelmäßigkeit aufgefallen, die vielleicht bei der Lösung weiterhilft:


    • Der Fehler "COM Error 80020003 - Mitglied nicht gefunden" tritt offensichtlich NUR beim Aufruf von Objekt-Methoden auf, die als Prozedur (ohne Rückgabewert) definiert sind.


    • Objekt-Methoden, die als Funktion definiert sind, d.h. einen Rückgabewert liefern, funktionieren ohne Probleme


    • Die Abfrage von Objekt-Eigenschaften (Properties) macht ebenfalls keine Probleme

    Da in anderen Sprachen das COM-Interface funktioniert, hat möglicherweise AutoIt ein Problem mit Methoden ohne Rückgabewert.

    Gibt es evt. einen Workaround, z.B. eine UDF, die den Aufruf von Methoden hinter einem DllCall mit Übergabe der Objekt-Referenz als DllStruct, verbirgt, und so einen Standard-konformen Aufruf dieser Methoden ermöglicht?

  • Hi,
    Vermultich liegt schon ein Problem in der Implementierung in AutoIt, doch hier würde ich nicht auf Besserung hoffen. Wenn du genügend Ausdauer hast, kannst du versuchen, das Objekt selbst aufzuurufen. Die nötigen Funktionen solltest du in der AutoItObject-UDF finden (vor allem unter den nicht dokumentierten).

    Was jetzt genau den Fehler auslöst, kann ich nicht im Ansatz vermuten und die Delphi-besonderheiten kenne ich überhaupt nicht.

    ProgAndy

  • @progandy,

    genau in " Die nötigen Funktionen solltest du in der AutoItObject-UDF finden (vor allem unter den nicht dokumentierten)." liegt das Problem. ?(

    Könntest Du mir da eine kleine Hilfestellung geben, wo genau ich bei den gefühlten 100 Funktionen anfangen muss zu suchen?

    Es müsste letztendlich dabei eine Funktion herauskommen, die in etwa so definiert ist:

    myObjMethodCall ($oObjRef, $sMethodName, $pOptPar1, $pOptPar2, ..., $pOptPar10) oder so ähnlich.

    Da es sich um ein "late binding" COM-Interface handelt, werden die Methode und die Parameter (soweit ich weiß) nur als String an das Objekt übergeben.

    Diese Funktion könnte dann wie folgt angewandt werden:

    $oHWP = ObjCreate ("HWP.Anwendung")

    if ($oHWP.Angemeldet = 0) Then
    myObjMethodCall ($oHWP, "Anmelden", "USER", "PASSWORD") ; statt $oHWP.Anmelden ("USER","PASSWORD") 
    endif

    $myObjMethodCall ($oHWP.Table("ADR"), "ErsterSatz") ; statt $oHWP.Table("ADR").ErsterSatz

    $oHWP = 0

    Hättest Du einen Ansatz, wo in der AutoItObject-UDF die erforderlichen Informationen zu finden sind bzw. wie an die Sache rangehen muss.

    Frank

  • Ganz so einfach ist es nicht. Du musst zuerst das IDispatch-Interface einbinden. Du kannst z.B. über _AutoItObject_IDispatchToPtr den pointer bekommen, diesen dann in ein Wrapper-Objekt übergeben, dabei musst du die IDispatch-vTable verwenden.

    Anschließend kannst du GetIDsOfNames aufrufen, um die ID zum Methodennamen zu erhalten:
    http://msdn.microsoft.com/en-us/library/aa909091.aspx

    Nun musst du die Invoke-Funktion aufrufen, und alle Parameter übergeben:
    http://msdn.microsoft.com/en-us/library/aa912367.aspx
    Dazu benötigst du z.B. die __Au3Obj_Variant...-Funktionen

  • Ok, so etwas hatte ich befürchtet ?(

    Gibt es für die folgenden Punkte Beispielcode in AutoIt oder zumindest einige Ansatzpunkte?


    P.S.: Sorry, so nah am System im Detail programmieren ist jetzt nicht gerade einer meiner ganz großen Stärken, genauer gesagt: bisher hatte ich damit noch nie zu tun (bevorzuge eher die Anwendungslogik).

  • Das schon recht komplex und ohne COM-Basiswissen eigentlich kaum zu machen.
    Progandy ist schon einiges zuzutrauen aber ob er ohne Software und Doku hier helfen kann ist auch eher fraglich.

    Vielleicht kann man die Objektabfragen in ein .vbs Skript auslagern und dann übergeben?

  • An Software und Doku soll's nicht scheitern, das kann ich zur Verfügung stellen.

    Aber zugegeben, ich dachte es wäre einfacher, eine COM-Objekt Methode über einen alternativen Weg, z.B. per API-Call o.ä. aufzurufen.

    Gibt es sonst noch irgendwelche Tricks in Bezug auf die COM-Nutzung in AutoIt?

    An eine Auslagerung in VBScript hatte ich auch schon gedacht, jedoch sehe ich hier zwei Probleme:

    • Performance, da jedesmal eine WSH Session gestartet werden muss (oder geht das auch einfacher?)
    • Übergabe des COM-Objektes von AutoIt an das VBScript
  • @kfl42ai

    Da ich die Software nicht besitze, kann ich den Quelltext auch nicht ausgiebig testen. Aber so sollte es funktionieren.


    [autoit]


    AutoItSetOption('MustDeclareVars', 1)

    #Region *** Declares ***
    Global $oHWP
    Global $oScriptHost
    Global $sCode
    Global $retVal
    #EndRegion *** Declares ***

    #Region *** Init ScriptControl ***
    $oScriptHost = ObjCreate("ScriptControl")
    $oScriptHost.Language = 'vbscript'

    $oScriptHost.Modules.Add('Login')
    $sCode = 'Sub Anmelden(ByVal HWP, ByVal Benutzer, ByVal Kennwort)'
    $sCode = $sCode & @CRLF & 'HWP.Anmelden Benutzer, Kennwort'
    $sCode = $sCode & @CRLF & 'End Sub'
    $oScriptHost.Modules('Login' ).AddCode($sCode)

    $oScriptHost.Modules.Add('MoveFirst')
    $sCode = 'Sub ErsterSatz(ByVal HWP)'
    $sCode = $sCode & @CRLF & 'HWP.Table("ADR").ErsterSatz'
    $sCode = $sCode & @CRLF & 'End Sub'
    $oScriptHost.Modules('MoveFirst' ).AddCode($sCode)

    $oScriptHost.Modules.Add('MoveNext')
    $sCode = 'Sub NaechsterSatz(ByVal HWP)'
    $sCode = $sCode & @CRLF & 'HWP.Table("ADR").NaechsterSatz'
    $sCode = $sCode & @CRLF & 'End Sub'
    $oScriptHost.Modules('MoveNext' ).AddCode($sCode)
    #EndRegion *** Init ScriptControl ***

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

    $oHWP = ObjCreate('HWP.Anwendung')
    If ($oHWP.Angemeldet = 0) Then
    $oScriptHost.Modules('Login' ).Run('Anmelden', $oHWP, 'USERNAME', 'PASSWORD')
    EndIf

    $retVal = $oHWP.Version
    $retVal = $oHWP.Table('ADR' ).Anzahl
    $oScriptHost.Modules('MoveFirst' ).Run('ErsterSatz', $oHWP)

    For $i = 1 To $retVal
    MsgBox(0, 'Information', $oHWP.Table('ADR' ).Feld('Nummer'))
    $oScriptHost.Modules('MoveNext' ).Run('NaechsterSatz', $oHWP)
    Next

    $oHWP = 0
    $oScriptHost = 0

    [/autoit]
  • Ich schätze, dass auch ein "Module" ausreichen sollte:

    Spoiler anzeigen
    [autoit]

    AutoItSetOption('MustDeclareVars', 1)

    #Region *** Declares ***
    Global $oHWP
    Global $oScriptHost
    Global $sCode
    Global $retVal
    #EndRegion *** Declares ***

    #Region *** Init ScriptControl ***
    $oScriptHost = ObjCreate("ScriptControl")
    $oScriptHost.Language = 'vbscript'

    $oScriptHost.Modules.Add('HWP')
    $sCode = 'Sub Anmelden(ByVal HWP, ByVal Benutzer, ByVal Kennwort)'
    $sCode &= @CRLF & 'HWP.Anmelden Benutzer, Kennwort'
    $sCode &= @CRLF & 'End Sub'

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

    $sCode &= @CRLF & 'Sub ErsterSatz(ByVal HWP)'
    $sCode &= @CRLF & 'HWP.Table("ADR").ErsterSatz'
    $sCode &= @CRLF & 'End Sub'

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

    $sCode &= @CRLF & 'Sub NaechsterSatz(ByVal HWP)'
    $sCode &= @CRLF & 'HWP.Table("ADR").NaechsterSatz'
    $sCode &= @CRLF & 'End Sub'
    $oScriptHost.Modules('HWP' ).AddCode($sCode)
    #EndRegion *** Init ScriptControl ***

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

    $oHWP = ObjCreate('HWP.Anwendung')
    If ($oHWP.Angemeldet = 0) Then
    $oScriptHost.Modules('HWP' ).Run('Anmelden', $oHWP, 'USERNAME', 'PASSWORD')
    EndIf

    $retVal = $oHWP.Version
    $retVal = $oHWP.Table('ADR' ).Anzahl
    $oScriptHost.Modules('HWP' ).Run('ErsterSatz', $oHWP)

    For $i = 1 To $retVal
    MsgBox(0, 'Information', $oHWP.Table('ADR' ).Feld('Nummer'))
    $oScriptHost.Modules('HWP' ).Run('NaechsterSatz', $oHWP)
    Next

    $oHWP = 0
    $oScriptHost = 0

    [/autoit]
  • @DaX, @progandy,

    super, es funktioniert bestens. Ist genau die Art von Workaround, die ich gesucht habe.

    Habe jetzt das Ganze noch so erweitert, dass auch die Tabellen-Namen als Parameter übergeben werden können und baue mir jetzt noch eine UDF.

    Nochmals vielen Dank!