Why does accessing unmanaged memory cause a System.AccessViolationException? [duplicate] - c#

This question already has an answer here:
Use XGBoost DLL from c# via p/invoke
(1 answer)
Closed 6 years ago.
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.

Here's the solution I've got thanks to NineBerry's answer.
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, out IntPtr outputPtr);
[DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, out ulong dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr;
ulong dmatrixColumns;
int result = XGDMatrixCreateFromFile("../../libs/test.txt", 0, out dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, out dmatrixColumns);
}
}
}

Related

How to DLLImport a C/C++ P/Invoke function that return a pointer to a pointer of struct in C#?

I installed libserialport for Debian 11 in raspberrypi (source code). I would like to write a .NET application that send data via USB CDC. My target is to create a wraper of libserialport for C# .NET 7 but I got trouble with this function SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr);
My C# code:
using System.ComponentModel.Design;
using System.Runtime.InteropServices;
namespace SerialPortC
{
internal class Program
{
static void Main(string[] args)
{
string portName = "/dev/ttyACM0";
IntPtr portPtr = new IntPtr();
int error = 0;
error = sp_get_port_by_name(portName, portPtr); // error = -1 after executing this
Console.WriteLine("Error Code: " + error);
}
[DllImport("libserialport.so", CallingConvention = CallingConvention.StdCall)]
public static extern int sp_get_port_by_name(string portName, IntPtr portPtr);
}
}
And after executing sp_get_port_by_name function. It always return -1 (Invalid Parameter).
What wrong in my C# code? Please help to correct it!
I have already tried
IntPtr portPtr = new IntPtr();
and
IntPtr portPtr = IntPtr.Zero;
But still got the same error (-1)
I exppected a correct C# DLLImport for SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr);

P/Invoke runtime STD error not redirected to file

I am working with a C++ DLL that I access with P/Invokes from C# code. The problem is I cannot redirect the std error that happens in the unmanaged side to a file. This works well if the build is in DEBUG but when the build is in RELEASE the STD logfile-unmanaged does not contain the error but contains and does not close the application, it keeps running like there was no error:
Before Error
After Error
In C++ the STD error is redirected to a file like this:
extern "C" __declspec(dllexport) void RedirectStd()
{
int fileNO = _fileno(stderr);
_close(fileNO);
int file = _open("logfile-unmanaged", O_CREAT | O_RDWR, 0644);
_dup2(file, fileNO);
}
A runtime error in C++ is generated like this:
extern "C" __declspec(dllexport) void DoException()
{
fprintf(stderr, "Before Error\n");
int a = 0;
int b = 10 / a;
fprintf(stderr, "After Error\n");
}
In C# I'm calling both of those methods:
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void RedirectStd();
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void DoException();
My Main function:
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static void Main(string[] args) {
Console.WriteLine("===== REDIRECT =====");
Console.WriteLine("Native/Unmanaged exception redirected to logfile-unmanaged.");
RedirectStd();
Console.WriteLine("Native/Unmanaged std error redirected.");
Console.WriteLine("");
Console.WriteLine("===== EXCEPTION =====");
DoException();
Console.WriteLine("Waiting for program to crash");
do {
} while (true);
}
EDIT:
C# console application:
program.cs
using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
namespace ConsoleWithErrors {
class Program {
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void RedirectStd();
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void DoException();
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static void Main(string[] args) {
RedirectStd();
DoException();
Console.WriteLine("Waiting for program to crash");
do {
} while (true);
}
}
}
The console application stays opened like there was no error on the unmanaged side.
This works well if the build is in DEBUG but when the build is in RELEASE the STD logfile-unmanaged does not contain the error
It's probably optimized out, since you don't use the result of the division by zero. Try something like this instead:
extern "C" __declspec(dllexport) int DoException()
{
fprintf(stderr, "Before Error\n");
int a = 0;
int b = 10 / a;
fprintf(stderr, "After Error\n");
return b;
}
This will break the result out of internal linkage and force the compiler to emit the code.
You seem to be new to C# P/Invoke however, so here's a few tips:
the SetLastError attribute instructs the marshaller that the function will use the Win32 API SetLastError to set its error state, and your functions absolutely do not do so. You should remove it, because lying to the marshaller is never a recipe for success.
the SecurityCritical attribute has to do with process elevation between trust levels. Again, your application has nothing to do with that and should be removed.
the HandleProcessCorruptedStateExceptions attribute is deprecated since .Net Core (and including .Net 5). So if you're relying on it, you're nearing the end-of-life of your program, and it seems you barely even started writing it. The good news is that division by 0 isn't one of the exceptions that require this attribute to be processed, so again you should remove it!

