Jump to content
LonelyPixel

C# Sample Plugin

Recommended Posts

LonelyPixel

erwin, das bedeutet ja, dass es laufen müsste, wenn du die weiteren Methoden dazu implementierst. Die Frage ist dann allerdings, wie DVBViewer auf die zugreifen sollte, denn sie sind ja in einer anderen Schnittstelle enthalten und COM-Objekte bieten ja immer nur eine einzelne, aber nie mehrere Schnittstellen gleichzeitig an.

 

Wenn man die Stack-Schäden nach der Rückkehr aus einer Funktion mal nicht beachtet, sollte es doch möglich sein, ein Objekt zu erstellen, das x Funktionen ohne Parameter bereitstellt, die einfach nur ihre Nummer protokollieren. Dann sieht man zumindest, die wievielte Funktion aufgerufen wird, auch wenn es danach gleich zuende geht.

Share this post


Link to post
erwin
Der Stackpointer steht scheinbar an einer völlig sinnlosen Stelle und dann wird versucht mit pushfd darauf zu schreiben.

Nachtrag: der obige asm-Code nochmal ein par Zeilen früher begonnen

 

[0012FA88  mov		 esp,840012FAh <<-------- !!!!!!
0012FA8D  daa			  
0012FA8E  jno		 0012FA90 
0012FA90  mov		 ah,0FAh 
0012FA92  adc		 al,byte ptr [eax] 
0012FA94  movs		byte ptr es:[edi],byte ptr [esi] 
0012FA95  pushfd

Der Stackpointer wird also absolut gesetzt, d.h. es liegt hier wahrscheinlich keine Stackcorruption vor.

 

erwin

Share this post


Link to post
erwin
erwin, das bedeutet ja, dass es laufen müsste, wenn du die weiteren Methoden dazu implementierst.

Das will ich schon glauben das ich wenn ich die Delphi-Sample-Architektur genauso 1zu1 umsetze, also mit der gleichen Vererbungshierarchie, dass es dann läuft. Aber die Arbeit die damit verbunden ist! Es soll erstmal die Proxy-Variante sauber laufen.

 

Die Frage ist dann allerdings, wie DVBViewer auf die zugreifen sollte, denn sie sind ja in einer anderen Schnittstelle enthalten und COM-Objekte bieten ja immer nur eine einzelne, aber nie mehrere Schnittstellen gleichzeitig an.

 

function InitPlugin2: IDVBViewerPlugin; stdcall; {export;}
begin
 if myplug = nil then
myplug := TBasePlugin.Create;
 result := myplug;
end;

....

TBasePlugin = class(TBaseOSDSetupPlugin)

...

TBaseOSDSetupPlugin = class(TBaseOSDPlugin, iPlugin)

...

TBaseOSDPlugin = class(TBaseDVBViewerPlugin, iOSDPlugin)


... usw

 

d.h das zurückgelieferte TBasePlugin.Create hat in seiner vtable sämtliche Methoden aus dieser Vererbungshierarchie. Sind sind also (bei dieser Vererbungshierarchie) da und können somit aufgerufen werden. Also nicht mehrere Schnittstellen sondern eine große die alle zusammenfasst.

 

erwin

Share this post


Link to post
LonelyPixel

Umm... und die ominöse GetInterface-Funktion? Reduziert die das nicht wieder? Jedes Objekt, das mehrere Schnittstellen implementiert, enthält alle Methoden, aber die sind in COM nie alle gleichzeitig sichtbar. Welcher Schnittstellenbeschreibung wollte man bei so einem Aufruf auch folgen, es gibt ja gar keine. Wie gesagt, ich bin mir noch nicht sicher, ob DVBViewer gegenüber Plugins tatsächlich ein COM-Client ist, oder ob man da nur sowas ähnliches nachgebaut hat. Offizielles weiß man aber nicht.

 

Nagut, für den unangenehmen Fall, dass es kein COM ist, müsste man doch nur die IUnknown-Ableitung in .NET ausschalten und entsprechend eigene Methoden implementieren. Also wie in C++. Könnte sowas funktionieren? Dann müsste man zwar alles selbst mit Reflection machen, könnte aber vielleicht sogar die fehlende Schnittstellenableitung realisieren.

 

Warten wir erstmal auf was offizielles. Jetzt komm ich auch mal wieder dazu, andere Dinge zu erledigen.

Share this post


Link to post
JMS
Ich hab mal InitPlugin2 debugged und zwar in folgender Konfiguration: In reinem C++ hab ich InitPlugin2() exportiert. Da InitPlugin2() ein IDVBViewerPlugin zurückliefern muss, hab ich eine IDVBViewerPlugin-Proxy Implementation bereitgestellt die die notwendigen Methoden:

 

AddRef()

Release()

QueryInterface()

Init()

Name()

SetApplication()

Terminate()

Mich würde mal folgendes interessieren: wenn Du damit ein C++ Debugging machst und auf InitPlugin2() einen Breakpunkt setzt. Dann durchlaufen bis zum Rücksprung. Wie sieht der Folgecode (im Viewer) aus (und wie sieht der Rücksprung aus: ret oder leave oder ... vielleicht einfach mal so 20 ASM Zeilen davor).

 

Jochen

 

<PS>Da Du einen Viewer hast: hast Du nicht mal Lust wie oben beschrieben mein kleines Sample zu probieren?</PS>

<PPS>Gerade (21:42) nochmal aktualisiert, Sourcen sind daneben.</PPS>

Edited by JMS

Share this post


Link to post
JMS
Nachtrag: der obige asm-Code nochmal ein par Zeilen früher begonnen

 

[0012FA88  mov		 esp,840012FAh <<-------- !!!!!!
0012FA8D  daa			  
0012FA8E  jno		 0012FA90 
0012FA90  mov		 ah,0FAh 
0012FA92  adc		 al,byte ptr [eax] 
0012FA94  movs		byte ptr es:[edi],byte ptr [esi] 
0012FA95  pushfd

Der Stackpointer wird also absolut gesetzt, d.h. es liegt hier wahrscheinlich keine Stackcorruption vor.

 

erwin

Mutige Behauptung :) Kann es nicht sein, dass Du beim Disassemblieren mitten in eine Instruktion gesprungen bist?

 

Jochen

Share this post


Link to post
LonelyPixel

Ich würde ja einfach nen Breakpoint an die Stelle setzen, aber das funktioniert beim Debuggen von kompilierten Programmen so schlecht, wenn dann Visual Studio immer durchdreht und wie wild im Kreis rennt.

 

Ich habe mal versuchshalber die Methoden der anderen beiden Schnittstellen auch in die IDVBViewerPlugin-Schnittstelle reinkopiert, so dass der CCW gleich am Anfang ein großes Objekt zurückgibt. Das ändert an Fehlersituation und -stelle aber gar nichts.

 

JMS: Dein Plugin gibt folgende Meldungen mit OutputDebugString aus:

[628] wsocket.pas initialization section
[628] PlugInFactory JMS.DVBViewer.SamplePlugIn.MyPlugIn

Hilft dir das weiter?

 

Lars, du lachst uns doch schon aus, oder?

Share this post


Link to post
JMS

Ich habe einen Fehler im Test eben gefixt (LPWSTR / LPCSTR). Kannst Du es damit nochmal probieren [selber Download] - sollte dann eigentliche abstürzen, wie Deins auch :)

 

