Sign in to follow this  

Bad values from DLL functions

This topic is 1174 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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!

Share this post


Link to post
Share on other sites

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..)

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites


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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

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
Edited by SyncViews

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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. Edited by Nypyren

Share this post


Link to post
Share on other sites

Unfortunately I can't post the code anyway.  There's too much stuff mixed into it that is not to be seen by the general public.

 

Not to be argumentative, but I've said more than "I tried stuff", I explained the specific things I tried, in this and other posts (Almost all my posts are in some way related to this problem if you do a search.  The other problems are largely fixed though, but lead up to this).

 

But the point is, if you know of any possible causes of the problem, or things that tend to go wrong in general when using C++ DLLs in C# (I'm not convinced the problem is specifically with the Hydra API, but probably the way I'm using the DLL in general), please post it.

 

Basically, there are a finite number of steps that need to be done to achieve this, and if I knew what those steps were, in a reliable way that won't cause errors, then I could do it, no problem!

 

Are you absolutely sure you don't have your code backed up?  You're the first person I know of who ever made a Hydra C# compatible.  No one other than us has even attempted it as far as I know.  If you don't have it, do you know of anyone else who does/might?

Share this post


Link to post
Share on other sites

You dont need to post the entire source file, infact I suspect no one wants to look at the entire source file...

 

Pick a simple function that does not work.

- Find the C/C++ declaration in a header file, post that. If it has a struct/object parameter, post that delcaration as well. If it uses a macro (e.g. MYLIB_DLLEXPORT) post or expand that, etc.

- Post your C# PInvoke declaration and anything directly related to it (e.g. if you passing or returning an object, and that object is not a standard type, post that objects data declaration, strip/remove all the C# method definitions).

 

e.g. more complex example....

//again untested
//C, according to MSDN docs, but you could use your actual header files
HANDLE WINAPI FindFirstFile(
  _In_   LPCTSTR lpFileName,
  _Out_  LPWIN32_FIND_DATA lpFindFileData
);
typedef struct _WIN32_FIND_DATA {
  DWORD    dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD    nFileSizeHigh;
  DWORD    nFileSizeLow;
  DWORD    dwReserved0;
  DWORD    dwReserved1;
  TCHAR    cFileName[MAX_PATH];
  TCHAR    cAlternateFileName[14];
} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;

typedef PVOID HANDLE;
typedef void *PVOID;//so HANDLE is just an opaque pointer, can use IntPtr to refer to that in C#
typedef unsigned long DWORD;
#ifdef UNICODE //am using CharSet.Unicode
 typedef WCHAR TCHAR;
#else
 typedef char TCHAR;
#endif
typedef wchar_t WCHAR;
typedef struct _FILETIME {
  DWORD dwLowDateTime;
  DWORD dwHighDateTime;
} FILETIME, *PFILETIME;



//C#
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct WIN32_FIND_DATA
{
    public uint dwFileAttributes;
    //there is actually an existing thing for FILETIME, so can use it rather than making another struct myself
    public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
    public uint nFileSizeHigh;
    public uint nFileSizeLow;
    public uint dwReserved0;
    public uint dwReserved1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
    public string cFileName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=14)]
    public string cAlternateFileName;
}

Not that I did not post the entire Windows headers, but that is pretty much everything needed for that specific PInvoke

Edited by SyncViews

Share this post


Link to post
Share on other sites

(Lactose, I don't have a card slot in my phone, and it's not a smart phone anyway.)

 

 

So, here's the status.  First I'll display the code from the sixense.h file that corresponds to the DLL that I'm using, with my own comments explaining it as I see it.  I don't use all the functions so there's no need to be overwhelmed:

 

 

 

// Standard definitions for exporting and stuff

 

#ifndef _SIXENSE_H_

#define _SIXENSE_H_

 

#if defined(WIN32)

  #ifdef SIXENSE_STATIC_LIB

    #define SIXENSE_EXPORT

  #else

    #ifdef SIXENSE_BUILDING_DLL

      #define SIXENSE_EXPORT __declspec(dllexport)

    #else

      #define SIXENSE_EXPORT __declspec(dllimport)

    #endif

  #endif

#else

  #define SIXENSE_EXPORT

#endif

 

// Flags for button states defined as bits within an int and some other constants

 

#define SIXENSE_BUTTON_BUMPER (0x01<<7)

#define SIXENSE_BUTTON_JOYSTICK (0x01<<8)

#define SIXENSE_BUTTON_1 (0x01<<5)

#define SIXENSE_BUTTON_2 (0x01<<6)

#define SIXENSE_BUTTON_3 (0x01<<3)

#define SIXENSE_BUTTON_4 (0x01<<4)

#define SIXENSE_BUTTON_START (0x01<<0)

#define SIXENSE_SUCCESS 0

