Ich glaube, ich habe ein Problem mit OCA-Dateien. War es nicht so, daß die
OCA-Dateien lokal pro PC erstellt werden, wobei sie untereinander aber nicht
kompatibel sind?
Konkret sieht die Lage so aus:
- 1 Standard-Exe, die ein Control verwendet (unter Strg+T)
- 1 DLL, die dasselbe Control verwendet (auch unter Strg+T)
Zu einem Zeitpunkt X wird eine Instanz des Controls vom Standard-Projekt ans
DLL-Projekt übergeben (As Object). Dort wird der Verweis einer typsierten
Variablen ("As vaSpread", das ist der Name der Control-Klasse) zugewiesen.
Das funktionierte bisher problemlos.
Alle nachfolgenden Ausführungen beziehen sich auf auf Rechner A. Exe und DLL
wurden auf Rechner B erstellt.
Fall 1:
Exe starten => alles ok, d.h. die Übergabe des Controls und Zuweisung an
typisierte Variable funktioniert
Fall 2:
1. DLL in IDE laden
2. F5
3. Exe starten
=> Die Zuweisung zur typisierten Variablen innerhalb der DLL in der IDE
scheitert: Fehler 13, Typen unverträglich. Typename(Quelle) liefert
"vaSpread". Die Zielvariable ist auch vom Typ vaSpread.
Das zeigt doch eigentlich schon, daß die DLL innerhalb der IDE anhand der
lokal erstellten OCA-Datei einen anderen Typ erwartet, als übergeben wurde.
In der kompilierten Exe werden noch die Typ-Informationen der OCA-Datei von
Rechner B verwendet. Kann das sein?
Fall 3:
1. DLL in IDE laden
2. F5
3. Exe in IDE laden
4. F5
=> Die Zuweisung scheitert mit Fehler 459, "Objekt oder Klasse unterstützt
diese Ereignismenge nicht"
Zur Problemlösung habe ich die lokale OCA-Datei gelöscht und sie von Rechner
B geholt. Problem ist nur: Wie kann ich sie registrieren? Ist es keine
normale Typelib? Mit regtlib kann ich sie jedenfalls nicht registrieren:
"LoadTypeLib of spr32x30.oca failed : 80029c4a"
Mit dem Typelib viewer aus dem OLE/COM-Viewer kann ich mir die OCA-Datei
aber ansehen. Dabei kann man auch feststellen, daß sich die GUIDs in den
OCAs zwischen den Rechnern A und B unterscheiden - was wohl die Ursache für
den Fehler 13 sein dürfte.
Bin ich hier auf der richtigen Spur oder wie kann ich das Problem lösen?
Zusatzfrage: Warum wird die OCA-Datei einmal im selben Verzeichnis wie die
OCX-Datei und ein anderes mal im Verzeichnis der vb6.exe angelegt?
Armin
Ich verstehe einiges nicht. Ich konnte die OCA-Datei nun fehlerfrei
registrieren. Weiß der Geier warum.
Das nützt mir aber immer noch nichts: Sobald ich ein Projekt, das das OCX
verwendet, in der IDE lade, überschreibt VB die soeben registrierte
OCA-Datei wieder (am Dateidatum erkennbar)! Wieso das denn? Ich dachte, die
OCA wird nur dann angelegt, wenn sie noch nicht da war. Das Spielchen kann
ich beliebig wiederholen, d.h. die originale OCA-Datei vom anderen Rechner
holen, registrieren - und wieder überschreiben lassen.
Ok, dann habe ich eben die OCA-Datei schreibgeschützt. Gut, VB überschreibt
die OCA-Datei nicht mehr - funktionieren tun tut's aber immer noch nicht
:-( Immer noch Fehler 13. Ich bin momentan ziemlich ratlos.
Mittlerweile weiß ich auch, warum der Fehler noch kommt: VB überschreibt
zwar nicht die schreibgeschützte Datei, aber legt stattdessen im
vb6.exe-Verzeichnis eine vbc06815.oca an. <argh>. Wie bringe ich denn VB nun
dazu, daß es nicht ständig neue OCA-Dateien erzeugt, v.a. dann, wenn sie
schon da ist?
Es muß doch irgendein Kriterium geben, das darüber entscheidet. Die Existenz
der Datei selbst kann es nicht sein. Es ist andererseits aber auch nicht so,
daß *jedesmal* die OCA-Datei angelegt wird, denn die OCA auf dem Rechner B
ist von Jan. 2000.
Armin
> Ich glaube, ich habe ein Problem mit OCA-Dateien.
Seitdem ich sie ignoriere und so oft wie möglich lösche (egal wo sie
stecken), habe ich keine Probleme mehr damit. Sie sind absolut nicht
notwendig.
Viele Grüße
Harald M. Genauck
ABOUT Visual Basic - das Webmagazin
http://www.aboutvb.de
Die Flut - Leser helfen Lesern
http://fluthilfe.aboutvb.de
*stöhn* ähem... auch auf Rechner B gibt es zwei OCAs. Ich hatte hierher die
aus dem OCX-Verzeichnis kopiert. Im vb6-Verzeichnis gibt es aber nochmal
eine. Wie ich jetzt festgestellt habe, wurde bei der Kompilierung die
letztere verwendet. Wenn ich dieser rüberziehe und hier registriere dann
kommt im geschilderte Fall 2 (1. DLL in IDE laden, 2. F5, 3. Exe starten)
wenigstens kein Fehler 13 mehr.
"Ersatzweise" kommt Fehler 459, "Objekt oder Klasse unterstützt diese
Ereignismenge nicht". Das gleiche Problem, wenn auch die Exe in der IDE
geladen wird. Beide Projekte zusammen in der IDE in einer Projektgruppe
jedoch funktionieren - glücklicherweise.
Das eigentliche Problem ist somit gelöst.
..schuldigung für die Störung.
Armin
Hallo Harald,
> Seitdem ich sie ignoriere und so oft wie möglich lösche (egal wo sie
> stecken), habe ich keine Probleme mehr damit. Sie sind absolut nicht
> notwendig.
Ja, das mache ich auch so.
Nur wuesste ich generell gern, wozu die OCAs ueberhaupt gut sind.
Oder, wenn es Nichtsnutze sind, was der gedachte Sinn ist.
-C-H-A-R-L-E-S-
Armin Zingler schrieb:
> Zur Problemlösung habe ich die lokale OCA-Datei gelöscht und sie von Rechner
> B geholt. Problem ist nur: Wie kann ich sie registrieren? Ist es keine
> normale Typelib? Mit regtlib kann ich sie jedenfalls nicht registrieren:
> "LoadTypeLib of spr32x30.oca failed : 80029c4a"
Ich hätte da ein Tool zur OCA Verwaltung auf meiner Website: VbRegTlbMod
(auch OCA-Killer genannt ;-)). Kann OCAs registrieren und
deregistrieren. Zeigt allerhand Registryinfos an (ua GUID, Version,
Speicherort) und kann auf Wunsch gleich den Oleviewer starten. Das Tool
ist generell ganz praktisch, wenn es darum geht, alle Arten von
COM-Komponenten zu verwalten.
> Zusatzfrage: Warum wird die OCA-Datei einmal im selben Verzeichnis wie die
> OCX-Datei und ein anderes mal im Verzeichnis der vb6.exe angelegt?
OCas werden angelegt, wenn ein Programm (Exe oder Dll) ein OCX verwendet
und für die aktuelle Version des OCXs noch kein OCA vorhanden ist. Das
OCA wird allerdings nur angelegt, wenn das Programm oder die DLL in der
IDE ausgeführt werden. Kompilierte Versionen benötigen keine OCAs und
legen auch keine an.
Wenn ein OCA angelegt wurde, bezieht sich dies immer auf eine bestimmte
Version des OCX. Es ist eine getarnte TypeLib und hat eine eigene
TLib-GUID (verschieden von der des OCX). Die TypeLib des OCAs ist in der
Registry wie jede andere TypeLib auch registriert. Zusätzlich hält VB in
der Registry eine Liste der von der IDE angelegten OCAs, die die
Verbindung zwischen OCa und zugehörigem OCX herstellt.
Wird ein Programm in der IDE gestartet, welches ein OCX verwendet.
schaut VB nach, ob es bereits ein OCA dafür hat. Wenn nein, legt es ein
OCA mit neuer GUID und dem Namen des OCX (aber mit der Endung OCA) in
dem Verzeichnis ab, in dem auch das OCX steht. Wird nun eine neue
Version des OCX erstellt und das Programm, welches dieses verwendet,
wieder gestartet, legt VB ein neues OCA an. Ist im Verzeíchnis des OCX
bereits ein OCA vorhanden, wird dieses nicht überschrieben, sondern ein
neues OCA mit Zufallsnamen (a la vbxyz.oca) und wiederum neuer GUID im
Verzeichnis abgelegt, in dem auch die VB.Exe steht. Schau da mal rein,
da dürfte es nur von vb....ocas so wimmeln.
Wenn Du möchtest, daß bei Versionsänderung des OCX immer im Verzeichnis
des OCXs unter dem OCX-Namen, aber mit der Endung OCA das OCA angelegt
wird, mußt Du das alte OCA vorher aus diesem Verzeichnis löschen.
Aber Vorsicht! Einfaches Löschen des Files beseitigt nicht die
Registry-Einträge der OCA-TypeLib! Um die Registry nicht zuzumüllen,
sollte es vorher deregistriert werden (dabei hilft wieder VBRegTLbMod).
Ansonsten können OCAs risikolos beliebig gelöscht werden. Fehlt eines,
legt VB wieder eines an. Sollte es Probleme geben, einfach alle OCAs
deregistrieren und löschen.
Ulrich
--
VB tips and tricks -> http://www.proSource.de/Downloads/
Hallo Ulrich,
> Aber Vorsicht! Einfaches Löschen des Files beseitigt nicht die
> Registry-Einträge der OCA-TypeLib! Um die Registry nicht zuzumüllen,
> sollte es vorher deregistriert werden (dabei hilft wieder VBRegTLbMod).
Oj, ich wusste garnicht, dass die OCAs auch registriert sind!
Das war sehr informativ, danke!
-C-H-A-R-L-E-S-
Charles Panke schrieb:
> > Seitdem ich sie ignoriere und so oft wie möglich lösche (egal wo sie
> > stecken), habe ich keine Probleme mehr damit. Sie sind absolut nicht
> > notwendig.
> Ja, das mache ich auch so.
> Nur wuesste ich generell gern, wozu die OCAs ueberhaupt gut sind.
> Oder, wenn es Nichtsnutze sind, was der gedachte Sinn ist.
Performancegewinn steckt dahinter. Ein UserControl besteht aus einer
UserControl-Klasse, die um einige Erweiterungen (Interfaces) ergänzt
wurde. Der Benutzer des OCX sieht normalerweise nur die öffentliche
Schnittstelle des OCX (also die Methoden und Eigenschaften, die der
Programmierer des OCX hinzugefügt hat) und die Schnittstelle, die der
Container des OCXs hinzugefügt hat. Der Programmierer des OCX sieht die
komplette Schnittstelle: UserControl, dessen öffentliche Schnittstelle
und weitere Schnittstellen wie zB die vom Container hinzugefügte.
Dahinter steckt ein COM-Mechanismus. COM erlaubt es, dynamisch
Schnittstellen zu Klassen hinzufügen. Das kostet allerdings Zeit.
Bei Programmen, welche ein OCX verwenden und kompiliert außerhalb der
IDE laufen, ist dieser zusätzliche Zeitaufwand eher gering, weil alles
dazu nötige bereits in die EXE hineinkompiliert wurde.
Läuft das Programm dagegen in der IDE, existieren diese Zusätze noch
nicht und VB müßte sie bei jedem Programmlauf neu schaffen. Dabei müßte
es allerding viele Fallunterscheidungen treffen, da die dynamisch
hinzugefügten Schnittstellen variieren und deren Eigenschaften erst
immer zusammengeklaubt werden müßten. Das würde verhältnismäßig viel
Zeit kosten (denk mal an eine Form, die vieleicht 100 OCXe verwendet).
VBs Trick ist es nun, dies zu entschärfen, in dem es eine Typelib
anlegt, die die komplette Schnittstelle des OCX beschreibt (die
statischen und die dynamischen Anteile so wie gewünscht), also diese
Fallunterscheidungen einmal macht und dann in dieser Typelib das
Ergebnis notiert. Diese Typelib wird unter der Endung OCA auf Platte
gespeichert. Jedesmal wenn nun in der IDE das Programm, welches das OCX
verwendet, ausgeführt werden soll, kann VB nun auf das Ergebnis seiner
vorab gemachten Berechnungen zurückgreifen und braucht dies nicht jedes
mal bei Programmaufruf tun.
Wird ein OCA gelöscht, legt VB es wieder an: es führt alle
Fallunterscheidungen erneut durch und notiert das Ergebnis wieder in
Form eines OCA-Files auf der Platte.
OCA-Files sind also reinrassige Typelibs, lassen sich aber nicht mit
RegSvr32 registrieren oder deregistrieren. Mit der Tlbinf.dll allerdings
geht dies. Mein Tool VBRegTlbMod verwendet diese Dll, um OCAs zu
registireren oder zu deregistrieren.
Ulrich Korndoerfer schrieb:
> OCA-Files sind also reinrassige Typelibs, lassen sich aber nicht mit
> RegSvr32 registrieren oder deregistrieren.
^^^^^^^^
Quark. Meinte natürlich RegTlb.
Danke für den Hinweis.
> > Zusatzfrage: Warum wird die OCA-Datei einmal im selben
> > Verzeichnis wie die OCX-Datei und ein anderes mal im
> > Verzeichnis der vb6.exe angelegt?
>
> OCas werden angelegt, wenn ein Programm (Exe oder Dll) ein OCX
> verwendet und für die aktuelle Version des OCXs noch kein OCA
> vorhanden ist. Das OCA wird allerdings nur angelegt, wenn das
> Programm oder die DLL in der IDE ausgeführt werden. Kompilierte
> Versionen benötigen keine OCAs und legen auch keine an.
>
> Wenn ein OCA angelegt wurde, bezieht sich dies immer auf eine
> bestimmte Version des OCX. Es ist eine getarnte TypeLib und hat
> eine eigene TLib-GUID (verschieden von der des OCX). Die TypeLib
> des OCAs ist in der Registry wie jede andere TypeLib auch
> registriert. Zusätzlich hält VB in der Registry eine Liste der
> von der IDE angelegten OCAs, die die Verbindung zwischen OCa und
> zugehörigem OCX herstellt.
>
> Wird ein Programm in der IDE gestartet, welches ein OCX verwendet.
> schaut VB nach, ob es bereits ein OCA dafür hat. Wenn nein, legt
> es ein OCA mit neuer GUID und dem Namen des OCX (aber mit der
> Endung OCA) in dem Verzeichnis ab, in dem auch das OCX steht.
Soweit nichts Neues. Nur beim letzten Satz würde ich Einspruch erheben. Wie
geschrieben wird manchmal das OCX-Verzeichnis und manchmal das
vb6.exe-Verzeichnis verwendet. Ich habe noch nicht herausgefunden, wann
welches verwendet wird.
> Wird nun eine neue Version des OCX erstellt und das Programm,
> welches dieses verwendet, wieder gestartet, legt VB ein neues
> OCA an. Ist im Verzeíchnis des OCX bereits ein OCA vorhanden,
> wird dieses nicht überschrieben, sondern ein neues OCA mit
> Zufallsnamen (a la vbxyz.oca) und wiederum neuer GUID im
> Verzeichnis abgelegt, in dem auch die VB.Exe steht. Schau da mal
> rein, da dürfte es nur von vb....ocas so wimmeln.
>
> Wenn Du möchtest, daß bei Versionsänderung des OCX immer im
> Verzeichnis des OCXs unter dem OCX-Namen, aber mit der Endung
> OCA das OCA angelegt wird, mußt Du das alte OCA vorher aus
> diesem Verzeichnis löschen.
Da es sich nicht um ein eigenes OCX handelt, sich die Version also
(zumindest in nächster Zeit) nicht ändert, besteht das Problem nicht.
> Aber Vorsicht! Einfaches Löschen des Files beseitigt nicht die
> Registry-Einträge der OCA-TypeLib! Um die Registry nicht
> zuzumüllen, sollte es vorher deregistriert werden (dabei hilft
> wieder VBRegTLbMod).
Ja, ist klar.
> Ansonsten können OCAs risikolos beliebig gelöscht werden. Fehlt
> eines, legt VB wieder eines an. Sollte es Probleme geben,
> einfach alle OCAs deregistrieren und löschen.
Wie mein Fall zeigt, sollte man das lieber lassen. Wenn ich die OCA-Datei,
mit der die Exe kompiliert wurde, nicht mehr hätte, könnte ich nicht in der
IDE debuggen. Die kompilierte Exe enthält die Typeninfos aus der OCA,
während in der IDE eine neue Version angelegt wird. Dadurch passen die
beiden nicht mehr zusammen. Ist aber zugegebenermaßen nur dann der Fall,
wenn die Exe auf einem anderen Rechner kompiliert wurde.
Danke
Armin
Doch, denn die Exe wurde gegen eine andere OCA kompiliert, als die, die
jetzt durch VB neu anglegt wurde. Folglich der Fehler 13. Seit ich die OCA
wieder verwenden kann, mit der die Exe kompiliert wurde, ist das Problem
gelöst.
Aber man soll ja ohnehin keine Controls an DLLs übergeben. ;-)
Armin
auch ich habe gerne mitgelesen, danke.
--
Viele Grüße
Michael Bauer
Befehl oder Dateiname nicht gefunden. ;-)
regtl*i*b
mußte ich loswerden weil ichs heute ungefähr 37 Mal auch falsch getippt
habe. :)
Armin
Armin Zingler schrieb:
> > Wird ein Programm in der IDE gestartet, welches ein OCX verwendet.
> > schaut VB nach, ob es bereits ein OCA dafür hat. Wenn nein, legt
> > es ein OCA mit neuer GUID und dem Namen des OCX (aber mit der
> > Endung OCA) in dem Verzeichnis ab, in dem auch das OCX steht.
>
> Soweit nichts Neues. Nur beim letzten Satz würde ich Einspruch erheben. Wie
> geschrieben wird manchmal das OCX-Verzeichnis und manchmal das
> vb6.exe-Verzeichnis verwendet. Ich habe noch nicht herausgefunden, wann
> welches verwendet wird.
Ähem, das habe ich doch im Absatz darunter erläutert, nach welchen
Regeln die OCA-Verzeichnisse und Namen ausgewählt werden. Wenn im
Verzeichnis, in dem das OCX liegt, kein OCA vorhanden ist, wird ein
neues OCA in diesem Verzeichnis erstellt. Dieses OCA hat den gleichen
Namen wie das OCX, aber natürlich die Extension OCA. Wenn dagegen in dem
Verzeichnis des OCX bereits eine OCA existiert, wird eine neue, weitere
OCA im VB6-Verzeichnis unter vb....oca angelegt. Hätte man aber vorher
das OCA im OCX Verzeichnis deregistriert und gelöscht, würde das neue
OCA wieder im OCX Verzeichnis angelegt werden.
Allerdings ist die Art und Weise, wie VB feststellt, ob schon ein OCA im
OCX-Verzeichnis vorhanden ist, etwas besonders. Es sieht dazu nämlich
nicht direkt in dem Verzeichnis nach (wäre etwas langsam, da ja auch
noch eine Typelib ausgelesen werden müßte).
VB hält eine Liste in der Registry (bei mir
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualBasic\6.0). In diesem Zweig
sind die Subkeys die TypeLibGuids incl. Versionsnummern der OCXe, für
die VB bereits OCAs angelegt hat. Der Standardwert dieser Subkeys ist
der Pfad zum zugehörigen OCA.
Gibts nun in dieser Liste keinen Eintrag zum OCX, wird ein OCA im
Verzeichnis des OCX erstellt. Sollte (wenn man so herumspielt wie Du
:-)) da zufälligerweise bereits eine Datei stehen, die genauso heißt,
wie das OCA, welches angelegt werden soll, wird diese bereits bestehende
Datei offensichtlich ohne viel Federlesen überschrieben. Für VB
existiert sie nicht, da nicht in der internen Liste eingetragen (und
eingetragen in der Liste war sie in Deinem Fall nicht, weil VB auf
diesem Rechner das OCA nicht selbst erzeugt hat, sondern Du es einfach
von einem anderen Rechner dorthin kopiert hast).
Hm, ich kann Dir nicht ganz folgen. Eine kompilierte EXE benötigt kein
OCA. ZB Du hast ein OCX. Dann erstellst Du in der IDE ein Programm,
welches dieses OCX verwendet und laßt es einmal in der IDE laufen.
Darauf hin hast Du ein OCA. Nun kompilierst Du die EXE, deregistrierst
das OCA und löscht es und läßt die kompilierte EXE außerhalb der IDE
laufen. Klappt.
Ich glaube, auch in Deinem Fall (getrennte Rechner und Dll und Exe
verwenden beide das gleiche Control) braucht man keine alten OCas. Mir
scheint, als ob es hier ein prinzipielles Problem gibt für den Fall, daß
beide Teile (Exe und Dll) Verweise austauschen, aber der eine Teil
kompiliert läuft und der andere in der IDE oder beide in der IDE, aber
gertennten IDE Instanzen.
Bei Dir hat es ja funktioniert, als Du beide in der *gleichen*
IDE-Instanz hast laufen lassen.
Uff, bevor ich jetzt auf alle Details eingehe, erst mal Pause. Wenns
Dich noch interessiert, könnte man ja weiterforschen.
Armin Zingler schrieb:
> > Quark. Meinte natürlich RegTlb.
>
> Befehl oder Dateiname nicht gefunden. ;-)
>
> regtl*i*b
>
> mußte ich loswerden weil ichs heute ungefähr 37 Mal auch falsch getippt
> habe. :)
Die armen Fingerlein ;-) Als alter Mauser benutze ich lieber mein Tool
(Drag und Drop und Klick und weg).
Und fürs nächste Mal: DOS-Box öffnen, Verzeichnis wechseln, einmal
regtlib richtig eingeben und dann mit Pfeil nach oben immer wieder
verwenden :-)
Ulrich
Die OCX heißt spr32x30.ocx und liegt im Verzeichnis
\programme\spread30\bin
Es wird manchmal die Datei
\programme\spread30\bin\spr32x30.oca
und manchmal
\programme\Microsoft Visual Studio\VB98\spr32x30.oca
angelegt, d.h. nicht unter vbcxyz.oca
Ich habe mich gefragt, warum die Datei einmal im einen und einmal im anderen
Verzeichnis angelegt wurde.
> Allerdings ist die Art und Weise, wie VB feststellt, ob schon
> ein OCA im OCX-Verzeichnis vorhanden ist, etwas besonders. Es
> sieht dazu nämlich nicht direkt in dem Verzeichnis nach (wäre
> etwas langsam, da ja auch noch eine Typelib ausgelesen werden
> müßte).
>
> VB hält eine Liste in der Registry (bei mir
> HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualBasic\6.0). In
> diesem Zweig sind die Subkeys die TypeLibGuids incl.
> Versionsnummern der OCXe, für die VB bereits OCAs angelegt hat.
> Der Standardwert dieser Subkeys ist der Pfad zum zugehörigen OCA.
Das ist mir klar.
> Gibts nun in dieser Liste keinen Eintrag zum OCX, wird ein OCA im
> Verzeichnis des OCX erstellt. Sollte (wenn man so herumspielt
> wie Du :-)) da zufälligerweise bereits eine Datei stehen, die
> genauso heißt, wie das OCA, welches angelegt werden soll, wird
> diese bereits bestehende Datei offensichtlich ohne viel
> Federlesen überschrieben. Für VB existiert sie nicht, da nicht
> in der internen Liste eingetragen (und eingetragen in der Liste
> war sie in Deinem Fall nicht, weil VB auf diesem Rechner das OCA
> nicht selbst erzeugt hat, sondern Du es einfach von einem
> anderen Rechner dorthin kopiert hast).
Selbst wenn es in der Registry bereits einen Key für die OCX gibt und
außerdem die OCA-Datei, auf die darin verwiesen wird, schon besteht, wird
die OCA-Datei trotzdem durch VB mit einer neuen Version überschrieben. Das
war ja einer der Punkte, die ich nicht verstanden habe.
Nein, aber in der OCA stehen Informationen, die mit in die Exe kompiliert
werden. Das ist ja auch der Grund, warum man z.B. für DLLs die
Binärkompatibilität einstellen muß, da sonst Exen, die gegen die alte
Version einer DLL kompiliert wurden, nicht mehr mit der neue DLL-Version
laufen würden.
> ZB Du hast ein OCX. Dann erstellst Du in der
> IDE ein Programm, welches dieses OCX verwendet und laßt es
> einmal in der IDE laufen. Darauf hin hast Du ein OCA. Nun
> kompilierst Du die EXE, deregistrierst das OCA und löscht es und
> läßt die kompilierte EXE außerhalb der IDE laufen. Klappt.
Richtig.
> Ich glaube, auch in Deinem Fall (getrennte Rechner und Dll und Exe
> verwenden beide das gleiche Control) braucht man keine alten
> OCas. Mir scheint, als ob es hier ein prinzipielles Problem gibt
> für den Fall, daß beide Teile (Exe und Dll) Verweise
> austauschen, aber der eine Teil kompiliert läuft und der andere
> in der IDE oder beide in der IDE, aber gertennten IDE Instanzen.
>
> Bei Dir hat es ja funktioniert, als Du beide in der *gleichen*
> IDE-Instanz hast laufen lassen.
Ich versuch's nochmal zu erklären. Es ist wirklich ganz einfach: ;-)
Als die EXE kompiliert wurde, greift sie auf Typelib-Infos aus der OCA
Version A zu. Das werden Interface-IDs oder sonstwas sein. Starte ich nun
hier die DLL in der IDE, wird eine neue OCA-Datei Version B erstellt (da auf
diesem Rechner zum ersten Mal gestartet). Die Informationen (GUIDs usw.)
sind in den OCA-Dateien Version A und B nicht dieselben. Das Objekt, das von
der Exe an die in der IDE laufenden DLL übergeben wird, entspricht nicht
dem, was die DLL erwartet. Daher Fehler 13.
Kurz: Die OCAs sind nicht binärkompatibel. Die kompilierte EXE übergibt
Version A, während die DLL Version B erwartet.
Sobald die EXE hier genauso wie die DLL in der IDE gestartet wird, verwenden
beide dieselben Typelib-Infos, nämlich aus der aktuellen OCA-Version. Daher
funktioniert es auch (bis auf den Fehler 458, aber das ist ein anderes
Problem bei der Verwendung mehrere IDE-Instanzen).
> Uff, bevor ich jetzt auf alle Details eingehe, erst mal Pause.
> Wenns Dich noch interessiert, könnte man ja weiterforschen.
Danke, ist nicht nötig. :)
Armin
> > Seitdem ich sie ignoriere und so oft wie möglich lösche (egal wo
> > sie stecken), habe ich keine Probleme mehr damit. Sie sind
> > absolut nicht notwendig.
> Doch, denn die Exe wurde gegen eine andere OCA kompiliert, als die, die
> jetzt durch VB neu anglegt wurde. Folglich der Fehler 13. Seit ich die
OCA
> wieder verwenden kann, mit der die Exe kompiliert wurde, ist das Problem
> gelöst.
>
> Aber man soll ja ohnehin keine Controls an DLLs übergeben. ;-)
Siehste - übergibste keine Controls, brauchste keine OCAs...
;-)
> Nein, aber in der OCA stehen Informationen, die mit in die Exe kompiliert
> werden.
Wer sagt denn dieses?
> Das ist ja auch der Grund, warum man z.B. für DLLs die
> Binärkompatibilität einstellen muß, da sonst Exen, die gegen die alte
> Version einer DLL kompiliert wurden, nicht mehr mit der neue DLL-Version
> laufen würden.
Nein, das kann nicht der Grund sein. Die Binärkompatibilität liegt
ausschließlich bei den OCXen (DLLs) selber, in deren Typbibliotheken.
> Ich versuch's nochmal zu erklären. Es ist wirklich ganz einfach: ;-)
> Als die EXE kompiliert wurde, greift sie auf Typelib-Infos aus der OCA
> Version A zu.
Nicht die EXE greift auf die OCAs zu, sondern die IDE (oder allenfalls der
Kompiler).
> Soweit nichts Neues. Nur beim letzten Satz würde ich Einspruch erheben.
Wie
> geschrieben wird manchmal das OCX-Verzeichnis und manchmal das
> vb6.exe-Verzeichnis verwendet. Ich habe noch nicht herausgefunden, wann
> welches verwendet wird.
Es könnte mit dem inneren "CurDir"-Status der IDE zusammenhängen. Genauer
geforscht habe ich allerdings auch noch nicht.
> > Ansonsten können OCAs risikolos beliebig gelöscht werden. Fehlt
> > eines, legt VB wieder eines an. Sollte es Probleme geben,
> > einfach alle OCAs deregistrieren und löschen.
> Wie mein Fall zeigt, sollte man das lieber lassen. Wenn ich die
OCA-Datei,
> mit der die Exe kompiliert wurde, nicht mehr hätte, könnte ich nicht in
der
> IDE debuggen. Die kompilierte Exe enthält die Typeninfos aus der OCA,
> während in der IDE eine neue Version angelegt wird. Dadurch passen die
> beiden nicht mehr zusammen. Ist aber zugegebenermaßen nur dann der Fall,
> wenn die Exe auf einem anderen Rechner kompiliert wurde.
Auch aus diesem Grunde kompiliere ich nie mit dem "Arbeitssatz" der (aller)
Projekt-Dateien. Zum erstellen eines Test-Kompilats zwischendurch lege ich
eine Kopie des/der Projektordner(s) an und kompiliere dort und lösche (und
deregistirere) die Execuables (EXE, OCX, DLL) danach wieder, bevor ich mit
den eigentlichen Arbeitsdateien weiterarbeite.
ich kann Dir immer noch nicht zustimmen.
Kernpunkte:
- OCAs sind irrelevant. Können jederzeit gelöscht werden. Man braucht
auch keine alten OCAs aufheben.
- Deine Probleme werden nicht durch Verwendung eines alten OCAs gelöst.
- Deine Probleme kommen von der Art, wie Du Dll und Exe laufen läßt:
Exe und Dll beide kompiliert -> ok
Exe und Dll gemeinsam in einer IDE -> ok
Dll in der IDE, Exe kompiliert -> Probleme
Dll in der IDE, Exe in einer separaten IDE - Probleme
im Zusammenspiel damit, daß Du eine Referenz auf eine Instanz eines
OCX
in der Exe As Object an eine Klasse der Dll übergibst und diese
Referenz dort
auf den OCX Type gecastet wird.
Die Probleme, die in Deiner Konstellation auftreten, sind unabhängig
davon, daß Du sie auf einem zweiten Rechner (Rechner A in Deinem
Ursprungsposting) hattest, der initial nur eine Kopie der Sourcen und
des Kompilates der Exe und der Dll hatte und natürlich eine Kopie des
OCX. Auch auf dem "Entwicklungsrechner" (dein Rechner B in Deinem
Ursprungsposting) hättest Du diese Probleme.
Ich habe mir die Mühe gemacht, das mal nachzuvollziehen.
- Ein beliebiges OCX wird aus dem WWW geholt (per Google und Zufall wars
ein OCX mit der ProgID STbutton.STbut für einen aufgemotzten
Commandbutton), auf den Rechner kopiert und registriert (regSvr32). Das
OCX war vorher noch nie nicht auf dem Rechner.
- Ein EXE-Testprojekt wird erstellt. Dieses setzt einen Verweis auf das
OCX und hat eine Instanz dees OCX auf einer Form. Das Testprojekt wird
kompiliert. Dadurch wird ein OCA im Verzeichnis des OCX erstellt.
Außerden erscheint ein Verweis auf dieses OCA unter der Tlib-GUID des
OCX in der VB-Registryliste.
- Ein Dll-Testprojekt wird erstellt. Unter "Komponenten" wird das OCX
markiert. Die einzige Klasse der Dll hat eine einzige Methode:
<Class1>
Public Sub UseOCX(ByVal OCX As Object, ByRef Msg As String)
Dim lOCX As STbutton.STbut
Set lOCX = OCX
lOCX.Caption = Msg
End Sub
</Class1>
- Die Dll wird kompiliert (Projektkompatibilität)
- Das Exe-Testprojekt wird erneut geöffnet und ein Verweis unter
"Verweise" auf die Test-Dll gesetzt. Ein Commandbutton und eine Textbox
werden hinzugefügt und folgender Code in die Form eingefügt:
<Form1>
Private Sub Command1_Click()
Dim c As ukTestArmin.Class1 'Test-Dll
Set c = New ukTestArmin.Class1
c.UseOCX STbut1, Text1.Text
End Sub
</Form1>
Nun wird in verschiedenen Konstellationen die Exe ausgeführt:
1 Die Exe wird in der IDE gestartet, der Command1 geklicked -> ok
2 Die Exe wird außerhalb der IDE gestartet -> ok
3 Die Dll wird in die IDE geladen und in der IDE ausgeführt, die
kompilierte Exe wird gestartet: Fehlermeldung
4 Die Dll wird in die IDE geladen und in der IDE ausgeführt, die Exe
wird in eine separate IDE gestartet und in dieser ausgeführt: -> ok
Wobei hier Fall 3 dem Fall 2 in Deinem Ursprungsposting entsprichet.
Ich kann daraus nur schließen, daß es generell Probleme bei einer
solchen Konstellation gibt.
AFAIK wird bei der Übergabe eines Controls das Extender-Objekt übergeben.
Das Extender-Objekt unterstützt die IIDs, wie sie in der OCA-Datei
festgelegt sind. Wenn nun die DLL das Objekt empfängt, prüft sie per
Queryinterface über die IID, ob die Schnittstelle unterstützt wird.
Da die Exe auf einem anderen Rechner mit anderer OCA und mit anderer
IID kompiliert wurde, unterstützt der Extender nicht die IID, die von
der DLL erwartet wird. Die DLL erwartet deshalb eine andere IID, weil
hier die OCA-Datei neu angelegt wurde und dabei auch neue IIDs generiert
wurden.
Läuft auch die DLL nicht in der IDE, tritt deshalb kein Fehler auf, weil in
der DLL ebenso noch die alten IIDs stehen und sie somit mit den IIDs der Exe
übereinstimmen.
> > Das ist ja auch der Grund, warum man z.B. für DLLs die
> > Binärkompatibilität einstellen muß, da sonst Exen, die gegen
> > die alte Version einer DLL kompiliert wurden, nicht mehr mit
> > der neue DLL-Version laufen würden.
>
> Nein, das kann nicht der Grund sein. Die Binärkompatibilität liegt
> ausschließlich bei den OCXen (DLLs) selber, in deren
> Typbibliotheken.
Ohne das OCX getauscht zu haben, funktioniert es beim Einsatz der
einen OCA nicht, mit der anderen aber schon. An der Binärkompatibilität der
OCX hat sich nichts geändert. Vermutlich sind nur die Extender nicht
binärkompatibel.
> > Ich versuch's nochmal zu erklären. Es ist wirklich ganz
> > einfach: ;-) Als die EXE kompiliert wurde, greift sie auf
> > Typelib-Infos aus der OCA Version A zu.
>
> Nicht die EXE greift auf die OCAs zu, sondern die IDE (oder
> allenfalls der Kompiler).
Ich habe absichtlich nicht geschrieben, daß die Exe auf die OCA zugreift,
sondern, daß bei der Kompilierung der EXE Infos aus der OCA einfliessen.
Armin
Ich kann leider nur wiederholen, daß das Problem seit Einsatz der alten OCA
gelöst ist. Es lässt sich beliebig oft reproduzieren:
1. Ich kopiere die alte OCA-Version in das Verzeichnis, auf das im genannten
Registryzweig (HKLM\SOFTWARE\Microsoft\Visual Basic\6.0\usw) verwiesen wird.
Der Dateiname ist selbstverständlich auch derselbe.
2. Registrierung mit regtlib
3. Start der DLL in der IDE und Start der kompilierten EXE => kein Fehler
Nach Beendigung der EXE und DLL:
4. Kopieren der durch VB neu auf dem Rechner für das OCX angelegten
OCA-Datei
5. Registrierung mit regtlib
6. Start der DLL in der IDE und Start der kompilierten EXE => Fehler 13
Das Spielchen von vorne ergibt die gleichen Ergebnisse.
Mehr zu Deinem Test s.u.
> - Deine Probleme kommen von der Art, wie Du Dll und Exe laufen
> läßt:
> Exe und Dll beide kompiliert -> ok
> Exe und Dll gemeinsam in einer IDE -> ok
> Dll in der IDE, Exe kompiliert -> Probleme
> Dll in der IDE, Exe in einer separaten IDE - Probleme
>
> im Zusammenspiel damit, daß Du eine Referenz auf eine Instanz
> eines
> OCX
> in der Exe As Object an eine Klasse der Dll übergibst und diese
> Referenz dort
> auf den OCX Type gecastet wird.
>
> Die Probleme, die in Deiner Konstellation auftreten, sind
> unabhängig davon, daß Du sie auf einem zweiten Rechner (Rechner
> A in Deinem Ursprungsposting) hattest, der initial nur eine
> Kopie der Sourcen und des Kompilates der Exe und der Dll hatte
> und natürlich eine Kopie des OCX. Auch auf dem
> "Entwicklungsrechner" (dein Rechner B in Deinem
> Ursprungsposting) hättest Du diese Probleme.
>
> Ich habe mir die Mühe gemacht, das mal nachzuvollziehen.
Das gleiche habe ich ebenso gemacht. Allerdings mit einer soeben selbst
erstellten OCX-Datei.
> [...]
>
> Nun wird in verschiedenen Konstellationen die Exe ausgeführt:
>
> 1 Die Exe wird in der IDE gestartet, der Command1 geklicked -> ok
ACK
> 2 Die Exe wird außerhalb der IDE gestartet -> ok
ACK
> 3 Die Dll wird in die IDE geladen und in der IDE ausgeführt, die
> kompilierte Exe wird gestartet: Fehlermeldung
NACK. Läuft hier ohne Fehler.
> 4 Die Dll wird in die IDE geladen und in der IDE ausgeführt, die
> Exe wird in eine separate IDE gestartet und in dieser
> ausgeführt: -> ok
ACK
> Wobei hier Fall 3 dem Fall 2 in Deinem Ursprungsposting
> entsprichet.
>
> Ich kann daraus nur schließen, daß es generell Probleme bei einer
> solchen Konstellation gibt.
Da bei mir alle 4 Fälle ohne Fehler laufen, kann es sich nicht um ein
generelles Problem handeln. Ich führe den Test weiter:
1. Beenden der IDEs
2. Löschen der OCA-Datei
3. Laden des Exe-Projekts. Dadurch wird bereits eine neue OCA-Datei
angelegt.
4. Durchspielen der genannten 4 Fälle:
- 1 kompilierte DLL + EXE in IDE: Fehler 13
- 2 kompilierte DLL + kompilierte EXE: ok
- 3 DLL in IDE + kompilierte EXE: Fehler 13
Fall 3 ist wieder genau mein Problemfall
- 4 DLL in IDE + EXE in IDE: ok
Wie man sieht, tritt der Fehler dann auf, wenn entweder DLL oder EXE in der
IDE und das jeweils andere Projekt in der kompilierten Version laufen. Das
bestätigt eigentlich nur das, was ich schon ausgeführt habe. Es liegt daran,
daß das Projekt in der IDE und die kompilierte Version des jeweils anderen
Projekts auf unterschiedlichen, inkompatiblen OCA-Versionen beruhen.
Tatsache ist, daß sich zwischen erstem, also Deinem Testlauf und meinem
zweiten Testlauf ausschließlich die OCA-Datei geändert hat.
Armin
> > > Nein, aber in der OCA stehen Informationen, die mit in die Exe
> > > kompiliert werden.
> > Wer sagt denn dieses?
> AFAIK wird bei der Übergabe eines Controls das Extender-Objekt übergeben.
> Das Extender-Objekt unterstützt die IIDs, wie sie in der OCA-Datei
> festgelegt sind. Wenn nun die DLL das Objekt empfängt, prüft sie per
> Queryinterface über die IID, ob die Schnittstelle unterstützt wird.
> Da die Exe auf einem anderen Rechner mit anderer OCA und mit anderer
> IID kompiliert wurde, unterstützt der Extender nicht die IID, die von
> der DLL erwartet wird. Die DLL erwartet deshalb eine andere IID, weil
> hier die OCA-Datei neu angelegt wurde und dabei auch neue IIDs generiert
> wurden.
>
> Läuft auch die DLL nicht in der IDE, tritt deshalb kein Fehler auf, weil
in
> der DLL ebenso noch die alten IIDs stehen und sie somit mit den IIDs der
Exe
> übereinstimmen.
Das mag sein, dass in der IDE ein Extender-Objekt hin und her geschoben
wird, das auf den OCA-Schnittstellen beruht. Das ist aber auch insofern
verständlich und nachvollziehbar, als dass die IDE zwecks eines einfacheren
Managements und auch zum Debuggen möglicherweise ein wenig schummelt. Die
OCA-Geschichte hat außerhalb der IDE keinerlei Bedeutung - im Rahmen der
COM-Spezifikationen gibt es sie überhaupt nicht.
In einer EXE taucht von den OCA-Schnittstellen im Normalfall *nichts* auf
(und falls doch, dann wäre das nach 6 Jahren COM2-Spezifikation - zumindest
für mich - eine absolute Neuigkeit!). Es sei denn man würde ausdrücklich
einen Verweis darauf in ein Projekt aufnehmen und sie im Setup mitliefern -
wobei aber schon kein Setup-Tool (erst recht nicht der VB-PWA) darauf
eingerichtet wäre, sie auf dem Zielrechner registrieren zu können. Einen
praktischen Sinn hätte es aber trotzdem nicht.
Bezüglich der Übergabe von privaten VB-Objekten gilt nach wie vor und
uneingeschränkt die dokumentierte (und von Fehlermeldungen untermauerte)
Regel, dass sie nicht über COM-Grenzen hinweg übergeben werden sollen, da
sie keine vollständigen COM-Objekte darstellen. U.a. haben sie keine
VTable, sondern verfügen nur über die IDispatch-Schnittstelle des
Extenders - eine "native" Schnittstelle (= die individuellen Members eines
Controls) haben sie überhaupt nicht. Durch die Aggregation eines Controls
und der VB-internen Extender-Schnittstelle wird ein Control immer noch
nicht zum COM-Objekt. Lediglich die nicht-aggregierte, reine
Basis-Schnittstelle eines externen Controls (in einem OCX bzw. einer DLL),
die über die Eigenschaft .object des Control-Aggregats bzw. des
VBControlExtender-Objekts (bei dynamisch geladenen externen Controls)
bietet eine vollständige, duale COM-Schnittstelle (auch ein Grund dafür,
dass bei dynamisch geladenen VB-internen Controls *kein*
VBControlExtender-Objekt erzeugt wird).
Ehrlich - und mit Verlaub - gesagt: Den OCA-Schnittstellen irgendeine
programmtechnisch relevante Bedeutung zuzumessen, hat einen ziemlich
"esoterischen" Touch...
Siehe auch:
Matthew Curland, "Advanced Visual Basic 6", Seiten 65 - 71 und 76
(http://www.aboutvb.de/res/lit/resbchadwadvancedvb6.htm)
Armin Zingler schrieb:
> > 1 Die Exe wird in der IDE gestartet, der Command1 geklicked -> ok
>
> ACK
>
> > 2 Die Exe wird außerhalb der IDE gestartet -> ok
>
> ACK
>
> > 3 Die Dll wird in die IDE geladen und in der IDE ausgeführt, die
> > kompilierte Exe wird gestartet: Fehlermeldung
>
> NACK. Läuft hier ohne Fehler.
Merkwürdig. Vielleicht spielt es eine Rolle, ob das OCX selbst erzeugt
wurde oder nicht.
Bei mir gibts nen Fehler (vierhundertirgendwas).
> > 4 Die Dll wird in die IDE geladen und in der IDE ausgeführt, die
> > Exe wird in eine separate IDE gestartet und in dieser
> > ausgeführt: -> ok
>
> ACK
>
> > Wobei hier Fall 3 dem Fall 2 in Deinem Ursprungsposting
> > entsprichet.
> >
> > Ich kann daraus nur schließen, daß es generell Probleme bei einer
> > solchen Konstellation gibt.
>
> Da bei mir alle 4 Fälle ohne Fehler laufen, kann es sich nicht um ein
> generelles Problem handeln. Ich führe den Test weiter:
>
Ich auch.
> 1. Beenden der IDEs
>
> 2. Löschen der OCA-Datei
>
> 3. Laden des Exe-Projekts. Dadurch wird bereits eine neue OCA-Datei
> angelegt.
>
> 4. Durchspielen der genannten 4 Fälle:
>
> - 1 kompilierte DLL + EXE in IDE: Fehler 13
Bei mir immer noch OK.
> - 2 kompilierte DLL + kompilierte EXE: ok
Bei mir auch ok
> - 3 DLL in IDE + kompilierte EXE: Fehler 13
>
> Fall 3 ist wieder genau mein Problemfall
Bei mir wieder gleicher Fehler wie vorher
>
> - 4 DLL in IDE + EXE in IDE: ok
Bei mir auch ok
>
> Wie man sieht, tritt der Fehler dann auf, wenn entweder DLL oder EXE in der
> IDE und das jeweils andere Projekt in der kompilierten Version laufen. Das
> bestätigt eigentlich nur das, was ich schon ausgeführt habe. Es liegt daran,
> daß das Projekt in der IDE und die kompilierte Version des jeweils anderen
> Projekts auf unterschiedlichen, inkompatiblen OCA-Versionen beruhen.
> Tatsache ist, daß sich zwischen erstem, also Deinem Testlauf und meinem
> zweiten Testlauf ausschließlich die OCA-Datei geändert hat.
Ok. Resumee:
- Bei mir gibt es in Fall 3 einen Fehler, egal ob OCA frisch angelegt
oder gelöscht und neu angelegt. Das bestätigt mich natürlich in meiner
Vermutung, daß es nicht am OCA liegt.
- Bei Dir gibt es in allen 4 Fällen anfangs keinen Fehler, nach Löschen
und neuem OCA machen in 2 Fällen Fehler. Das scheint natürlich Deine
Vermutung zu bestätigen, daß es am OCA liegt.
Der Unterschied zwischen unseren Fällen ist, daß Du dein OCX selbst
erzeugt hast, ich ein Fertiges verwendet habe (wobei noch dazu kommen
könnte, daß "mein" OCX evtl nicht mit VB erstellt wurde, weiß ich aber
nicht).
Typisches Patt :-))
Ich kanns nicht erklären. Trotzdem glaube ich, daß Du auf dem Holzweg
bist. Ich kann da nur Harald zustimmen. Die IIDs in der OCA sind
bestenfalls temporär relevant (in der IDE) und sind auf jedenfall im
Kompilat nicht vorhanden. Das Kompilat kann diese IIDs gar nicht
verwenden, weil sie bei Weitergabe des Kompilats und des OCXs nicht mehr
da sind (weil das OCA nicht mit weitergeben wurde und auch nicht
weitergeben werden soll). Nochmal ganz deutlich: das Kompilat verwendet
keine IIDs aus den OCAs, diese Information ist nicht hineinkompiliert
worden.
Deswegen klappt das auch immer, wenn Dll und Exe beide kompiliert
laufen.
Wenn nun nur Dll oder nur Exe in der IDE laufen, könnten schon die IIds
des OCAs zum tragen kommen. Das sehe ich an meinem Fehlerfall. Das wäre
aber ein prinzipielles Problem, denn in der IDE gibts halt die OCA-IIDs
und im Kompilat nicht. Also Ärger im Mischbetrieb (wobei es den Ärger
nicht geben würde, wenn nicht Referenzen ausgetauscht würden). Dem
wiederum stehen Deine Ergebnisse gegenüber.
Wieder ein Mysterium mehr :-)
"Harald M. Genauck" schrieb:
> Siehe auch:
>
> Matthew Curland, "Advanced Visual Basic 6", Seiten 65 - 71 und 76
> (http://www.aboutvb.de/res/lit/resbchadwadvancedvb6.htm)
Der Hinweis war gut. Zu meiner Schande muß ich gestehen, daß ich das
Teil noch nicht komplett durchhabe.
> > Siehe auch:
> >
> > Matthew Curland, "Advanced Visual Basic 6", Seiten 65 - 71 und 76
> > (http://www.aboutvb.de/res/lit/resbchadwadvancedvb6.htm)
> Der Hinweis war gut. Zu meiner Schande muß ich gestehen, daß ich das
> Teil noch nicht komplett durchhabe.
Ich auch noch nicht bis ins letzte Detail...
Aber ein Blick ins Stichwortregister reichte aus, um zu sehen, was einer
der Väter der ganzen COM-Geschichte(n) in VB zu dem Punkt zu sagen hat.
Wenn die OCAs irgendeine relevante Bedeutung hätten und Möglichkeiten
böten, zB/u.a. den Nachteil der nicht erlaubten Übergabe von VB-internen
Objekten zu umgehen, hätte Matthew dem wohl sicher angemessenen Raum
eingeräumt...
> > - 3 DLL in IDE + kompilierte EXE: Fehler 13
> >
> > Fall 3 ist wieder genau mein Problemfall
> Bei mir wieder gleicher Fehler wie vorher
Dieser Falls ist ja an sich auch irgendwie "irregulär"...
Denn zunächst einmal (und normalerweise) kann ja keine EXE kompiliert
werden, wenn eine abhängige Komponente nicht bereits kompiliert ist. Das
heißt also, dass beim Kompilieren der EXE bereits ein Kompilat der
Komponente existieren muss, auf die Verweis im EXE-Projekt enthalten sein
muss - wohlgemerkt: ein Verweis auf die Komponente selbst, nicht auf
irgendeine irrelevante, temporäre OCA-Version der Komponente!
Sollte man nun Interna der Komponente ändern wollen (Änderungen der
Schnittstelle sind ja nach dem Kompilat der EXE nicht mehr zulässig), macht
es natürlich Sinn, die Komponente in der IDE debuggen zu wollen. Dann muss
allerdings Binärkompatibilität zur nach wie vor vorhandenen (!) und
registrierten (!) Kompilat der Komponente gesetzt werden (auch hier wieder:
Binärkompatibilität zur Komponente selbst, und nicht zu irgendeiner OCA!).
Wenn ich dann das Komponenten-Projekt *alleine* in einer IDE-Instanz lade
und starte (entweder im Debuggen-Modus "Warten bis Komponente erstellt ist"
oder unter "Programm starten" die EXE angegeben), gibt es bei mir *keine*
Probleme mit einer simplen Muster-Komponente (1 Klasse, eine einzige
schlichte "Test"-Methode). Wenn es denn Probleme geben sollte, sollten
diese nicht an OCAs liegen, sondern dürfte an völlig anderen Ursachen
liegen - womöglich an nicht zulässigen Übergaben von privaten VB-Objekten.
Wenn es doch letzteres sein sollte, kann ich nur sagen: Sorry - wenn man
nicht zulässige Dinge macht, auch wenn sie scheinbar (!) funktionieren,
sollte man sich nicht über früher oder später auftretende Probleme
wundern...
"Harald M. Genauck" schrieb:
> Aber ein Blick ins Stichwortregister reichte aus, um zu sehen, was einer
> der Väter der ganzen COM-Geschichte(n) in VB zu dem Punkt zu sagen hat.
> Wenn die OCAs irgendeine relevante Bedeutung hätten und Möglichkeiten
> böten, zB/u.a. den Nachteil der nicht erlaubten Übergabe von VB-internen
> Objekten zu umgehen, hätte Matthew dem wohl sicher angemessenen Raum
> eingeräumt...
Matt ist auf news.devx.com in den Foren vb.oop und vb.general häufiger
vertreten und scheut sich dort nicht, auch auf solche Anfängerfragen
:-)) einzugehen.
Fragen kostet ja nichts, oder?
Sie funktionieren nicht nur scheinbar, sondern tatsächlich.
Seit Jahren übrigens. Das Problem ist aufgrund einer vorübergehenden
Ausnahemsituation aufgetreten. Das hat aber seine Ursachen, die nicht
prinzipiell sondern in der Situation, die es abzuschaffen gilt, begründet
sind. Stricke also kein generelles Problem draus und stelle mich nicht so
hin, als wäre es ein grundsätzlicher Fehler meinerseits. Es hat nichts mit
"wundern" zu tun, wenn ich versuche, der Sache auf den Grund zu gehen.
Zur Sache kann ich nur noch einmal wiederholen, daß meine Ausführungen sehr
stark darauf hinweisen, daß es eben doch in der OCA-Datei begründet ist.
Wäre es nicht so, würde deren Austausch keinerlei Auswirkungen haben. Hat er
aber.
Ich kann Dir gerne auch noch tiefergehende Infos geben. Die Zuweisung
innerhalb der DLL wird wie folgt umgesetzt:
00E91976 push offset ___vba@001D2D5C (0E91560h)
00E9197B push dword ptr [ocx]
00E9197E call ___vbaCastObj (0E91166h)
00E91983 push eax
00E91984 lea eax,[localocx]
00E91987 push eax
00E91988 call ___vbaObjSet (0E9116Ch)
ocx ist das Argument, das an die Prozedur As Object übergeben wird. localocx
ist die typisierte Variable. Wenn man das Offset aus der ersten
Zeile (0E91560h) hernimmt und sich den Speicherauszug an der Stelle ansieht,
steht dort folgende Byte-Folge:
3b c8 dc 05 7b c0 32 45 8e eb f0 66 3a 5a 5c 8a
Dann hier der Auszug aus dem Typelib-Viewer für die OCA-Datei:
[
uuid(05DCC83B-C07B-4532-8EEB-F0663A5A5C8A),
version(3.0),
hidden,
nonextensible
]
dispinterface _UserControl1 {
Wenn man die Byte-Folge der UUID in den ersten drei durch "-" getrennten
Abschnitte umkehrt, entsteht:
3BC8DC05-7BC0-3245-8EEB-F0663A5A5C8A
Man vergleiche diese mit der Byte-Folge im Programm: Ist dieselbe. Ich
schließe daraus: An vbaCastObj wird ein Pointer auf die UUID übergeben.
Die Funktioni prprüft, ob das übergebene Objekt die durch die UUID
definierte Schnittstelle unterstützt. Die UUID wurde fest mit
in die DLL kompiliert. Sie stammt definitiv aus der OCA-Datei.
Wird nun die Exe in der IDE mit einer neuen OCA-Version ausgeführt, ist
definitiv auch die IID des Extender-Objekts anders, da sich bei jeder
Neuanlage der OCA-Datei die IID ändert. In der kompilierten DLL steht aber
noch die IID, die zu dem Zeitpunkt der Kompilierung der DLL existierte. Wenn
nun die DLL anhand der IID prüft, ob das Objekt die IID unterstützt, kann
die Antwort nur "nein" sein.
Es hat sich damit eigentlich nur das bestätigt, was ich von Anfang an
vermutet habe. Wenn Dir/Euch das nicht Beweis genug ist, kann ich leider
auch nicht mehr machen.
... Doch, es geht noch mehr:
1. OCA-Datei gelöscht
2. Exe-Projekt geladen => neue OCA wird angelegt
3. Dieselbe Test-Sub im Exe-Projekt hinzugefügt. Aufgerufen wird in der
Exe nun:
test UserControl11
c.test UserControl11
4. Exe kompiliert
Es gibt nun zwei Zuweisungen: Einmal in sub test und einmal in c.test. Bei
beiden wird anhand der IID geprüft, ob die Schnittstelle unterstützt wird.
Wenn meine Vermutungen stimmen, müssen es nun unterschiedliche IIDs sein, da
die DLL noch mit der alten OCA kompiliert wurde, während die Exe mit der
neuen OCA kompiliert wurde.
Zuerst untersuche ich die neue OCA-Datei. Dort steht als UUID:
5F61A68E-AC14-4C94-909A-C2816F6A666E. Wie gesagt: Neue OCA=>Neue UUID
Vorauszusehen ist, daß die Exe die neue UUID bei der Prüfung der Gültigkeit
der Zuweisung verwendet, während in der DLL noch die alte verwendet wird.
Starte ich die Exe, gibt es Laufzeitfehler 13 bei zweiten Zuweisung, also
innerhalb der DLL. Im Debugger nachgeprüft, ergibt sich folgendes:
Bei der Zuweisung in der Exe wird an vbaCastObj ein Pointer auf folgende
Byte-Folge übergeben:
8e a6 61 5f 14 ac 94 4c 90 9a c2 81 6f 6a 66 6e
Wie man sieht ist das die neue UUID. Die Zuweisung läuft erwartet
fehlerfrei. Lässt man das Programm weiterlaufen, sieht man, daß in der DLL
noch ein Pointer auf die alte UUID an vbaCastObj übergeben wird.
Der Aufruf von vbaCastObj schlägt somit fehl (vbaSetObj wird nicht mehr
ausgeführt).
Eindeutiger geht's für meinen Geschmack nicht mehr.
Armin
Ok, anscheinend werden doch IIDs aus dem OCA in das Kompilat übernommen.
Das begreife ich zwar nicht, denn das würde ja bedeuten, daß die
dazugehörige Typelibrary (das OCA) vorhanden sein muß. Also müßte man
beim Weitergeben einer Applikation, die ein OCX verwendet, auch das OCA
mit weitergeben. Das ist aber im Normalfall nicht nötig.
VB scheint die IID aber nicht auszuwerten, auch wenn sie im Kompilat
vorhanden ist. Indizien dafür:
- Eine Exe1 mit dem Code:
Private Sub Form_DblClick()
UseOCX STbut1, "Hello " & CStr(Rnd() * 1000000 \ 1000)
End Sub
Public Sub UseOCX(ByVal OCX As Object, ByRef Msg As String)
Dim lOCX As STbutton.STbut
Set lOCX = OCX
lOCX.Caption = Msg
End Sub
- und eine Exe2 mit dem Code:
Private Sub Form_DblClick()
UseOCX STbut1, "Hello " & CStr(Rnd() * 1000000 \ 1000)
End Sub
Public Sub UseOCX(ByVal OCX As STbutton.STbut, ByRef Msg As String)
Dim lOCX As STbutton.STbut
Set lOCX = OCX
lOCX.Caption = Msg
End Sub
Der Disassembler sagt, daß in Exe1 eine IID verwendet wird, in Exe2
nicht.
Nun kompiliere man beide Exes und lasse die Kompilate laufen - alles ok
Nun deregistriere man das OCA und lösche es und lasse die beiden
(unveränderten) Kompilate laufen - immer noch ok
Dann lege man ein neues OCA wieder an (durch laden eines der beiden
Projekte in die IDE, ohne es dort zu kompilieren) und lasse die (immer
noch unveränderten) Kompilate laufen - wieder ohne Probleme.
Dem Kompilat der Exe1 ist es offensichtlich völlig schnurz, ob die IID
und das zugehörige OCA, welches zur Zeit der Kompilierung existierte,
später dann noch vorhanden ist oder nicht oder gar durch eine anderes
ersetzt wurde. Wie wird denn dann diese Information verwertet? Wozu ist
dann die Angabe der IID im Code überhaupt nötig?
Ist dagegen der UseOCX-Code in einer Dll, scheinen sich die Sachverhalte
wieder zu ändern. Hier kommt es dann zu Fehlern, wenn man am OCA dreht.
Konnte übrigens Deine Fehler inzwischen nachvollziehen. Traten aber erst
auf, nachdem ich die Dll und das OCX deregistriert und wieder
registriert hatte.
Bin inzwischen vollkommen verwirrt. Diese OCA-Geschichte ist schwer zu
fassen. Man sollte vielleicht wirklich mal bei Matthew Curland anfragen.
habe noch etwas im Curland an der von Harald angegebenen Stelle gelesen.
So weit ich ihn verstehe, schafft man sich Abhängigkeiten vom *OCA*
(auch im Kompilat), wenn man in einer Dll unter Projekt/*Komponenten*
ein OCX auswählt. Das könnte Ärger beim Deployment geben (wie ja von Dir
erlebt).
Wenn es aber nun nötig ist, daß eine Exe einer Helferfunktion in einer
Dll einen Verweis auf eine Instanz eines OCX (welche in der Exe angelegt
ist) übergibt, damit die Helferfunktion diverse Routineaufgaben auf dem
OCX durchführt (es ist ja sinnvoll, solche immer wiederkehrenden
Aufgaben in eine Dll zu verlagern), und man die erwähnten Abhängigkeiten
vermeiden möchte, könnte man einen der Wege gehen, die Curland
vorschlägt.
Die Wege unterscheiden sich im Aufwand und benötigen teilweise
Unterstützung durch eine Dll, die er zu seinem Buch mitliefert.
Einer dieser Wege ist sehr einfach mit Bordmitteln zu realisieren. Er
hat allerdings eine Beschränkung: geht man ihn, hat man in der Dll nur
Zugriff auf das native Interface des Controls (also nicht auf die
Extenderfunktionen wie Top, Left, etc). Als Ausgleich sind diese
Zugriffe aber deutlich schneller als die über den Extender (vTable
gegenüber 2x IDispatch und Invoke).
- In der Dll unter Projekt/*Verweise* eine Referenz auf das OCX setzen
(dazu muß "Durchsuchen" gewählt werden!).
- In der Helferfunktion der Dll den Übergabeparameter als "ByVal MyOCX"
deklarieren
- Der Aufrufer (die Exe) übergibt an die Helferfunktion MyOCX.*Object*.
Zu beachten ist noch, daß die Referenz auf das OCX in der Dll nicht in
der Projekte/Verweise-Liste notiert wird. Hat man einen Verweis auf das
OCX so hinzugefügt und öffnet später erneut Projekt/Verweise, ist dort
der Verweis *nicht* eingetragen. Deshalb hat man auch Schwierigkeiten,
den Verweis wieder loszuwerden (falls nötig). Um ihn loszuwerden, muß
der entsprechende Eintrag aus der *.vbp Projektdatei gelöscht werden.
Ich habe diesen Weg bis jetzt selbst noch nicht ausprobiert, klingt aber
gut.
Vielleicht hilft Dir das weiter.
> > Sorry - wenn man nicht zulässige Dinge macht,
> > auch wenn sie scheinbar (!) funktionieren, sollte man sich nicht
> > über früher oder später auftretende Probleme wundern...
> Sie funktionieren nicht nur scheinbar, sondern tatsächlich.
> Seit Jahren übrigens. Das Problem ist aufgrund einer vorübergehenden
> Ausnahemsituation aufgetreten. Das hat aber seine Ursachen, die nicht
> prinzipiell sondern in der Situation, die es abzuschaffen gilt, begründet
> sind. Stricke also kein generelles Problem draus und stelle mich nicht so
> hin, als wäre es ein grundsätzlicher Fehler meinerseits. Es hat nichts
mit
> "wundern" zu tun, wenn ich versuche, der Sache auf den Grund zu gehen.
Ich gestehe zu, dass ich Dir im konkreten Fall vielleicht Unrecht tue -
solange das Problem nicht abschließend geklärt ist, lassen wir das einfach
mal offen.
Grundsätzlich kann ein "ist seit Jahren gut gegangen" jedoch kein Argument
sein, wenn man bewusst etwas macht, von dem von vornherein ganz klar als
unzulässig abgeraten wird. Denn die Ausnahmesituation ist ja dann genau
dieses berühmte "erste Mal", an dem es eben nicht mehr gut geht. Selbst
wenn man einen Workaround um die Ausnahmesituation unter Beibehaltung der
Unzulässigkeit finden sollte, kann früher oder später die nächste
"Ausnahme" auftreten, auf die das unzulässige Vorgehen aufläuft. Bei der
Nutzung von undokumentierten Features (wozu auch das scheinbare
Funktionieren eines Widerspruchs zu dokumentierten Aussagen gehört) muss
man halt immer mit unvorhergesehenen Komplikationen rechnen. Das einfach
mal prinzipiell und allgemein für alle gesagt...
> ...
>
> Es gibt nun zwei Zuweisungen: Einmal in sub test und einmal in c.test.
Bei
> beiden wird anhand der IID geprüft, ob die Schnittstelle unterstützt
wird.
> Wenn meine Vermutungen stimmen, müssen es nun unterschiedliche IIDs sein,
da
> die DLL noch mit der alten OCA kompiliert wurde, während die Exe mit der
> neuen OCA kompiliert wurde.
>
> Zuerst untersuche ich die neue OCA-Datei. Dort steht als UUID:
> 5F61A68E-AC14-4C94-909A-C2816F6A666E. Wie gesagt: Neue OCA=>Neue UUID
> Vorauszusehen ist, daß die Exe die neue UUID bei der Prüfung der
Gültigkeit
> der Zuweisung verwendet, während in der DLL noch die alte verwendet wird.
> Starte ich die Exe, gibt es Laufzeitfehler 13 bei zweiten Zuweisung, also
> innerhalb der DLL. Im Debugger nachgeprüft, ergibt sich folgendes:
> Bei der Zuweisung in der Exe wird an vbaCastObj ein Pointer auf folgende
> Byte-Folge übergeben:
> 8e a6 61 5f 14 ac 94 4c 90 9a c2 81 6f 6a 66 6e
> Wie man sieht ist das die neue UUID. Die Zuweisung läuft erwartet
> fehlerfrei. Lässt man das Programm weiterlaufen, sieht man, daß in der
DLL
> noch ein Pointer auf die alte UUID an vbaCastObj übergeben wird.
> Der Aufruf von vbaCastObj schlägt somit fehl (vbaSetObj wird nicht mehr
> ausgeführt).
Hast Du die kompilierte DLL disassembliert und so festgestellt, dass die
IID einkompiliert worden ist?
Oder was hast Du im Debugger untersucht? Den Lauf des DLL-Projekts in der
IDE? Oder den Lauf der kompilierten DLL?
Da OCX hier schon als native Schnittstelle deklariert ist, müsste sich
eigentlich die Hilfsvariable lOCX erübrigen - an dieser Stelle dürfte
eigentlich nichts passieren. Ein
OCX.Caption = Msg
müsste die gleiche Wirkung haben.
> Der Disassembler sagt, daß in Exe1 eine IID verwendet wird, in Exe2
> nicht.
Aber:
Wie sieht es denn in Exe1 aus, wenn Du nicht den Control-Extender, sondern
das Control-object (also gleich die native Schnittstelle) übergibst?
Private Sub Form_DblClick()
UseOCX STbut1.object,...
End Sub
Oder wenn auf der Aufrufer-Seite schon das Schnittstellen-Casting
vorgenommen wird?
Private Function CSTbut(STbut As STbutton.STbut) As STbutton.STbut
CSTbut = STbut
End Function
Private Sub Form_DblClick()
UseOCX CSTbut(STbut1),...
End Sub
> Nun kompiliere man beide Exes und lasse die Kompilate laufen - alles ok
>
> Nun deregistriere man das OCA und lösche es und lasse die beiden
> (unveränderten) Kompilate laufen - immer noch ok
>
> Dann lege man ein neues OCA wieder an (durch laden eines der beiden
> Projekte in die IDE, ohne es dort zu kompilieren) und lasse die (immer
> noch unveränderten) Kompilate laufen - wieder ohne Probleme.
>
> Dem Kompilat der Exe1 ist es offensichtlich völlig schnurz, ob die IID
> und das zugehörige OCA, welches zur Zeit der Kompilierung existierte,
> später dann noch vorhanden ist oder nicht oder gar durch eine anderes
> ersetzt wurde.
BTW - bist Du sicher, dass die IID zur OCA gehört?
> Wie wird denn dann diese Information verwertet? Wozu ist
> dann die Angabe der IID im Code überhaupt nötig?
Sollte eigentlich nicht nötig sein. Wäre auch noch die Frage, ob die
OCA-IID
sowohl in einem P-Code- als auch in einem Native-Kompilat auftaucht.
> Ist dagegen der UseOCX-Code in einer Dll, scheinen sich die Sachverhalte
> wieder zu ändern. Hier kommt es dann zu Fehlern, wenn man am OCA dreht.
> Konnte übrigens Deine Fehler inzwischen nachvollziehen. Traten aber erst
> auf, nachdem ich die Dll und das OCX deregistriert und wieder
> registriert hatte.
Da müsste man einmal prüfen, worin die Unterschiede zwischen beiden
Registrierungen bestehen.
> Bin inzwischen vollkommen verwirrt. Diese OCA-Geschichte ist schwer zu
> fassen. Man sollte vielleicht wirklich mal bei Matthew Curland anfragen.
Jo, macht mal...
;-)
Wieso sollte man das müssen? Man braucht sie zur Laufzeit genauso wenig, wie
andere Typelibs für "normale" DLLs. Wie Du selbst schreibst, werden die IIDs
in das Kompiltat übernommen. Danach sind sie nicht mehr notwendig.
> VB scheint die IID aber nicht auszuwerten, auch wenn sie im
> Kompilat vorhanden ist. Indizien dafür:
> [...]
> Dem Kompilat der Exe1 ist es offensichtlich völlig schnurz, ob
> die IID und das zugehörige OCA, welches zur Zeit der
> Kompilierung existierte, später dann noch vorhanden ist oder
> nicht oder gar durch eine anderes ersetzt wurde.
Genau.
> Wie wird denn
> dann diese Information verwertet? Wozu ist dann die Angabe der
> IID im Code überhaupt nötig?
Weil der Extender ein Pseudo-Com-Objekt ist und dafür braucht es auch
projektintern IIDs, damit es sich auch wie eines verhält. Damit sich VB
nicht jedesmal neue IDs ausdenken muß, wird eben einmalig die OCA angelegt,
in der diese definiert sind. Ich könnte mir auch gut vorstellen, daß man
sich seitens MSFT ein Hintertürchen für die Übergabe zwischen Projekten
(Exe->DLL) offenhalten wollte. Dafür müssen die IDs projektübergreifend
einheitlich sein und werden eben in der OCA abgelegt.
> Ist dagegen der UseOCX-Code in einer Dll, scheinen sich die
> Sachverhalte wieder zu ändern. Hier kommt es dann zu Fehlern,
> wenn man am OCA dreht. Konnte übrigens Deine Fehler inzwischen
> nachvollziehen. Traten aber erst auf, nachdem ich die Dll und
> das OCX deregistriert und wieder registriert hatte.
>
> Bin inzwischen vollkommen verwirrt.
Komisch, mir ist mittlerweile alles klar. :-)
> Diese OCA-Geschichte ist
> schwer zu fassen. Man sollte vielleicht wirklich mal bei Matthew
> Curland anfragen.
Kennichnich. Machduma. ;-)
Armin
"Harald M. Genauck" schrieb:
> Da OCX hier schon als native Schnittstelle deklariert ist, müsste sich
> eigentlich die Hilfsvariable lOCX erübrigen - an dieser Stelle dürfte
> eigentlich nichts passieren. Ein
>
> OCX.Caption = Msg
>
> müsste die gleiche Wirkung haben.
Ja klar, wollte aber zum Vergleich der beiden Fälle im disassemblierten
Code möglichst identische Verhältnisse haben.
> > Der Disassembler sagt, daß in Exe1 eine IID verwendet wird, in Exe2
> > nicht.
>
> Aber:
>
> Wie sieht es denn in Exe1 aus, wenn Du nicht den Control-Extender, sondern
> das Control-object (also gleich die native Schnittstelle) übergibst?
>
> Private Sub Form_DblClick()
> UseOCX STbut1.object,...
> End Sub
Fehler 13 (Typen unverträglich). Offensichtlich kann aus dem nativen
Controlobject nicht auf das extended Controlobjekt zurückgecastet
werden.
> Oder wenn auf der Aufrufer-Seite schon das Schnittstellen-Casting
> vorgenommen wird?
>
> Private Function CSTbut(STbut As STbutton.STbut) As STbutton.STbut
> CSTbut = STbut
> End Function
>
> Private Sub Form_DblClick()
> UseOCX CSTbut(STbut1),...
> End Sub
"Objektvariable nicht festgelegt" :-)) Du hast das Set in CStbut
vergessen .-)
Im Ernst, drehst Du Dich da nicht etwas im Kreis? STBut1 ist bereits vom
Typ STbutton.STbut, da braucht man nichts casten.
Aber der Ansatz, an den Typen der Parameter zu drehen, ist schon
richtig. Siehe mein anderes Posting.
> Da müsste man einmal prüfen, worin die Unterschiede zwischen beiden
> Registrierungen bestehen.
Was das OCX betrifft: in beiden Fällen RegSvr32. Was die Dll betrifft:
bei der Erstellung hat sie VB selbst registriert, dann dereg und rereg
per regsvr32. Das OCA wird bei der Erstellung immer durch VB
registriert, dann dereg mit VBRegTlbMod. Sein Augenmerk müßte man wohl
hauptsächlich auf die Dll Registrierung richten.
Mir ist schon klar, daß da Unterschiede bestehen können, nur das Prüfen
der Unterschiede ist ja nicht so trivial und auch ganz schön
arbeitsaufwendig (wenn man mal von der Registryseite her das Ganze
angehen würde).
Armin Zingler schrieb:
> > Das begreife ich zwar nicht, denn das würde ja bedeuten, daß die
> > dazugehörige Typelibrary (das OCA) vorhanden sein muß. Also
> > müßte man beim Weitergeben einer Applikation, die ein OCX
> > verwendet, auch das OCA mit weitergeben. Das ist aber im
> > Normalfall nicht nötig.
>
> Wieso sollte man das müssen? Man braucht sie zur Laufzeit genauso wenig, wie
> andere Typelibs für "normale" DLLs. Wie Du selbst schreibst, werden die IIDs
> in das Kompiltat übernommen. Danach sind sie nicht mehr notwendig.
Und warum war es dann bei Dir nötig? Warum hat es erst funktioniert,
nachdem Du das OCA auf den anderen Rechner kopiert und *registriert*
hast? Offensichtlich war es nötig, die Typelibrary auf dem Zielsystem zu
*registrieren*. Dazu muß sie natürlich erstmal auf das Zielsystem
kopiert werden.
Was sind für Dich "normale" Dlls? Keine COM-Dlls? In dem Fall hast Du
recht, da brauchts Du die Typelib nicht weitergeben.
Bei COM-Dlls ist das anders. Das sind Einheiten aus Typelibrary und
Implementation der Types. Wenn Du eine Exe gegen eine COM-Dll
kompilierst und in der Exe ein Set xyz = New foodll.barclass schreibst
und die Exe weitergibst, mußt Du auch die Dll weitergeben. Und das nicht
nur wegen des implementierenden Codes in der Dll, sondern auch wegen der
Typelib in der Dll. Die wird nämlich dazu benötigt, die Dll auf dem
Zielsystem zu registrieren. Und ohne diese Registrierung (wozu die
Typelib benötigt wird), wird auch Deine Exe nicht laufen.
Bei den OCAs ist das spezieller gelagert, weil dies reine Typelibs ohne
Implementierung sind. Die Implementierung steckt nun in Deiner Exe (dort
steht der Code, der dynamisch die in der OCA angegebenen Types
implementiert). Deshalb wird bei der Weitergabe auch das OCA benötigt
(nur in dem von Dir geschildertem speziellen Fall mit der Weitergabe
einer OCX-Referenz an eine Dll, normalerweise nicht).
> > > Der Disassembler sagt, daß in Exe1 eine IID verwendet wird, in Exe2
> > > nicht.
> >
> > Aber:
> >
> > Wie sieht es denn in Exe1 aus, wenn Du nicht den Control-Extender,
sondern
> > das Control-object (also gleich die native Schnittstelle) übergibst?
> >
> > Private Sub Form_DblClick()
> > UseOCX STbut1.object,...
> > End Sub
> Fehler 13 (Typen unverträglich). Offensichtlich kann aus dem nativen
> Controlobject nicht auf das extended Controlobjekt zurückgecastet
> werden.
Äh... wo tritt der Fehler auf? In UseOCX? Wenn die Komponente STbutton ein
externes OCX ist, sollte das eigentlich kein Problem sein.
> > Oder wenn auf der Aufrufer-Seite schon das Schnittstellen-Casting
> > vorgenommen wird?
> >
> > Private Function CSTbut(STbut As STbutton.STbut) As STbutton.STbut
> > CSTbut = STbut
> > End Function
> >
> > Private Sub Form_DblClick()
> > UseOCX CSTbut(STbut1),...
> > End Sub
>
> "Objektvariable nicht festgelegt" :-)) Du hast das Set in CStbut
> vergessen .-)
>
> Im Ernst, drehst Du Dich da nicht etwas im Kreis? STBut1 ist bereits vom
> Typ STbutton.STbut, da braucht man nichts casten.
Nein, auf der Aufruferseite ist STbut1 zunächst nur ein von VB erzeugtes
Aggregat aus Control bzw. VBControlExtender und der nativen Schnittstelle
STbut, genaugenommen jenes Extender-Objekt, von dem Armin spricht, das
dynamisch um die nativen Members von STbut erweitert worden ist. STbut in
der normalen Verwendung in VB ist *nicht* die native Schnittstelle, in die
mittels der Hilfsfunktion CSTbut explizit umgecastet werden soll.
> Aber der Ansatz, an den Typen der Parameter zu drehen, ist schon
> richtig. Siehe mein anderes Posting.
>
> > Da müsste man einmal prüfen, worin die Unterschiede zwischen beiden
> > Registrierungen bestehen.
>
> Was das OCX betrifft: in beiden Fällen RegSvr32. Was die Dll betrifft:
> bei der Erstellung hat sie VB selbst registriert, dann dereg und rereg
> per regsvr32. Das OCA wird bei der Erstellung immer durch VB
> registriert, dann dereg mit VBRegTlbMod. Sein Augenmerk müßte man wohl
> hauptsächlich auf die Dll Registrierung richten.
Ich meinte nicht Weg/Form der Registrierung, sondern die Inhalte - also
das, was die Registrierung aus der IDE beim Kompilieren in die Registry
geschrieben hat (da kommt RegSvr32 ja nicht zum Zuge), was nach dem
Deregistrieren vielleicht noch übrig geblieben sein könnte (vielleicht ja
doch irgendwelche OCA-Überreste, die bei der normalen Deregistrierung ja
unbekannt wären und unberücksichtig bleiben müssten), und was die erneute
Registrierung mit RegSvr32 dann produziert hat.
> Mir ist schon klar, daß da Unterschiede bestehen können, nur das Prüfen
> der Unterschiede ist ja nicht so trivial und auch ganz schön
> arbeitsaufwendig (wenn man mal von der Registryseite her das Ganze
> angehen würde).
Ich versuche ja nur, Anregungen zu geben (wem auch immer)...
"Harald M. Genauck" schrieb:
>
> Hallo Ulrich,
>
> > > > Der Disassembler sagt, daß in Exe1 eine IID verwendet wird, in Exe2
> > > > nicht.
> > >
> > > Aber:
> > >
> > > Wie sieht es denn in Exe1 aus, wenn Du nicht den Control-Extender,
> sondern
> > > das Control-object (also gleich die native Schnittstelle) übergibst?
> > >
> > > Private Sub Form_DblClick()
> > > UseOCX STbut1.object,...
> > > End Sub
>
> > Fehler 13 (Typen unverträglich). Offensichtlich kann aus dem nativen
> > Controlobject nicht auf das extended Controlobjekt zurückgecastet
> > werden.
>
> Äh... wo tritt der Fehler auf? In UseOCX? Wenn die Komponente STbutton ein
> externes OCX ist, sollte das eigentlich kein Problem sein.
Ja, in UseOCX an der Stelle Set lOCX = OCX
> > > Oder wenn auf der Aufrufer-Seite schon das Schnittstellen-Casting
> > > vorgenommen wird?
> > >
> > > Private Function CSTbut(STbut As STbutton.STbut) As STbutton.STbut
> > > CSTbut = STbut
> > > End Function
> > >
> > > Private Sub Form_DblClick()
> > > UseOCX CSTbut(STbut1),...
> > > End Sub
> >
> > "Objektvariable nicht festgelegt" :-)) Du hast das Set in CStbut
> > vergessen .-)
> >
> > Im Ernst, drehst Du Dich da nicht etwas im Kreis? STBut1 ist bereits vom
> > Typ STbutton.STbut, da braucht man nichts casten.
>
> Nein, auf der Aufruferseite ist STbut1 zunächst nur ein von VB erzeugtes
> Aggregat aus Control bzw. VBControlExtender und der nativen Schnittstelle
> STbut, genaugenommen jenes Extender-Objekt, von dem Armin spricht, das
> dynamisch um die nativen Members von STbut erweitert worden ist. STbut in
> der normalen Verwendung in VB ist *nicht* die native Schnittstelle, in die
> mittels der Hilfsfunktion CSTbut explizit umgecastet werden soll.
Du verwendest den Typ STButton.STbut in der Exe, in der das Control auf
einer Form liegt und per Projekt/Komponenten in das Projekt eingefügt
ist. STButton.STbut ist hier immer das extended Control (wenn Du's nicht
glaubst, schau Dir doch mal per Intellisense die Eigenschaften von STbut
an). Auf die native Schnittstelle mußt Du mit STbut1.Object zugreifen
(hast Du weiter oben sogar selbst geschrieben). Allerdings bekommst Du
ein Object zurück, und der passende Typ zum casten ist in der Exe nicht
vorhanden. Dazu müßtest Du schon einen Verweis auf das OCX direkt
setzen. CSTbut castet nix.
> > Aber der Ansatz, an den Typen der Parameter zu drehen, ist schon
> > richtig. Siehe mein anderes Posting.
> >
> > > Da müsste man einmal prüfen, worin die Unterschiede zwischen beiden
> > > Registrierungen bestehen.
> >
> > Was das OCX betrifft: in beiden Fällen RegSvr32. Was die Dll betrifft:
> > bei der Erstellung hat sie VB selbst registriert, dann dereg und rereg
> > per regsvr32. Das OCA wird bei der Erstellung immer durch VB
> > registriert, dann dereg mit VBRegTlbMod. Sein Augenmerk müßte man wohl
> > hauptsächlich auf die Dll Registrierung richten.
>
> Ich meinte nicht Weg/Form der Registrierung, sondern die Inhalte - also
> das, was die Registrierung aus der IDE beim Kompilieren in die Registry
> geschrieben hat (da kommt RegSvr32 ja nicht zum Zuge), was nach dem
> Deregistrieren vielleicht noch übrig geblieben sein könnte (vielleicht ja
> doch irgendwelche OCA-Überreste, die bei der normalen Deregistrierung ja
> unbekannt wären und unberücksichtig bleiben müssten), und was die erneute
> Registrierung mit RegSvr32 dann produziert hat.
Ja, eben. Der Weg ist unterschiedlich und damit können auch die
Resultate unterschiedlich sein (was also tatsächlich in die Registry
geschrieben wurde, was drin stehen bleibt etc). Deshalb meinte ich ja,
daß das Prüfen dieser Unterschiede (durch Registryvergleiche) aufwendig
wird, weil ich in der Registry mir alles zusammenklauben müßte (es wird
ja in diversen Registryzweigen was geändert).
> > Mir ist schon klar, daß da Unterschiede bestehen können, nur das Prüfen
> > der Unterschiede ist ja nicht so trivial und auch ganz schön
> > arbeitsaufwendig (wenn man mal von der Registryseite her das Ganze
> > angehen würde).
Is klar. Mir war anfangs schon durchaus bekannt, daß man das lieber lassen
sollte. Aber man spielt zunächst damit rum, probiert aus und wenn es klappt
setzt man es ein. Auch gegen den guten Ratschlag. Wenn sich dann auch nach
längerer Zeit kein Fehlverhalten einstellt, ist die Sache für mich ok. Sie
ist es auch dann noch, wenn sich nun diese einmalige Situation ergeben hat.
Für mich ist daher nur letzteres der Auslöser, nicht die Vorgehensweise
selbst.
Wäre das also geklärt. :-)
> > 00E91976 push offset ___vba@001D2D5C (0E91560h)
> > 00E9197B push dword ptr [ocx]
> > 00E9197E call ___vbaCastObj (0E91166h)
> > 00E91983 push eax
> > 00E91984 lea eax,[localocx]
> > 00E91987 push eax
> > 00E91988 call ___vbaObjSet (0E9116Ch)
> [...]
> Hast Du die kompilierte DLL disassembliert und so festgestellt,
> dass die IID einkompiliert worden ist?
Ja
> Oder was hast Du im Debugger untersucht? Den Lauf des
> DLL-Projekts in der IDE? Oder den Lauf der kompilierten DLL?
Der Code oben sieht nach native Exe aus. Ich habe mir nicht die Mühe
gemacht, mich näher mit p-Code zu beschäftigen. ;-)
Ich habe die kompilierte Exe gestartet und mich dann eingeklinkt und
debuggt.
Armin
Weil ich eines der beiden Projekte in der IDE laufen liess. In der IDE wird
die OCA gebraucht.
> Warum hat es erst
> funktioniert, nachdem Du das OCA auf den anderen Rechner kopiert
> und *registriert* hast?
Damit die IIDs zwischen kompilierter Exe und DLL in der IDE übereinstimmen.
Ich weiß ehrlich gesagt nicht, ob die Registrierung notwendig war, oder ob
es genügt hätte, daß der Verweis aufs OCA in der Registry unter dem
VB6-Zweig eingetragen ist. Ich hab das OCA einfach jedes Mal nach dem
Kopieren registriert. Was ohne Registierung passiert wäre, weiß ich nicht.
> Offensichtlich war es nötig, die
> Typelibrary auf dem Zielsystem zu *registrieren*. Dazu muß sie
> natürlich erstmal auf das Zielsystem kopiert werden.
>
> Was sind für Dich "normale" Dlls? Keine COM-Dlls? In dem Fall
> hast Du recht, da brauchts Du die Typelib nicht weitergeben.
Sorry, war missverständlich: ich meinte ActiveX-DLLs, also keine OCXen. Da
das (fast) dasselbe ist, wollte ich damit eigentlich nur ausdrücken, daß es,
wenn es für DLLs nicht notwendig ist, sicher auch nicht für OCXen notwendig
ist, die Typelib weiterzugeben. Allerdings liegt bei mit VB erzeugten DLLs
die Typelib in derselben Datei, insofern lässt sich das nicht trennen. Bei
anderen Komponenten kommt es aber vor, daß die TLB eine eigene Datei ist und
die Implementierung in einer Typelib-freien DLL liegt.
> Bei COM-Dlls ist das anders. Das sind Einheiten aus Typelibrary
> und Implementation der Types. Wenn Du eine Exe gegen eine COM-Dll
> kompilierst und in der Exe ein Set xyz = New foodll.barclass
> schreibst und die Exe weitergibst, mußt Du auch die Dll
> weitergeben. Und das nicht nur wegen des implementierenden Codes
> in der Dll, sondern auch wegen der Typelib in der Dll. Die wird
> nämlich dazu benötigt, die Dll auf dem Zielsystem zu
> registrieren. Und ohne diese Registrierung (wozu die Typelib
> benötigt wird), wird auch Deine Exe nicht laufen.
Sicher? Das ist doch nur notwendig, wenn ein *Entwickler* auf die Typelib,
die in der DLL liegt, zugreifen will. VB macht es halt so, daß Typelib +
Inhalt in einer DLL liegen. Es könnten aber genauso gut zwei getrennte
Dateien sein und dann würde es genügen, die pure DLL (=ohne Typelib) zu
vertreiben. Registriert werden muß die DLL natürlich, aber ich weiß nicht,
ob DLLRegisterServer die Infos aus der auch in der DLL enthaltenen Typelib
abruft, oder die Registrierungsinformationen nochmal getrennt abgelegt sind.
Sollte es ersteres sein, wäre die Typelib in der DLL natürlich doch
notwendig. Zur Laufzeit braucht man sie aber nicht.
> Bei den OCAs ist das spezieller gelagert, weil dies reine
> Typelibs ohne Implementierung sind. Die Implementierung steckt
> nun in Deiner Exe (dort steht der Code, der dynamisch die in der
> OCA angegebenen Types implementiert). Deshalb wird bei der
> Weitergabe auch das OCA benötigt (nur in dem von Dir
> geschildertem speziellen Fall mit der Weitergabe einer
> OCX-Referenz an eine Dll, normalerweise nicht).
Auch in dem speziellen Fall braucht man die OCA nicht, solange beide
Projekte in der kompilierten Version laufen.
Sorry, für mich ist hier EOT, da für mich alles geklärt, und außerdem ist
wieder Arbeitstag. ;-)
Danke für die Anteilnahme. :)
Armin