我是一名C#新手,尝试在C#里调用JL,全网搜索了很多资料,大部分都是英文的,并且JL的版本很低。我现在用的JL1.2版(64)位,参照其中的一个帖子(Embedding Julia in C# - Stack Overflow
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void jl_init(string julia_home_dir);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void jl_eval_string(string str);
public static void Julia()
{
// 下句出现错误:System.EntryPointNotFoundException:“无法在 DLL“libjulia.dll”中找到名为“jl_init”的入口点。”
**jl_init(@"C:\JuliaPro-1.2.0-1\Julia-1.2.0\bin\");**
jl_eval_string("print(sqrt(2.0))");
}
补充:我也已经把JL的安装目录添加到了path。
也看了JL的文档和网上的一些案例,但都是关于C/C++的,实在搞不明白,因此特来向大佬们求教,非常感谢!
已经有大佬写了“ 在Windows下编写C/C++ DLL与Julia调用的正确姿势”,如果有C#调用JL的就更好了~
jl_init
是个宏,C/C++ 里可以这样写,其他语言改用 jl_init__threading
。
#define jl_init jl_init__threading
—— julia/julia.h · JuliaLang/julia
注意事项
Julia 1.2.0 x64 联用
- VS2017 + .Net 4.6.1 @x64
- VS2017 + .Net Core 2.1 @x64
测试通过
using System.Runtime.InteropServices;
namespace JuliaCall
{
class Program
{
[DllImport("kernel32.dll")]
static extern bool SetDllDirectory(string lpPathName);
//const string julia_dll = @"C:\Users\woclass\AppData\Local\Julia-1.2.0\bin\libjulia.dll";
const string julia_dll = @"libjulia.dll";
[DllImport(julia_dll, CallingConvention = CallingConvention.Cdecl)]
private static extern void jl_init__threading();
[DllImport(julia_dll, CallingConvention = CallingConvention.Cdecl)]
public static extern void jl_eval_string(string str);
[DllImport(julia_dll, CallingConvention = CallingConvention.Cdecl)]
public static extern void jl_atexit_hook(int a);
static void Main(string[] args)
{
const string julia_home_dir = @"C:\Users\woclass\AppData\Local\Julia-1.2.0\bin";
SetDllDirectory(julia_home_dir);
// init
jl_init__threading();
jl_eval_string("print(sqrt(2.0))");
jl_atexit_hook(0);
}
}
}
ref:
如楼上所说,DLL里没有导出jl_init
这个符号,它会被宏改成jl_init__threading
。
其他语言嵌入Julia的话就按照官方文档操作就好了,Julia使用的是C接口,几乎在哪里都可以用,与C#无关。
正好我在公司,电脑是苹果的,拿PowerShell凑合一下
/Users/azure> Add-Type -MemberDefinition @"
>> const string LIB_PATH="/Applications/Julia-1.2.app/Contents/Resources/julia/lib/libjulia.dylib";
>> [DllImport(LIB_PATH,EntryPoint="jl_init__threading")]
>> public static extern void jl_init();
>> [DllImport(LIB_PATH)]
>> public static extern IntPtr jl_eval_string(string str);
>> [DllImport(LIB_PATH)]
>> public static extern void jl_atexit_hook(int status);
>> "@ -Name LibJulia -Namespace "" -PassThru
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False LibJulia System.Object
/Users/azure> [LibJulia]::jl_init()
/Users/azure> [LibJulia]::jl_eval_string('println("你好,世界")')
你好,世界
4420485128
/Users/azure> [LibJulia]::jl_atexit_hook(0)
/Users/azure>
对于非编程科班的同学来说,完全将Julia嵌入C#里面,还是非常晦涩难懂的。比如有两个非常关键的问题:
1)如何把C#的值作为参数传给JL?
2)C#如何获取JL计算后返回的值?
大佬们如果能继续给一些案例就更好了。
如何把C#的值作为参数传给JL?
jl_value_t *jl_call(jl_function_t *f, jl_value_t **args, int32_t nargs)
可以调用Julia的函数。
C#如何获取JL计算后返回的值?
看到楼上代码的IntPtr了吗?其实他们返回的都是指针,也就是jl_value_t*
。如果返回的是bits
类型,它们也会被装箱。用之前需要使用jl_typeis
或者jl_isa
判断一下类型,然后比如你要拿到返回的Float64
,那就调用jl_unbox_float64
。
还有需要注意的就是垃圾回收。Julia在发生内存分配的时候可能会触发GC,然后你的指针就无效了,所以在jl_xxx
调用之前最好固定一下拿到的对象。更详细的看我上面发的链接吧,很详细了。
总之,直接使用libjulia比较麻烦,需要考虑很多事情。如果对性能要求不高,不如用命名管道、套接字之类的万金油方式,具体看这篇 Julia如何调用C# dll动态链接库?