Jochen

Share this post


Link to post
LonelyPixel
sollte dann eigentliche abstürzen, wie Deins auch :)

Na das hat aber mal nicht funktioniert... B)

 

Aber im 2. Anlauf hast du's nun doch geschaft. DVBViewer ist weg. Toll. ;)

 

[964] wsocket.pas initialization section
[964] PlugInFactory JMS.DVBViewer.SamplePlugIn.MyPlugIn 
[964] CreatePlugIn 
[964] PlugIn JMS.DVBViewer.SamplePlugIn.MyPlugIn, JMS.DVBViewer.SamplePlugIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Share this post


Link to post
JMS

Mist - aber wie gesagt, Dein Code sah schon ok aus.

 

Danke

 

Jochen

Share this post


Link to post
JMS

Ich bin ziemlich sicher, dass die Signatur der InitPlugin2 von Delphi anders umgesetzt wird, und zwar in sowas wie

 

void __stdcall InitPlugin2(IDVBViewerPlugin **plugin); // void InitPlugin2(out IDVBViewerPlugin plugin); in .NET

 

Ich habe mein kleines Testdingens mal aktualisiert, damit Du es schnell probieren kannst. ABER: wenn das so ist [ich habe mir das MyInternet.dll PlugIn mal mit Deinem MFCTest1 geladen und reingesteppt, daher bin ich recht sicher, was das angeht], dann kann es sein, dass Delphi aus dem Name() Aufruf auch einen richtigen COM Aufruf gemacht hat (i.e. HRESULT get_Name(BSTR *ret) in C++ oder string Name { get; } in .NET), dann kracht es vermutlich ein kleines Stück später. Allerdings dann im PlugIn und das sollte lokalisiertbar sein.

 

