Embedding Julia Script into C#: jl_get_function doesn't exist? - c#

Currently I am working on a project where I have to get some Julia scripts written by other people to be called from C# within Unity. I've been trying to do some basic examples just to see what works and what doesn't. On the Julia documentation, it says to use the function: jl_get_function to grab a pointer to a function withing a julia module. However, I get an EntryPointNotFound in the libjulia.dll, and when I open up the dll on my computer with DependencyWalker I can't find a function called that. Am I crazy or did I install something oddly? other things like jl_eval_string and jl_unbox_float64 work fine.
Also, I'm not entirely sure how to get a pointer to the module for jl_get_function. I've thought of grabbing a pointer from a Memory Mapped file object, or from grabbing the IntPtr from jl_eval_string(include([module name in directory]));, but I'm not sure.
Here's my code in Julia for this test.
module TestModule
export calculate
function calculate(a::Float64,b::Float64)::Float64
return 3a+b^2
end
function calcMore(a,b)
return ones(a,b)::Array{Float64,2};
end
function moreTest(a::Float64, b::Float64)
return (a+b)::Float64;
end
end
and here's my code in C#, that's been snipped a bit
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace TestCInCSharp
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
[DllImport("libjulia.dll", SetLastError = true)]
public static extern void jl_init(string path);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_eval_string(string input);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_box_float64(float value);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double jl_unbox_float64(IntPtr value);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_get_function(IntPtr func, string name);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jl_call2(IntPtr func, IntPtr v1, IntPtr v2);
[DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void jl_atexit_hook(int a);
static void Main(string[] args)
{
string p = #"C:\Users\schulk4\Documents\Programming\TestJuliaSim\Assets\test_julia.jl";
string julia_path = #"C:\Users\schulk4\AppData\Local\Julia-0.5.2\bin";
IntPtr module, module2;
IntPtr a, b, c;
SetDllDirectory(julia_path);
jl_init(julia_path);
p = #"C:\\Users\\schulk4\\Documents\\Programming\\TestJuliaSim\\Assets\\test_julia.jl";
p = "include(\"" + p + "\")";
module = jl_eval_string(p); //holds module pointer?
a = jl_eval_string("TestModule.calculate(3.0,4.0)");
double d = jl_unbox_float64(a);
Console.WriteLine(d);
a = jl_eval_string("TestModule.calculate");
b = jl_box_float64(3.0f);
c = jl_box_float64(4.0f);
module2 = jl_call2(a, b, c);
d = jl_unbox_float64(module2);
Console.WriteLine(d);
a = jl_eval_string("TestModule.moreTest");
b = jl_box_float64(12.0f);
c = jl_box_float64(13.0f);
module2 = jl_call2(a, b, c);
d = jl_unbox_float64(module2);
Console.WriteLine(d);
IntPtr f = jl_get_function(module, "calculate"); //EntryPointNotFoundException
jl_atexit_hook(0);
Console.ReadLine();
}
}
}
You can see my attempts at getting a pointer to a function with jl_eval_string in the code. This is an example run before the exception:
25
1.5977136277678E-314
1.08223857600744E-314
I've been running into all sorts of problems, I was just wondering if anybody would be able to help me. I am not very familiar with this topic, I learned about P/Invoke about a week ago.

jl_get_function is an inline function. You can use jl_get_global.
Also note that your code can crash at any time. All jl_value_t* that needs to be used across a call to julia runtime/functions must be rooted. See memory managing section in the embedding doc. I don't know how you can translate that to C#.

Related

Use XGBoost DLL from c# via p/invoke

I'm trying to use XGBoost's dll (libxgboost.dll) to create a DMatrix (which is like a 2D array) and get how many columns it has. It runs fine until it throws a System.AccessViolationException at the int cols = ... line in the code below:
using System;
using System.Runtime.InteropServices;
namespace basicXgboost
{
class Program
{
[DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, IntPtr outputPtr);
[DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr = Marshal.AllocHGlobal(1000000);
IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);
int result = XGDMatrixCreateFromFile("../../libs/test.txt", 0, dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);
Marshal.FreeHGlobal(dmatrixPtr);
Marshal.FreeHGlobal(dmatrixColumnsPtr);
}
}
}
Why does accessing unmanaged memory allocated with XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr) cause a System.AccessViolationException?
One possibility might be that I'm using pinvoke incorrectly for these functions. Below are the definitions for each dll function I use:
XGDMatrixCreateFromFile()
/*!
* \brief load a data matrix
* \param fname the name of the file
* \param silent whether print messages during loading
* \param out a loaded data matrix
* \return 0 when success, -1 when failure happens
*/
XGB_DLL int XGDMatrixCreateFromFile(const char *fname,
int silent,
DMatrixHandle *out);
XGDMatrixNumCol()
/*!
* \brief get number of columns
* \param handle the handle to the DMatrix
* \param out The output of number of columns
* \return 0 when success, -1 when failure happens
*/
XGB_DLL int XGDMatrixNumCol(DMatrixHandle handle,
bst_ulong *out);
Here is the repo for my project. I'm using Visual Studio Enterprise 2015 . It's built in "Debug" mode (targeting x64) on Windows 10 Pro (64-bit). x64 binaries for libxgboost.dll can be found here. Although the linked repo does contain a copy of libxgboost.dll.
Try to use the calling convention Cdecl which seems to be used by the DLL.
Also, the signature of the XGDMatrixCreateFromFile function is wrong. The parameter expected is not a pointer to some memory allocated by you, but the function will allocate memory itself and then return the pointer as an output parameter.
Try the following code. Note the use of the the out keyword on the outputPtr parameter in the XGDMatrixCreateFromFile function.
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr;
IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);
int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);
Marshal.FreeHGlobal(dmatrixColumnsPtr);
}
When this works, you can then also simplify the call to get the number of columns by using the ulong datatype:
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, out ulong dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr;
ulong dmatrixColumns;
int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, out dmatrixColumns);
}

