P/Invoking "wglChoosePixelFormatARB" Unbalances the Stack - c#

I am trying to use some pinvokes to set up a wgl context for some unit tests, but (so far) one of my methods unbalances the stack.
What I have done is to first create a window and get its DC. This all works as I am talking to the kernel32, user32, gdi32 libraries. I am wanting to draw to a pbuffer with OpenGL, and in order to create the PB, I need to use the extensions. Which requires that I have a context... This is all sadly normal and working so far.
The problem comes when I am trying to create the pbuffer. When I try get the configurations using wglChoosePixelFormatARB, this appears to unbalance the stack. I have just executed another ARB method (wglGetExtensionsStringARB) earlier to check the extensions - and that works fine using the same parent DC.
So, on to code... My delegate looks like this:
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public delegate bool wglChoosePixelFormatARBDelegate(
IntPtr dc,
[In] int[] attribIList,
[In] float[] attribFList,
uint maxFormats,
[Out] int[] pixelFormats,
out uint numFormats);
I find it like this:
[DllImport(opengl32)]
public static extern IntPtr wglGetProcAddress(string lpszProc);
// ...
var ptr = wglGetProcAddress("wglCreatePbufferARB");
wglCreatePbufferARB = Marshal.GetDelegateForFunctionPointer(ptr, typeof(wglChoosePixelFormatARBDelegate));
And I am invoking it like this:
var iAttrs = new int[]
{
Wgl.WGL_ACCELERATION_ARB, Wgl.WGL_FULL_ACCELERATION_ARB,
Wgl.WGL_DRAW_TO_WINDOW_ARB, Wgl.TRUE,
Wgl.WGL_SUPPORT_OPENGL_ARB, Wgl.TRUE,
Wgl.NONE, Wgl.NONE
};
var fAttrs = new float[2];
var piFormats = new int[1];
uint nFormats;
wglChoosePixelFormatARB(
parentDC,
iAttrs,
fAttrs,
(uint)piFormats.Length,
piFormats,
out nFormats);
if (nFormats == 0)
{
return IntPtr.Zero;
}
var pbuf = extensions.wglCreatePbufferARB(parentDC, piFormats[0], 1, 1, null);
The native side of this is (which is not exported):
BOOL WINAPI wglChoosePixelFormatARB (
HDC hdc,
const int *piAttribIList,
const FLOAT *pfAttribFList,
UINT nMaxFormats,
int *piFormats,
UINT *nNumFormats);
And the function def is this:
typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (
HDC hdc,
const int *piAttribIList,
const FLOAT *pfAttribFList,
UINT nMaxFormats,
int *piFormats,
UINT *nNumFormats);
The code looks OK to me, but there must be something wrong :) I hope someone can point out my error.
In case more code is required, I have it all here in a single file that is just a plain console app:
https://gist.github.com/mattleibow/755eba3c8ff5eafb9549842a0abb0426
(the code has large chunks of comments, this is just because I am busy porting from C++ to C#. And a third of the code is just the dllimports/structs/enums)

The function declaration looks reasonable but it seems that you are simply importing the wrong function.
You want wglChoosePixelFormatARB but actually import wglCreatePbufferARB. This smells like a class copy/paste SNAFU.
Fix this by correcting the name that you pass to GetProcAddress.

Related

How to P/invoke double*

I am trying to call a C++ method from C#. Unfortunately, the documentation for the C++ library is pretty skimpy, but I have access to a header file that defines the methods, so.
The C++ declaration is:
DWORD FAR PASCAL EXPORT Init(int numOfChannels,int startLead,int smpRate,double* pMV_in_1_NUM,int mode = 0);
The documentation unfortunately only gives a C++ example for using this method, which is:
Init (8, 1, 500, &pMV_in_1_NUM, 1);
But I'm trying to call it from C#, so my code is:
[DllImport("NVECGUSB.dll", EntryPoint = "Init")]
[return: MarshalAs(UnmanagedType.U4)]
private static extern UInt32 Init(
int numOfChannels,
int startLead,
int smpRate,
[param: MarshalAs(UnmanagedType.R8)]
ref double pMV_in_1_NUM,
int mode
);
and then...
double pmvIn1Num = 0;
resultCode = Init(8, 1, 500, ref pmvIn1Num, 1);
Console.WriteLine("Init returned {0}", resultCode.ToString("x"));
It doesn't crash or anything, but the resultCode is indicating that the parameters are incorrect, even though they values are correct according to the documentation, so I'm guessing there's something wrong with how I'm passing that double * parameter. (I'm willing to accept that I'm wrong there but right now I can't think of another reason for the error)
Note that I've also tried
[DllImport("NVECGUSB.dll", EntryPoint = "Init")]
[return: MarshalAs(UnmanagedType.U4)]
private static extern UInt32 Init(
int numOfChannels,
int startLead,
int smpRate,
double[] pMV_in_1_NUM,
int mode
);
and
double[] pmvIn1Num = new double[] { 0 };
resultCode = Init(6, 1, 250, pmvIn1Num, 1);
because Google searches throw up results about arrays of double, but I'm not sure this is applicable in my scenario.
So my question is, am I doing it correctly?
(Note that this relates to the Norav 1200M ECG device so if someone has worked wiht that and can tell me what I'm doing wrong, that would be great too)

P/Invoke C++ DLL for filling a managed buffer

Basically I have a DLL and a header file (C++) for a data acquisition device that I use for my research. The device has 8 sensors and each sensor has x,y,z,etc data points. I'd like to create a UI program in WPF for this device. So far I've successfully been able to communicate with the device in .NET via an unsafe code implementation and read data into an array of structs (where each struct corresponds to one of the sensors). However I'd like to see if I can get it working with a strictly safe code implementation, but I've just been hitting brick walls for a while now. This is my first time working with unmanaged vs. managed code so please bear with me. I've searched countless threads online and the rabbit hole just keeps getting deeper so I'd like some advice from someone with experience. Basically the API header file has a function with the following definition:
int GetSynchronousRecord(USHORT sensorID, void *pRecord, int recordSize);
Essentially we pass the function a buffer by reference, and it fills it up. I have the option of getting either a single sensor's data, or all sensors at once depending on the sensorID argument I pass. What HAS worked for me so far (in the managed world) is if I do the following:
[StructLayout(LayoutKind.Sequential)]
public struct DOUBLE_POSITION_ANGLES_TIME_Q_RECORD
{
public double x;
public double y;
public double z;
public double a;
public double e;
public double r;
public double time;
public ushort quality;
};
...
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD record, int recordSize);
...
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD record = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD();
// Get the data from SENSOR_1 only
errorCode = GetSynchronousRecord(1, ref record, Marshal.SizeOf(record));
So this implementation works fine, I can get all the coordinate data and at a really good speed. However, I'd like to get ALL the sensors at once. In the C++ API code samples, they pass the GetSynchronousRecord function an ARRAY of STRUCTS, one struct for each sensor. I tried to do the same in C# as follows:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record, int recordSize);
// Define Array of Struct for each sensor
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[8];
while(recording) {
...
// Get data from ALL sensors (0xffff)
errorCode = GetSynchronousRecord(0xffff, ref record, Marshal.SizeOf(record)*8);
...
}
But this straight up crashes my program with an System.ExecutionEngineException error. I've read that since my function is expecting a void* pointer, that I should use an IntPtr argument, but this approach seemed quite confusing to be honest. Another thing I tried is to actually loop over each sensor and call the function for the sensor, but this dropped the speed INSANELY almost to 1 record/second (instead of 100 records/second). Many other similar threads on stackexchange say to use out parameter, or to use [In, Out] attribute on the function definition, but none of these suggestions worked.
TL;DR: If I understand my situation correctly, I have a MANAGED array of structs that I need to correctly pass to a C++ function as an argument (pass by reference), and then the function will fill my structs with data from a data acquisition device.
I apologize for the wall of text/code, any information for me from someone with experience would be much appreciated.
EDIT: Just to clarify, the GetSynchronousRecord function is INSIDE a while loop where on each iteration I'm getting new data points for each struct.
Your second p/invoke declaration is wrong. You had
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(
ushort sensorID,
ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record,
int recordSize
);
The problem is the array parameter. Because you pass that array by ref that actually makes it a double pointer. Instead you want to simply remove the ref and declare the import like so:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(
ushort sensorID,
[Out] DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record,
int recordSize
);
The [Out] attribute tells the marshaler that the data is flowing out of the function. Without it the default assumption is that the data flows in.
When you call the function do so like this:
errorCode = GetSynchronousRecord(0xffff, record, Marshal.SizeOf(record)*record.Length);
The best way is probably using IntPtr rather than ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD.
I'd change the P/Invoke signature to:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, IntPtr record, int recordSize);
Create an IntPtr that points to the needed memory space:
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>() * 8);
Call the P/Invoke function (which should fill the memory with the structs):
errorCode = GetSynchronousRecord(0xffff, ptr, Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>() * 8);
Get the structures from the memory block:
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] records = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[8];
for (i = 0; i < 8; i++) {
records[i] = Marshal.PtrToStructure<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>(IntPtr.Add(ptr, i * Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>()));
}
Marshal.FreeHGlobal(ptr);

