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);
}
Related
I have a rather baffling issue, a program that used to always work now only works once per reboot, when running it again I'm granted with:
Exception thrown: read access violation. this was 0xBF13D000.
I've extended a C++ project with C exports so I can use it from C#:
C exports:
KeyFinder::AudioData* new_audio_data(const unsigned frame_rate, const unsigned channels, const unsigned samples)
{
auto audio_data = new KeyFinder::AudioData();
audio_data->setFrameRate(frame_rate);
audio_data->setChannels(channels);
audio_data->addToSampleCount(samples);
return audio_data;
}
void audio_data_set_sample(KeyFinder::AudioData* audio_data, const unsigned index, const double value)
{
audio_data->setSample(index, value);
}
void keyfinder_progressive_chromagram(KeyFinder::KeyFinder* key_finder, KeyFinder::AudioData* audio_data, KeyFinder::Workspace* workspace)
{
key_finder->progressiveChromagram(*audio_data, *workspace);
}
KeyFinder::key_t keyfinder_key_of_chromagram(KeyFinder::KeyFinder* key_finder, KeyFinder::Workspace* workspace)
{
return key_finder->keyOfChromagram(*workspace);
}
enum key_t {
A_MAJOR = 0,
A_MINOR,
B_FLAT_MAJOR,
B_FLAT_MINOR,
B_MAJOR,
B_MINOR = 5,
C_MAJOR,
C_MINOR,
D_FLAT_MAJOR,
D_FLAT_MINOR,
D_MAJOR = 10,
D_MINOR,
E_FLAT_MAJOR,
E_FLAT_MINOR,
E_MAJOR,
E_MINOR = 15,
F_MAJOR,
F_MINOR,
G_FLAT_MAJOR,
G_FLAT_MINOR,
G_MAJOR = 20,
G_MINOR,
A_FLAT_MAJOR,
A_FLAT_MINOR,
SILENCE = 24
};
C# declarations:
[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr new_audio_data(
uint frameRate, uint channels, uint samples);
[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern void audio_data_set_sample(
IntPtr audioData, uint index, double value);
[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern void keyfinder_progressive_chromagram(IntPtr keyFinder, IntPtr audioData, IntPtr workspace);
[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern Key keyfinder_key_of_chromagram(IntPtr keyFinder, IntPtr workspace);
public enum Key
{
AMajor = 0,
AMinor,
BFlatMajor,
BFlatMinor,
BMajor,
BMinor = 5,
CMajor,
CMinor,
DFlatMajor,
DFlatMinor,
DMajor = 10,
DMinor,
EFlatMajor,
EFlatMinor,
EMajor,
EMinor = 15,
FMajor,
FMinor,
GFlatMajor,
GFlatMinor,
GMajor = 20,
GMinor,
AFlatMajor,
AFlatMinor,
Silence = 24
}
C# usage:
public void SetSample(uint index, double value)
{
audio_data_set_sample(_audioData, index, value);
}
What is really puzzling is that when I debug it, the seemingly disposed/destroyed pointer is already visible in the C# part: SetSample._audioData. Initially, when new_audio_data is called from C# I get a valid pointer like 0x032fe940 but for some reason it becomes 0xBF13D000. Note that it always become the value 0xBF13D000 so I've searched about such value online in a hope to spot a known memory pattern but without success.
As I said, there hasn't been any changes to the program, so I'm at a total loss as on what could be causing this.
Try add volatile for _audioData
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/volatile
It turned out that some of the native libraries needed to be all rebuilt using the same compiler version, now it works flawlessly !
my c++ dll:
int test2::CallMe(int y) {
return y;
}
c# code:
[DllImport("test2.dll",CharSet = CharSet.Anci)]
private static extern int CallMe(int y);
Console.WriteLine(CallMe(7));
if the dll and the test program are compiled in x86 i get a print:
7
but if i compile them at X64 for c++ and X64 or any CPU for c# the print is:
0
Any suggestion?
edit: the problem is the call, because in debugger i see that the CPP receives 0 , or null in case of struct.
edit 2: the functions are exported using a def file. if i export a function using extern "C" it works fine but i cant export a function of a calss, or i dont know how
edit 3: apparently the arguments are not actually zero, only the last argument is zero, all arguments are shifted, the second param is set to the first one and so on
Calling a C++ function from C# isn't exactly/completely/transparently supported. You can try using the CallingConvention = CallingConvention.ThisCall in the DllImport, but it isn't an exact science. Let's say that it should work for simple cases...
[DllImport("test2.dll", CallingConvention = CallingConvention.ThisCall)]
private static extern int CallMe(IntPtr obj, int y);
where obj is a reference to the C++ object (what in C++ is called this). The difference you get between 32 and 64 bits happens because the place where the this pointer is placed changes between 32 and 64 bits.
For very simple cases, when in truth the this isn't used by the C++ method (and it doesn't use virtual functions), you can pass IntPtr.Zero as the pointer. Normally you would have an extern C method that creates the C++ object "C++ side" and returns an IntPtr (a void*) to C#, and then C# calls the C++ methods passing this IntPtr.
The "brittle" point is that the C++ compiler mangles the name of C++ methods (methods that aren't in an extern "C" block), so you have to "manually" discover them (for example using the DUMPBIN /exports)... And to make things "easier", the mangled names change between 32 and 64 bits :-)
An example:
C++-side:
class Store
{
private:
int value;
public:
__declspec(dllexport) void Put(int value)
{
this->value = value;
}
__declspec(dllexport) int Get()
{
return this->value;
}
__declspec(dllexport) void Increment()
{
this->value++;
}
};
extern "C"
{
__declspec(dllexport) int PlusOne(int x)
{
return x + 1;
}
__declspec(dllexport) Store* NewStore()
{
return new Store;
}
__declspec(dllexport) void DeleteStore(Store* store)
{
delete store;
}
}
class Program
{
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
extern static int PlusOne(int x);
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
extern static IntPtr NewStore();
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
extern static void DeleteStore(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Put#Store##QAEXH#Z")]
extern static void Put32(IntPtr store, int value);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Get#Store##QAEHXZ")]
extern static int Get32(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Increment#Store##QAEXXZ")]
extern static void Increment32(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Put#Store##QEAAXH#Z")]
extern static void Put64(IntPtr store, int value);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Get#Store##QEAAHXZ")]
extern static int Get64(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Increment#Store##QEAAXXZ")]
extern static void Increment64(IntPtr store);
static void Main(string[] args)
{
int x = PlusOne(1);
Console.WriteLine(x);
IntPtr store = NewStore();
int ret;
if (IntPtr.Size == 8)
{
Put64(store, 5);
Increment64(store);
ret = Get64(store);
}
else
{
Put32(store, 5);
Increment32(store);
ret = Get32(store);
}
Console.WriteLine(ret);
DeleteStore(store);
}
}
c++ code is
MSIPC_SDK LONG __stdcall Ms_IpcClient_CaptureImage(LONG nUserId, char *sFilePath,
int nPathLen, const char *sDiskPath = NULL);//sDiskPath example: "C: \\".
Affect: Take a snapshoot
Parameters remark:
LONG nUserId: Ms_Ipc_Login()//Return value after login successfully
char *sFilePath: //destination for saving the recording files
int nPathLen: //the length of the path
const char *sDiskPath = NULL: //which disk to be saved
my c# code is:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
[MarshalAs(UnmanagedType.LPStr)]
string sFilePath,
int nPathLen,
[MarshalAs(UnmanagedType.LPStr)]
string sDiskPath
);
and using is method:
var ret = Ms_IpcClient_CaptureImage(loginID, "C:\\a.bmp", 10000, "C:\\");
It is working in .Net Framework 2 but does not work in .Net Framework 4.
How can I fix it in .Net Framework 4?
sFilePath is used to pass a string from callee to caller. That's why the type is char* rather than const char*, and that's why there is a parameter for buffer length. That means you need to use StringBuilder rather than string. The p/invoke should be:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
StringBuilder sFilePath,
int nPathLen,
string sDiskPath
);
And the call should be:
var filePath = new StringBuilder(260);
var ret = Ms_IpcClient_CaptureImage(loginID, filePath, filePath.Capacity, "C:\\");
Your code has always been wrong and you've just been getting away with it up until now. The fact that you passed a made up buffer length value of 10000 should have set the alarm bells ringing!
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.
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#