Bad values from DLL functions

Started by
19 comments, last by myvraccount 9 years, 7 months ago

Hi, I have a HUGE problem with some code I'm writing, and I was wondering if anyone could advise me. BTW, I'm not at all a beginner to programming, but I'm a beginner to the specific APIs I'm using, and using DLLs in general, so please keep that in mind when you reply.

I'm using C# (VS2012 Express) in Windows 7 with XP compatibility, and I'm trying to use the Razer Hydra API (although the problem may not be specifically about this, but DLL usage in general, and I think that very well may be the case).

I've already sent this problem to Razer and after going back and forth with them, answering all their questions, they never actually gave me any suggestions and finally just told me to contact Sixense (the company that made the API) directly, which I did, and I haven't gotten a response for over 3 weeks. So needless to say, I'm quite frustrated and if anyone could help me I would REALLY appreciate it! Thanks.

So here's the problem:

I'm using the DLL, and I tried using it in a C++ project (in .NET), and it works fine, gives me good results, etc. However, it would be much more convenient for me to use it in C#, so as to be compatible with other code I have written. So I put it into C# using the Interop Services thing (I may not remember all the terminology and don't have the code with me at this computer so please humor me).

So anyway, it compiles, runs, and calls the functions correctly without causing exceptions. However, I don't get the data back that I expect to get returned. I'm using the functions in the same way and order that I use them in my C++ code, but in C# I get different results for some return values.

I especially get different results in a struct parameter that is byref. It's a variable that holds all the data for the current state of the currently selected Hydra controller (stick positions, position of the controller itself, buttons pressed etc.). There's a function that I call to retrieve all the data at once into this structure, and it gives me weird results:

- The button states are stored in a short, but it's always a random number each time I get it, and doesn't correctly represent the actual state.

- There's a 4x4 matrix of floats that holds vectors for the direction/orientation of the controller, and it returns the value "1" in the x, y and z coordinates of the first column, but all other values are "0". This may be a default matrix, but suggests that it might be communicating correctly to some extent (instead of all "0" or random data), except not getting the correct data for some reason.

- All other values in the struct (analog stick and trigger position, etc.) are always "0".

Notice, the way I built the struct was different in C# than the way the DLL specifies it in C++. The DLL uses arrays of fixed size. All arrays in C# are referential, so they wouldn't exist inline, so instead I just put float variables like: mat00, mat01, mat02 ... and so on. I think I sometimes had to change from one data type to another of the same size. There may have been a couple other little things I changed to try to make it compatible, but I think I used a class rather than a struct (can I even use a struct in C#? I never tried.)

So ultimately, I just need it to give me the right results. I'm not sure if I set the parameter up correctly. Does anyone know the correct way to make a C# object compatible with a C++ struct such that a DLL function would recognize it correctly? Or am I using the wrong calling convention or something? I remember setting it up to something like cdecl or call-something or other (I really apologize that I don't have the info with me).

Or is it just a Hydra-specific problem and if so, does anyone know how to fix it?

P.S.: I also want to do essentially the same thing with the Novint Falcon API/DLL. But that one requires I set up a couple callback functions using function pointers. Is there anything I need to know about operating with callback functions between a C# and a DLL written in C++?

Thank you very much!

Advertisement

You do realize you failed to post literally any code that could help find the problems you have... it would help very much. In any case, yes, if you need to send a C# struct to a native library you may need to pack it to have the required binary layout, you can achieve this using the interop services and StructLayout (it comes with a variety of struct packing strategies, and a FieldOffset attribute to tell the compiler exactly where you want each of your fields to be in memory if you are desperate). You can also use fixed size arrays in C# if needed, they are tricky and invasive so you don't want to abuse them, but it can be done.

You should use struct instead of class, because by default class fields are reorganized to suit the CLR's needs. Struct fields aren't, because they just happen to have the StructLayout.Sequential attribute by default and so will usually (but not always) have the right layout if you just copied them from the native struct declaration. I think this may be your problem.

Just to be clear, the API is C-compatible (and the DLL itself implemented in C++) and you are using PInvoke with autogenerated (or handwritten) extern function declarations? Or some other interop technology?


P.S.: I also want to do essentially the same thing with the Novint Falcon API/DLL. But that one requires I set up a couple callback functions using function pointers. Is there anything I need to know about operating with callback functions between a C# and a DLL written in C++?