Viel Glück

 

Jochen

Share this post


Link to post
erwin
Wie gesagt, ich bin mir noch nicht sicher, ob DVBViewer gegenüber Plugins tatsächlich ein COM-Client ist, oder ob man da nur sowas ähnliches nachgebaut hat. Offizielles weiß man aber nicht.

Lt. Lars_MQ ist es COM-ligth. Es arbeitet ohne Registrierung, die ist aber auch nicht zwingend vorgeschrieben.

 

Mutige Behauptung cool.gif Kann es nicht sein, dass Du beim Disassemblieren mitten in eine Instruktion gesprungen bist?

Da hast du recht. Ein Byte-Shift im Maschinencode mit dann falschen Disassemblys ist um vieles wahrscheinlicher als eine Konstruktion die den Stackpointer so ominös auf read-only Speicher neu setzt.

 

 

Mich würde mal folgendes interessieren: wenn Du damit ein C++ Debugging machst und auf InitPlugin2() einen Breakpunkt setzt. Dann durchlaufen bis zum Rücksprung. Wie sieht der Folgecode (im Viewer) aus (und wie sieht der Rücksprung aus: ret oder leave oder ... vielleicht einfach mal so 20 ASM Zeilen davor).

Der Rücksprung sieht eigentlich ganz OK aus:

	return rc;
10001238  mov		 eax,dword ptr [ebp-18h] 
}
1000123B  mov		 ecx,dword ptr [ebp-0Ch] 
1000123E  mov		 dword ptr fs:[0],ecx 
10001245  pop		 ecx  
10001246  mov		 esp,ebp 
10001248  pop		 ebp  
10001249  ret

 

Ich bin ziemlich sicher, dass die Signatur der InitPlugin2 von Delphi anders umgesetzt wird, und zwar in sowas wie

 

void __stdcall InitPlugin2(IDVBViewerPlugin **plugin); // void InitPlugin2(out IDVBViewerPlugin plugin); in .NET

Zu dieser Meinung bin ich inzwischen auch gekommen. Schon wenn ich mir das Disassembly des InitPlugin2()-Aufrufes ansehe:

00711B49  lea		 eax,[ebp-4] 
00711B4C  push		eax  
00711B4D  call		esi; <<<<<----------- call InputPlugin2()
00711B4F  mov		 edx,dword ptr [ebp-4] 
00711B52  lea		 eax,[edi+8] 
00711B55  call		004076E4 
00711B5A  xor		 eax,eax 
00711B5C  pop		 edx  
00711B5D  pop		 ecx

Ich habs so gelernt: Integrale Returnwerte werden i.d.R im AX-Register zurückgeliefert. Hiervon keine Spur. Im Code oben wird scheinbar überhaupt keine Auswertung des returns gemacht. Dafür wird vor dem Aufruf eine effektive Adresse auf den Stack gelegt - wie ein Pointerparameter eben

 

erwin

Edited by erwin

Share this post


Link to post
erwin

Juchu!! In der Tat dass wars. Mit dieser Sig läuft es jetzt. Stellt sich für mich als Delphi-Laie die Frage ist die InputPlugin2-Signatur *falsch* angegeben wurden? Wohl nicht, denn im Delphi-Sample wurde InputPlugin2() ja genau so verwendet. Allerdings hab ich das nicht nochmal kompiliert. Kann mal ein Delphi-Kenner sich dazu äußern wie das mit den Signaturen ist wenn als Parameter out-Referenzen verwendet werden?

 

erwin

Share this post


Link to post
JMS

Ich habe gestern noch eine ganze Weile mit dem MyInternet.dll PlugIn gespielt und glaube, Deine Freude ist weit verfrüht. Hier mein aktueller Stand: die IDVBViewerPlugin Methoden Init, SetApplication und Terminate verhalten sich wie ganz normale COM stdcall Methoden, i.e. sie erwarten die Parameter auf dem Stack in der natürlichen Reihenfolge und räumen den Stack auf:

 

Stack + 0 : Return

Stack + 4 : this

Stack + 8 : Parameter 1 (e.g. SetApplication)

...

 

ABER: Name genau wie InitPlugi2 tun das nicht. Name erwartet folgende Parameter - und das ist selbst mit C++ nur schwer nachzubilden (handgepatchte VTable):

 

Stack + 0 : Return

Stack + 4 : Adresse des Rückgabewertes (!)

Stack + 8 : this

Stack + 12 : (nur eine Vermutung) Parameter 1

...

 