AccessViolationException when accessing function with char * argument in c++ dll with C#

Problem Trying to use a function in a c++ dll with the following prototype...
int connectDfuBootloader(char * usbIndex)
...from C#, using any of these P/Invoke signatures, causes an AccessViolationException:
IntPtr...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int connectDfuBootloader(IntPtr usbIndex);
public static int connectDfuBootloader(string usbIndex)
{
IntPtr value = Marshal.StringToHGlobalAuto(usbIndex);
return connectDfuBootloader(value);
}
String...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader(string usbIndex);
StringBuilder...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader(StringBuilder usbIndex);
Variations on the theme of MarshalAs, tried LPStr, LPTStr etc...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader([MarshalAs(UnmanagedType.LPStr)]string usbIndex);
Variations on the theme of specifying the character set, ANSI, Unicode, etc
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl, CharSet =CharSet.Ansi)]
public static extern int connectDfuBootloader(string usbIndex);
Can anyone offer any suggestions?
Other things I've tried
Various StackOverflow posts, such as
AccessViolationException when accessing unmanaged C++ DLL with C#
Also, I tried making my own small C++ dll for test purposes, which contained a function with the same signature as the problem one above:
int functionAlpha(char * a)
{
if (a[0] == 'a')
return 10;
else
return 20;
}
This function I was able to access from C# without issue, using any of the methods above, e.g.
[DllImport(#"test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int functionAlpha(string a);
Which makes me think there is something special about the 'int connectDfuBootloader(char * usbIndex)' function. But they have the same signature, why would one work and the other not? I do not have the source code for the dll, so can't look in there to see if there are any other differences.
Update - Tried C++/CLI wrapper as suggested by #Taekahn below, same thing!
So, I have this in a header in the C++/CLI wrapper project:
#include "include/CubeProgrammer_API.h"
using namespace System;
namespace CLI
{
public ref class CubeWrapper
{
public:
int wrappedConnectDfuBootloader(String^ a);
};
}
Then in a cpp file in the wrapper project, I have:
namespace CLI
{
static char* string_to_ncchar_array(String^ string)
{
char* str = (char*)(Marshal::StringToHGlobalAnsi(string)).ToPointer();
return str;
}
int CubeWrapper::wrappedConnectDfuBootloader(String^ a)
{
a = "USB0";
// Calls the function from the linked c++ dll I'm trying to wrap
return connectDfuBootloader(string_to_ncchar_array(a));
}
}
And it gives the same result, System.AccessViolationException! What could be going on here?
Also tried using the dll from c++
Tried the same sequence of operations with the dll from a c++ application, and it worked fine.
Turns out that dxiv was on the right track; looked through some of the c++ example code supplied with the API and saw that several callbacks had to be setup first. Once I sorted those, problem fixed.

Type conversion issue in C#

I'm trying something (originally comes from here) about Heap and Pointer on C# Console project. And my program looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
public class Win32
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr malloc(int size);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int free(IntPtr region); //Change IntPtr befroe free method to int ---update---
}
public class Program
{
public unsafe void Heap()
{
int* num1, num2, answer;
num1 = Win32.malloc(sizeof(int));
*num1 = 999; // 999 should be the value stored at where pointer num1 refers to
num2 = Win32.malloc(sizeof(int));
*num2 = 1; // 1 should be the value stored at where pointer num2 refers to
answer = Win32.malloc(sizeof(int));
*answer = *num1 + *num2; // 1000 should be the value of pointer answer's reference
Console.WriteLine(*answer); // 1000?
Win32.free(num1);
Win32.free(num2);
Win32.free(answer);
}
}
After debugging, got error message says:
Error 1 Cannot implicitly convert type 'System.IntPtr' to 'int*'. An
explicit conversion exists (are you missing a cast?)
error CS1502: The best overloaded method match for 'Win32.free(System.IntPtr)' has some invalid arguments
error CS1503: Argument 1: cannot convert from 'int*' to 'System.IntPtr'
My questions here are why can't i use IntPtr before malloc and free, since both method return void? What changes should i make on my codes?
Thank you for help.
---Update---
change: public static extern IntPtr free(int hWnd); to public static extern int free(IntPtr region); , free(*num) to free(num)
gives extra 'CS1502' and 'CS1503' two errors.
---second Update---
C# deal heap thing automatically. There is no equivalent of malloc in C#. It's a dead end. T_T
A few mistakes:
in C/C++
void * malloc(int sizeToAllocate);
int free(void * region);
you pass into free the value returned by malloc. Thus your imports should be:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr malloc(int size);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int free(IntPtr region);
and consequently your freeing code should then become:
var num1Ptr = Win32.malloc(sizeof(int));
int * num1 = (int*) num1Ptr.ToPointer();
...
var num2Ptr = Win32.malloc(sizeof(int));
int * num2 = (int*) num2Ptr.ToPointer();
...
var answerPtr = Win32.malloc(sizeof(int));
int * answer = (int*) answerPtr.ToPointer();
...
Win32.free(num1Ptr);
Win32.free(num2Ptr);
Win32.free(answerPtr);

IndexOutOfRangeException - Cannot see call stack using PInvoke

I'm developing a C# app that takes data from a SerialPort, then it uses a C++ project (that I cannot change) to compute the read data.
The C++ project is using some native C code, that will call C# functions when the data are computed.
This is some examples of the called C# code which calls the C++ function using PInvoke:
[DllImport("MyLib.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void PacketHandler_HandlePacket(IntPtr packetHandler, IntPtr packet, int packetType);
[SuppressUnmanagedCodeSecurity]
[DllImport("MyLib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern float DeviceManager_AddSampleToBattery(IntPtr self, float sample, double sampleRate);
[SuppressUnmanagedCodeSecurity]
[DllImport("MyLib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern double DeviceManager_GetTimestamp(IntPtr self, int packetType);
[DllImport("MyLib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern void PacketHandler_AddCallbackBattery(IntPtr self, Delegate #delegate);
Then, the C++ code:
extern "C"
{
__declspec(dllexport) void _cdecl PacketHandler_HandlePacket(int * packetHandler, char * packet, int packetType){
PacketHandler_handlePacket((PacketHandlerStruct *)packetHandler, packet, (PacketHandlerPacketType)packetType);}
}
This C function is calling a C# function that I've set like this:
__declspec(dllexport) void _cdecl PacketHandler_AddCallbackBattery(int * packetHandler, void(*f)(void * obj, unsigned short int sample)){
PacketHandlerStruct * myPointer = ((PacketHandlerStruct *)packetHandler);
myPointer->delegate.addSampleToBattery = f;
}
Finally, the C code will call the "addSampleToBattery" function which is this C# callback (in where the first two are PInvoke calls like the first one I've posted)
private static float CallbackBattery(IntPtr self, ushort sample)
{
var value = DeviceManager_AddSampleToBattery(DeviceManager, sample, Const.BaseBvpSampleRate);
var timestamp = DeviceManager_GetTimestamp(DeviceManager, (int)PacketHandlerPacketType.Battery);
SocketManager.OnNewBatteryArrived(value, timestamp);
return value;
}
Other details:
The C# delegates are declared as follows:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate float DelegateBattery(IntPtr self, short sample);
private static readonly DelegateBattery DelegateCallbackBattery = CallbackBattery;
And setted like this:
var intptrDelegate = Marshal.GetFunctionPointerForDelegate(DelegateCallbackBattery);
var a = Marshal.GetDelegateForFunctionPointer(intptrDelegate, typeof(DelegateBattery));
PacketHandler_AddCallbackBattery(packetHandler, a);
So, everything seems to works, but a lot of times a IndexOutOfRangeException occurs. The main issue is that, even in Debug Mode with all the symbols loaded, I can't see the line that is throwing the exception because only the Disassembly View is available, and of course I can't get meaningful info from it.
Unhandled Exception: 'MyProgram.exe' (Win32):
The program '[3000] MyProgram.exe' has exited with code 0 (0x0).
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Threading.ThreadPoolWorkQueue.Dequeue(ThreadPoolWorkQueueThreadLocals tl, IThreadPoolWorkItem& callback, Boolean& missedSteal)
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
Thanks!
Semantics in correspondence with an IndexOutOfRangeException indicate that you've attempted to access memory too far from a base memory address, and thus you shouldn't be trying to deference that point in memory.
Think of an int array of 3 elements. If you try to index [3], that is *(base_address + (sizeof(int) * 3))* being dereferenced so that the next 4 bytes at that place in memory are provided for the value as an int. The only valid indexes are 0, 1, and 2, because those offsets are within the range of the 3 elements that we've allocated the space for.
We still need more code, as I don't see where any of this is relevant to the exception message.

Categories