No problem. You just need to declare a delegate (= callback) type and it should just work. Once you've sorted out your interop problem you can ask another question to work on that, but rest assured there is no limitation that prevents you from using callbacks in C# smile.png

Writing PInvoke for native DLL's is not hard work in general, and can be done mechanically (you might even try existing tools that automate this, but in my experience they tend to choke on exotic stuff or produce subtle, hard to find bugs) but you do need to be aware of all the different things the DLL functions expect, and be somewhat comfortable with memory layout, how to work in unsafe mode and take pointers to C# objects, and other low-level C# stuff, especially when the API is complex (e.g. callbacks and function pointers, strings in structs, double pointer parameters, etc..)

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

Bacterius, thanks for your reply, and please don't infect me. I'll address each of your statements in order:

The reason I didn't post any code is because (A) I'm not currently at the computer where I wrote it, so I don't have it to post, (B) that computer doesn't have an internet connection so I can't post from it anyway and (C) the code is of a secure nature and so I don't want to just put it online.

I'll look into the StructLayout and the FieldOffset and fixed size arrays (I didn't think they were possible in C#. But in what way are they invasive?), and I should have time to do all this tomorrow I think.

I didn't know the fields of an object are reorganized. That's good to know though. Though I'm not sure they fields being reorganized would cause this problem. Think of the evidence: It's not keeping the same values but putting them in the wrong place, instead its putting mostly "0" almost everywhere, with a few "1" values (in places in a matrix where you might even expect a 1 as a default), and putting a random number in one field, which would be an entirely different problem than reordering.

As I recall, I'm using the "extern" keyword (although I may be thinking of when I did it in C++).

So C# delegates are 1:1 compatible with C++ function pointers? I wasn't sure.

I wouldn't call the API "exotic", at least not from a software perspective, although the hardware it interacts with is a bit exotic.


The reason I didn't post any code is because (A) I'm not currently at the computer where I wrote it, so I don't have it to post, (B) that computer doesn't have an internet connection so I can't post from it anyway and © the code is of a secure nature and so I don't want to just put it online.

Fair enough, but be aware this makes it rather difficult to offer accurate advice.


I'll look into the StructLayout and the FieldOffset and fixed size arrays (I didn't think they were possible in C#. But in what way are they invasive?), and I should have time to do all this tomorrow I think.

Fixed size arrays are problematic because they require to be in an unsafe context to use, so you want to use it only where necessary lest it infect your code base. StructLayout and friends are perfectly fine though. You can also set the calling convention as a parameter to the DLLImport attribute that you tagged your extern functions with.


I didn't know the fields of an object are reorganized. That's good to know though. Though I'm not sure they fields being reorganized would cause this problem. Think of the evidence: It's not keeping the same values but putting them in the wrong place, instead its putting mostly "0" almost everywhere, with a few "1" values (in places in a matrix where you might even expect a 1 as a default), and putting a random number in one field, which would be an entirely different problem than reordering.

There's plenty of things that you could've gotten wrong that screws up the struct contents. It's not really possible to guess your problem from this description. If nothing else works, might I suggest you whip up a quick C++ DLL that has a similar struct and a dummy function that operates on it? Then you could try and it would tell you if it's coming from your code or is just a bug with the Hydra DLL, and you could also post code.


As I recall, I'm using the "extern" keyword (although I may be thinking of when I did it in C++).

You certainly are, otherwise the compiler would complain the function definition is missing.

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

One thing you need to be carefull when using dlls is the calling convention, that got me a few times, especially if the dll and the code using it arent made by the same compiler or language. Having said that, it's hard to say without seeing any code.

Sorry for the delay; it's been a hectic week and I had also forgotten my notes :-/.

I checked my code to clarify all this stuff:

First of all, I was actually using struct all along, not class. I've also been using "static extern type function-name();" style declarations for the functions, and CallingConvention.Cdecl in the attributes.