Ich vermute einfach mal, dass das bei InitPlugin2 genau so ist - hier gibt es nur kein this. Technisch gesehen entspricht das der Delphi Calling Convention safecall, obwohl das eigentlich nicht sein kenn (selbst im Beispiel steht überall stdcall). Ich verstehe auch nicht so ganz, warum LibTyp und InitPlugin2 sich unterschiedlich verhalten. Ich vermute, es liegt an der Art des Rückgabewertes: PChar ist etwas anderes als ein WideString [theoretisch ein BSTR mit besonderen Regeln zur Freigabe] oder eine Schnittstelle.

 

Wenn man ein PlugIn baut, das keinerlei Methoden mit Rückgabewert dieser Art verwendet (da könnte man in C++ und .NET aber leicht tricksen) oder implementieren muss (da sehe ich dann selbst für C++ eher schwarz, auch wenn es gehen würde - ich habe selbst in C# schon dynamische VTables gebaut), dann könnte es klappen. Aber wehe, der Viewer Ruft IDVBViewerPlugin::Name auf...

 

So weit mein Wissensstand - kann man leicht mit dem MyInternet.Dll PlugIn, Euerem MFCTest1.exe und einem Native Debugger verifizieren oder wiederlegen.

 

Jochen

Share this post


Link to post
LonelyPixel

Nur kurz: Ich kann bestätigen, dass die Änderung der Signatur von InitPlugin2 dazu führt, dass der Startprozess von DVBViewer scheinbar normal weiterläuft. Später wird dann noch SetMenuHandle aufgerufen. Die ganzen unkonditionalen EventMsg-Aufrufe habe ich mal deaktiviert, weil ich sonst wieder von Messageboxen überflutet werde. Sowas dürfte auch für DVBViewer nicht grade gesund sein. Die Methode Name() wird noch nicht aufgerufen, ich hab da noch ne MessageBox eingefügt.

 

public static void InitPlugin2(ref IDVBViewerPlugin retval)

Ob das PLugin jetzt im weiteren Verlauf auch funktioniert, kann ich mangels Funktionalität noch nicht sagen. In der Versionsinfo erscheint es nicht, was diese Anzeige tut, ist aber noch unklar.

 

Danke JMS für diesen entscheidenden Hinweis! Muss jetzt erstmal zur Arbeit...

 

Ach ja, in den Delphi-Schnittstellen werden gelegentlich Properties verwendet. Die habe ich noch gar nicht in C# übersetzt, weil ich keine Ahnung hab, wie die in Delphi, COM und im CCW funktionieren. In C# wüsste ich es wohl, aber scheint mir das direkte Verfahren zunächst inkompatibel, da die set- und get-Methoden in der Schnittstelle ja explizit erwähnt werden. Vielleicht muss man die Eigenschaften aber auch gar nicht übersetzen, weil Delphi implizit sowieso die get- und set-Methoden aufruft und die Eigenschaften selbst im binären Layout gar nicht vorkommen.

Share this post


Link to post
erwin
IDVBViewerPlugin Methoden Init, SetApplication und Terminate verhalten sich wie ganz normale COM stdcall Methoden

OK

ABER: Name genau wie InitPlugi2 tun das nicht.

Nur zur Klarstellung: Name ist eine COM-Interfacemethode, InitPlugin2() nicht.

 

Name erwartet folgende Parameter - und das ist selbst mit C++ nur schwer nachzubilden (handgepatchte VTable):

 

Stack + 0 : Return

Stack + 4 : Adresse des Rückgabewertes (!)

Stack + 8 : this

Stack + 12 : (nur eine Vermutung) Parameter 1

Wie soll man dies nun verstehen? Name() hat auch so eine *verdrehte* Signatur, d.h. der signierte return ist eigentlich

eine out-Referenz? Und dann die Parameterreihenfolge? Kein stdcall?

 

Ich verstehe auch nicht so ganz, warum LibTyp und InitPlugin2 sich unterschiedlich verhalten. Ich vermute, es liegt an der Art des Rückgabewertes: PChar ist etwas anderes als ein WideString [theoretisch ein BSTR mit besonderen Regeln zur Freigabe] oder eine Schnittstelle.

Du hast hier nicht InitPlugin2() gemeint sondern Name()? Nun LibTyp() stammt noch aus der Vor-Com-Zeit und Name() ist eine COM-Interfacemethode.

 

Aber wehe, der Viewer Ruft IDVBViewerPlugin::Name auf...

Mein nun funktionierender Proxy protokolliert (in dieser Reihenfolge):

 

AddRef()

Release()

SetApplication()

Init()

 

Beenden DVBV

 

Terminate()

Release()

 

Kein Name()! Kein Name - kein Test.

 

erwin

Share this post


Link to post
erwin
In der Versionsinfo erscheint es nicht, was diese Anzeige tut, ist aber noch unklar.

 

2. Verpass deiner DLL eine Versions-Ressource. Die kannst Du dann unter Help/About/Versionsinfo sehen (wenn alles gut gegangen ist)

Ich hab hier eine ganz stinknormale Versionsressource gemeint. Add/Ressource...

 

Ich habe eine Versionsressource, das macht .NET automatisch,

Meinst du AssemblyInfo? Mit diesem .Net-Kram kann DVBV wahrscheinlich nichts anfangen.

 

erwin

Share this post


Link to post
JMS
OK

 

Nur zur Klarstellung: Name ist eine COM-Interfacemethode, InitPlugin2() nicht.

 

 

Wie soll man dies nun verstehen? Name() hat auch so eine *verdrehte* Signatur, d.h. der signierte return ist eigentlich

eine out-Referenz? Und dann die Parameterreihenfolge? Kein stdcall?

 

 

Du hast hier nicht InitPlugin2() gemeint sondern Name()? Nun LibTyp() stammt noch aus der Vor-Com-Zeit und Name() ist eine COM-Interfacemethode.

 

 

Mein nun funktionierender Proxy protokolliert (in dieser Reihenfolge):

 

AddRef()

Release()

SetApplication()

Init()

 

Beenden DVBV

 

Terminate()

Release()

 

Kein Name()! Kein Name - kein Test.

 

erwin

 

:) Nein, Du irrst Dich, ich habe die Namen der Methoden im Beispiel mehr als bewußt gewählt. Ich will hier auf eine mögliche Verhaltensweise von Delphi hinweisen, die meiner Ansicht nach COM und Standardexports betrifft. Der Vergleich LibTyp und InitPlugin2 könnte darauf hinweisen, dass dieses von mir vermutete Verhalten abhängig von der Art des Rückgabewertes ist.

 

