MonoMac / Xamarin.Mac binding simple dylib not working - mono

I'm trying to bind some native code for use in MonoMac / Xamarin.Mac, but I'm not sure where I'm going wrong. I create a simple dylib to test with:
nativelibrary.h:
- (NSString *)echo:(NSString *)message;
I know that my library is fine, because I reference it and use it in an Objective-C / Cocoa application.
Next, I try to generate the initial binding file using parse.exe:
mono parse.exe [path...]/nativelibrary.h
Problem #1
No 'gen.cs' file is generated as per Miguel's guide
Problem #2
Parse.exe does actually output something to the console, although it's missing my only method?
[BaseType (typeof (NSObject))]
interface nativelibrary {
}
Regardless, I go ahead and make my own gen.cs file, filling in the missing method manually:
gen.cs:
using MonoMac.Foundation;
namespace ManagedConsumer
{
[BaseType (typeof (NSObject))]
interface Binding
{
[Export ("echo:")]
string Echo(string message);
// I also tried like this:
// NSString Echo(NSString message);
}
}
Next, I try to create my binding DLL using bmac.exe:
mono bmac.exe -o="dynamiclibrary.dll" -d="MONOMAC" -r="System.Drawing" -v [path].../gen.cs
This spits out a .dll which I reference in my MonoMac project.
Finally, I add the .dylib itself to my MonoMac project, and specify the 'content' build action. I verify that the .dylib is copied to the 'Resources' directory of my bundle.
I can instantiate an instance of my binding object no problem:
Binding b = new Binding();
Console.WriteLine(b.ToString());
Problem 3 However, trying to call my method:
Binding b = new Binding();
var result = b.Echo((NSString)"Hello, world");
results in an unmanaged crash:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00000000bf74bffc
I have seen in another question, that we need to force the .dylib to load. So I try to insert this line into my main.cs, before Application.Init() is called:
Dlfcn.dlopen ("nativelibrary.dylib", 0);
But I get the same crash. Since the call to dlopen returns 0 rather than a valid pointer, I assume that the issue is in loading my dynamic library. I also tried to use the attribute:
[assembly:MonoMac.RequiredFramework("nativelibrary.dylib")]
But that only gets me:
System.Exception: Unable to load required framework: 'nativelibrary.dylib'
What am I doing wrong?

After a lot of trial and error, I was able to make this work. Two changes:
In my homebrew gen.cs file, the interface name needed to match the name of my native class, i.e.
nativelibrary.h
#interface nativelibrary : NSObject
- (NSString *)echo:(NSString *)message;
gen.cs
using MonoMac.Foundation;
namespace ManagedConsumer
{
[BaseType (typeof (NSObject))]
interface nativelibrary
{
[Export ("echo:")]
string Echo(string message);
}
}
Secondly, it seems there was something about my native library itself that means it couldn't be opened with dlopen. I think the problem is that the XCode 'library' project defaults to x64, and it appears only x86 will work.
I compiled it from the command line instead, like so:
gcc -arch i386 -framework Cocoa -o nativelibrary.o -c [path...]/nativelibrary.m
Then built my library:
libtool -dynamic -flat_namespace -lSystem -undefined suppress -macosx_version_min 10.6 -install_name $CURRENT_DIR/nativelibrary.dylib -o nativelibrary.dylib nativelibrary.o
And it now works.

Related

New (.NET Core) C++/CLI project has compile issues when definition and implementation are split

I created a "CLR Empty Project (.Net Core)" project using Visual Studio 2019.
I simply created a new class using the 'add class' menu option and it generated this class.
I added the function called Test in the header:
using namespace System;
public ref class Class1 {
// TODO: Add your methods for this class here.
void Test();
};
Then using visual studio's generate implementation option it created this function defenition in the .cpp file:
#include "EngineEditorLayer.h"
#include "pch.h"
void Class1::Test() { throw gcnew System::NotImplementedException(); }
When compiling it gave me this error :
error C2653: 'Class1': is not a class or namespace name
I can only resolve this error by moving the implementation to the header file.
Am I missing something? Is there a setting I have to change to enable .cpp compilation?
Is there a compiler bug that prevents me from doing this currently?
Hans Passant from the comments was right. The pch.h has to be included first.
I unfortunately never got the warning. This was my output log:
1>------ Build started: Project: EngineEditorLayer, Configuration: Debug x64 ------
1>EngineEditorLayer.cpp
1>E:\Other Projects\PixEngine\EngineEditorLayer\EngineEditorLayer.cpp(7,18): error C2653: 'Class1': is not a class or namespace name
1>Done building project "EngineEditorLayer.vcxproj" -- FAILED.

