I'm having an issue building the Unity3d project in Xcode (to test on a device) with a Objective C plug-in I've made.
Here are the files:
The TestPlugin.h file:
#import <Foundation/Foundation.h>
#interface TestPlugin : NSObject
+(int)getGoodNumber;
#end
The TestPlugin.m file:
#import "TestPlugin.h"
#implementation TestPlugin
+(int)getGoodNumber
{
return 1111111;
}
#end
And finally the C# script in unity that's supposed to print out the value that getGoodNumber() returns:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class PluginTest : MonoBehaviour
{
[DllImport ("__Internal")]
public static extern int getGoodNumber();
void OnGUI()
{
string goodNum = getGoodNumber().ToString();
GUILayout.Label (goodNum);
}
}
As much as I can tell there shouldn't be any problem with the code. But even though I followed many different tutorials, when I try to compile I get an error in Xcode:
Undefined symbols for architecture armv7:
"_getGoodNumber", referenced from:
RegisterMonoModules() in RegisterMonoModules.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I tried a million different things and nothing seems to help. As much as I could read from other tutorials I do not need any special settings for Xcode and I can leave them same as it was for the Unity project without the plug-in.
I would also like to clarify a few things:
The plugin files are located in /Plugins/iOS/ folder in Unity3d
The build target was iPad Air 2 with the latest iOS, so no problem should arise there. Also using the newest Xcode on the newest OS X version.
I have the newest available version of Unity3d, and if that matters - I do have the Pro version of Unity3d.
The project works if the plugin is removed, so it's not a problem between Unity3d and Xcode.
I do not think I need to use extern "C" wrapper in Objective-C code as it's a ".m" file, not ".mm", so there shouldn't be a problem of name-mangling.
The plug-in was created in Xcode through the "Cocoa Touch Static Library" template. I was able to successfully build the Xcode project by itself before importing it to Unity3d.
If someone encountered such a problem as solved it, I would love to hear the solution.
You've written an "objective-c" class and method, but that can not be exposed to Unity. You need to create a "c" method (which could then call an objective-c method if needed).
Eg:
plugin.m:
long getGoodNumber() {
return 111;
}
Here is a fuller example that demonstrates out params to get a gyro.
Let's make a motion manager to get gyro (faked out for now). This would be standard objective-c:
MyMotionManager.h
#interface MyMotionManager : NSObject { }
+(MyMotionManager*) sharedManager;
-(void) getGyroXYZ:(float[])xyz;
#end
MyMotionManager.m:
#implementation MyMotionManager
+ (MyMotionManager*)sharedManager
{
static MyMotionManager *sharedManager = nil;
if( !sharedManager )
sharedManager = [[MyMotionManager alloc] init];
return sharedManager;
}
- (void) getGyroXYZ:(float[])xyz
{
// fake
xyz[0] = 0.5f;
xyz[1] = 0.5f;
xyz[2] = 0.5f;
}
#end
Now lets expose it via a C external reference (no need for extern as it's in a .m (not a .mm):
MyMotionManagerExterns.m:
#import "MyMotionManager.h"
void GetGyroXYZ(float xyz[])
{
[[MyMotionManager sharedManager] getGyroXYZ:xyz];
}
Finally, in Unity C# lets call it:
MotionPlugin.cs:
using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;
public class MotionPlugin
{
[DllImport("__Internal")]
private static extern void GetGyroXYZ(float[] xyz);
public static Vector3 GetGyro()
{
float [] xyz = {0, 0, 0};
GetGyroXYZ(xyz);
return new Vector3(xyz[0], xyz[1], xyz[2]);
}
}
Related
I'm trying to create a DLL exposing some static functions to use then in C.
Recently I read an article of Microsoft named "An Overview of Managed/Unmanaged Code Interoperability" and in this there is no a clear explanation on how to "Exposing a Managed API as a Flat API".
I installed this plugin to Visual Studio (https://www.nuget.org/packages/UnmanagedExports) but I still can't compile a project in C.
My C# project exposes a function like this:
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace libcallcstest
{
public class Class1
{
[DllExport("add", CallingConvention = CallingConvention.Cdecl)]
public static int add(int a, int b)
{
return a + b;
}
}
}
After building project, result these three files:
libcallcstest.dll
libcallcstest.pdb
libcallcstest.tlb
My C code is:
#include <stdio.h>
#include <stdlib.h>
int add(int, int);
int main(int argc, char** argv) {
int z = add(2,5);
printf("%d\n", z);
return (EXIT_SUCCESS);
}
And finally when I try to compile this file with:
gcc -o main.exe main.c -lcallcstest
Not work properly, files created by building the C# project are in the same folder as the main.c file.
Pleas any help!!!
One way to go: you may want to host CLR in your process. I would recommend against it though, because hosting is not the easiest procedure out there.
Also it's often not really needed or you can use some slower methods to communicate with .Net code from unmanaged environment (for example, present your library as a local server and access it through network interfaces. As I see it that way you'll have ten times less work to do).
Or you could go with your original variant using utilities to help you like mentioned here.
I want to bind an objective c library (For using a cable) in xamarin. I am new to xamarin platform, Can anyone help me to convert the below .h file to "ApiDefinition.cs" in Xamarin binding project.
#import <UIKit/UIKit.h>
#ifndef CABLE_
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#protocol CableManagerDelegate;
/*
This protocol), describes the main interface to the Cable Socket Manager layer.
To use, call factory method below [CableManager sharedInstance]
*/
#protocol CableManagerProtocol <NSObject>
// set delegate for cable connect callbacks
-(void)setDelegate:(id < CableManagerDelegate >) delegate;
-(BOOL)isCableConnected;
-(NSString *)getAccessoryFirmwareVersion;
#end
#protocol CableManagerDelegate <NSObject>
//Cable was connected
- (void) cableConnected:(NSString *)protocol;
// Cable was disconnected and/or application moved to background
- (void) cableDisconnected;
#end
#interface CableManager : NSObject
+ (id < CableManagerProtocol >)sharedInstance;
#end
Can't write it for you, but the sample below is the general pattern which you can learn more about in the guide linked below. One of your key challenges is going to be making sure the callback on the delegate fires. To Map this, check out the "Binding Protocols" section.
http://developer.xamarin.com/guides/ios/advanced_topics/binding_objective-c/binding_objc_libs/
ApiDefinition.cs
using MonoTouch.ObjCRuntime;
using MonoTouch.Foundation;
using System;
namespace MyNamespace
{
[BaseType (typeof (NSObject))]
interface MyObjCWrapper
{
[Export("initWithArg1:arg2:arg3:")]
void Constructor(string first, string second, string third);
[Export("mySelectorTaking1arg:")] // note colon, takes 1 arg
void DoSomethingWith1Arg(string filePath);
[Export("getSomething")] // note no colon, takes 0 args
int GetSomething();
}
To complete this binding, you should add the native library to the
project. You can do this by adding the native library to your project,
either by dragging and dropping the native library from Finder onto
the project in the solution explorer, or by right-clicking the project
and choosing Add > Add Files to select the native library. Native
libraries by convention start with the word "lib" and end with the
extension ".a". When you do this, Xamarin Studio will add two files:
the .a file and an automatically populated C# file that contains
information about what the native library contains:
You will end up with a file like this (libLibraryName.linkwith.cs):
using System;
using MonoTouch.ObjCRuntime;
[assembly: LinkWith ("libLibraryName.a", SmartLink = true, ForceLoad = true)]
Use Objective-Sharpie. It will do the initial bulk of work for you and you just need to fill in the gaps...
I am working on WP8 project that includes class library project as C# source code and Windows Runtime Component as C++ source code. Does anyone know whether or not it is possible to create such C# class library which would reference Windows Runtime Component? The ultimate result should be .NET assembly and .WIMND/.DLL runtime component that can be used for application. Currently I cannot build class library because it doesn't see Windows Runtime Component, even though I added it to the project.
More specific. I have, say, MyNs.MyClass.MyMethod() which is defined in C++ runtime component and used from C# class library. Currently I cannot compile C# due to missing method although I have windows runtime component project attached to the same solution.
Although I am butting in because this is not my area, I tried Googling for "c# call windows runtime component". There seem to be many hits/examples, e.g. the first one is https://msdn.microsoft.com/en-us/library/hh755833.aspx.
Does that not help you?
I solved this by adding reference to Windows runtime component manually into the C# class library .csproj file as follows
...
<ItemGroup>
<Reference Include="WindowsRuntimeComponent.winmd" />
</ItemGroup>
...
I managed to make a C++ WRL project and use a class in that project from a C# project by adding a reference in the normal way. The Wrl project (not C++/CX, which also works) was made using some WRL template that I found somewhere on the web. The wrl project required me to make a .idl to define the interface, and produced its .dll and .winmd. Here is some code for those who are battling with this type of thing:
The Wrl class:
#include "pch.h"
#include "WrlTestClass2_h.h"
#include <wrl.h>
using namespace Microsoft::WRL;
using namespace Windows::Foundation;
namespace ABI
{
namespace WrlTestClass2
{
class WinRTClass: public RuntimeClass<IWinRTClass>
{
InspectableClass(RuntimeClass_WrlTestClass2_WinRTClass, BaseTrust)
public:
WinRTClass()
{
}
// http://msdn.microsoft.com/en-us/library/jj155856.aspx
// Walkthrough: Creating a Basic Windows Runtime Component Using WRL
HRESULT __stdcall Add(_In_ int a, _In_ int b, _Out_ int* value)
{
if (value == nullptr)
{
return E_POINTER;
}
*value = a + b;
return S_OK;
}
};
ActivatableClass(WinRTClass);
}
}
The C# code that uses this class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
namespace CSharpClientToWrl
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
WrlTestClass2.WinRTClass _winRtTestClass = new WrlTestClass2.WinRTClass();
int _answer = _winRtTestClass.Add(4, 6);
Assert.AreEqual(_answer, 10);
}
}
}
The .idl file of the wrl project:
import "inspectable.idl"; import "Windows.Foundation.idl";
#define COMPONENT_VERSION 1.0
namespace WrlTestClass2 {
interface IWinRTClass;
runtimeclass WinRTClass;
[uuid(0be9429f-2c7a-40e8-bb0a-85bcb1749367), version(COMPONENT_VERSION)]
interface IWinRTClass : IInspectable
{ // http://msdn.microsoft.com/en-us/library/jj155856.aspx // Walkthrough: Creating a Basic Windows Runtime Component Using WRL HRESULT Add([in] int a, [in] int b, [out, retval] int* value);
}
[version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)]
runtimeclass WinRTClass
{
[default] interface IWinRTClass;
} }
I'm new to unity3D and C# (and IOS :), but need to get things working with a legacy C library (.h & .a files) on iPhone. I've read some about Plugins from unity documentation, but still feel overwhelmed by the complicated procedures. Could any guru show me the correct way out of the mess? Thanks!
Go through this link. This gives the idea for making plugins for iOS. If any doubt, ask.
A practical example for plugin
1) Make a C# file called AppControllerBinding.cs in the plugins folder in Unity and add the code as followed:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
// All Objective-C exposed methods should be bound here
public class AppControllerBinding
{
[DllImport("__Internal")]
private static extern void _MyFunction(string myName);
public static void MyFunction(string MyNameIN)
{
// Call plugin only when running on real device
if( Application.platform == RuntimePlatform.IPhonePlayer )
_MyFunction(MyNameIN);
}
}
2) Add the MyFunction() function to the bottom on the AppController.mm file inside Xcode as follows (after the #end statement):
extern "C"
{
void _MyFunction(const char* MyName)
{
NSString* s = [NSString stringWithUTF8String: MyName];
[Self logName:s]; //<-----logName is method which takes string parameter
printf_console("_MyFunction() called in Appcontroller.mm in Xcode.\n");
}
}
3) When you want to use the logName function of AppController.mm inside Unity just make a call like this:
AppControllerBinding.MyFunction("Nick");
I have simple static lib in xcode with the only class
test.h:
#interface TestClass : NSObject {
NSString *SomeString;
}
#property(nonatomic, readwrite, copy) NSString *SomeString;
- (NSString *) getString;
- (int) getInt;
#end
test.m:
#implementation TestClass
#synthesize SomeString;
- (id)init
{
if ((self = [super init]) == nil)
return nil;
SomeString = #"test string value";
return self;
}
- (NSString *) getString {
return #"Lorem ipsum dolor sit amet";
}
- (int) getInt {
return 123;
}
#end
I've copied TstClass from the dll generated by btouch. if i'm using original dll's implementation:
[Register ("TestClass", true)]
public class TstClass : NSObject
variables Handle, ClassHandle etc are nulls, but app runs and returns nulls as a getInt, getString and SomeString. if I change definition to
[Register ("TestClass")]
public class TstClass : NSObject
inner variables are valid (meaning not null), but app crashes with no output when I'm trying to call a TstClass's function.
During my research I've found someone has fixed this by unchecking "thumb" option in XCode build setting, but I can not find anything that looks like thumb in xcode project. (just in case: XCode 4.2 build 4C199; and I'm using latest version of Monotouch)
How do I create static lib in xcode and use it with monotouch? what's wrong with my code?
and the last question: I have a .a and its .h files. is there an easier way to generate bindings from library and header files?
The easiest path to bind your library is to use the binding generator (this is the "btouch" tool which is part of MonoTouch):
A detailed document is here:
http://docs.xamarin.com/ios/advanced_topics/binding_objective-c_types
The reason your variables are null is that you did not properly initialize the library (ClassHandle is a virtual method that should return the return value from Class.GetHandle ("ClassName") in this case).
Use the following contract file with btouch to generate a proper binding:
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
namespace AlexD {
[BaseType (typeof (NSObject))]
interface TestClass {
[Export ("SomeString")] string SomeString { get; set; }
[Export ("getString")] string GetString ();
[Export ("getInt")] int GetInt ();
}
}
Save that into AlexD.cs and then run:
/Developer/MonoTouch/usr/bin/mtouch AlexD.cs
This will generate AlexD.dll that contains your binding to your native library. You can use this plus the proper set of command line arguments to mtouch to access your library.
You can also bundle your native library inside the DLL, to simplify distribution (single .dll will contain both the C# binding and the native library), for details on how to do this, see:
http://docs.xamarin.com/ios/advanced_topics/binding_objective-c_types#Linking_the_Dependencies
it took some time to figure out how to make it work.
Make sure lib and app target architectures are the same. (I had armv7 for lib and armv6 for app)
Make sure build targets are the same (was iOS device for lib and iOS simulator for app. iOS simulator target sets architecture to i386, which makes lib and app build incompatible)
for some reason additional mtouch arguments requires "-cxx -gcc_flags" and not just "-gcc_flags";
for some reason .a lib was not linked without "-force_load" argument;
add "-v -v -v" to additional mtouch arguments to see full build log. That helped a lot to find this solution.
Thumb instructions set must be disabled (fixed by adding User-defined project option GCC_THUMB_SUPPORT:NO)
that's it!