Ciao

 

Jochen

Share this post


Link to post
LonelyPixel
Ich hab hier eine ganz stinknormale Versionsressource gemeint. Add/Ressource...

 

Meinst du AssemblyInfo? Mit diesem .Net-Kram kann DVBV wahrscheinlich nichts anfangen.

Ich mein das, was man in den Dateieigenschaften (Explorer -> Datei -> Eigenschaften) auf der Seite "Version" sehen kann. Ist das nicht die Versions-Ressource?

Share this post


Link to post
LonelyPixel
Ich will hier auf eine mögliche Verhaltensweise von Delphi hinweisen, die meiner Ansicht nach COM und Standardexports betrifft. Der Vergleich LibTyp und InitPlugin2 könnte darauf hinweisen, dass dieses von mir vermutete Verhalten abhängig von der Art des Rückgabewertes ist.

Sowas muss doch irgendwo dokumentiert sein! In dem Delphi-Buch, das mir zur Verfügung steht, ist im COM-Kapitel nirgends erwähnt, dass eine Signaturübersetzung wie bei .NET stattfinden würde. Dafür spricht auch, dass viele Methoden mit HRESULT und manche auch mit out,retval definiert sind. Die vielen nicht-HRESULT-Methoden sprechen nur für schlechten (?) COM-Stil. Gegen diese puristische Annahme spricht allerdings, dass die InitPlugin2-Funktion anscheinend anders compiliert ist, als im Delphi-Code angegeben.

Share this post


Link to post
erwin
Ich mein das, was man in den Dateieigenschaften (Explorer -> Datei -> Eigenschaften) auf der Seite "Version" sehen kann. Ist das nicht die Versions-Ressource?

Der Explorer könnte ".NET aware" sein. Weiss nicht.

 

erwin

Share this post


Link to post
erwin
Wie soll man dies nun verstehen? Name() hat auch so eine *verdrehte* Signatur, d.h. der signierte return ist eigentlich

eine out-Referenz? Und dann die Parameterreihenfolge? Kein stdcall?

Dies und einiges anderes würde passen, wenn die Delphi-Samples safecall anstatt stdcall vverwenden würden. Siehe hier

 

http://en.wikipedia.org/wiki/X86_calling_conventions

 

The safecall calling convention is the same as the stdcall calling convention, except that exceptions are passed back to the caller in EAX as a HResult (instead of in FS:[0]), while the function result is passed by reference on the stack as though it were a final "out" parameter.

 

