-1

I've got a small issue regarding P/Invoke. Currently, I am implementing a wrapper for a C API for a hardware device whose original C# implementation sucks. The issue/inconvenience I am having is the following:

The API implements multiple structs for getting/setting device settings. These are of similar structure, thus I am giving an example implementation:

typedef struct _Struct1
{
    bool b1;
    unsigned int ui1;
    unsigned int ui2;
    unsigned int ui3;
    unsigned int ui4;
    unsigned int ui5;
} Struct1;

The getter and setter are implemented as follows:

unsigned int SetSetting(bool b1, Struct1 s1);
unsigned int GetSetting(bool b1, Struct1 &s1);

The C# DllImport is:

[DllImport("api.dll", EntryPoint = "GetSetting", CallingConvention = CallingConvention.StdCall]
public static extern uint GetStatus(bool b1, CStruct1 s1);

[DllImport("api.dll", EntryPoint = "SetSetting", CallingConvention = CallingConvention.StdCall]
public static extern uint SetStatus(bool b1, SStruct1 s1);

The C# struct implementation is done using LayoutKind.Sequential and represents the C struct exactly and the P/Invoke calls work quite well except for: As you may noticed, the getter and setter DllImports differ slightly in that the setter uses a C# struct (SStruct1) and the getter a C# class (CStruct1):

[StructLayout(LayoutKind.Sequential)]
public struct SStruct1
{
    public bool b1;
    public uint ui1;
    public uint ui2;
    public uint ui3;
    public uint ui4;
    public uint ui5;
}

[StructLayout(LayoutKind.Sequential)]
public class CStruct1 { /* same as struct */ }

I have not been able to get P/Invoke working using just one, either a class or a struct.

Do you have any tips regarding a more convenient solution for this problem?

Changing the struct to class and vice versa does give a PInvokeStackImbalance exception.

I am guessing this has to do with the fact that the setter is expecting a struct by value and the getter by reference, given it's out-parameter-equivalent. I've tried any Marshal attribute and parameter definitions, I could think of, but am hitting a dead end. Google does not seem to help.

Any help is greatly apprechiated.

Edit

I've been mistaken: The API is ANSI C, not C++.

Update

I've tried the solution suggested (SStruct for setter, out SStruct for getter) and although working fine for a function with a struct consisting entirely of uints, it produces complete garbage for the struct in question:

| Name | correct value   | returned value |
| ---- | --------------- | -------------- |
| b1   | False           | True           |
| ui1  | 0               | 3355443223     |
| ui2  | 0               | 1677721600     |
| ui3  | 0               | 0              |
| ui4  | 0               | 0              |
| ui5  | 0               | 0              |

Remember: When using using aforementioned DllImports, this works just fine.

  • You could work in either C++ to make a C interface or C++/CLI to wrap all of the data structures. – crashmstr May 13 '16 at 13:22
  • First I wouldn't use StdCall instead use C calling convention since you are using c++ and c#. Standard calling is windown standard calling conventions. Structures need to be calling by reference (pointers) not by value. So you must declare the structures as IntPtr. So use Marshal StructureToPtr and Marshal PtrToStructure. – jdweng May 13 '16 at 13:24
  • You need to use `struct` since the structure is passed by value to `SetSetting`. Then `GetSetting` accepts the address of the structure, so use an `out` parameter or `ref` parameter for the structre. Do ignore @jdweng, there's no need at all to use `IntPtr` to pass the structure. I downvoted the question though because it is incomplete. You failed to show the structure declaration. – David Heffernan May 13 '16 at 13:34
  • The getter argument must be declared as `[Out] out CStruct1`. Using `bool` is troublesome, it is likely to be 1 byte in a C++ program instead of the default of 4 bytes in C. Use [MarshalAs(UnmanagedType.U1)]. Do verify that Marshal.SizeOf() in C# matches sizeof in C++ so you can be sure that you got the structure declaration correct. – Hans Passant May 13 '16 at 13:34
  • I've tried the solution @DavidHeffernan suggested and am very confused: Although working fine on one call, on another one, it does give me back garbage for the other one. I will update the question shortly to depict my problem. – Account015497 May 13 '16 at 14:27
  • So confused that you still cannot bring yourself to reveal your struct translation? – David Heffernan May 13 '16 at 14:28
  • What you claim does not seem plausible. We know that you aren't showing the real code. Perhaps you should show a [mcve], These "guess what my code really is" games are unproductive. – David Heffernan May 13 '16 at 15:12

2 Answers2

0
  1. Check your build settings x86 or x64 for C# projects.
  2. Verify calling convention. I think that in VC++ default is cdecl, try to specify it in DllImport.
Darko Djuric
  • 754
  • 4
  • 10
0

Building on the comment from @HansPassant, you want to use out SStruct for the getter method. Also, you need to make sure that the size of a bool is the same both in the dotnet struct and the compiled api.dll struct (the struct packing might be 1 byte but it is very unlikely see here:).

Also, you use CallingConvention=StdCall. The symptom of using the wrong calling convention is a stack imbalance exception. You want to try using CallingConvention.Cdecl if you don't know it.

Most often, when opening the dll in depends, if symbols don't end-up with @<some int>, the convention is Cdecl with no guarantee though.

Florent DUGUET
  • 2,786
  • 16
  • 28