Powershell using C# code from Add-Type crashes shell (SystemParamtersInfo)

I'm trying to write a script that toggles mouse properties for me on execution ... but I can't get past the process to query some of the attributes. I'm not a C# programmer and only a budding Powershell scripter but I have experience in other scripting languages ... this has the feeling of me just having done something dumb.
Executing the script below causes a crash, with very little useful information.... so my first question, I guess, is this: is there some way to get better information as to why my script crashed powershell?
As far as doing this other ways, I already tried munging the registry, but I don't want to require a restart to to apply the settings (this is more or less a quick way for me to toggle a setting on and off once I get it squared away)... As far as I've read so far, SystemParametersInfo (from user32) is the way to do it, but I don't have a C# IDE or the means to compile programs in C#, and would rather use powershell if it's not too cumbersome.
Additionally, it seems that using the ref int lpvParam version of the SPI imported call works (I used it with SPI_GETMOUSESPEED) but the ref int[] lpvParam version does not.. Maybe it's the function definition that's being problematic? I assumed that I'd need to import it multiple times and overload it, but maybe this is wrong? Replace the function call at the end to test this -- use $mouse.spiGetMouseSpeed
MS's documentation (https://msdn.microsoft.com/en-us/library/ms724947) suggests that there are multiple varible types that can go in for lpvParam so I figured overloading it was the way to go...
Here's the code.
#https://msdn.microsoft.com/en-us/library/ms724947
Add-Type #"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Enigma
{
public class Mouse {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(
int uAction,
int uParm,
ref int lpvParam,
int fuWinIni);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(
int uAction,
int uParm,
ref int[] lpvParam,
int fuWinIni);
private const int SPI_GETMOUSE = 0x0003;
private const int SPI_GETMOUSEHOVERHEIGHT = 0x0064;
private const int SPI_GETMOUSEHOVERTIME = 0x0066;
private const int SPI_GETMOUSEHOVERWIDTH = 0x0062;
private const int SPI_GETMOUSESPEED = 0x0070;
private const int SPI_GETMOUSETRAILS = 0x005E;
private const int SPI_GETMOUSEWHEELROUTING = 0x201C;
public int[] spiGetMouse() {
int[] res = new int[3];
SystemParametersInfo(SPI_GETMOUSE, 0, ref res, 0);
return res;
}
public int spiGetMouseSpeed() {
int res = 0;
SystemParametersInfo(SPI_GETMOUSESPEED, 0, ref res, 0);
return res;
}
}
}
"#
$mouse = New-Object Enigma.Mouse
# This function call fails and crashes PowerShell ISE
$mouse.spiGetMouse()
exit
Arrays are reference types in .NET, so them does not need additional ref qualifier to be passed by reference. Thus you should use int[] lpvParam instead of ref int[] lpvParam. int (System.Int32) is a value type, so it have to have ref qualifier to be passed by reference.