Hallo DVBV-Entwickler lest ihr mit? Eine Stellungnahme wäre wirklich sehr hilfreich.

 

erwin

 

PS: Vielleicht ist es ja eine (Delphi-) Compilereinstellung: Verwende safecall überall wo stdcall steht. D.h. es war den DVBV-Mannen gar nicht bewusst.

Edited by erwin

Share this post


Link to post
Tjod
Hallo DVBV-Entwickler lest ihr mit?

Ich glaube die sind alle z.Z. alle recht beschäftigt. Und ich vermute mal, dass sich eine Vernünftige Antwort zu dem Thema nicht unbedingt in 5 Minuten nebenher schreiben lässt.

Share this post


Link to post
erwin
Ich glaube die sind alle z.Z. alle recht beschäftigt. Und ich vermute mal, dass sich eine Vernünftige Antwort zu dem Thema nicht unbedingt in 5 Minuten nebenher schreiben lässt.

Schon klar. So hab ich das auch nicht erwartet. Ist womöglich etwas schief rübergekommen. Sorry

 

erwin

Share this post


Link to post
Lars_MQ

Die Aufruf reihenfolge lautet (das vorhergehende wurde ausgelassen):

- InitPlugin2

- IUnknown._AddRef

- IUnknown._AddRef

- IUnknown._Release

- PluginName

- PluginName

- PluginName

- PluginName

- PluginName

- PluginName

- PluginName

- PluginName

- PluginName

- IDVBViewerPlugin.SetApplication

- IDVBViewerPlugin.Init

- IUnknown._AddRef

- IUnknown._AddRef

- IUnknown._Release

 

Danach werden funktionen abhängig vom kontext aufgerufen.

 

Die aufruf konvention ist stdcall. Die benennung der Addref und Release sind delphi spezifisch und vernachlässigbar, da sie sich aus der position im Interface (vtable) ergeben ( http://msdn.microsoft.com/en-us/library/ms...28VS.85%29.aspx ).

Share this post


Link to post
JMS
PS: Vielleicht ist es ja eine (Delphi-) Compilereinstellung: Verwende safecall überall wo stdcall steht. D.h. es war den DVBV-Mannen gar nicht bewusst.

Gibt es so was wirklich? Dann wäre das einen Kommentar wert...

 

Jochen

Share this post


Link to post
LonelyPixel
Der Explorer könnte ".NET aware" sein. Weiss nicht.

Okay, ich hab jetzt 2 uralte Ressourcen-Anzeigeprogramme von früher ausprobiert und beide zeigen in meiner DLL keine Ressourcen an. Na dann eben nicht. So alten Kram können wir da nicht mehr unterstützen. :)

Share this post


Link to post
LonelyPixel

Was passiert eigentlich, wenn der Benutzer im DVBViewer-Optionen-Fenster eine vom Plugin hinzugefügte Kategorie anklickt? Momentan sieht es so aus, dass DVBViewer mit dem Fehler System.ExecutionEngineException abstürzt. MSDN sagt dazu:

Die Ausnahme, die bei einem internen Fehler im Ausführungsmodul der Common Language Runtime ausgelöst wird. Fehler des Ausführungsmoduls sind schwerwiegende Fehler, die niemals auftreten dürften. Derarartige Fehler treten hauptsächlich dann auf, wenn der Ausführungsmodul beschädigt ist oder wenn Daten fehlen. Diese Ausnahme kann jederzeit vom System ausgelöst werden.

Hört sich an wie die verwaltete Version des Bluescreens. Was treibt DVBViewer denn alles mit dem Plugin? So einen Fehler hab ich in meinem Leben noch nicht gesehen.

 

Der Fehler ist bereits aufgetreten, bevor ich mein Optionen-Fenster und den entsprechenden Aufruf im Code eingefügt habe. Das MyPrograms-Plugin hab ich diesbezüglich soweit durch. Fehlt noch irgendwas?

 

Aktueller Code an gewohnter Stelle: http://unclassified.de/tmp/DVBViewer/DvbTestPlugin.7z

 

Ich schau mir jetzt schonmal die eigentliche OSD-Steuerung an, wenn ich sie finde.

 

Wenn sich übrigens jemand geneigt fühlt, meine Schnittstellendateien zu dokumentieren, darf das gerne jederzeit tun. Denn jetzt haben wir zwar die Schnittstellen, aber wer wann was aufruft geht daraus nicht hervor und so bleiben die meisten Funktionen weiterhin verborgen. Eine Plugin-Schnittstelle sieht üblicherweise anders aus...

Share this post


Link to post
LonelyPixel

