是否有人尝试在Linux上使用Mono中的perlembed?
以下是链接:perlembed
这是我第一次尝试DllImport签名:
private const string PERL_LIB = "/usr/lib/perl5/5.8.8/i386-linux-thread-multi/CORE/libperl.so";
[DllImport(PERL_LIB, EntryPoint = "perl_alloc", SetLastError = true)]
public static extern IntPtr Alloc();
[DllImport(PERL_LIB, EntryPoint = "perl_construct", SetLastError = true)]
public static extern void Construct(IntPtr hPerl);
[DllImport(PERL_LIB, EntryPoint = "perl_destruct", SetLastError = true)]
public static extern void Destruct(IntPtr hPerl);
[DllImport(PERL_LIB, EntryPoint = "perl_free", SetLastError = true)]
public static extern void Free(IntPtr hPerl);
[DllImport(PERL_LIB, EntryPoint = "perl_parse", SetLastError = true)]
public static extern void Parse(IntPtr hPerl, IntPtr @null, int argc, StringBuilder argv, StringBuilder env);
[DllImport(PERL_LIB, EntryPoint = "perl_run", SetLastError = true)]
public static extern void Run(IntPtr hPerl);
[DllImport(PERL_LIB, EntryPoint = "eval_pv", SetLastError = true)]
public static extern void Eval(string expr, bool flag);
CORE目录可以根据Linux发行版进行更改。
以下代码运行,但在Parse()
方法崩溃:
try
{
Console.WriteLine("Alloc");
IntPtr perl = Alloc();
Console.WriteLine("Construct");
Construct(perl);
Console.WriteLine("Parse");
Parse(perl, IntPtr.Zero, 3, new StringBuilder("-e 0"), new StringBuilder());
Console.WriteLine("Run");
Run(perl);
Console.WriteLine("Eval");
Eval("$a = 3.14; $a **= 2", true);
Console.WriteLine("Destruct");
Destruct(perl);
Console.WriteLine("Free");
Free(perl);
}
catch (Exception exc)
{
Console.WriteLine(exc.ToString());
}
我的崩溃是:
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
Stacktrace:
in (wrapper managed-to-native) Woot:Parse (intptr,intptr,int,System.Text.StringBuilder,System.Text.StringBuilder) <0x4>
in (wrapper managed-to-native) Woot:Parse (intptr,intptr,int,System.Text.StringBuilder,System.Text.StringBuilder) <0xffff9f85>
in Woot:Main () <0x8d>
in (wrapper runtime-invoke) System.Object:runtime_invoke_void (object,intptr,intptr,intptr) <0x7c79b09>
原生堆栈跟踪:
mono(mono_handle_native_sigsegv+0xbb) [0x81368fb]
mono [0x8105670]
[0x4c45a440]
/usr/lib/perl5/5.8.8/i386-linux-thread-multi/CORE/libperl.so(perl_parse+0xa3) [0x4c6e6e93]
[0x43ad78]
[0x434cae]
[0x434abe]
mono(mono_runtime_exec_main+0x62) [0x80ae5a2]
mono(mono_runtime_run_main+0x152) [0x80af6e2]
mono(mono_main+0xef9) [0x805dae9]
mono [0x805c702]
/lib/libc.so.6(__libc_start_main+0xdc) [0x4c48d724]
mono [0x805c651]
perlembed提及了一些PERL_SYS_INIT3
和PERL_SYS_TERM
次来电,但我无法通过DllImport
调用这些方法。在这些情况下,我总是得到EntryPointNotFoundException
。我确定他们在我需要导入的不同文件中。
有人可以用正确的方式指导我拨打DllImports
以进行传呼吗?
答案 0 :(得分:6)
让它发挥作用!
制作perl程序, showtime.pl :
#/usr/bin/perl
sub showtime {
print "WOOT!\n";
}
制作了c程序 perlembed.c :
#include <EXTERN.h>
#include <perl.h>
static PerlInterpreter *my_perl;
void Initialize(char* processName, char* perlFile)
{
int argc = 2;
char *argv[] = { processName, perlFile },
*env[] = { "" };
PERL_SYS_INIT3(&argc, &argv, &env);
my_perl = perl_alloc();
perl_construct(my_perl);
perl_parse(my_perl, NULL, argc, argv, NULL);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
}
void Call(char* subName)
{
char *args[] = { NULL };
call_argv(subName, G_DISCARD | G_NOARGS, args);
}
void Dispose()
{
if (my_perl != NULL)
{
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
my_perl = NULL;
}
}
通过以下方式编译:
"gcc -shared -Wl,-soname,perlembed.so -o perlembed.so perlembed.c `perl -MExtUtils::Embed -e ccopts -e ldopts`"
制作了这个C#程序, perlembed.cs :
using System;
using System.Runtime.InteropServices;
public class Woot
{
[DllImport("perlembed.so", SetLastError = true)]
public static extern void Initialize(string processName, string perlFile);
[DllImport("perlembed.so", SetLastError = true)]
public static extern void Call(string subName);
[DllImport("perlembed.so", SetLastError = true)]
public static extern void Dispose();
static void Main()
{
Console.WriteLine("Starting up C#...");
try
{
Initialize("perlembed.exe", "showtime.pl");
Call("showtime");
}
catch(Exception exc)
{
Console.WriteLine(exc.ToString());
}
finally
{
Dispose();
}
Console.WriteLine("DONE!...");
}
}
用gmcs编译,得到输出:
Starting up C#...
WOOT!
DONE!...
希望这可以帮助那里的任何人,不能相信它需要3种语言才能实现。我将继续传递标量,数组等,但从这里开始应该是轻而易举的事。
答案 1 :(得分:3)
它在perl_parse()上失败,因为你的绑定不正确。
argv参数是一个char **(被解释为一个argc大小的char *数组):这与StringBuilder无关,它代表一个可修改的字符串。
我建议您手动封送此数组:使用IntPtr []作为argv和env参数,并使用指向字节字符串的指针填充数组元素,例如,如果编码足够好,则使用Marshal.StringToCoTaskMemAnsi()您。还要记住释放那个分配的内存。
当然,你应该在一个帮助器方法中完成所有这些工作,这个方法向C#程序员公开一个更自然的接口,它接受一个字符串[]而不是argc / argv对。