2 Stimmen

Type Equivalence funktioniert nicht für das Google Earth Plugin

Ich habe eine WinForms C#-Anwendung, die das Google Earth(GE)-Plugin einbettet. Dies geschieht über COM Interop, indem ein Verweis auf das Google Earth Plugin hinzugefügt wird.

Wir würden gerne verschiedene Versionen des GE-Plugins unterstützen, wenn wir unsere Anwendung bereitstellen. Wir referenzieren und erstellen unsere Anwendung mit der Version 5.0 des GE-Plugins auf einem Entwicklungsrechner und stellen sie dann auf einem anderen Rechner bereit, auf dem die Version 6.0 des GE-Plugins installiert ist, und lassen die Anwendung unter Verwendung der Typäquivalenz laufen.

Nach meinem Verständnis von Type Embedding und Type Equivalence in C# 4.0, ist dies genau das, was Type Equivalence Adresse. Allerdings habe ich nicht in der Lage, dies zu bekommen, um zu arbeiten.

Ich füge eine Referenz auf das Google Earth Plugin 5.2 in Visual Studio 2010 als COM-Referenz hinzu. Ich wähle die 'Embed Interop Types'.

Anschließend führe ich die Anwendung auf einem Rechner aus, auf dem das 6.0 GE Plugin installiert ist.

Ich erhalte eine Ausnahme, wenn ich die folgende Codezeile ausführe

string pluginVersion = ge.getPluginVersion();

wobei ge definiert ist als

 private IGEPlugin ge;

und 'IGEPlugin' ist eine von der COM Interop definierte Schnittstelle für die GE Plugin DLL.

System.AccessViolationException wurde vom Benutzercode nicht behandelt Message=Versucht, geschützten Speicher zu lesen oder zu schreiben. Dies ist oft ein Hinweis darauf, dass >anderer Speicher beschädigt ist.

Die Ausnahme ist anders, wenn sie umgekehrt wird (erstellt mit 6.0, ausgeführt gegen Version 5.2)

Der Code und die Anwendung funktionieren einwandfrei, wenn sie mit der gleichen Version des GE-Plugins erstellt und ausgeführt werden.

Ich bin mir also nicht sicher, was ich übersehe. Aus der MSDN-Dokumentation geht hervor, dass es automatisch funktionieren sollte.

Typenäquivalenz und eingebettete Interop-Typen

Ich hatte wirklich gehofft, zu vermeiden, auf die Verwendung von C# 4.0 "dynamisch" überall zu wechseln, um Kompatibilität über GE Plugin-Versionen zu erhalten, da ich alle Intellisense verlieren, und würde eine Menge Code ändern müssen.

2voto

Manuel Strausz Punkte 226

Ich habe eine Weile damit gerungen. Der Grund, warum es mit "Embed Interop Types" nicht funktioniert, ist, dass Google aus irgendeinem Grund eine völlig neue GUID für das COM-Objekt generiert, was bedeutet, dass die Interop-Bibliothek, die eingebettet ist, nicht mehr funktionsfähig sein wird, auch wenn die Schnittstelle genau die gleiche ist.

Ich bin erst kürzlich auf dieses Problem gestoßen, als das GoogleEarth-Plugin beschloss, sich selbst zu aktualisieren und dadurch die Interop-Kompatibilität zu verletzen, weil es eine neue COM-GUID hat.

Ich verstehe, dass Sie nicht überall "dynamisch" verwenden möchten, da Sie die IntelliSense verlieren - ich würde vorschlagen, dass Sie eine Interop-Referenz für die anfängliche Entwicklung verwenden, so dass Sie in dieser Phase mit statischer Typisierung haben, und dann zu "dynamisch" wechseln, sobald es stabil genug ist und Sie nur sicherstellen möchten, dass es immer noch mit zukünftigen Versionen von GoogleEarth funktioniert, ohne eine Cast-Ausnahme zu werfen.

Ich weiß, dass Sie bereits wissen, wie man dieses Problem mit "dynamisch" lösen kann, aber ich poste die Lösung, die ich verwendet habe, unten, vielleicht können andere Leute sie verwenden.

Mit c# 4.0, haben Sie eine einfache Lösung für diese. Ich habe dieses Problem gelöst, indem ich "ge" NICHT als Typ "IGEPlugin" deklariert und alle Verweise auf GoogleEarth im Projekt entfernt habe. Stattdessen deklarieren Sie "ge" wie folgt:

private dynamic ge;

Jetzt können Sie JEDE Methode auf dem ge-Objekt aufrufen, ohne dass der Compiler den genauen Typ des Objekts kennen muss. Er wird zur Laufzeit aufgelöst. Sie müssen nur selbst sicherstellen, dass Sie den richtigen Methodennamen mit den richtigen Parametern aufrufen.

Wenn Sie Ihr Projekt ohne JEDEN Verweis auf GEPlugin kompilieren können, sind Sie auf dem richtigen Weg. Bei mir hat es für jede GoogleEarth-Plugin-Version funktioniert, sobald ich es wie folgt geändert habe.

Ich werde die Klasse veröffentlichen, die ich verwende, um das Plugin zu kapseln, das in ein Browser-Steuerelement eingebettet ist:

[ComVisible(true)]
public class GoogleEarthWebPluginHolder : IGoogleEarthJS {
    dynamic ge = null;
    GoogleEarthWebViewer parent;

    public GoogleEarthWebPluginHolder(GoogleEarthWebViewer parent) {
        this.parent = parent;
    }

    public dynamic Plugin {
        get {
            return ge;
        }
    }

    public void JSInitSuccessCallback_(object pluginInstance) {
        ge = (dynamic)pluginInstance;
        this.parent.JSInitSuccessCallback();
    }

    public void JSInitFailureCallback_(string error) {
        MessageBox.Show("Error: " + error, "Plugin Load Error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
    }
}

Beachten Sie, dass "parent" im Konstruktor das Steuerelement ist, das das Browser-Steuerelement und das Plugin beherbergt. Es greift auf den Plugin Holder zu, um mit GoogleEarth zu interagieren, sobald der JSInitSuccessCallback_ aufgerufen wurde. Die Schnittstelle für den Plugin Holder sieht wie folgt aus (ich bin mir nicht mehr sicher, warum ich sie brauche, aber hier ist sie trotzdem):

[ComVisible(true)]
interface IGoogleEarthJS {
    void JSInitSuccessCallback_(object pluginInstance);
    void JSInitFailureCallback_(string error);
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X