Noch eine Frage: Wenn ich eine eigene OSD-Seite im Hauptmenü hinzufüge (über IWindowManager.NewWindow), die im Betrieb auswähle, das Fenster dann schwarz wird und das OSD gar nicht mehr funktioniert (der Rest anscheinend schon), was ist dann passiert? Die XML-Datei existiert, musste ja auch einen Dateinamen angeben. Gibt's irgendwo sowas wie ein Fehlerprotokoll, das Plugin-Entwickler bei der Fehlersuche unterstützen kann? Sonst such ich jetzt wegen jeder Banalität wochenlang rum, bloß weil es am Ende nicht dokumentiert war.

 

Außerdem kann ich keinen Sender mehr einstellen, wenn mein Plugin geladen wurde. Die DLL-Funktion Execute habe ich gefunden und irgendwo stand, dass sie true zurückgeben muss, um den Senderwechsel zu erlauben. Aber weder bei true noch false passiert was.

 

Muss eigentlich jedes Plugin alle DLL-Funktionen exportieren? Was passiert, wenn welche nicht exportiert werden? Knallt DVBViewer dann auf den Boden oder prüft der zur Abwechslung auch mal, ob die Funktion existiert?

 

Fragen über Fragen... ich bewundere die Scharen (?) von Entwicklern, die sich all dieses Wissen zuvor in mühevoller Kleinarbeit ausgetüftelt haben. Hat dann niemand von denen irgendwas dokumentiert? Müssen wir tatsächlich immer und immer wieder ganz von vorne anfangen? Was für eine Entwicklergemeinschaft ist das hier eigentlich? Ist Plugin-Entwicklung am Ende vielleicht doch unerwünscht? Dokumentation kostet Zeit, aber sie ist einfach notwendig, wenn man ernsthaft daran interessiert ist, dass andere was beitragen.

 

In diesem Sinne, ich muss meinen DVBViewer jetzt für ne Aufnahme in Ruhe lassen. Bis später.

Share this post


Link to post
LonelyPixel

Oh, doch so viele Antworten. Naja. Ich hab die Entwicklung des C#-Plugins bis auf weiteres eingestellt, nachdem von Seiten der DVBViewer-Entwickler nicht mit der dafür notwendigen Unterstützung zu rechnen ist. Ich habe den letzten Stand des Quelltextes an diesen Beitrag angehängt und werde die vorherigen Dateien unter den bislang angegebenen URLs löschen. Wer mag kann sich gerne weiter damit beschäftigen. Ich beschränke mich damit wieder auf kleinere Anpassungen von OSD-Skins. Zumindest konnte ich aber einiges zu .NET COM Interop lernen, das ist doch auch was...

 

Edit: Hab auch noch den Interface Converter angehängt.

DvbTestPlugin.zip

InterfaceConverter.zip

Edited by LonelyPixel

Share this post


Link to post
ProgMaq

Hello friends.

 

Using "Import Type Library ..." Delphi in the file "DVBViewer.exe" I managed to get the COM libraries.

 

The problem I have is that one does not get the COM library to create Plugin.

 

Does anyone help me?

Share this post


Link to post
erwin

Was passiert eigentlich, wenn der Benutzer im DVBViewer-Optionen-Fenster eine vom Plugin hinzugefügte Kategorie anklickt? Momentan sieht es so aus, dass DVBViewer mit dem Fehler System.ExecutionEngineException abstürzt.

Hi all devs,

 

ich weiss nicht ob LonelyPixel noch am Thema dran ist. Ich antworte trotzdem mal. Nach dem Anklicken wird die IPlugin.Show() Methode aufgerufen. In dieser ist der Setttings-Dialog zu erstellen und anzuzeigen. Nach dem return von Show() ruft der DVBV die IPlugin.GetDescription() Methode auf, dessen Rückgabe er dann oben im Fenster anzeigt.

Ich hab das jetzt mal soweit mit C++ geschafft. So weit so gut. Die Crux liegt in der IPlugin.GetDescription()-Methode die laut den angegebenen Delphi-Demos wie stdcall aufgerufen wird. Stimmt aber nicht! Daher die Abstürze. Ähnlich wie schon bei InputPlugin2() festgestellt ist die calling convention safecall. Ich vermute mal alle Methoden die kein HRESULT zurückgeben sind safecall. Warum dann die DVBV-Entwickler trotzdem stdcall deklarieren ist mir ein Rätsel. Vielleicht passiert da was unter der Haube, z.B. mit der IDE-Einstellung "Safecall function mapping" auf "All v-table interfaces".

Ich hab aus der Version

 


