我想使用更大的C#代码作为Objective-C(Cocoa)应用程序的库。
我发现包装Cocoa代码的MonoMac项目,但我宁愿用Objective-C编写标准的Cocoa应用程序,它可以调用包装的C#代码(反过来)。
在Windows上我习惯于制作包含.NET代码的C ++ / CLI项目,并为基于C / C ++的应用程序导出普通的旧C接口。
有没有一些简单的方法来实现这个目标?
答案 0 :(得分:16)
显然,在Mac OS上没有C ++ / CLI这样的语言。在Windows上,C ++ / CLI实际上编译为CLR运行的托管代码,运行本机代码;因为在Mac OS Mono上没有集成到系统中,所以它恰恰相反。您的应用是原生的,可以托管托管代码。
Mono公开了在进程内托管CLR虚拟机的函数。由于CLR类没有直接暴露给您的C代码,因此您可以通过类似反射的调用来调用对象的方法。
官方网站上有documentation on how to embed Mono into an application。由于您对running .NET programs directly不感兴趣,因此您应该阅读"Invoking Methods in the CIL Universe"部分。在Mac OS上,您需要从/Library/Frameworks
文件夹中链接Mono框架,而不是使用pkg-config
。
这实际上不应取代上述文件的实际阅读,但以下内容可视为预期内容的指南:
#include <glib/glib.h>
#include <mono/jit/jit.h>
#include <mono-metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
// create an app domain
// http://en.wikipedia.org/wiki/Application_Domain
MonoDomain* domain = mono_jit_init("Domain");
// mandatory Cocoa call to show that Mono and ObjC work together
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* dll = [mainBundle pathForResource:@"your-dll" ofType:@"dll"];
// load the referenced assembly in our domain
MonoAssembly* assembly = mono_domain_assembly_open(domain, [dll UTF8String]);
MonoImage* image = mono_assembly_get_image(assembly);
// find the class we want to wrap and create an uninitialized instance
MonoClass* classHandle = mono_class_from_name(image, "Name.Space", "YourClass");
MonoObject* object = mono_object_new(domain, classHandle);
// this calls the default, argument-less ctor
// for more complex constructors, you need to find the method handle and call it
// (helpful hint: constructors are internally called ".ctor", so the description
// string will look like "Namespace.Class..ctor()")
mono_runtime_object_init(object);
// get a method handle to whatever you like
const char* descAsString = "Your.NameSpace.YourClass:YourMethod()";
MonoMethodDesc* description = mono_method_desc_new(descAsString);
MonoMethod* method = mono_method_desc_search_in_class(description, classHandle);
// call it
void* args[0];
mono_runtime_invoke(method, object, args, NULL);
// when you're done, shutdown the runtime by destroying the app domain
mono_jit_cleanup(domain);
如果你没有发现这个非常有吸引力,你可能想要反过来,如你所提到的那样,并查看MonoMac,它提供了对大部分API的.NET绑定。想要在Mac应用程序(Cocoa,CoreImage,CoreAnimation等)中使用,并且意味着创建自己的绑定。
答案 1 :(得分:3)
如果你在Mac上这样做,那么是的,这是可能的。在iOS上没那么多。
在Mac上,如果您可以在MonoMac中创建CLI应用程序,则可以使用NSTask从Objective-C应用程序中调用CLI应用程序。 NSTask允许您轻松启动命令行工具,然后捕获它的输出并与之交互。要做到这一点,你可以这样做:
NSArray *args = [NSArray arrayWithObjects:@"-arg1", @"-arg2", nil];
NSTask *foo = [[NSTask alloc] init];
[foo setLaunchPath:@"/usr/bin/foo"];
[foo setArguments:args];
NSPipe *pipe = [NSPipe pipe];
[foo setStandardOutput:pipe];
NSFileHandle *output = [pipe fileHandleForReading];
[foo launch];
NSData *data = [output readDataToEndOfFile];
NSString *outputStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Got stuff: %@", outputStr);
通常,您希望这样做的方式是在应用包中包含CLI应用。然后,您可以使用NSBundles -pathForResource:ofType:方法获取CLI应用程序的路径。
iOS不包含NSTask API,因此无法实现。我听说有些人使用MonoTouch使用C#制作iOS应用程序,但正如您所建议的那样,我认为如果可能的话,我最好坚持使用Objective-C。使用像你描述的CLI应用程序肯定是Mac上的一个选项,当你拥有一组已经编写和测试过的代码并且你只想用Cocoa GUI“Wrap”工作时,它会特别有用。
因此,NSTask是使用应用程序包中包含的外部CLI可执行文件执行此操作的一种方法。另一方面,您可能想知道,您可以将C#代码直接链接到Objective-C吗?
嗯,Objective-C是C的超集,因此它具有C的所有功能。此外,如果使用Objective-C ++,它也具有C ++的所有功能。所以,如果你可以让MonoMac生成一个C或C ++静态库,那么是的,你甚至可以将你的静态库与你的Objective-C可可代码链接起来,它就会起作用。我不能告诉你如何从MonoMac创建库,但链接它只是在Xcode的构建设置中将它添加到链接库中。
编辑:我不熟悉C ++ / CLI语言,我误解了C ++ / CLI的含义,意思是“C ++命令行界面应用程序”。那就是说......我描述的技术仍然可以作为你想做的事情的可能方法,但它不像你在Windows上那样使用C ++ / CLI界面。