Mit ctypes auf Visual Basic 6 DLL/OCX zugreifen? (COM)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo!

Kann man mit ``ctypes`` auch auf DLLs und OCXe zugreifen, die von Visual Basic 6 erzeugt wurden?

Wenn ja, wie? Kleines Beispiel?

lg
Gerold
:-)
Zuletzt geändert von gerold am Samstag 17. Februar 2007, 22:49, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Das sind IIRC COM-Objekte, also sollten die mit den COM-APIs manipulierbar sein.

Da gibts doch win32com, wenn ich mich nicht irre...
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

birkenfeld hat geschrieben:Das sind IIRC COM-Objekte, also sollten die mit den COM-APIs manipulierbar sein. Da gibts doch win32com, wenn ich mich nicht irre...
Hi birkenfeld!

Neuer Avatar für den Frühling -- nett :-)

Ich habe in einem anderen Thread ganz "gscheid" behauptet, dass mir der Schritt, "ins Python 2.5 ctypes statt pywin32 aufzunehmen", sehr gefällt und ich mir wünsche, dass es demnächst viele, viele Python-Schnittstellen geben wird, die ctypes einsetzen. :roll:

Ich setze sehr oft ``win32com`` ein, um auf alte VB6-Programme zuzugreifen. Das wollte ich jetzt ändern und auf ``ctypes`` umstellen. Das einzige was ich bis jetzt gefunden habe: ``DllGetClassObject``
Aber auch die Erklärung im Windows Platform SDK hat mich nicht unbedingt weiter gebracht. Ich finde nicht raus, wie ich eine einfache Funktion in einer VB6-DLL aufrufen kann.

Um z.B. auf die Klasse ``Employee`` in der DLL ``sw3demp.dll`` zuzugreifen, würde ich mit win32com so vor gehen:

Code: Alles auswählen

from win32com.client import Dispatch
employee_class = Dispatch("sw3demp.Employee")
print employee_class.Employee_Count()
Dieses Beispiel greift auf die Methode ``Employee_Count`` zu und gibt mir die Anzahl der in der Datenbank eingetragenen Mitarbeiter zurück.

Wie würde so etwas mit ctypes funktionieren?

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Tut mir leid, hier kein Windows, und die Online-MSDN Library scheint inzwischen für Firefox gänzlich unbenutzbar gemacht worden zu sein.

Letztendlich glaube ich, dass es eben spezielle COM-API-Funktionen gibt, mit denen du Methoden und Eigenschaften von COM-Objekten aufrufen und manipulieren kannst.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

http://groups.google.de/group/comp.lang ... d8ea84ffd1

Jetzt bin ich wirklich verärgert :evil:

Was soll denn das schon wieder? Warum wird in der Windows-Version von Python nicht das, meines Erachtens, essentielle ``ctypes.com`` mitgeliefert? Jeder kleine Sch... in Windows ist ein COM-Objekt.

So wird wieder nichts aus einem allgemein brauchbaren Schnittstellensystem für Python.

Oder liege ich da total falsch mit der Annahme, dass ich ctypes.com für den Zugriff auf ActiveX-Objekte brauche?
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

gerold hat geschrieben:Was soll denn das schon wieder? Warum wird in der Windows-Version von Python nicht das, meines Erachtens, essentielle ``ctypes.com`` mitgeliefert? Jeder kleine Sch... in Windows ist ein COM-Objekt.
Update: siehe unten
gerold hat geschrieben:So wird wieder nichts aus einem allgemein brauchbaren Schnittstellensystem für Python.

Oder liege ich da total falsch mit der Annahme, dass ich ctypes.com für den Zugriff auf ActiveX-Objekte brauche?
Hast du es schon mal mit comtypes versucht? Das ist von Thomas Heller, dem ctypes-Autor und es scheint mir, als würde das das gleiche machen wollen wie ctypes.com. Ist zwar nicht mitgeliefert, aber es hat den Vorteil, dass es pures Python ist und daher problemlos mit deinen Programmen mitgeliefert werden kann. Ist damit sicherlich eine interessante Alternative zu pywin32.

Edit:
Thomas Heller hat geschrieben:comtypes and venster to not contain any C code, so changes are good
that they work in Python 2.5 as well. AFAIK, venster still uses the
no longer supported ctypes.com package, it should be updated to
use comtypes instead.
Sieht so aus, als hätte Thomas Heller kein Interesse daran gehabt den COM-Code in ctypes zu maintainen. Das wird wohl zumindest einer der Gründe sein.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Leonidas hat geschrieben:Hast du es schon mal mit comtypes versucht? Das ist von Thomas Heller, dem ctypes-Autor und es scheint mir, als würde das das gleiche machen wollen wie ctypes.com. Ist zwar nicht mitgeliefert, aber es hat den Vorteil, dass es pures Python ist und daher problemlos mit deinen Programmen mitgeliefert werden kann.
Hi Leonidas!

Das werde ich jetzt gleich ausprobieren. Dass es nicht für jede Version kompiliert werden muss, reißt comtypes wieder raus. Denn es ist wirklich kein Problem, ein paar Python-Dateien in das eigene Projekt mit aufzunehmen. Mir ging es ja genau darum.

Ich werde berichten...

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Das hier funktioniert schon mal. Wie es mit Parametern aussieht, werde ich noch austesten.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

#from win32com.client import Dispatch
#employee_class = Dispatch("sw3demp.Employee")
#print employee_class.Employee_Count()

from comtypes.client import CreateObject
employee_class = CreateObject("sw3demp.Employee")
print employee_class.Employee_Count()
lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Und hier ein Beispiel mit Rückgabeparametern, die im Visual Basic 6 als ``byref`` angegeben sind.