#define SIXENSE_FAILURE -1

#define SIXENSE_MAX_CONTROLLERS 4

 

// The structure that holds all the data for the current state of the controller.  This is what's giving me the most trouble!

 

typedef struct _sixenseControllerData

{

  float pos[3];

  float rot_mat[3][3];

  float joystick_x;

  float joystick_y;

  float trigger;

  unsigned int buttons;

  unsigned char sequence_number;

  float rot_quat[4];

  unsigned short firmware_revision;

  unsigned short hardware_revision;

  unsigned short packet_type;

  unsigned short magnetic_frequency;

  int enabled;

  int controller_index;

  unsigned char is_docked;

  unsigned char which_hand;

  unsigned char hemi_tracking_enabled;

} sixenseControllerData;

 

// Extra structure to hold an array of controllers.  It doesn't do me much good at the moment, so you can probably ignore it.

 

typedef struct _sixenseAllControllerData

{

  sixenseControllerData controllers[4];

} sixenseAllControllerData;

 

// More standard definitions

 

#if defined(__LANGUAGE_C_PLUS_PLUS)||defined(__cplusplus)||defined(c_plusplus)

extern "C" {

#endif

 

// Function signatures

 

SIXENSE_EXPORT int sixenseInit( void );

SIXENSE_EXPORT int sixenseExit( void );

SIXENSE_EXPORT int sixenseGetMaxBases();

SIXENSE_EXPORT int sixenseSetActiveBase( int i );

SIXENSE_EXPORT int sixenseIsBaseConnected( int i );

SIXENSE_EXPORT int sixenseGetMaxControllers( void );

SIXENSE_EXPORT int sixenseIsControllerEnabled( int which );

SIXENSE_EXPORT int sixenseGetNumActiveControllers();

SIXENSE_EXPORT int sixenseGetHistorySize();

SIXENSE_EXPORT int sixenseGetData( int which, int index_back, sixenseControllerData * );

SIXENSE_EXPORT int sixenseGetAllData( int index_back, sixenseAllControllerData * );

SIXENSE_EXPORT int sixenseGetNewestData( int which, sixenseControllerData * );

SIXENSE_EXPORT int sixenseGetAllNewestData( sixenseAllControllerData * );

SIXENSE_EXPORT int sixenseSetHemisphereTrackingMode( int which_controller, int state );

SIXENSE_EXPORT int sixenseGetHemisphereTrackingMode( int which_controller, int *state );

SIXENSE_EXPORT int sixenseAutoEnableHemisphereTracking( int which_controller );

SIXENSE_EXPORT int sixenseSetHighPriorityBindingEnabled( int on_or_off );

SIXENSE_EXPORT int sixenseGetHighPriorityBindingEnabled( int *on_or_off );

SIXENSE_EXPORT int sixenseTriggerVibration( int controller_id, int duration_100ms, int pattern_id );

SIXENSE_EXPORT int sixenseSetFilterEnabled( int on_or_off );

SIXENSE_EXPORT int sixenseGetFilterEnabled( int *on_or_off );

SIXENSE_EXPORT int sixenseSetFilterParams( float near_range, float near_val, float far_range, float far_val );

SIXENSE_EXPORT int sixenseGetFilterParams( float *near_range, float *near_val, float *far_range, float *far_val );

SIXENSE_EXPORT int sixenseSetBaseColor( unsigned char red, unsigned char green, unsigned char blue );

SIXENSE_EXPORT int sixenseGetBaseColor( unsigned char *red, unsigned char *green, unsigned char *blue );

 

// Ending definitions

 

#if defined(__LANGUAGE_C_PLUS_PLUS)||defined(__cplusplus)||defined(c_plusplus)

}

#endif

 

#endif /* _SIXENSE_H_ */

 

 

 

 

 

Now for the fun part.  Here's my code in C# (sorry the formatting and colors got messed up):

 

 

 

 

using System.Runtime.InteropServices;

