Exception - the native class hasn't been loaded

JakiToriJakiTori USMember
edited September 2013 in Xamarin.Mac

I am receiving the following error when trying to run my MonoMac app using a 'wrapped' (bound/linked?) Objective C library.

Could not create an native instance of the type 'MacVolumeCtrl.MacVolumeControl': the native class hasn't been loaded.
It is possible to ignore this condition by setting MonoMac.ObjCRuntime.Class.ThrowOnInitFailure to false.

I am using MonoMac which for some reason doesn't have binder projects (unlike IOS) so I have to use bmac.exe, this is the command;

mono bmac.exe bound.cs -r:System.Drawing.dll

This is bound.cs;

`
using System;
using System.Drawing;
using MonoMac.ObjCRuntime;
using MonoMac.Foundation;

namespace MacVolumeCtrl 
{

    [BaseType (typeof (NSObject))]
    public partial interface MacVolumeControl {

        [Export ("Cwlog")]
        void Cwlog (string x);

        [Export ("Cwlog")]
        void Cwlog (string x, string result);

        [Export ("Run")]
        int Run { get; }
    }
}`

It compiles just fine and referencing the generated dll works fine as well, until I actually try to create an object of the class. Again, I can't use an IOS binder project so I don't really see how this loads the real objective C lib (is it embedded?) but any threads I find on this issue (like this one) are IOS specific so despite the 6 hours I spent on this already; I don't know what to try next. Hope you can help.