I wasn't initially using StructLayout, but then I tried setting it with LayoutKind.sequential, and that didn't affect it (that's supposedly the default, so I'm not surprised, but I thought it was worth a try). I didn't try LayoutKind.explicit with FieldOffset yet, but I wouldn't think that was necessary because primitive types have fixed sizes anyway, and if for some reason they were the wrong sizes (which I really don't think they are) then the data would just be in the wrong places but I wouldn't be seeing as many 0 values. I could try it anyway though.

Also, what's weird is that I had been using a "ref" keyword with my parameter and function declaration, but I think the C++ version uses a pointer, so I made an unsafe block and changed them to a pointer instead (I never did that in C# before). I got an error that it couldn't compile without "/unsafe", and I found how to set that, and it compiled.

But then when I ran it, I got something like this: "System.Runtime.InteropServices.MarshalDirectiveException: pointers cannot reference marshaled structures. Use ByRef instead." This implies I had it right the first time (although ByRef is a VB keyword), even though the C++ code is using a pointer, C# still expects a reference for some reason.

Bacterius, to answer your question about the C/C++ compatibility of the API, it all sounds correct, although I'm not actually sure whether it was written in C or C++ (it's not using objects so it could have been C).

The structure doesn't have any strings or double pointers or fancy stuff like that. I'm not using those at all.

I also tried changing the groups of variables (mat00, mat01, etc.) to an array instead. I declared it like "fixed float var-name[count];" (I tried to use a 2D array but it didn't compile, so I crammed it all into 1D) and it seemed to make some of my 1 values change to 0, which may possibly be worse but either way, I'm not getting the right values. I don't think it affects it in terms of spacing though.

I hope someone knows the solution with all this info, because I think it's just about all the info I can think of, unless someone has more specific questions that haven't been thought of yet, or if I misinterpreted them somehow. Anyway, I'd really like to get this to work and have been trying to fix it for a LONG time! Please tell me any info that could be useful. Thank you!

No more ideas? :( This is frustrating because I've been working on this problem for months, and I've encountered every possible snag along the way, and now I'm about 95% of the way to getting it working perfectly, and suddenly there's no more information available and I'm totally out of ideas! And the manufacturer(s) won't help me at all. If anyone has any suggestions at all, I'll consider them!

Not read most of the above, but couldnt see any useful pieces of code.

  1. How are you trying to call the native DLL from C#? Ive always used the DllImport attribute on a static extern function. If your doing stuff with IntPtr etc. show that.
  2. How are you exporting the function from the DLL? C or C++? The whole thing, expand macros fully, if you have struct/object parameters and returns show their declarations as well.

e.g.


//Not tested, from memory for x86

//C#
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int foo(int x, int y, string str);

//C++
extern "C" __declspec(dllexport) int foo(int x, int y, const char *str);//assuming compiler default of __cdecl

Well first of all, I can't show any code because it's not on this computer, and the one it's on doesn't have an Internet connection.

That said, I'm doing exactly what you're doing in the C# code. It's static extern, and even the CallingConvention is the same. I don't think I'm using any IntPtr or anything like that. What is that, just a pointer to an int, or a pointer that is the size of an int? In any case, I tried to use a pointer for a parameter but got an error so used ref instead (see details posted above).

I'm not sure what you mean by your next question, so I'll just give the details of the DLL: It came with a .h file, so it was either written in C or C++, but it doesn't seem to use any objects, so it may have been just C. But then I import it into C# to use it (I've also tried importing it into C++ and it works perfectly, but it doesn't work correctly in C#). I didn't make any macros. There's one function that uses a struct. I don't remember the name of the struct or function off hand, but it just gets the current state of the controller, but if you want more info, it's all in the Razer Hydra SDK manual. It's not a very complicated structure, and I think it's composed entirely of primitive types.

Anyway, I've tried a lot of different things (details posted above), but for some reason, even though the functions all compile and run and return values without any exceptions, the values are just wrong. It seems like I may be transferring data the wrong way or something, but then I would expect the data to be still correct, but out of order, or otherwise all random if it were getting the data from the wrong memory, but that's not the result I'm getting, so I don't know what's wrong.

Well first of all, I can't show any code because it's not on this computer, and the one it's on doesn't have an Internet connection.


Buy a cheap USB drive and transfer the files. Essentially everything you've told us boils down to "I tried stuff, but it didn't work." Playing guessing games about what could be wrong is a programmer's least favorite game ever.

I've gotten my Razer Hydra working in C# before, but I lost my code, so I can't post it here.

This topic is closed to new replies.

Advertisement