Importing Ruby from C# (Windows) - c#

I am trying to create a C# library that "embeds" a Ruby interpreter, using DllImport to execute C-Ruby functions.
public const string RUBY_DLL = #"msvcrt-ruby18";
[DllImport(RUBY_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern void ruby_init();
// ... Everything in between...
[DllImport(RUBY_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern void ruby_finalize();
This works perfectly fine, and I am quite able to import the functions and interact with Ruby, but only if using msvcrt-ruby18.dll, which is obviously outdated. I would like to use msvcrt-ruby240.dll, or even msvcrt-ruby19*.dll, but every attempt I make to do so fails. I created a variant that loads the functions with LoadLibrary and GetProcAddress, that way I could use any installed version of Ruby, but everything fails.
When using DllImport, I get the "DllNotFoundException", which seems to indicate a missing dependency of the Ruby dll somewhere. I have ensured that I am building under x86, and using x86 Ruby library, so this is not a BadImageFormat issue. When using the LoadLibrary, I can actually call ruby_init without an error in newer versions, but invoking rb_eval_string fails with anything other than msvcrt-ruby18.dll.
I am quite familiar with using P/Invoke, and am not asking "how" to link to them. I AM quite green when it comes to actually writing C code, or understanding exactly the the differences in builds of the msvcrt-ruby***.dll, static libraries, etc.
After extensive Google research, I cannot find one single example that links C# and Ruby that uses anything newer than msvcrt-ruby18.dll, and was hoping to gain some insight as to how I can and what I have to do. I am not opposed to compiling Ruby myself if that is required, but would really appreciate any tips on that if it is required, and what I would have to edit, etc.
EDIT:
Here's what I am doing.
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public class RubyHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32")]
public static extern bool FreeLibrary(IntPtr hModule);
public RubyHandle(string rubyDllPath) : base(true)
{
SetHandle(LoadLibrary(rubyDllPath));
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public override bool IsInvalid
{
get => handle == IntPtr.Zero;
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return FreeLibrary(handle);
}
public static implicit operator IntPtr(RubyHandle rubyHandle)
{
return rubyHandle.DangerousGetHandle();
}
}
And to bind the functions...
[SuppressUnmanagedCodeSecurity]
public static class Ruby
{
public const string RUBY_DLL = #"C:\Ruby24\bin\msvcrt-ruby240.dll";
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
private static RubyHandle _rubyLib;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void VoidArgs0();
private static VoidArgs0 _ruby_init;
private static VoidArgs0 _ruby_finalize;
private static VoidArgs0 _ruby_show_version;
private static VoidArgs0 _ruby_show_copyright;
public static void Initialize(string rubyDllPath = null)
{
_rubyLib = new RubyHandle(rubyDllPath ?? RUBY_DLL);
ImportFunctions();
_ruby_init.Invoke();
}
private static void ImportFunctions()
{
_ruby_init = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_init");
_ruby_finalize = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_finalize");
_ruby_show_version = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_show_version");
_ruby_show_copyright = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_show_copyright");
}
private static object ImportFunction<T>(string functionName)
{
var ptr = GetProcAddress(_rubyLib, functionName);
if (ptr == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
}
public static void Release()
{
_ruby_finalize.Invoke();
_rubyLib.Dispose();
}
public static void ShowVersion()
{
_ruby_show_version.Invoke();
}
}
The error occurs right in the beginning, before it even gets started on the call to "LoadLibrary", I get the "Specified module was not found" error. I have also made sure that both "C:\Ruby24\bin\ruby_builtin_dlls" and "C:\Ruby24\bin" are included in PATH.
I am beating my head against a wall. I see no reason why this does not work...

OK, so finally figured this out, will post the answer here for anyone else who may stumble into a similar problem.
I ended up manually adding the directories for Ruby's dependencies via "AddDllDirectory":
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool AddDllDirectory(string lpPathName);
And then using "LoadLibraryEx" opposed to "LoadLibrary", and specifying the "LOAD_LIBRARY_SEARCH_DEFAULT_DIRS" flag.
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[System.Flags]
enum LoadLibraryFlags : uint
{
DontResolveDllReferences = 0x00000001,
LoadIgnoreCodeAuthzLevel = 0x00000010,
LoadLibraryAsDatafile = 0x00000002,
LoadLibraryAsDatafileExclusive = 0x00000040,
LoadLibraryAsImageResource = 0x00000020,
LoadLibrarySearchApplicationDir = 0x00000200,
LoadLibrarySearchDefaultDirs = 0x00001000,
LoadLibrarySearchDllLoadDir = 0x00000100,
LoadLibrarySearchSystem32 = 0x00000800,
LoadLibrarySearchUserDirs = 0x00000400,
LoadWithAlteredSearchPath = 0x00000008
}
And then...
public static void Initialize(string rubyDllPath = null)
{
AddDllDirectory(#"C:\Ruby24\bin");
AddDllDirectory(#"C:\Ruby24\bin\ruby_builtin_dlls");
_rubyLib = new RubyHandle(rubyDllPath ?? RUBY_DLL);
ImportFunctions();
_ruby_init.Invoke();
}
Obviously the final product will do this dynamically, but this way successfully loads the library.

Related

golang c-shared library callback from another language

I building a dll with golang, and need the dll receive a callback to trigger function directly from the dll
I tried execute callback on golang library but dont work
This is my code on golang
package main
import "C"
import "fmt"
type externFunc func(int)
//export Connect
func Connect(fn externFunc) {
fmt.Println("fn ",fn)
for i:= 0; i<3;i++{
// this is the func/method received from c#, and tried execute from golang
fn(i)
}
}
and so i build the library
go build -buildmode=c-shared -o library.dll main.go
this is my code on c#
class Program
{
static void Main(string[] args)
{
Connect()
}
private static unsafe void Connect()
{
Dll.CallbackDelegate cb = Callback;
// here sent the method to dll, to wait the multiple calls
Dll.Connect(cb);
}
private static unsafe void Callback(int num)
{
Console.WriteLine("call #: "+num);
}
}
class Dll {
private const string DllPath = "library.dll";
private IntPtr _dllHandle = IntPtr.Zero;
private static DllHelper _instance;
public static DllHelper Instance
{
get { return _instance ?? (_instance = new DllHelper()); }
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern void FreeLibraryAndExitThread(IntPtr hModule, UInt32 dwExitCode);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
internal Dll()
{
_dllHandle = IntPtr.Zero;
LoadDLL();
}
internal void LoadDLL()
{
bool? freeResult = null;
if (_dllHandle != IntPtr.Zero)
{
freeResult = FreeLibrary(_dllHandle);
}
// Load dll ..
_dllHandle = LoadLibrary(DllPath);
if (_dllHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
// Marshal.
throw new Exception(string.Format("Failed to load library (ErrorCode: {0})", errorCode));
}
}
and this part load the function from library
public unsafe delegate void CallbackDelegate(int num);
private unsafe delegate void ConnectDelegate(CallbackDelegate pFunc);
internal unsafe void Connect(CallbackDelegate pFunc)
{
var funcaddr = GetProcAddress(_dllHandle, "Connect");
var function = Marshal.GetDelegateForFunctionPointer(funcaddr, typeof(ConnectDelegate)) as
ConnectDelegate;
function.Invoke(pFunc);
}
}
this is the result when tried execute my console program created on C# with the go library
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x627ca9ab]
I think need receive an uinptr or something like that in the Connect and convert
the pointer on a type func but reading on other post says the type externFunc func is an func signature, and this is already a pointer.
Any idea what I need to make callbacks?

c# how to call functions from function table?

I have .NET DLL import library in my project, which functions I want to call taking its name from function table (List<string>).
Assuming all they has same return type and parameters.
I have functions_table[] with something like "Func1", "Func2" ....
I randomly select from that table (really it is like List) and call it in my program.
As I can understand C# delegate is not for this solution.
I want to randomly choose function with name Func1() (for example) be called from managed C# code with their parameters.
How can be that achieved?
Because you said that this functions must be called from managed code, I believe that functions DLL is native. So firstly, you need some native methods to load\free this library and call functions:
public static class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibrary(string filename);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
}
Then use this code to load DLL:
var libHandle = NativeMethods.LoadLibrary(fileName);
if (libHandle == IntPtr.Zero)
{
var errorCode = Marshal.GetLastWin32Error();
// put error handling here if you need
}
And to free:
if (libHandle != IntPtr.Zero)
NativeMethods.FreeLibrary(libHandle);
You will also need the delegate to make the call . For example,
delegate int FuncDelegate(int arg1, bool arg2);
And then to call the function from DLL:
var func1Address = NativeMethods.GetProcAddress(libHandle, "Func1");
var func1 = (FuncDelegate)Marshal.GetDelegateForFunctionPointer(func1Address, typeof(FuncDelegate));
var result = func1(42, true);
And of course you can (and probably should) cache this functions:
private Dictionary<string, FuncDelegate> _functionsCache = new Dictionary<string,FuncDelegate>();
private int CallFunc(string funcName, int arg1, bool arg2)
{
if (!_functionsCache.ContainsKey(funcName))
{
var funcAddress = NativeMethods.GetProcAddress(libHandle, funcName);
var func = (FuncDelegate)Marshal.GetDelegateForFunctionPointer(funcAddress, typeof(FuncDelegate));
_functionsCache.Add(funcName, func);
}
return _functionsCache[funcName](arg1, arg2);
}
MethodInfo handler = GetType.GetMethod("NameMethod");
handler.Invoke(context, new object[] {parameters}
does the trick

Loading/Injecting .Net Assembly into existing .net process?

In my situation i want to load a custom .net assembly into a running .net process's domain, for example Windows Explorer, What i have tried already is just injecting the assembly to explorer.exe but that doesn't seem to work for no obvious reason.
Injector Code:
public class CodeInjector
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, int bInheritHandle, uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttribute, IntPtr dwStackSize, IntPtr lpStartAddress,
IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
private static CodeInjector _instance;
public static CodeInjector GetInstance
{
get { return _instance ?? (_instance = new CodeInjector()); }
}
public InjectionResult Inject(string sProcName, string sDllPath)
{
if (!File.Exists(sDllPath))
{
return InjectionResult.DllNotFound;
}
var procs = Process.GetProcesses();
var procId = (from t in procs where t.ProcessName == sProcName select (uint)t.Id).FirstOrDefault();
if (procId == 0)
{
return InjectionResult.ProcessNotFound;
}
if (!Inject(procId, sDllPath))
{
return InjectionResult.InjectionFailed;
}
return InjectionResult.InjectionSucceed;
}
private static bool Inject(uint pToBeInjected, string sDllPath)
{
var hndProc = OpenProcess((0x2 | 0x8 | 0x10 | 0x20 | 0x400), 1, pToBeInjected);
if (hndProc == IntPtr.Zero)
{
return false;
}
var lpLlAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (lpLlAddress == IntPtr.Zero)
{
return false;
}
var lpAddress = VirtualAllocEx(hndProc, (IntPtr)null, (IntPtr)sDllPath.Length, (0x1000 | 0x2000), 0X40);
if (lpAddress == IntPtr.Zero)
{
return false;
}
var bytes = Encoding.ASCII.GetBytes(sDllPath);
if (WriteProcessMemory(hndProc, lpAddress, bytes, (uint)bytes.Length, 0) == 0)
{
return false;
}
if (CreateRemoteThread(hndProc, (IntPtr)null, IntPtr.Zero, lpLlAddress, lpAddress, 0, (IntPtr)null) == IntPtr.Zero)
{
return false;
}
CloseHandle(hndProc);
return true;
}
}
As another option you can use existing library - ManagedInjector - https://github.com/cplotts/snoopwpf/tree/master/ManagedInjector . There is a tool snoopwpf that can show details of any WPF process, and it uses process injection for that. I used it some time ago and it worked well.
You need to build it, add to your project as reference and call like this:
Injector.Launch(someProcess.MainWindowHandle,
typeof(Loader).Assembly.Location,
typeof(Loader).FullName,
"Inject");
Loader is name of type that will be loaded into process and Inject is a static method that will be executed. In my case i had:
public class Loader
{
public static void Inject()
{
// i did CBT Hook on main window here
// and also displayed sample message box for debugging purposes
MessageBox.Show("Hello from another process");
}
}
That ManagedInjector is written in Managed C++ code. Basically it hooks own unmanaged C++ method as MessageHookProc and it will start specified assembly after injection and run specified method. It should work fine for both managed and unmanaged programs. In my case i used it for unmanaged program.
UPDATE
I tested it locally and it successfully injects my message box into explorer process under Windows 8.1 x64. I compiled ManagedInjector64-4.0 and my sample console project also has x64 in platform selection. Here is my working code:
class Program
{
static void Main(string[] args)
{
var proc = Process.GetProcessesByName("explorer").FirstOrDefault();
Injector.Launch(proc.MainWindowHandle, typeof(Loader).Assembly.Location, typeof(Loader).FullName, "Inject");
}
}
public class Loader
{
public static void Inject()
{
MessageBox.Show("Hello");
Task.Run(() =>
{
Thread.Sleep(3000);
MessageBox.Show("Hello again");
Thread.Sleep(5000);
MessageBox.Show("Hello again again");
});
}
}

Cant access C# static class

I am having trouble implementing an answer I got here!, Can anyone help me access this private static class?
namespace VEParameterTool
{
class ProcessorPlugIn
{
private static class UnsafeNativeMethods
{
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", SetLastError = true)]
static extern bool FreeLibrary(IntPtr hModule);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ErrorCode ProcessorFunction(ref IntPtr pRx);
IntPtr hLib = UnsafeNativeMethods.LoadLibrary("Processor.dll");
}
}
When I try to LoadLibrary, I get the error about protection levels:
'VEParameterTool.ProcessorPlugIn.UnsafeNativeMethods.LoadLibrary(string)' is inaccessible due to its protection level
I have searched for the solution but cant see anything to do with static classes.
Any hep would be greatly appreciated.
Andy
Why are you attempting to declare a private static class? The only method I can think of potentially being able to access information within the class is if you were to make it a nested private static class with public methods - and based on the code posted that is not the case.
Private means just that - it cannot be accessed from outside. Read more about access modifiers here: http://msdn.microsoft.com/en-us/library/ms173121.aspx
See here for an example of a private static class implementation:
https://dotnetfiddle.net/Lyjlbr
using System;
public class Program
{
public static void Main()
{
Bar.DoStuff();
// Bar.DoOtherStuff(); // Cannot be done due to protection level
Bar.DoStuffAndOtherStuff(); // note that this public static method calls a private static method from the inner class
}
private static class Bar
{
public static void DoStuff()
{
Console.WriteLine("Test");
}
public static void DoStuffAndOtherStuff()
{
DoStuff();
DoOtherStuff();
}
private static void DoOtherStuff()
{
Console.WriteLine("other stuff");
}
}
}
EDIT: apparently you can also use reflection to access private classes/members/functions, but I don't know much about it. See here: Why can reflection access protected/private member of class in C#?
You can leave the inner class private, making it only accessible to the ProcessorPlugIn class, but you have to make the methods public.
private static class UnsafeNativeMethods
{
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);
}
These methods will be only accessible from where their containing class can be accessed, in this example, ProcessorPlugIn.

PerlEmbed - C# - Mono - Linux

Has anyone attempted using perlembed in Mono on Linux?
Here is the link: perlembed
Here is my first attempt at the DllImport signatures:
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);
The CORE directory can change per Linux distro.
The following code runs, but crashes on the Parse() method:
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());
}
My crash reads:
=================================================================
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>
Native stacktrace:
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]
There are some PERL_SYS_INIT3, and PERL_SYS_TERM calls that perlembed mentions, but I have not been able to call those methods through DllImport. I always get EntryPointNotFoundException in those cases. I'm sure they are in a different file I need to import.
Can someone direct me in the correct way to call DllImports to perlembed?
Got it to work!
Made the perl program, showtime.pl:
#/usr/bin/perl
sub showtime {
print "WOOT!\n";
}
Made the c program, 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;
}
}
Compiled it via:
"gcc -shared -Wl,-soname,perlembed.so -o perlembed.so perlembed.c `perl -MExtUtils::Embed -e ccopts -e ldopts`"
Made this C# program, 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!...");
}
}
Compiled it with gmcs, and got the output:
Starting up C#...
WOOT!
DONE!...
Hope this helps anyone out there, can't believe it took 3 languages to happen. I will move on to passing scalars, arrays, etc. but it should be a breeze from here.
It fails on perl_parse() because your binding is incorrect.
The argv argument is a char** (to be interpreted as an argc-sized array of char*): this has no relation to StringBuilder, which represents a modifiable string.
I suggest you manually marshal this array: use a IntPtr[] as the argv and env arguments and fill the elements of the array with pointers to byte strings, for example using Marshal.StringToCoTaskMemAnsi() if the encoding is good enough for you. Remember also to free the memory allocated by that.
Of course, you should make all this work inside an helper method that exposes a more natural interface to C# programmers that takes a string[] instead of the argc/argv pair.

Categories

Resources