Posts

  • rjmrjm CAMember ✭✭✭

    You are responsible for shipping the native library with your bundle.

    This link shows you how to deploy and load the native library.

  • JakiToriJakiTori USMember

    Thanks for your quick reply, but unfortunately I'm still receiving the same error. I made an archive with minimalistic stuff so hopefully someone can reproduce the issue.

    http://we.tl/h1Z2CkWjFU

    There are absolute paths in the following that need to be altered for anyone trying to use it;

    1. binder/compile.sh
    2. The post-build command in the xamarin project (to copy the lib)

    Also don't forget to copy the compiled .a file to the 'xcode' directory if you change it.

    I'm trying to convince my boss Mono is an awesome solution to our cross platform woes so I'd greatly appreciate it if someone can help me figure this out.

  • rjmrjm CAMember ✭✭✭

    I haven't opened your project, but I can tell you that an .a file won't work

    Monomac only supports static libraries (.dylib) and frameworks.

    Compile your native stuff into a static library, and make sure it has the architecture for x86/i386.

  • JakiToriJakiTori USMember
    edited September 2013

    But .a is static and .dylib is dynamic (hence the dy) so am I to understand Monomac only supports dynamic libraries? Which is fine btw but that would explain my confusion.

    EDIT: I tried with a dynamic lib but the same issue occurs. I can't build for x86 though because I get the error '-fobjc-arc is not supported on platforms using the legacy runtime'. Building the monomac project for x64 to match the lib architecture doesn't change the issue though.

    EDIT2: Ok, turns out hat was the issue. I disabled automatic reference counting and set both projects to x86. I'm not sure what ARC does (I'm not a mac developer) but I assume it's important.. what do I do when I need to bind a lib that uses it?

  • rjmrjm CAMember ✭✭✭

    Sorry my bad, I got them backwards. Yes - monomac only supports dynamic libraries - dylib or frameworks.

    Currently monomac only has a 32bit runtime. Changing your monomac project to x64 won't affect anything. Because of that it can only load 32bit native frameworks. I know that a x64 runtime is on their roadmap but I don't know when.

    ARC is only supported on the x64 Cocoa runtime. ARC is essentially a compile time process that uses static analysis to insert the proper memory management constructs into your code. By disabling ARC, your code is now missing the proper retain/release statements and almost certainly leaking memory all over the place. You will need to fix the code to proper manage memory manually. Try using the 'Analyze' function in XCode to get an idea of the scope of the changes needed.

  • JakiToriJakiTori USMember
    edited September 2013

    Whoa, that's very bad news. For me mono is a win productivity wise even with the manual memory management but it's going to make it that much harder to convince my boss ;(

    Anyway, I got it to work but only for methods that don't take a parameter.. as soon as I add one, I get an exception; unrecognized selector sent to instance

    The binding script

    namespace MacVolumeCtrl
    {
    [BaseType (typeof (NSObject))]
    public partial interface MacVolumeControl {
    
        [Export ("SetVolume")]
        int SetVolume (float volume);
    }
    }
    

    The header

    #import <Foundation/Foundation.h>
    
    @interface MacVolumeControl : NSObject
    
    - (int)SetVolume: (float)volume;
    
    @end
    

    The code

    #import "MacVolumeControl.h"
    #include <AvailabilityMacros.h>
    #include <CoreAudio/CoreAudioTypes.h>
    #include <CoreFoundation/CoreFoundation.h>
    #import <AudioToolbox/AudioServices.h>
    
    @implementation MacVolumeControl
    
    - (int)SetVolume: (float)volume
    {
        // blablabla
        return 1;
    }
    
    @end
    

    This code works fine when called from a normal objective C app, but not when called from mono. If I use the exact same code, but take out the parameter, it works fine all of a sudden... any ideas maybe?

    Full error:

    2013-09-17 22:54:19.683 MyProj.Ui.Mac[3640:1007] -[MacVolumeControl SetVolume:volume]: unrecognized selector sent to instance 0x5f3a9d0
    2013-09-17 22:54:19.686 MyProj.Ui.Mac[3640:1007] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MacVolumeControl SetVolume:volume]: unrecognized selector sent to instance 0x5f3a9d0'
    *** Call stack at first throw:
    (
        0   CoreFoundation                      0x98e1fe9b __raiseError + 219
        1   libobjc.A.dylib                     0x9a1ef52e objc_exception_throw + 230
        2   CoreFoundation                      0x98e23b0d -[NSObject(NSObject) doesNotRecognizeSelector:] + 253
        3   CoreFoundation                      0x98d6be97 ___forwarding___ + 487
        4   CoreFoundation                      0x98d6bc42 _CF_forwarding_prep_0 + 50
        5   ???                                 0x05e4b413 0x0 + 98874387
        6   ???                                 0x05e4b314 0x0 + 98874132
        7   ???                                 0x000d80d8 0x0 + 884952
        8   ???                                 0x000d82bf 0x0 + 885439
        9   libmono-2.0.dylib                   0x0040dc05 mono_jit_runtime_invoke + 741
        10  libmono-2.0.dylib                   0x005c49ae mono_runtime_invoke + 126
        11  libmono-2.0.dylib                   0x005c8dc4 mono_runtime_exec_main + 420
        12  libmono-2.0.dylib                   0x005c9135 mono_runtime_run_main + 725
        13  libmono-2.0.dylib                   0x00478685 mono_jit_exec + 149
        14  libmono-2.0.dylib                   0x0047abbf mono_main + 9519
        15  MyProj.Ui.Mac            0x0000308f main + 2047
        16  MyProj.Ui.Mac            0x00002885 start + 53
    )
    

    C# code

    var baseAppPath = Directory.GetParent(Directory.GetParent(System.AppDomain.CurrentDomain.BaseDirectory).ToString());
    var libPath = baseAppPath + "/Frameworks/MacVolDy.dylib";
    Dlfcn.dlopen(libPath, 0);
    
    var x = new MacVolumeCtrl.MacVolumeControl ();
    x.SetVolume (10f);
    
    x.SetVolume (10f);
    
  • rjmrjm CAMember ✭✭✭

    You need the colon (':') in your SetVolume binding, e.g.

    [Export ("SetVolume:")]
    
  • JakiToriJakiTori USMember

    lol can't believe how many hours I wasted on that one. Thanks buddy, how do I accept an answer on this forum?

  • rjmrjm CAMember ✭✭✭

    The Q&A function of the forum is a bit weird... When you create your post, you either create a 'New Thread' or 'Ask a Question'. 'Ask a Question' is buried inside a drop down, so most people don't see it.

    Only 'Questions' can be answered. However, I honestly don't think it matters that much, it's not like Stack Overflow :)

Sign In or Register to comment.