How do we dynamically change the assembly path in DLLImport attribute? - c#

How do we change the assembly path in DLLImport attribute inside an if conditional statement?
e.g. I want to do something like this:
string serverName = GetServerName();
if (serverName == "LIVE")
{
DLLImportString = "ABC.dll";
}
else
{
DLLImportString = "EFG.dll";
}
DllImport[DLLImportString]

You can't set attribute's value wich is calculated during runtime
You can define two methods with diff DllImports and call them in your if statement
DllImport["ABC.dll"]
public static extern void CallABCMethod();
DllImport["EFG.dll"]
public static extern void CallEFGMethod();
string serverName = GetServerName();
if (serverName == "LIVE")
{
CallABCMethod();
}
else
{
CallEFGMethod();
}
Or you can try to Load dll dynamicaly with winapi LoadLibrary
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
static extern IntPtr GetProcAddress( int hModule,[MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
static extern bool FreeLibrary(int hModule);
Create delegate that fits method in dll
delegate void CallMethod();
And then try to use something like that
int hModule = LoadLibrary(path_to_your_dll); // you can build it dynamically
if (hModule == 0) return;
IntPtr intPtr = GetProcAddress(hModule, method_name);
CallMethod action = (CallMethod)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(CallMethod));
action.Invoke();

You need to manually load the dll via LoadLibrary/GetProcAddress.
I had the same need for a small application and used c++/cli.
In c# it will look something like:
delegate int MyFunc(int arg1, [MarshalAs(UnmanagedType.LPStr)]String arg2);
public static void Main(String[] args)
{
IntPtr mydll = LoadLibrary("mydll.dll");
IntPtr procaddr = GetProcAddress(mydll, "Somfunction");
MyFunc myfunc = Marshal.GetDelegateForFunctionPointer(procaddr, typeof(MyFunc));
myfunc(1, "txt");
}
Edit: Here is complete example

May be you could differentiate your builds using conditional compilation?
If you can /define that a build is for server A, eg. compiling with /define serverA, then you could have
#if serverA
DllImport["ABC.dll"]
#else
DllImport["EFG.dll"]
#endif
More #if info

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?

Importing Ruby from C# (Windows)

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.

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

How to load a Dll (c++) into at C# Application at runime

I have this code in a c++ file, with compiles to a dll.
#include "stdafx.h"
#include "WHUU.h"
#include "stdafx.h"
typedef int (__stdcall * Callback)(const int text);
static int x , y= 0;
Callback Handler = 0;
int getX()
{
return x;
}
void setX(int i)
{
x = i;
}
void setY(int i)
{
y = i;
}
int getY()
{
return y;
}
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall addX(int x) {
setX(x);
}
extern "C" __declspec(dllexport)
void __stdcall addY(int y) {
setY(y);
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int z = getX() + getY();
int retval = Handler(z);
}
My c# application now has to load this dll on runtime. Add to the callback and call the functions. I dont want to use a class. I could load the class and with
Type[] types = moduleAssembly.GetTypes();
But this overkill! Also c++ is not managed.
I mean its so tiny! (and yes this is an example , but the "real" is just as big as this example).
How do i do that?
Thanks you for your help!
add:
i dont like frameworks (like pinvoke / assembly)
the function names / types are fixed and will never change (think of a driver.dll read write)
this dll is written by customers so it should be as easy as possible!
You could also do it over p/invoke, as example:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
private extern static int yourFunction(int var1, int var2);
i dont like frameworks (like pinvoke / assembly)
I'll suggest P/Invoke anyway. I don't think there are any reasonable alternatives. Perhaps writing a managed wrapper in managed C++, but really?
When you use P/Invoke the .NET runtime will dynamically load the DLL's that you have specified (and this is done when you first call the function). There is no reason to use P/Invoke to call LoadLibrary first.
You can either use P/Invoke to call the dll directly, or you can create a C++/CLI wrapper for it.
Here's how you can do it using P/Invoke:
Add the compiled dll to your C# project, set its 'Copy to Output Directory' property to 'Copy Always'. and call it like this:
class Program
{
[DllImport("cppdll.dll")]
private static extern void addX(int x);
[DllImport("cppdll.dll")]
private static extern void addY(int y);
static void Main(string[] args)
{
addX(5);
addY(7);
}
}
One way to do this would be through API. Find below an example for the setX and getX method from your sample above.
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr LoadLibraryEx(string libraryPath, IntPtr fileHandle, int actionFlag);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern bool FreeLibrary(IntPtr libraryHandle);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetProcAddress(IntPtr dllPointer, string functionName);
IntPtr ptr = IntPtr.Zero;
private delegate void setX (int i);
private delegate int getX();
private setX setXDel;
private getX getXDel;
public Constructor()
{
loadLib();
}
public void YourMethod()
{
setXDel(100);
int y = getXDel();
Console.WriteLine(y.ToString());
}
private void loadLib()
{
string path = "your dll path";
ptr = LoadLibraryEx(path, IntPtr.Zero, 0);
if (ptr == IntPtr.Zero)
throw new Exception("Cannot load dll.");
IntPtr addressPtr = GetProcAddress(ptr, "setX");
setXDel = (setX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(setX));
addressPtr = GetProcAddress(unrarPtr, "getX");
getXDel = (getX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(getX));
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
FreeLibrary(ptr);
}
}
I used P/Invoke, post my own cause the callback was not included by the others. But they were good answers! The C# Applicationhas use this GUI.
The Code for the Application is this : (mind it is a prototype to answer a technical question)
public partial class Form1 : Form
{
private delegate int Callback(int text);
private Callback mInstance; // Ensure it doesn't get garbage collected
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addX(int x);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addY(int y);
[DllImport(#"C:\\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void TestCallback();
private int Handler(int text)
{
textBox3.Text = text.ToString();
return 42;
}
private void button1_Click(object sender, System.EventArgs e)
{
mInstance = new Callback(Handler); // to set the callback in lib
SetCallback(mInstance); // could also be withhin constructor!
addX(int.Parse(textBox1.Text)); // other people in this thread posted this correct answer
addY(int.Parse(textBox2.Text));
TestCallback();
}
public Form1()
{
InitializeComponent();
}
So if you want to use a c++ lib with functions and callbacks (like in the question posted) you can use this.
But how to change the lib?
The Path to the lib is hardcoded (see [dllImport ......] ). But you
can swop the lib in filesystem. This can happen AFTER you build this
application.
If the lib is not on the given path an exception is thrown. So if
your program wants to use this lib first check the lib is present on the filesystem.
(not included in this simple prototype)
So to switch functionality (swop a driver lib ect) you copy a different lib with the
same name into the given path. (Copy paste - maybe change name)
This solution has the least overhead and is good if you never change the interface of an dll after the application was build!

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