I know the basic premise for creating a plug ins for Unity is to create a wrapper for all the classes you want to call from within Unity. However, all of the docs and examples I have found have all expected a corresponding .cpp (if c++) or .mm (if objective-c) file.
However I'm trying to create a plug in for some native iOS code that doesn't contain any source files. All I have access to is four header files and a single .a file. Which I have gotten from here.
Now, because I don't have any .mm files to wrap I'm a bit confused as to how I can go about bringing in these files into Unity so that I can call them from within. Has anyone ever done this before?
Can someone point me to some documentation, or anything that may help me, in bringing in 4 header files and .a file into Unity as a plug in?
Please remember, there are no source files that I have access to. Only the header files themselves.
You don't need source files, even the header files are not needed as long as you know the functions' declarations.
As described in Building Plugins for iOS:
1.) Put your .a file in Assets/Plugins/iOS
2.) Look at the header files to get the function signatures you need e.g.
void RegisterUnityIPodCallbackListener (const char* gameObject, const char* method);
3.) Declare RegisterUnityIPodCallbackListener within a C# class by:
public class IPodHandler {
[DllImport("__Internal")]
private static extern void RegisterUnityIPodCallbackListener (string gameObject, string method);
public static void MyRegisterUnityIPodCallbackListener () {
if (Application.platform == RuntimePlatform.IPhonePlayer) {
RegisterUnityIPodCallbackListener (GAME_OBJECT, METHOD);
}
}
}
4.) Call at an appropriate location:
IPodHandler.MyRegisterUnityIPodCallbackListener ("MyCallbackGameObject", "MyCallbackMethod");
Common pitfalls:
You should not build directory structures within Assets/Plugins/iOS otherwise files don't get copied to the generated Xcode project
Provide a fallback solution when testing in editor player
Here is another guide: How to build Unity3d Plugin for iOS
A few notes if you get an error something like "Undefined symbols for architecture..."
1) Make sure the used architecture in .a-file matches yours (for example armv7)
2) Make sure the .a-file is compiled with libstdc++ (GNU C++ standard library) since Unity requires that.
The Estimote SDK you're are using has ObjectiveC classes. Functions in ObjectiveC can't be called directly via a Unity plugin (either can C++ classes for that matter). The reason is that function names are managled when you compile.
The interface between a Unity C# class and a library must a pure C interface. That is why there is often .mm or .cpp files along side a .a file. These are to wrap a C++ or ObjectiveC class in a pure C wrapper.
Your job is a little easier because most of the Estimote functions seem to be class functions. So you don't need to write wrapper functions to create and delete NSObjects.
Here is an example I wrote, a pure C wrapper around the TestFlightSDK. https://github.com/notoes/TestFlightUnity/blob/master/src/TestFlightCBinding.mm
Notice the extern "C" block, forcing the file the code to be compiled without name mangling. A c function signature and then an ObjectiveC call within the function.
So using the Esitmote ESTBeacon class as an example, the connectToBeacon call could like this:
extern "C" {
void ESTBeacom_Connect()
{
[ESTBeacon connectToBeacon];
}
}
And the .cs function would look like this:
class ESTBeacon {
[DllImport ("__Internal")]
private static extern void ESTBeacon_Connect();
public static void Connect() {
if( Application.platform == RuntimePlatform.IPhonePlayer )
ESTBeacon_Connect()
}
}
Put the .cs, .mm and .a files in Plugins/iOS and you'll be all good.
Use this reference to find out how to pass various data types. http://www.mono-project.com/Interop_with_Native_Libraries
Related
Recently I have been trying to get some Point Cloud Library functionality going in my .NET framework application, and considering that there is no completely functional wrapper for PCL for C#, I made my own for a few functions as a test. Something like this:
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr StatisticalOutlierFilter(IntPtr data, int length, int meanK = 50, float mulThresh = 1.0f);
Which calls a function from a C++ library, such as this:
EXPORT VectorXYZ* StatisticalOutlierFilter(VectorXYZ* data, int length, int meanK, float mulThresh) {
auto processedCloud = process.StatisticalOutlierFilter(data, length, meanK, mulThresh);
auto processedVector = convert.ToVectorXYZ(processedCloud);
return processedVector;
}
Where EXPORT is defined such for gcc:
#define EXPORT extern "C" __attribute__ ((visibility ("default")))
And relevant processing function from PCL is implemented such in a class (note that the returned is a boost shared pointer):
PointCloud<PointXYZ>::Ptr Processors::StatisticalOutlierFilter(VectorXYZ* data, int length, int meanK, float mulThresh) {
auto cloud = PrepareCloud(data, length);
PointCloud<PointXYZ>::Ptr cloud_filtered(new PointCloud<PointXYZ>);
StatisticalOutlierRemoval<PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(meanK);
sor.setStddevMulThresh(mulThresh);
sor.filter(*cloud_filtered);
return cloud_filtered;
}
This procedure works well with a dll built w/MSVC and running the whole thing on Windows, though the final target is gcc/Linux/Mono, where I get several errors of the following type (this is from mono debug):
'libavpcl_dll.so': '/usr/lib/libavpcl_dll.so: undefined symbol: _ZN3pcl7PCLBaseINS_8PointXYZEE13setInputCloudERKN5boost10shared_ptrIKNS_10PointCloudIS1_EEEE'.
I have investigated quite a bit so far, and have set my CmakeLists.txt to set(CMAKE_CXX_VISIBILITY_PRESET hidden) , therefore, I imagine, only functions I defined as EXPORT should be visible and imported - however, that is not the case, and I get the aforementioned errors. PCL was installed on Windows via vcpkg and on Xubuntu via apt. I am somewhat stumped as to what is the error source, considering the code runs well on windows, and builds without issue on Linux. Thanks.
I've been running into the same issue as you. I solved it by adding each reference library into the CMakeLists.txt file (I was missing the reference files which gave me the similar missing symbol issues).
I'm at the 'I don't know why this worked' stage but I can give you step by step implementation (I'm also trying to use DllImport into .NET on Linux).
Started with this:
https://medium.com/#xaviergeerinck/how-to-bind-c-code-with-dotnet-core-157a121c0aa6
Then added my in-scope files thanks to the main comment here: How to create a shared library with cmake?:
add_library(mylib SHARED
sources/animation.cpp
sources/buffers.cpp
[...]
)
run cmake .
run make -j$(grep -c ^processor /proc/cpuinfo)
copy path to .so file
DllImport path from above to my c# app
I wrote some wrapper code for an existing library (wiringPi) to read a temperature sensor but ended up with an error while consuming this library.
My wrapper lib looks like:
mylib.h
#ifndef mylib_h__
#define mylib_h__
extern void read_sensor();
#endif
mylib.c
#include "mylib.h"
#include <wiringPi.h>
void read_sensor() {
//here is the first call on the wiringPi lib
if (wiringPiSetup() == -1)
exit(1);
...
}
then i use gcc to compile my library:
gcc -Wall -Werror -fPIC -c mylib.c
gcc -shared -o libmylib.so mylib.o -lwiringPi
cp libmylib.so /usr/lib/
Hint: In case of a normal C program consumption of this library everything works fine.
Now there‘s my C# program which use PInvoke to call read_sensor() from this library:
Program.cs
class Program
{
[DllImport("wiringPi")]
static extern int wiringPiSetup();
[DllImport("mylib")]
static extern void read_sensor();
static void Main(string[] args)
{
wiringPiSetup();
read_sensor();
}
}
This program is compiled with the following arguments:
dontet publish -r linux-arm
and copied to my Raspberry-Pi.
Now i execute this C# program and the following error is thrown:
./my-program-name: symbol lookup error: /usr/lib/libmylib.so: undefined symbol: wiringPiSetup
What‘s going wrong here?
My first thought was, my program didn‘t know the wiringPi library. So i added an export for this dll and called wiringPiSetup() for testing. Same result with or without this statement.
I added also a test function without the wiringPi dependency into my custom library. This is called fine by C#.
Did i mess something up at linking time?
Edit:
The command ldd /usr/lib/libmylib.so gives this output:
linux-vdso.so.1 (0x7efad000)
/usr/lib/arm-linux-gnueabihf/libarmmem.so (0x76f73000)
libwiringPi.so => /usr/local/lib/libwiringPi.so (0x76f40000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76dff000)
libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0x76d84000)
libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0x76d5c000)
librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0x76d45000)
libcrypt.so.1 => /lib/arm-linux-gnueabihf/libcrypt.so.1 (0x76d05000)
/lib/ld-linux-armhf.so.3 (0x54abc000)
It comes down to name decoration. The C++ compiler doesn't just put the name of a function in the object file - it adds information to the name according to the function's definition (most notably its parameters).
From https://msdn.microsoft.com/en-us/library/56h2zst2.aspx
Functions, data, and objects in C and C++ programs are represented
internally by their decorated names. A decorated name is an encoded
string created by the compiler during compilation of an object, data,
or function definition. It records calling conventions, types,
function parameters and other information together with the name. This
name decoration, also known as name mangling, helps the linker find
the correct functions and objects when linking an executable.
But Compiled C code does not do this - name decorating (or name mangling) came in with C++.
So, you have to tell C# "this function's name is not decorated."
To do that use an attribute like this:
[DllImport("TestDll.dll", EntryPoint="myproc", ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
It's the "CallingConvention" bit that says "the function is a C function."
"Cdecl" means "C declaration", if I remember right.
More information can be found at: http://www.codeguru.com/csharp/csharp/cs_data/article.php/c4217/Calling-Unmanaged-Code-Part-1--simple-DLLImport.htm
I have a simple c++ Application. This Application is just printing text out.
I have also a c# .dll NET 3.5 which parses complex xml files, extracts values and saves them into a List. Its like 2 Classes with 4 methods. They open a file, parse the xml and store it into a List. When the c# .dll is done, it has a List with 10000 values;
Since i do not want to write the complex parsing XML in c++, i would like to use my c# xml parsing .dll.
Is it possible for me, to call my c# .dll from inside my c++ application, let the c# .dll parse a specified xml file, and return that created List, with the parsed xml values, to my c++ application?
In my c++ application i would proceed to modify the data within the returned list.
edit: i would be using vc++ (Microsoft Visual Studio 2010)
edit2: the vc++ app would be an expension/plug-in to another bigger Application. I would register the plug-in to that bigger application, and every time i press the icon in the menu, my vc++ application would be started
edit3: Has anyone experience with such a task? I kinda need a clear yes or no if it is possible.
edit4: i do want to avoid reading files that have been written by my .net .dll. I want my c++ app to send a string to my .net .dll and receive back a list/array object. Or is this a bad idea and i should do the xml parsing in c++ itself?
Yes, you can do it, but its kinda hard. One way to do it is to use C++ to load up CLR and execute your function, something like this:
code inspired by Blizzhackers.cc
void StartNET()
{
DWORD result;
ICLRRuntimeHost* pCLR = NULL;
CorBindToRuntimeEx(NULL, L"wks", NULL, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&pCLR);
pCLR->Start();
pCLR->ExecuteInDefaultAppDomain(L"C:\\myNET.dll", L"myNet.Program", L"Main", L"arg", &result);
pCLR->Stop();
}
This C++ code will execute the int Main(string arg) function from namespace myNet and class Program, by that I mean:
myNET.dll:
namespace myNet
{
class Program
{
int Main(string arg)
{
//and here you can run your XML parser:
List<string> myList = XMLParse();
FileStream fs = new Filestream("xmllist.txt");
StreamWriter sw = new StreamWriter(fs);
foreach(string s in myList)
sw.WriteLine(s);
sw.Close();
fs.Close();
return 1;
}
List<string> XMLParse()
{
//Your code here
return aList;
}
}
}
And after this you could use c++ to get the files from xmllist.txt, something like:
vector<char[]> getList()
{
vector<char[]> *myVector = new vector<char>;
ifstream cin("xmllist.txt");
while(!cin.eof())
{
char line[100];
cin >> line;
myVector.push(line);
}
cin.close();
return myVector;
}
I don't know if this last function works, but you get the general idea.
I created a managed/unmanaged dll in C++ some time ago. (Note though that C++.net has not the easiest syntax.)
Actually, I do not know where to start now, maybe this has the information http://channel9.msdn.com/Forums/TechOff/101918-Mixing-Managed-and-Unmanaged-C-in-a-DLL . It certainly was well possible with.. VS 2005? With that, you can have both managed an unmanaged code in one binary/assembly, and thus call the C# dll.
Apart from that, you can wrap your C# objects as COM objects. http://msdn.microsoft.com/en-us/library/ms404285.aspx . Then you can use COM interop.
In each case, marshalling the input/output would require some trial and error - it is not that obvious if you have never done it before.
Actually, this link should cover it all: http://msdn.microsoft.com/en-us/library/ms973872.aspx .
I have to replace an existing dll call that is registered and called using RegFn and CallFn respectively.
I am trying to write the dll using C# in the hope that as long as the function signature match and the dll is in the right place it will work.
so
pnHndl= RegFn("CALCULATE", "I", "I", "AJons.DLL")
pnRetVal = CallFn(pnHndl, 0)
My code is as follows:
[Guid("EAB7C2CD-2471-4BDA-90E9-F70403BAA557")]
[ComVisible(true)]
public class AJons : _AJons
{
[ComVisible(true)]
public int CALCULATE(int value)
{
return value * 2;
}
}
Foxpro doesn't play ball I just get 'could not load library AJon.dll'
Does anyone have any experience here?
Cheers.
From what I can see, those really old RegFn and CallFn are for calling Win32 native Dlls - completely different from COM.
What you need to create a Win32 dll that will work with those functions is C++.
What you should do (if you HAVE to keep using FoxPro) is at least use the latest version of VFP.
written on my iPhone
Update 1
1) Just in case I wasn't clear, you cannot make this kind of DLL from .NET.
2) Have a look at this link here for a very simple example of how to write a Win32 dll.
I am having trouble with using P/Invoke for C#. Here is the function (written in C++) that I am trying to call from the .dll:
string
BeatTracker::getName() const
{
return "Tempo and Beat Tracker";
}
And here is my code for trying to call this function:
[DllImport("qm-vamp-plugins.dll",EntryPoint="BeatTracker")]
public static extern string getName();
public QMTempo()
{
Console.WriteLine(getName());
}
What seems to be wrong? I am getting a BadImageFormatException. And how can I know what is wrong in future references aside from the vague names the IDE is giving me? I am using Visual Studio 2008 by the way.
Also I am using (but not sure if right) EntryPoint, to let it know that I am using the getName function from the BeatTracker class (because there are also getName functions for other classes, which are included in the single .dll file)
Thanks!
This exception can be caused by a mismath between the .NET runtime proc architecture used and the imported dll one.
More precisely:
Do you use a 64bit Windows? The runtime will, by default, run in 64bit. If your C++ library was compiled targeting 32bit, you will get a BadFormatException upon library loading. The Same goes if your .NET app is running 32bit and your C++ library was compiled targeting x64.
If you can recompile the library, do it. Otherwise you can force the .NET runtime to use a specified architecture at compilation, but it will prevent it from running on the other architecture. It's your choice ;) When coding against .NET or java, we tend to forget what really happen under the hood.
[DllImport("qm-vamp-plugins.dll",EntryPoint="BeatTracker")]
The EntryPoint should be getName(), not BeatTracker which is a class!
But even then you cannot call that, because getName() is member function which cannot be callled without instance.
So I would suggest that define free functions in the DLL, and export them. You can use class internally, in the DLL. You can work with handle of classes.
Example,
DLL code:
typedef BeatTracker* PBeatTracker;
typedef PBeatTracker HBeatTracker;
//exported functions
HBeatTracker CreateBeatTracker()
{
return new BeatTracker();
}
void DeleteBeatTracker(HBeatTracker handle)
{
delete handle;
}
string getName(HBeatTracker handle)
{
return handle->getName();
}
C# Code:
[DllImport("qm-vamp-plugins.dll",EntryPoint="CreateBeatTracker")]
public static extern IntPtr CreateBeatTracker();
[DllImport("qm-vamp-plugins.dll",EntryPoint="DeleteBeatTracker")]
public static extern void DeleteBeatTracker(IntPtr);
[DllImport("qm-vamp-plugins.dll",EntryPoint="getName")]
public static extern string getName(IntPtr);
public QMTempo()
{
IntPtr handle = CreateBeatTracker();
Console.WriteLine(getName(handle));
DeleteBeatTracker(handle);
}