Realm throws exception with empty unit test

In an Objective-C project, we started writing our new Unit Tests in Swift. I'm just now trying to create our first Unit Test of successfully saving the results of a parsed JSON. However, the test already fails during setup() due to the following error:
[ProjectTests.Project testInitializingOverlayCollectionCreatesAppropriateRealmObjects] : failed: caught "NSInvalidArgumentException", "+[RLMObjectBase ignoredProperties]: unrecognized selector sent to class 0x759b70
So apparently it tries to execute ignoredProperties on the RLMObjectBase class, and that method isn't implemented yet. Not sure how this happens, because I have yet to initialise anything, beyond creating a RLMRealms object with a random in-memory identifier.
ProjectTests.swift
import XCTest
class ProjectOverlayCollectionTests: XCTestCase {
var realm: RLMRealm!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
let realmConfig = RLMRealmConfiguration()
realmConfig.inMemoryIdentifier = NSUUID().UUIDString
do {
realm = try RLMRealm(configuration: realmConfig) // <-- Crashes here.
}
catch _ as NSError {
XCTFail()
}
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testInitializingOverlayCollectionCreatesAppropriateRealmObjects() {
XCTAssertTrue(true)
}
}
Project-Bridging-Header.h
#import <Realm/Realm.h>
Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.1'
def shared_pods
pod 'Realm', '0.95.0'
end
target 'Project' do
shared_pods
end
target 'ProjectTests' do
shared_pods
end
As mentioned in the Realm documentation;
Avoid Linking Realm and Tested Code in Test Target
Remove the Realm pod from the ProjectTests target and all is right with the world.
Update: This answer is outdated. As #rommex mentions in a comment, following the current Realm installation documentation should link it to both your module and test targets without problems. However, I have not checked this.

What does compilationOptions.emitEntryPoint mean?

Just installed the rc1 tools and created a new web project to see what has changed in the template.
I noticed that project.json now contains:
"compilationOptions": {
"emitEntryPoint": true
}
But it's unclear what this does.
Does anyone have an idea?
As mentioned below: It looks like it is a flag to the compiler to indicate that the project is a console application vs. a library (namely: a console application must contain public static void Main())
You can see from the source here.
In the new RC1 default web application template, you'll notice at the bottom of Startup.cs there is a new expression bodied method that acts as the entry point:
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
If you remove this method then perform a build (dnu build) you will get an error:
error CS5001: Program does not contain a static 'Main' method suitable for an entry point
However, if you change the emitEntryPoint flag to false and attempt to build again, it will succeed. This is because it is creating a library instead of a console app.
I see this in the source;
var outputKind = compilerOptions.EmitEntryPoint.GetValueOrDefault() ?
OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary;
Looks like it tells the compiler whether to create a Console Application or a Library.
Additionaly, if you create a new Class Library (Package) and Console Application (Package) in VS2015 you'll see that project.json for the Console Application includes the following, while the Class Library does not;
"compilationOptions": {
"emitEntryPoint": true
}

MonoDevelop debugging error

I've recently changed to Ubuntu 11.10 (from Windows Vista).I use mono (2.8.5) as my IDE (C# VS.net before) to program.
As a quick test I want to make a console program with the following code:
using System;
namespace DeleteMe1
{
class MainClass
{
public static void Main (string[] args)
{
Console.WriteLine ("Hello World!");
Console.ReadKey ();
}
}
}
If I run the program I see a screen appear and disappear (very quickly). When I debug I get the following error:
Could not open port for debugger. Another process may be using the port.
Is someone familiar with these messages? I've already tried the following:
https://stackoverflow.com/questions/8...using-the-port
But the property key (Property key="MonoTouch.Debugger.Port" value="10000") is not available.
Whats wrong?
Thanks in advance and with best regards.

How to suppress 'Maybe this is program method' warnings from ProGuard

I'm using ProGuard with my Android application and I'm running getting the warnings below in my build log. I've added the appropriate '-keep public class com.foo.OtherClass { public static *; }' statement to my proguard.cfg file, but I still get the warnings. My app runs fine and is dynamically accessing the class correctly. Is it possible to suppress these warnings?
[proguard] Note: com.foo.MyClass accesses a method 'getInstance()' dynamically
[proguard] Maybe this is program method 'com.foo.OtherClass { com.foo.OtherClass getInstance(); }'
You can avoid it by explicitly mentioning the method in the configuration:
-keep class com.foo.OtherClass { com.foo.OtherClass getInstance(); }
Alternatively, you can suppress notes on a class:
-dontnote com.foo.MyClass
You suppress all messages of type Note by adding the following line:
-dontnote **