system.access.violation exception while calling c++ function from a thread in c#

i am importing c++ function from a dll to my winform c# app:
[DllImport(#"eyeWhere.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int Eye_GetPositionS(string filename, [MarshalAs(UnmanagedType.LPArray, SizeConst = 9)] double[] sensors);
when i call the function from the constructor its working fine.
the problem is when i am calling it from a new thread opened within the
"fleck websocket server" Fleck, "onMessage" Action,
then it throws the "system.access.violation" exception.
i managed to narrow down the problem to the double array that i am passing,
it seems like the pointer to it is corrupted.
i cant find the source of the problem one thing is sure the function from the dll is working as i tested it.
function call(two stages):
open new thread within "fleck":
socket.OnMessage = message =>
{Thread locationThread = new Thread( unused => processLocation(fileName,socket,sensorsList,sensors) );
locationThread.Start();}
the actual function:
private void processLocation(string fileName, IWebSocketConnection sock, List<Sensor> sensorsList, double[] sensors)
{
int map_position = Eye_GetPositionS(fileName,sensors);
string locationString = "floor:1,mx:" + (map_position / 10000) + ",my:" + (map_position % 10000);
// send location string to user
sock.Send(LOCATION_CODE + "-" + locationString);}
the interface is:
extern "C" __declspec(dllexport) int Eye_GetPositionS(const wchar_t *fname_mob, double sensors[9], int &map_x, int &map_y)
i am not passing the two last arguments (int&) as agreed with the man who wrote the dll.
I am not passing the two last arguments (int&) as agreed with the man who wrote the DLL.
Well, there's the problem. You cannot omit parameters. It should be:
[DllImport(#"eyeWhere.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode)]
public static extern int Eye_GetPositionS(
string filename,
[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 9)]
double[] sensors,
ref int map_x,
ref int map_y
);
You simply cannot omit these parameters, no matter what the guy who wrote the DLL says. Perhaps he means that the parameters are really int* and you can pass nullptr.
I've used ref for these parameters, but perhaps they should be out. You presumably know which is appropriate.
Likewise I'm guessing at the intent of the sensors parameter. If the data flows out rather than in, then use Out. If the data flows in both directions, use [In, Out, ...].

Using DLLImport with char** and float**

I'm trying to use a DLL file in a C# program using DLLImport.
I have the following function in the DLL:
int method(char* method, char** params, int n_params, float** res_arr, int* n_res);
Function call should be something like this:
method = "method1"
char** = {"param1=1", "param2=2"}
n_params = 2
res_arr = the DLL function allocates an array and points this to it
n_res = the DLL function sets to the number of results
There is a seperate function for freeing the float**.
My current code in C# is this:
private static extern int method(string method, ref IntPtr params, Int32 n_params, ref IntPtr res_arr, IntPtr n_res);
I'm new to C# (and my C knowledge is a bit lacking) and can for the life of me not figure out how to call this function (been faceplanting my keyboard for two days). Could someone give me an example of how this should be done and how to call the function?
My main problem is what to do with the char** and float**, I don't know if it's the correct pointer types in the declaration and don't know how I'm supposed to create and send my char** to the function.
Worth noting is that I may not change anything in the DLL file.
EDIT
This is the description of the function which releases the result array:
free_results(float* res_arr)
EDIT2
I can now call the method and I get values back, my problem now is that I seem to have a problem accessing the float values. As suggested I'm using Marshal.Copy() like this:
[DllImport("libs\\myDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int method(string method, string[] params, Int32 n_params, out IntPtr res_arr, ref int n_res);
IntPtr res_arr = IntPtr.Zero;
int n_res = 0;
string[] s = new string[] { "param1" };
method("analyze", s, s.Length, out res_arr, ref n_res);
float[] f_res = new float[n_res];
Marshal.Copy(res_arr, f_res, 0, n_res);
The problem is I only seem to get rubbish values in the float vector. For example in one case i should get 100.0 but get either 15.0 or 3840.0, which tells me that either I'm using the pointer wrong when copying or there is something else fishy. The code in the DLL is working as it should since there is another program written in C which gets the correct values. It feels like I making a float of the pointer and not what it points at.
This is the code that solved my initial problem (as suggested by Hans Passant):
[DllImport("libs\\myDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int method(string method, string[] params, Int32 n_params, out IntPtr res_arr, ref int n_res);
IntPtr res_arr = IntPtr.Zero;
int n_res = 0;
string[] s = new string[] { "param1" };
method("analyze", s, s.Length, out res_arr, ref n_res);
float[] f_res = new float[n_res];
Marshal.Copy(res_arr, f_res, 0, n_res);
My second problem, where the float array gave rubbish values, were a result of me being a complete ass hat. There is another function in the DLL which has to be called before using method(...), in order for it to have values to process. After adding that call everything worked perfectly.

Categories

Resources