namespace DLLImporter
{
 public unsafe partial class Form1 : Form
 {
  //[StructLayout(LayoutKind.Sequential)]
  unsafe struct controller
  {
   float X;
   float Y;
   float Z;
   /*float mat00;
   float mat01;
   float mat02;
   float mat03;
   float mat10;
   float mat11;
   float mat12;
   float mat13;
   float mat20;
   float mat21;
   float mat22;
   float mat23;
   float mat30;
   float mat31;
   float mat32;
   float mat33;*/
   fixed float mat[16];
   float joyX;
   float joyY;
   float trigger;
   uint buttons;
   char seqNum;
   /*float quatX;
   float quatY;
   float quatZ;
   float quatW;*/
   fixed float quat[4];
   ushort firmwareRev;
   ushort hardwareRev;
   ushort packetType;
   ushort magFreq;
   int enabled;
   int index;
   char docked;
   char hand;
   char trackingEnabled;
  };
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseInit();
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseExit();
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseGetMaxBases();
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseSetActiveBase(int which);
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseGetNumActiveControllers();
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseIsControllerEnabled(int which);
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseIsBaseConnected(int i);
  [DllImport("sixense.dll", CallingConvention = CallingConvention.Cdecl)]
  static extern int sixenseGetNewestData(int which, sixenseControllerData *data);
  public Form1()
  {
   unsafe
   {
    InitializeComponent();
    sixenseInit();    // Returns 0
    int maxBases = sixenseGetMaxBases(); // Returns 4
    sixenseSetActiveBase(1);  // Returns 0
    sixenseGetNumActiveControllers(); // Returns 0
    sixenseIsBaseConnected(0);  // Returns 0
    sixenseControllerData data = new sixenseControllerData();
    sixenseGetNewestData(0, &data);  // Throws MarshalDirectiveException
    sixenseExit();    
   }
  }
 }
}

 

 

 

 

As you can see, I tried to do the mat and quat as individual variables, and switched to making them fixed arrays, but it didn't have much effect (although the mat values changed I think, but they're still not right, and have lots of 0 values).

 

I've tried it with and without using the StructLayout attribute.  It doesn't seem to have an effect.

 

I tried it with and without the unsafe block.  Originally I was using the data variable in SixenseGetNewestData using a ref keyword instead of making it a pointer and using &.  When I did it that way, it didn't throw an exception, but put bad values in the data variable.  Now it throws an exception instead.  More details are posted above.

 

If anyone could please solve this, I've been working on it for months now and I can feel that I'm so close, but something stupid keeps getting in my way somehow!  Thank you very much!

Share this post


Link to post
Share on other sites

Okay, first of all, the mat member in the C++ struct is a 3x3 float array (i.e. 9 floats), whereas the C# one has 16 = 4x4 floats. So that's not going to work, fix it.

 

Secondly, don't use char, in C# char is a unicode character, if you just want a byte use "byte" (or "sbyte" if you really need it to be signed). This is one of those things that you could stare at for days smile.png

 

Third, you don't need to unroll the matrices, they are floats and C++ arrays are guaranteed to (well, defined to) not have any padding, just like C# arrays with the "fixed" keyword, at least for floats as I am not sure about smaller types (though it may ultimately be more convenient to not use fixed arrays, since as I said earlier using fixed arrays basically requires everything that even so much as looks at the struct to be marked unsafe).

 

Fourth, there's something wrong with your code: the DllImport for sixenseGetNewestData uses "sixenseControllerData", but your struct is called "controller" huh.png in any case that line of code shouldn't throw an exception, so make sure to check your types (or just make it take a ref struct, that is usually the recommended way to do it).

 

Okay, try and fix these immediate problems and see if it's any better. There may still be padding issues, but at least the first few members will normally be filled in right, which should suggest things are improving.

Share this post


Link to post
Share on other sites

Wow, I can't believe I overlooked the array size problem.  I could have sworn it was 4x4!  Who the hell makes a 3x3 array anyway?  I mean I know it's 3D but 4x4 is standard because it's easier to do rotations and such, although with an input device I guess that's not necessary.

 

Although, could I somehow make a [3][3] array instead of [9]?  It's not a big deal, but it seems more reasonable.  For some reason I think I had trouble doing that.

 

I didn't realize char would be 2 bytes!  Thanks.

 

I'll keep the fixed/unsafe problem in mind.  I may well just use the individual variables then.

 

I see what you mean about the controller name.  That's a weird editing glitch.  Just pretend the names match, when they do it compiles fine, so I wouldn't worry about that.

 

Thanks a bunch, and I can't wait to try these alterations, but since my schedule's bad, it'll probably be on Saturday, so I'll post my results next week.

Share this post


Link to post
Share on other sites

Who the hell makes a 3x3 array anyway?  I mean I know it's 3D but 4x4 is standard because it's easier to do rotations and such, although with an input device I guess that's not necessary.


Outside of excursions into homogeneous space there is really not much use for 4x4 matrices. All orientations (= all rotations) can be described completely in 3x3 matrices. Even if you have to include a translation that's easier modeled as a 3x4 matrix or a more standard Mx + b affine mapping.

Share this post


Link to post
Share on other sites


I'll keep the fixed/unsafe problem in mind. I may well just use the individual variables then.

 

One common idiom is to have an unsafe struct that contains fixed arrays and everything, and a "managed" struct that contains a proper matrix type, etc.. and a helper function to convert from one to the other. Then you can interface with the DLL using your managed struct transparently with all the low-level details being handled right before or after the call.

Share this post


Link to post
Share on other sites

This topic is 1174 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this