Das hier ist der Kopf der Funktion, wie sie im Visual Basic definiert wurde:

Code: Alles auswählen

'***************************************************************************************************
'* Erstellt von: Gerold Penz      Am: 17.10.2000 17:35:03
'* Beschreibung: Gibt die neue Bonnummer und die Zufallsnummer zurück
'*               000 00000 0000000
'*               000 00000 AAAAA
'* Rückgabe: eTrue = OK
'*           eErrorMisc = Fehler
'***************************************************************************************************
Public Function GetNewBonnummer( _
    ByVal iKassaNr As Integer, _
    ByVal dBonzeit As Date, _
    ByRef sBack_NewBonnummer As String, _
    ByRef lBack_Monatsschluessel As Long, _
    ByRef lBack_NeuerZaehler As Long, _
    ByRef sBack_NewBonnummerZufall As String _
) As Enum_TrueFalseError
"comtypes" legte beim ersten Aufruf von "CreateObject" im Ordner "J:\Python24\Lib\site-packages\comtypes\gen" die Datei "sw3dpos.py" an. In dieser wird auf die Datei "_5E257BE7_C564_4128_9A0B_A6DB037DA844_0_8_1.py" verwiesen. Wenn man diese Datei öffnet, dann findet man in dieser eine komplette Typdefinition der aufgerufenen DLL.

Ich suche mal die interessantesten Stellen zur aufgerufenen Funktion raus:

Code: Alles auswählen

class _Bon_Real(comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0.IDispatch):
    _case_insensitive_ = True
    _iid_ = GUID('{340EC989-AEC8-4A0F-BDC8-92618BE0B3C7}')
    _idlflags_ = ['nonextensible', 'dual', 'oleautomation', 'hidden']
_Bon_Real._methods_ = [

Code: Alles auswählen

    COMMETHOD([dispid(1610809347)], HRESULT, 'GetNewBonnummer',
              ( ['in'], c_short, 'iKassaNr' ),
              ( ['in'], c_double, 'dBonzeit' ),
              ( ['in', 'out'], POINTER(BSTR), 'sBack_NewBonnummer' ),
              ( ['in', 'out'], POINTER(c_int), 'lBack_Monatsschluessel' ),
              ( ['in', 'out'], POINTER(c_int), 'lBack_NeuerZaehler' ),
              ( ['in', 'out'], POINTER(BSTR), 'sBack_NewBonnummerZufall' ),
              ( ['retval', 'out'], POINTER(comtypes.gen._2A06DCF9_CD17_4277_AB15_DEDD1EF2F6E2_0_329_0.Enum_TrueFalseError), 'None' )),
Mit diesen Informationen ist es ein Leichtes, die Parameter korrekt vorzubereiten. Sogar ein Hinweis auf das Rückgabe-Enum (Enum_TrueFalseError) ist zu finden. Die Definition ist also in der Datei "_2A06DCF9_CD17_4277_AB15_DEDD1EF2F6E2_0_329_0.py". -- wenn man sie braucht.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import datetime
from comtypes.client import CreateObject
import comtypes as ct

bon_real = CreateObject("sw3dpos.Bon_Real")


def to_win_serialdate(datetimeobj):
    """
    Wandelt das `datetimeobj` in das "Serielle Datumsformat" von
    Windows um. Dieses beginnt beim 1.1.1900 und zählt jeden Tag
    vor dem Komma. Nach dem Komma steht die Zeit. 0,5 bedeutet
    12:00 Uhr Mittag
    """
    
    begindatetime = datetime.datetime(1900, 1, 1)
    td = datetimeobj - begindatetime + datetime.timedelta(days = 2)
    return td.days + (1.0 / (24 * 60 * 60) * td.seconds)
    

iKassaNr = ct.c_short(1)
dBonzeit = ct.c_double(to_win_serialdate(datetime.datetime.now()))
sBack_NewBonnummer = ct.BSTR()
lBack_Monatsschluessel = ct.c_int()
lBack_NeuerZaehler = ct.c_int()
sBack_NewBonnummerZufall = ct.BSTR()

print bon_real.GetNewBonnummer(
    iKassaNr,
    dBonzeit,
    sBack_NewBonnummer,
    lBack_Monatsschluessel,
    lBack_NeuerZaehler,
    sBack_NewBonnummerZufall
)

print "iKassaNr:", iKassaNr.value
print "dBonzeit:", dBonzeit.value
print "sBack_NewBonnummer:", sBack_NewBonnummer.value
print "lBack_Monatsschluessel:", lBack_Monatsschluessel.value
print "lBack_NeuerZaehler:", lBack_NeuerZaehler.value
print "sBack_NewBonnummerZufall:", sBack_NewBonnummerZufall.value

Code: Alles auswählen

(BSTR(u'001391140000039'), c_long(39114), c_long(39), BSTR(u'00139114PMHRM'), -1)
iKassaNr: 1
dBonzeit: 39130.9320139
sBack_NewBonnummer: 001391140000039
lBack_Monatsschluessel: 39114
lBack_NeuerZaehler: 39
sBack_NewBonnummerZufall: 00139114PMHRM
"comtypes" scheint auf Anhieb besser zu funktionieren, als ich es mit "win32com" je zusammengebracht hatte.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hi Gerold.
gerold hat geschrieben: "comtypes" scheint auf Anhieb besser zu funktionieren, als ich es mit "win32com" je zusammengebracht hatte.
Das hört sich interessant an. Werde kommende Woche auch mal austesten ob ich damit meine tools auch zum laufen kriege. Das wäre tatzächlich eine sehr gutz Alternative, da man für COM nicht mehr auf pywin angewiesen wäre 8)
Antworten