Calling Pocketsphinx in C# AccesViolationException

I'm trying to do the pocketsphinx tutorial in C# using pinvoke but get an AccessViolationException when I try to decode using ps_decode_raw().
IntPtr ps = PocketSphinx.ps_init(config);
IntPtr fh = Win32Util.fopen(#"goforward.raw", "rb");
int rv = PocketSphinx.ps_decode_raw(ps, fh, "goforward", -1);
The functions are wrapped as follows
//ps_decoder_t* ps_init(cmd_ln_t* config)
[DllImport("pocketsphinx.dll",
SetLastError = true,
CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr ps_init(
IntPtr config);
//int ps_decode_raw(ps_decoder_t *ps, FILE *rawfh, char const *uttid, long maxsamps);
[DllImport("pocketsphinx.dll",
SetLastError = true,
CallingConvention = CallingConvention.Cdecl)]
public extern static int ps_decode_raw(
IntPtr ps,
IntPtr rawfh,
[MarshalAs(UnmanagedType.LPStr)] string uttid,
int maxsamps);
[DllImport("msvcrt.dll",
SetLastError = true,
CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr fopen(
[MarshalAs(UnmanagedType.LPStr)] string _Filename,
[MarshalAs(UnmanagedType.LPStr)] string _Mode);
I wrapped C's fopen as well just because it was the quickest way I can think of implementing the tutorial.
I tried calling cmd_ln_retain on ps to make sure that ps wasn't causing the problem. (it wasn't). I also removed my debug code in the above.
I'm pretty sure something is up with the fopen but I'm not sure what.
Someone asked for the pocketsphinx log. https://justpaste.it/h52t
You don't check for errors anywhere. And it's wrong to set SetLastError to true for these functions. They won't call SetLastError.
Your big problem though is that the library uses a particular instance of the C runtime, depending on how you built it. And your fopen import is from a different instance of the C runtime.
You'll need to add some code to the library that exposes functions to create and destroy FILE* objects. By doing that you'll get a FILE* made by the correct runtime.

Pinvoke "System.AccessViolationException" when trying to import unmanaged dll

I am trying to import a driver dll for a piece of equipment my company uses, but I can't seem to get this to work. I am new to c# so please go easy on me. This is related to a post I made yesterday, I am attempting to convert a C program over to C#.
I wrote this code so that I could start to understand PInvoke
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace PInvokeTest {
class Program {
static void Main(string[] args) {
Int32 session_handle = 0;
Byte state_buffer = 0;
Int16 result = 1, PortNum = 1, PortType = 1;
session_handle = TMExtendedStartSession(PortNum, PortType);
result = TMSearch(session_handle, state_buffer, 1, 1, 0xEC);
if (result == 1)
Console.WriteLine("Device Found");
if (result == -201)
Console.WriteLine("Hardware Driver Not Found");
else
Console.WriteLine("Network Error");
Console.ReadKey();
}
[DllImport("IBFS32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 TMExtendedStartSession(Int16 PortNum, Int16 PortType);
[DllImport("IBFS32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern Int16 TMSearch(Int32 session_handle, Byte state_buffer, int p1, int p2, int p3);
}
}
I am trying to use these 2 functions
TMExthendedStartSession http://files.maximintegrated.com/sia_bu/licensed/docs/1-wire_sdk_win/TMEX/exst8l9q.html
and TMSearch
http://files.maximintegrated.com/sia_bu/licensed/docs/1-wire_sdk_win/TMEX/sear1ezy.html
When I run TMExthendedStartSession I get System.AccessViolationException, but when I run TMSearch alone I get a message
"Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\PInvokeTest\Debug\PInvokeTest.vshost.exe'."
The function TMSearch does return a value of -201 though.
Any help is appreciated.
In 32 bit Windows, the pascal calling convention maps to stdcall. There is a #define near the top of WinDef.h (or minwindef.h in more modern SDKs) that maps pascal to __stdcall.
On top of that, your parameters are all wrong. It should be like this:
[DllImport("IBFS32.dll")]
public static extern int TMExtendedStartSession(
short PortNum,
short PortType,
IntPtr EnhancedOptions
);
[DllImport("IBFS32.dll")]
public static extern short TMSearch(
int session_handle,
IntPtr state_buffer,
short p1,
short p2,
short p3
);
The state_buffer parameter might perhaps be better declared as byte[]. It's hard to tell from here what the semantics are.

Attempted to read or write protected memory with dllimport in c#

I have a problem with my project:
In dll c++:
extern "C" __declspec(dllexport) int results(char* imgInput, void* tree)
{
struct kd_node* nodeTree = new(tree)kd_node ; // new kd_tree with data from memory address
...
...
int ret = atoi(retValueStr.c_str());
return ret;
}
extern "C" __declspec(dllexport) void* buildKDTree(char* folder)
{
struct kd_node* kd_root;
....
feature *LFData = listFeat.data();
kd_root = kdtree_build(LFData,listFeat.size());
void* address_kdtree = (void*)&kd_root; // get memory address of kd_tree
return address_kdtree;
}
and I use to dllimport in c#:
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "buildKDTree", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern void* buildKDTree(byte[] urlImage);
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "results", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I4)]
public unsafe static extern int results(byte[] imgInput, void* tree);
static unsafe void Main()
{
string urlImg1 = "C:/Users../test img/1202001T1.jpg";
string urlImg = "C:/export_features";
try
{
IntPtr result;
int result1;
result1 = results(convertStringToByte(urlImg1), 5, buildKDTree(convertStringToByte(urlImg))); // this error
Console.WriteLine("results = %d",result1);
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
}
when i run the program, this program show error :
Attempted to read or write protected memory. This is often an indication that other memory is corrupt
what error do you know and how to resolved ?
thank you!
You don't need a convertStringToByte method here. You can tell the runtime to marshal your string as a char *. Also, I would suggest that you make the method return an IntPtr, like this:
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "buildKDTree",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr buildKDTree([MarshalAs(UnmanagedType.LPStr)]string urlImage);
[DllImport(#"kdtreeWithsift.dll", EntryPoint = "results",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.I4)]
public static extern int results([MarshalAs(UnmanagedType.LPStr)]string imgInput, IntPtr tree);
You can then call it with:
IntPtr tree = buildKDTree(urlImg);
int result1 = results(urlImg, 50, tree);
Console.WriteLine("results = {0}",result1);
Well, for one thing, the C function is called buildKDTree, but you are importing it in the C# code with entry point "buildKDTreeWithFeatures". Try making these consistent and see if you get better results.
I try to call it :
IntPtr tree = buildKDTree(urlImg);
int result1 = results(urlImg, 50, tree);
Console.WriteLine("results = {0}",result1);
but it is not your fault where you said.
I think the variable intPtr tree in function results([MarshalAs(UnmanagedType.LPStr)]string imgInput, IntPtr tree); that caused the error
I think its similar problem due to char* parameters, in my own problem thanks to this below link question solves the problem.
So your only solution is to pass the string parameters as IntPtr.
Allocate the memory with Marshal.StringToHGlobalAnsi
Attempted to read or write protected memory with dllimport in c#

Entry Point Not Found Exception

I'm trying to use a C++ unmanaged dll in a C# project and I'm getting an error when trying to call a function that says that entry point cannot be found.
public class Program
{
static void Main(string[] args)
{
IntPtr testIntPtr = aaeonAPIOpen(0);
Console.WriteLine(testIntPtr.ToString());
}
[DllImport("aonAPI.dll")]
public static extern unsafe IntPtr aaeonAPIOpen(uint reserved);
}
Here is the dumpbin for the function:
5 4 00001020 ?aaeonAPIOpen##YAPAXK#Z
I changed the dll import to [DllImport("aonAPI.dll", EntryPoint="?aaeonAPIOpen")] and [DllImport("aonAPI.dll", EntryPoint="_aaeonAPIOpen")] and no luck.
Using the undname.exe utility, that symbol demangles to
void * __cdecl aaeonAPIOpen(unsigned long)
Which makes the proper declaration:
[DllImport("aonAPI.dll", EntryPoint="?aaeonAPIOpen##YAPAXK#Z",
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr aaeonAPIOpen(uint reserved);
It looks like the function you're trying to call is compiled as a C++ function and hence has it's name mangled. PInvoke does not support mangled name. You need to add an extern "C" block around the function definition to prevent name mangling
extern "C" {
void* aaeonAPIOpen(uint reserved);
}

Categories

Resources