Why do I get a spurious TypeLoadException "Method <foo> in type <bar> from assembly <baz> does not have an implementation"?

StackOverflow https://stackoverflow.com/questions/16323653

  •  13-04-2022
  •  | 
  •  

Question

I am upgrading an existing VS2008-based C++/CLI project (to be more specific, CefSharp) to VS2012 and am facing some challenges in the meanwhile. To avoid depending on VS2008 for compilation, I have upgraded the project to target the .NET Framework 4.0.

All is well and the project compiles correctly, but when I run it and try to create a WebView instance, I get the following TypeLoadException:

Method 'SetCursor' in type 'CefSharp.Wpf.WebView' from assembly 'CefSharp.Wpf, Version=1.25.0.35385, Culture=neutral, PublicKeyToken=null' does not have an implementation.":"CefSharp.Wpf.WebView"

Obviously, I've examined both the WebView.h and the WebView.cpp quite closely. Their content is (relevant parts):

public ref class WebView sealed : public ContentControl, IRenderWebBrowser
{
public:
    virtual void SetCursor(CefCursorHandle cursor);
}

And the implementation:

void WebView::SetCursor(CefCursorHandle cursor)
{
    SafeFileHandle^ handle = gcnew SafeFileHandle((IntPtr)cursor, false);
    Dispatcher->BeginInvoke(DispatcherPriority::Render,
        gcnew Action<SafeFileHandle^>(this, &WebView::SetCursor), handle);
}

CefCursorHandle is defined like this:

#define CefCursorHandle cef_cursor_handle_t

...and cef_cursor_handle_t is defined like this:

#define cef_cursor_handle_t HCURSOR

i.e. a windef.h structure.

The interface (IRenderWebBrowser) btw. defines the method like this.

public interface class IRenderWebBrowser : IWebBrowser
{
public:
    void SetCursor(CefCursorHandle cursor);
}

At one point, I thought the problem was related to "const" vs "non-const" issue, but as can be seen in the examples, there is no const specifiers involved. :-(

What is the obvious gotcha I'm missing here? I skimmed through this post but it didn't really help me.

I also thought about the possibility of multiple/old versions of the assemblies hanging around somewhere, so I tried bumping the assembly version just to be sure the right one was loaded - which it was. I've also looked with ILSpy on the assembly and the method is sure there. However, its method signature is this:

public unsafe void SetCursor(HICON__* cursor)

Could it be related to the unsafe part, or the fact that its parameter (HICON__*) is inaccessible from a C#/CLR context? (I'm trying to instantiate the type from a C# assembly.) I must admit that C++/CLI isn't really my area of expertise, yet... :-)

Anyone?

(If someone wants to look into the details more closely, feel free to clone/investigate the project at https://github.com/perlun/CefSharp/ - this stuff lives on the vs2012 branch.)

Was it helpful?

Solution 2

Let's hope this helps someone else... It turned out my suspicion was right, seemingly. Because the types are unmanaged (and inaccessible to the managed world), the C++ compiler creates the CefCursorHandle/HICON struct as an internal CLR struct within the assembly in which the WebView is declared. This makes the method essentially be uncallable (a public method with an internal parameter => impossible to call from C#), and the CLR appears to barf about this when the type is attempted to be instantiated from C#...

Changing the parameter type to IntPtr made the code work as intended. "void *" is also an option, and it can sometimes be easier to use on the C++ side.

(In theses cases, the methods don't need to be used no the C#/managed side so it's not a problem to just using IntPtr/void *. If they were actually being used, proper marshalling would be a better idea than this.)

OTHER TIPS

Followup: I tried to implement the IRenderWebBrowser interface in C#, just to experiment:

public unsafe class Foo : IRenderWebBrowser
{
    public void SetBuffer(int width, int height, void* buffer)
    {
        throw new NotImplementedException();
    }

    // ...and so forth.
}

Now, compiling this (after enabling the /unsafe flag) gives me a lot more insight into what (might) be causing this:

1>Y:\git\ataranto.CefSharp\CefSharp.Wpf.Example\MainWindow.xaml.cs(167,31,167,38): error CS0122: 'HICON__' is inaccessible due to its protection level
1>Y:\git\ataranto.CefSharp\CefSharp.Wpf.Example\MainWindow.xaml.cs(167,21,167,30): error CS0051: Inconsistent accessibility: parameter type 'HICON__*' is less accessible than method 'CefSharp.Wpf.Example.Foo.SetCursor(HICON__*)'
1>Y:\git\ataranto.CefSharp\CefSharp.Wpf.Example\MainWindow.xaml.cs(182,45,182,52): error CS0122: 'CefRect' is inaccessible due to its protection level
1>Y:\git\ataranto.CefSharp\CefSharp.Wpf.Example\MainWindow.xaml.cs(182,21,182,44): error CS0051: Inconsistent accessibility: parameter type 'CefRect*' is less accessible than method 'CefSharp.Wpf.Example.Foo.SetPopupSizeAndPosition(CefRect*)'

If HICON__ is inaccessible to the C#/managed world, it might be quite possible that the fact that these methods exist in the type make the CLR a bit "confused" to say the least, making the type impossible to instantiate from a managed context. Is there any way in C++/CLI to specify that the methods are to be public (to implement the interface in question), but "hidden from CLR" (to avoid stupid TypeLoadExceptions and the like)?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top