开发者

Can't get Type Equivalence to work for the Google Earth Plugin

开发者 https://www.devze.com 2023-03-17 14:55 出处:网络
I have a WinForms C# application that embeds the Google Earth(GE) Plugin. This is done via COM Interop, by adding a reference to the Google Earth Plugin.

I have a WinForms C# application that embeds the Google Earth(GE) Plugin. This is done via COM Interop, by adding a reference to the Google Earth Plugin.

What we'd like to be able to do is support different versions of the GE Plugin when we deploy our application. So we reference and build our app against version 5.0 of the GE Plugin on a develop machine, and then deploy to another machine that has version 6.0 of the GE Plugin installed, have the app run using type equivalence.

From my understanding of Type Embedding and Type Equivalence included in C# 4.0, this is exactly what Type Equivalence address. However, I have not been able to get this to work.

I add a reference to the Google Earth Plugin 5.2 in Visual Studio 2010 as a COM reference. I select the 'Embed Interop Types'

I then run the application on a machine with the 6.0 GE Plugin installed.

I get an exception running the following line of code

string pluginVersion = ge.getPluginVersion();

where ge is defined as

 private IGEPlugin ge;

and 'IGEPlugin' is an interface defined by the COM Interop for the GE Plugin DLL.

System.AccessViolationException was unhandled by user code Message=Attempted to read or write protected memory. This is often an indication that >other memory is corrupt.

The exception is different when reversed (build with 6.0, run against version 5.2)

The code and application work fine when built and ran against the same version of the GE Plugin.

So not sure what I am missing, from the MSDN documenta开发者_运维知识库tion it would appears that it should work automagically.

Type Equivalence and Embedded Interop Types

I was really hoping to avoid having to switch to the use of the C# 4.0 'dynamic' everywhere to get compatibility across GE Plugin versions, since I lose all the Intellisense, and would have to change a lot of code.


I struggled with this for a while. The reason it's not working with "Embed Interop Types" is that Google for some reason generates a completely new GUID for the COM-object, which means that the interop library which is embedded won't be functional anymore, even if the interface is exactly the same.

I just recently ran into this problem when the GoogleEarth plugin decided to auto-update itself, and thereby breaking the Interop-Compatibility by having a new COM-GUID.

I understand you don't want to use "dynamic" everywhere since you lose the IntelliSense - I would suggest you use an interop reference for the initial development so you have that in that phase, with static typing, and then switch to "dynamic" once it's stable enough and you just want to make sure it's still working with future versions of GoogleEarth without throwing a cast exception.

I realize you already know how to solve this with "dynamic", but I'm posting the solution I used below, maybe other people could use it.

With c# 4.0, you have an easy solution for this. I solved this problem by NOT declaring "ge" as type "IGEPlugin" and removing all references to GoogleEarth in the project. Instead, declare "ge" as following:

private dynamic ge;

Now you can call ANY method on the ge object, without the compiler having to know the exact type of it. It will be resolved at runtime. You just need to make sure yourself that you are calling the correct method name with the correct parameters.

If you can get your project to compile without ANY references to GEPlugin, you're on the right track, it worked for me for any GoogleEarth plugin version once I changed it like this.

I'll post the class I'm using to encapsulate the plugin which is embedded into a browser control:

[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);
    }
}

Note that "parent" in the constructor is the control hosting the browser-control and the plugin. It accesses the Plugin Holder to interact with GoogleEarth, as soon as the JSInitSuccessCallback_ got called. The Interface for the Plugin Holder looks like this (not sure anymore why I needed it, but here it goes anyway):

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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号