PerlEmbed — C# — моно — Linux

Кто-нибудь пытался использовать perlembed в Mono в Linux?

Вот ссылка: 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]

Есть несколько вызовов PERL_SYS_INIT3 и PERL_SYS_TERM, которые упоминает perlembed, но мне не удалось вызвать эти методы через DllImport. Я всегда получаю EntryPointNotFoundException в таких случаях. Я уверен, что они находятся в другом файле, который мне нужно импортировать.

Может ли кто-нибудь указать мне, как правильно позвонить DllImports в perlembed?


person jonathanpeppers    schedule 28.07.2009    source источник


Ответы (2)


Получил работу!

Создал 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 языка. Я перейду к передаче скаляров, массивов и т. д., но отсюда все должно быть на одном дыхании.

person jonathanpeppers    schedule 30.07.2009
comment
есть ли способ пропустить часть perlembed.c? Отличная работа, кстати! - person Vivek Bernard; 16.04.2012
comment
Мы фактически отказались от этого маршрута вскоре после того, как я заработал. Моя компания закончила тем, что написала конечный продукт полностью на Perl — на самом деле это было временное решение для повторного использования некоторого кода C#, который делал то же самое. - person jonathanpeppers; 16.04.2012
comment
О..Понятно..но, к сожалению, я должен использовать этот маршрут.Можете ли вы провести меня по этому пути.. ? так можно ли пропустить часть «C» и использовать только C# и библиотеку perlembed - person Vivek Bernard; 16.04.2012
comment
Я думаю, что вы должны использовать C в некоторой степени наверняка. perlembed содержит макросы повсюду, и вы не можете вызывать их из C#. Мы также отказались от него, потому что вы не можете вызывать perl из нескольких потоков одновременно, поэтому вам придется запускать perl в его собственном потоке и использовать блокировку в многопоточной системе. Единственным другим вариантом было бы запустить несколько интерпретаторов perl, но мы не могли понять, как заставить это работать. - person jonathanpeppers; 17.04.2012

Он не работает на perl_parse(), потому что ваша привязка неверна.

Аргумент argv представляет собой char** (который следует интерпретировать как массив char* размером с argc): он не имеет отношения к StringBuilder, который представляет модифицируемую строку.

Я предлагаю вам вручную маршалировать этот массив: используйте IntPtr[] в качестве аргументов argv и env и заполните элементы массива указателями на байтовые строки, например, используя Marshal.StringToCoTaskMemAnsi(), если кодировка достаточно хороша для вас. Не забудьте также освободить память, выделенную этим.

Конечно, вы должны заставить все это работать внутри вспомогательного метода, который предоставляет программистам на C# более естественный интерфейс, который принимает строку [] вместо пары argc/argv.

person lupus    schedule 29.07.2009
comment
Я попробую это, кто-нибудь знает, как сделать вызовы PERL_SYS_INIT3 и PERL_SYS_TERM, я уверен, что они необходимы для того, чтобы все работало. - person jonathanpeppers; 29.07.2009
comment
Я получил perl_parse() для работы с вашим методом, но теперь я получаю исключение EntryPointNotFoundException для eval_pv(). Бьюсь об заклад, мне нужно импортировать скомпилированную версию EXTERN.H, но в упомянутом каталоге CORE больше нет файлов .so... Кто-нибудь знает инструмент, который будет отображать все методы, содержащиеся в файле .so? ? - person jonathanpeppers; 30.07.2009
comment
Эта функция экспортируется как Perl_eval_pv, поэтому вам нужно использовать ее и в C#. Вы можете проверить экспортированные символы libperl, например: nm -D /usr/lib/libperl.so | группа "Т" - person lupus; 31.07.2009