INTERFACE IDVBViewerPluginOptions : public IDVBViewerPlugin
{
virtual HRESULT __stdcall Apply( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall Cancel( const BSTR strModuleName ) = 0;

virtual BSTR __stdcall GetDescription( const BSTR strModuleName ) = 0;

virtual HRESULT __stdcall Hide( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall LoadData( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall LoadLanguage( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall Show( const BSTR strModuleName, DWORD Parenthandle ) = 0;
};
[/codeBOX]

 

dann mal dies gemacht

 

[codeBOX]
INTERFACE IDVBViewerPluginOptions : public IDVBViewerPlugin
{
virtual HRESULT __stdcall Apply( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall Cancel( const BSTR strModuleName ) = 0;

virtual HRESULT __stdcall GetDescription( BSTR * strDescription, const BSTR strModuleName ) = 0;

virtual HRESULT __stdcall Hide( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall LoadData( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall LoadLanguage( const BSTR strModuleName ) = 0;
virtual HRESULT __stdcall Show( const BSTR strModuleName, DWORD Parenthandle ) = 0;
};

[/codeBOX]

 

und mit viel Trickserei gehts dann.

 

Wie dem C++ Progammierer vielleicht bekannt ist, werden bei Methodenaufrufen immer noch ein zusätzlicher Parameter als erster aber unsichtbarer (in der Funktionsdeklaration) übergeben. Dieser hidden Parameter kann mit [b]this[/b] angesprochen werden und ist sematisch der Pointer auf die Objektinstanz. Eine wie

 

virtual HRESULT __stdcall GetDescription( BSTR * strDescription, const BSTR strModuleName ) = 0;

 

deklarierte Methode wird also wie

 

HRESULT __stdcall GetDescription( IDVBViewerPluginOptions * this, BSTR * strDescription, const BSTR strModuleName )

 

aufgerufen. So ähnlich macht das auch Delphi. Jetzt kommt diese safecall ins Spiel (welches es in C++ nicht gibt).

Dieses safecall bewirkt das als erster Parameter der deklarierte Return Wert als Out-Referenz benutzt wird. Der self-Pointer wird zum 2. Parameter.

 

Also so verstehts der C++ Compiler

 

HRESULT __stdcall GetDescription( IDVBViewerPluginOptions * this, BSTR * strDescription, const BSTR strModuleName )

 

und so wirds wirklich aufgerufen

 

HRESULT __stdcall GetDescription( BSTR * strDescription, IDVBViewerPluginOptions * this, const BSTR strModuleName )

 

D.h. die Deklaration

 

virtual HRESULT __stdcall GetDescription( BSTR * strDescription, const BSTR strModuleName ) = 0;

 

hat keinen offensichtlichen Zugriff auf den Out-Parameter da er ja der erste - hidden - Parameter ist.

 

Mit dieser Erkenntnis gehts dann doch:

 

1. den Return BSTR weise [b]* this[/b] zu. Beachte [b]this[/b] ist hier jetzt nur noch das syntaktische Mittel zum Zugriff auf den ersten -hidden- Parameter. Die Sematik als Pointer auf die Objektinstanz ist verlorengegangen.

 

2. diese Rolle übernimmmt jetzt der erste sichtbare Parameter (hier als [b]BSTR * strDescription[/b] deklariert). D.h. alle this-> Zugriffe sind über strDescription-> abzuwickeln, auch die impliziten, also auch Zugriffe auf alle Member wo nicht explizit this-> davor steht.

 

FAZIT: Prinzipiell geht es mit C++. Der Code ist dann aber absolut unleserlich/verwirrend. Wenn es die DVBV-Entwickler bei safecall belassen bestehen kaum Chancen hier mit C++ (wahrscheinlich auch C#) zu entwickeln. Besonders robust wäre diese Trickserei aber auch nicht, da wie es scheint safecall in der Delphi-IDE aus stdcall generiert wird. Eine neue/andere Compilation generiert dann vielleicht doch wieder stdcall.

 

erwin

Share this post


Link to post
xice

Hallo,

 

ich hab mich ein wenig mit diesem Thema beschäftigt und die Plugin-Funktionalität ein wenig erweitert.

Das Plugin benötigt das .NET Framework 3.5. Die aktuelle .NET Version scheint nicht kompatibel zu sein.

Vielleicht könnte sich das mal LonelyPixel anschauen.

 

Änderungen:

  • Menüeintrag unter Plugins
  • Menüeintrag unter Optionen
  • Unerstützung des DVBViewer COM Server

DVBViewerPlugin.zip

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...