How to P/invoke double* - c#

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)

Related

P/Invoking "wglChoosePixelFormatARB" Unbalances the Stack

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.

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, ...].

conversation with typedef variable in c++

i have c++ type definition like this
typedef void* ScreenNode;
and use it in my c++ code like this
ret = InitNode("factory", &g_screen_node, 128, 128, EN_C1Dev);
**EN_C1Dev is a ENUM
my typedef variable is a void pointer and InitNode return a value by changing pointer value "g_screen_node".
now i use this function in a dll inside a C# program with this definition
c++ code
extern "C" __declspec(dllexport) int CB_initNode(ScreenNode* p_g_screen_node,int width,int heigth,EN_DevType EN_C1Dev)
{
int ret = 0;
//InitNode ----------------------------------------------------------------
ret = InitNode("factory", p_g_screen_node, 128, 128, EN_C1Dev);
return ret;
}
dll definition class
[DllImport(dllName)]
public static extern
int CB_initNode(out IntPtr g_screen_node, int width, int heigth, EN_DevType EN_C1Dev);
and finally my main program
IntPtr g_screen_node = new IntPtr();
ret = BBIA.CB_initNode(out g_screen_node , 128, 128, EN_DevType.EN_C3Dev);
the CB_initNode function return error code and g_screen_node not return Expected value and stay on 0x00000000!
Answering the parts of your question in order:
You should consult the documentation (or source code, if no documentation is available) of InitNode for the meanings of the input and output arguments of InitNode. Most likely the method actually allocates and initializes a ScreenNode and returns an opaque pointer to the caller -- one whose internal implementation is hidden. However, there is not enough information in your question to determine this for certain.
My answers to the remaining parts of your question assume that this is true. If not, please update your question with appropriate documentation from InitNode, then we can take another cut at answering.
c++ code. You need to decorate your function with extern "C" __declspec(dllexport):
extern "C" __declspec(dllexport) int CB_initNode(ScreenNode *p_g_screen_node,int width,int heigth, EN_DevType EN_C1Dev)
{
int ret = 0;
//InitNode ----------------------------------------------------------------
ret = InitNode("factory", p_g_screen_node, 128, 128, EN_C1Dev);
return ret;
}
You also need to pass in a pointer to a ScreenNode (i.e. a double pointer) so that the pointer value set in InitNode() can be communicated out.
And then in your c# extern declaration, you define the returned pointer as an out IntPtr:
[DllImport(dllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int CB_initNode(out IntPtr g_screen_node, int width, int heigth, EN_DevType EN_C1Dev);
Then in the main program:
IntPtr g_screen_node;
var ret = BBIA.CB_initNode(out g_screen_node, 128, 128, EN_DevType.EN_C3Dev);
Then later, if you need to pass a ScreenNode back to the same library, you would use the IntPtr previously returned. And when done with it, don't forget to free it -- presumably the library you are using has a method for that also.
Finally, you don't state what EN_DevType is, but I am guessing that it is an enum. If so, you should check you have the correct values. If not an enum, then my answer to your question might need to be updated.
Update
If InitNode needs to know the current value of the incoming void ** pointer because it is preallocated or preinitialized, the signatures would be as follows:
[DllImport(dllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int CB_initNode(ref IntPtr g_screen_node, int width, int heigth, EN_DevType EN_C1Dev);
IntPtr g_screen_node = // However you initialize are supposed to allocate it using your library, possibly just IntPtr.Zero.
var ret = BBIA.CB_initNode(ref g_screen_node, 128, 128, EN_DevType.EN_C3Dev);
In order for us to answer in confidence rather than guessing, please update your question with the documentation for InitNode, and let us know what functions you are calling before the call to InitNode, if any.
&g_screen_node is used to pass the address of g_screen_node to the InitNode function. This is usually means the parameter will be used as an output parameter.
As to why you're getting an error code, I'll take a stab and say your call to BBIA.CB_initNode needs &ab, not ab

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.

AccessViolationException when trying to call c++ external function with 'new char[...]' inside it

I catched a strange issue when i was trying to call unmanaged c++ function from c# code.
the c++ function looks like this:
extern "C"{
__declspec(dllexport) int Test(char * pixels, int length, int height){
int width = length / height;
// char * test = new char[length];
return width;
}
}
And from C# I'm trying to call it using next constructions:
[DllImport("Test.exe", EntryPoint = "Test")]
public static extern int Test(byte[] pixels, int length, int height);
...
var bytes = new byte[] { 1, 1, 1, 1, 1 };
var height = 1;
var result = Test(bytes, bytes.Length, height);
This code works well, but...
If I try to uncomment c++ line with 'new', i'm getting the System.AccessViolationException.
Can anybody explain what happening there and why?
UPDATED
Thanks to David Heffernan, and now my code looks like this:
[DllImport("Test.exe", EntryPoint = "Test",
CallingConvention = CallingConvention.Cdecl)]
public static extern int Test(byte[] pixels, int length, int height);
Unfortunately it changes nothing
RESOLVED
Don't use *.exe as module. Everything works well after compilation module as dynamic library.
It's caused by fundamental differences between DLLs and executable file linking. For more infomation see MSDN LoadLibrary page and MSDN About DLLs page.

Categories

Resources