Translating native API into C#, Marshalling structs arrays and delegates - c#

I've worked on a C# version of a C++ API but I can't manage to get it right.
Since the whole API was too big the first time I posted, I've cut it down to some core functions for which I really need some help to make a managed version of.
So basically, this is a C++ dll containing exported functions that are used to communicate with a technical analysis software.
C++ function I'd like to convert
#define PLUGINAPI extern "C" __declspec(dllexport)
PLUGINAPI int GetFunctionTable( FunctionTag **ppFunctionTable )
{
*ppFunctionTable = gFunctionTable;
// must return the number of functions in the table
return gFunctionTableSize;
}
GetFunctionTable is called by the software which provides a pointer to an array of FunctionTag called gFunctionTable:
typedef struct FunctionTag
{
char *Name;
FunDesc Descript;
} FunctionTag;
FunctionTag gFunctionTable[] = {"ExampleA",{ VExampleA, 0, 0, 0, 0, NULL },
"ExampleB",{ VExampleB, 1, 0, 1, 0, NULL }
};
FunctionTag structure contains an embedded structure called Fundesc:
// FunDesc structure holds the pointer to actual
// user-defined function that can be called by AmiBroker.
typedef struct FunDesc
{
AmiVar (*Function)( int NumArgs, AmiVar *ArgsTable );
UBYTE ArrayQty; // number of Array arguments required
UBYTE StringQty; // number of String arguments required
SBYTE FloatQty; // number of float args
UBYTE DefaultQty; // number of default float args
float *DefaultValues; // the pointer to defaults table
} FunDesc;
Finally, Fundesc contains AmiVar type:
#pragma pack( push, 2 )
typedef struct AmiVar
{
int type;
union
{
float val;
float *array;
char *string;
void *disp;
};
} AmiVar;
#pragma pack(pop)
C# conversion so far
Now, this is what I've written so far in an attempt to get my C# dll to "mimic" the C++ original API. The GetFunctionTable() exported function:
namespace AmiBrokerFrontDll
{
internal static class AmiBrokerFrontDll
{
[DllExport("GetFunctionTable", CallingConvention = CallingConvention.Cdecl)]
public static Int32 GetFunctionTable(ref FunctionTag[] ppFunctionTable)
{
FillFunction();
ppFunctionTable=gFunctionTable;
return gFunctionTableSize;
}
Then comes the definition of FunctionTag structure and gFunctionTableSize:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct FunctionTag
{
[MarshalAs(UnmanagedType.LPStr)]
public string Name;
public FunDesc Description;
}
public static FunctionTag[] gFunctionTable=new FunctionTag[1];
public static FunctionTag gfunc;
static Int32 gFunctionTableSize = Marshal.SizeOf(gFunctionTable) / Marshal.SizeOf(gfunc);
public static void FillFunction()
{
gFunctionTable[0].Name = "VExempleA";
gFunctionTable[0].Description.Function += VExempleDeMacd;
//ArrayQty, StringQty, FloatQty, DefaultQty, DefaultTablePtr
gFunctionTable[0].Description.ArrayQty = 0;
gFunctionTable[0].Description.StringQty = 0;
gFunctionTable[0].Description.FloatQty = 2;
gFunctionTable[0].Description.DefaultQty = 0;
gFunctionTable[0].Description.DefaultValues = new IntPtr();
}
FunDesc declaration includes a delegate:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate AmiVar FunctionDelegate(int NumArgs, ref AmiVar ArgsTable);
public struct FunDesc
{
[MarshalAs(UnmanagedType.FunctionPtr)]
public FunctionDelegate Function;
public byte ArrayQty; // The number of Array arguments required
public byte StringQty; // The number of String arguments required
public byte FloatQty; // The number of float args
public byte DefaultQty; // The number of default float args
public IntPtr DefaultValues; // The pointer to defaults table
}
Finally, we have an AmiVar structure:
[StructLayoutAttribute(LayoutKind.Explicit, Size = 8)]
public struct AmiVar
{
[FieldOffset(0)]
public Int32 type;
[FieldOffset(4)]
public Single val;
[FieldOffset(4)]
public IntPtr array;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.LPStr)]
public string name;
[FieldOffset(4)]
public IntPtr disp;
}
Sorry this is too long. Unfortunately, I couldn't make a small consise question.
So this code compiled (maybe not anymore since this is an extract from the bigger picture) but when loading the resulting dll from the technical analysis software, I received an ACCESS VIOLATION ERROR. I believe this means the C# conversion doesn't map the C++ variables size correctly. With the arrays of structures and delegates this project has become too difficult for me to solve alone.
Any help will be much appreciated!
Thanks,
Guillaume

I can't help in your specific case, but I can tell you a couple things that will make your life easier:
Function pointers created from managed delegates should never, ever, ever, be stored in unmanaged code. I don't say this lightly. There are claims that if you create a function pointer from a delegate with GetFunctionPointerForDelegate, that the appropriate thunks will get created and won't ever get garbage collected. This is NOT true. I have watched function pointers that were good on one call go sour on the next. The safest bet is to guarantee that function pointers will never get stored beyond the use of an unmanaged call.
P/Invoke is OK for some tasks, but by far the easiest way to integrate a non-C#-friendly C++ library is to make a better/more appropriate wrapper for it in C++/CLI. See here for a description of one way to approach the problem.

Related

Call C++ native/unmanaged member functions from C# when member functions were not exported

I have an unmanaged DLL that exports only a C style factory method that returns a new instance of a class (simplified here to look simple).
hello.h
#if defined(HWLIBRARY_EXPORT) // inside DLL
# define HWAPI __declspec(dllexport)
#else // outside DLL
# define HWAPI __declspec(dllimport)
#endif
struct HelloWorld{
public:
virtual void sayHello() = 0;
virtual void release() = 0;
};
extern "C" HWAPI HelloWorld* GetHW();
hello.cpp
#include "hello.h"
struct HelloWorldImpl : HelloWorld
{
void sayHello(){
int triv;
std::cout<<"Hello World!";
std::cin>>triv;
};
void release(){
this->HelloWorldImpl::~HelloWorldImpl();
};
HelloWorld* GetHW(){
HelloWorld* ptr = new HelloWorldImpl();
return ptr;
};
Now, I can use dllimport to access GetHW() but is there a way to access the member functions of the returned 'struct'... ie, sayHello and release?
I was also stuck with the same problem. This question was asked a while before. I commented to it for any better solution but didn't get any reply yet. So, reposting it.
When i googled, able to find out two solutions.
Solution1: Expose all the member functions in the C-style for the existing dll. Which i cant do, as it is a 3rd party dll.
Solution2: Write a managed C++ dll exposing the functionality of native C++ dll, which later can be used in your C# dll. Here many classes/functions are present. So, creating would take most of the time.
i got the above solutions from the link below.
How To Marshall
Please let me know if there is any better solution other than the above two solutions?
i have the source code for C++ solution. But what i though was not to touch C++ dll. If there is any possibility to do it in C#, it would be great.
If there is no alternative, i need to follow any one of the specified two solutions.
The C++ code is using the way abstract classes are implemented by the Visual C++ compiler. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx. This memory layout is "fixed" because it is used for implementing COM interfaces. The first member of the struct in memory will be a pointer to a vtable containing the function pointers of your methods. So for a
struct HelloWorldImpl : public HelloWorld
{
public:
int value1;
int value2;
}
the "real" layout in memory would be:
struct HelloWorldImpl
{
HelloWorldVtbl *vtbl;
int value1;
int value2;
}
where vtbl would be:
struct HelloWorldVtbl
{
void *sayHello;
void *release;
}
Just for the sake of doing a complete response, I'm writing the example for this signatures:
struct HelloWorld {
public:
virtual int sayHello(int v1, int v2, int v3) = 0;
virtual void release() = 0;
};
C# code:
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetHW();
[StructLayout(LayoutKind.Sequential)]
struct HelloWorldVtbl
{
public IntPtr sayHello;
public IntPtr release;
}
Your functions are void Func(void) or int Func(int, int, int), but in truth they have a hidden parameter, this, so you can write them as:
int sayHello(HelloWorld*, int, int, int);
void release(HelloWorld*);
so in C# the delegate is
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int Int32MethodInt32Int32Int32(IntPtr ptr, int v1, int v2, int v3);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void VoidMethodVoid(IntPtr ptr);
Then you can use
IntPtr ptr = GetHW();
IntPtr vtbl = Marshal.ReadIntPtr(ptr, 0);
HelloWorldVtblhw = (HelloWorldVtbl)Marshal.PtrToStructure(vtbl, typeof(HelloWorldVtbl));
Int32MethodInt32Int32Int32 sayHello = (Int32MethodInt32Int32Int32)Marshal.GetDelegateForFunctionPointer(hw.sayHello, typeof(Int32MethodInt32Int32Int32));
int res = sayHello(ptr, 1, 2, 3);
Console.WriteLine(res);
VoidMethodVoid release = (VoidMethodVoid)Marshal.GetDelegateForFunctionPointer(hw.release, typeof(VoidMethodVoid));
release(ptr);

PInvoke does not change the object

I have the following PInvoke:(C to C#)
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([MarshalAs(UnmanagedType.LPArray, SizeConst = 64)]sPiece[] board);
On C:
__declspec(dllexport) void InitBoard(sPiece board[8][8]);
In InitBoard function, the values ​​of the matrix changing, but after a call to PInvoke I do not see the change.
sPiece[] board = new sPiece[64];
InitBoard(board);
//Here the values ​​of the board is still initialized (as before the function call) at default values
I tried to change the variable to ref (although it already reference) but it stuck the program when the function was called, so I do not think it's the solution.
It took me a while to get here (I'm new to the subject) I'd love to help!
EDIT:
sPiece On C:
typedef struct Piece
{
ePieceType PieceType; //enum
ePlayer Player; //enum
int IsFirstMove;
} sPiece;
sPiece On C#:
[StructLayout(LayoutKind.Sequential)]
public struct sPiece
{
public ePieceType PieceType;
public ePlayer Player;
public int IsFirstMove;
}
Possibly you are failing to allocate memory before calling the function.
sPiece[] board = new sPiece[64];
InitBoard(board);
Declare the function like this:
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([Out] sPiece[] board);
The default marshalling is [In]. Although since your struct is blittable, the array you pass is pinned and the call behaves as though it was [In,Out]. So I think you could omit [Out] if you wished, but it is clearer as written above.
You can add the UnmanagedType.LPArray option if you wish but it's not needed.

Accessing array in struct of C DLL from C#

I am trying to access and make changes to elements in a struct that are in a DLL. I have followed this example on how to use struct from a DLL: http://nawatt.com/index.php/corporate/blog/78-using-c-dlls-in-c
I have been successful in making changes to non-array variables, but whenever I try to make changes to an array I get a Runtime Error.
This is an example of my C DLL code:
//lib_qrs.dll
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TEST_STRUCT
{
unsigned short check[5];
} test_struct;
__declspec(dllexport) void __stdcall test(test_struct *test, unsigned short val){
// This is an example of what DOES NOT WORK
test->check[0]=val+1;
test->check[1]=val+2;
test->check[2]=val+3;
test->check[3]=val+4;
test->check[4]=val+5;
}
#ifdef __cplusplus
}
#endif
This is an example of my C# code:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT
{
public UInt16[] check;
}
public class Program
{
[DllImport("lib_qrs.dll", EntryPoint="test", CallingConvention = CallingConvention.StdCall)]
public static extern void test(ref TEST_STRUCT test, int val);
public TEST_STRUCT testStruct = new TEST_STRUCT();
static void Main(string[] args)
{
testStruct.check=new UInt16[5];
// WHERE ERROR OCCURS
test(ref testStruct, 5);
}
}
The error that I get is:
*"An unhandled exception of type 'System.AccessViolationException' occurred in Test.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I understand that I have to be very careful with memory allocation when I replicate my structure in C#, but I don't know what I am doing wrong or how I can fix this array issue.
Does anyone have an idea of how I could get around this?
The default marshaling for that array is unsigned short*, not unsigned short[]. You'll need to apply the [MarshalAs] attribute to tell the pinvoke marshaller about it. Fix:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public UInt16[] check;
}
And since you are returning values in the array, you also have to tell the pinvoke marshaller that it needs to copy the array values back. That requires the [Out] attribute:
[DllImport("lib_qrs.dll")]
public static extern void test([Out] ref TEST_STRUCT test, int val);
Do note that neither is necessary at all if the you just declare the argument as ushort[], assuming that the structure doesn't contain any additional fields.

Struct from C++ to C#

I have a project which uses a library which was mostly build on and around C++.
The delivered DLL with this library, i have imported into my C# project.
After importing the following method in Unity:
[DllImport("pst")]
private static extern int pst_get_sensor(PSTSensor sensor);
I require this PSTSensor struct, so actually use the method.
In the C++ .h file, the struct is defines as :
struct PSTSensor
{
char name[80]; /**< Device name */
int id; /**< Device identifier (for other tracking interfaces */
float pose[16]; /**< Device pose estimate as row-major matrix */
double timestamp; /**< Time the data was recorded */
};
I have tried to replicate it in C#, and i ended up with this following:
struct PSTSensor{
PSTSensor(char[] name, int id, float[] pose, double timestamp){
this.name = name;
this.id = id;
this.pose = pose;
this.timestamp = timestamp;
}
public char[] name;
public int id;
public float[] pose;
public double timestamp;
}
In the example C++ code that came with this project was stated to call pst_get_sensor(&sensor) this '&' sign, i do not recognize? How would i call this method in C#, and make it work?
I think i ruined the struct, seeing how i never worked with them before. At least it doesn't throw errors on compilation anymore, but i think it is still wrong. Any ideas on that?
Many thanks in advance,
Smiley
I'm not sure if I'm answering your question entirely, but in c++ the & is used to pass an argument by reference which means that the argument you're passing in can be manipulated inside the function. To me it looks like the original function is used to fill out a sensor struct.
int C# you can pass by reference with the ref or out keyword:
private static extern int pst_get_sensor(PSTSensor ref sensor);
Why did you add a constructor in your C# implementation?
It seams that all you need to do is declare the parameter as ref
private static extern int pst_get_sensor(ref PSTSensor sensor);
If you want to have a look at an example, there is one here
You did not show the C++ declaration of your function, but give that you call it like this:
pst_get_sensor(&sensor);
it is presumably declared like so:
int pst_get_sensor(PSTSensor *sensor);
The function receives a pointer to the struct. That is clear since the call uses the & operator which takes the address of an object.
On the C# side you translate this parameter as a ref parameter. Like this:
[DllImport(...)]
static extern int pst_get_sensor(ref PSTSensor sensor);
Now, your other problem is that your struct is declared incorrectly. It contains inline arrays and you must communicate the lengths of those arrays to the marshaller. Like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct PSTSensor
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string name;
public int id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] pose;
public double timestamp;
}

AccessViolationException when marshaling a C++ struct to C# class

I'm trying to wrap a C++ library (libclang) into a C# wrapper, using PInvoke.
Everything was shiny, until I tried to call a C++ method that returns a struct.
I did everything by the book, but when this method gets called, I get AccessViolationException.
Now, I read that it's probably because there's something messed up with the memory image of the objects. I checked and doublechecked if I've put all the arrtibutes and what nots everywhere but the Exception won't go away. (I've been looking at this code for hours, so I may have missed some things, you guys don't).
The C++ part (not my code, but I'm sure that it works):
CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
/* Parse the hell out of a lot of data, an then make a string
In the end, string gets wrapped in a custom struct: CXString.
*/
return createCXString(Out.str(), true);
}
Here's CXString:
typedef struct {
void *data;
unsigned private_flags;
} CXString;
So I have my C# classes to represent the wrapped C++ originals:
public class Diagnostic
{
private IntPtr _nativeObject;
internal IntPtr NativeObject { get { return _nativeObject; } }
[DllImport("libclang.dll", EntryPoint = "clang_formatDiagnostic")]
[return: MarshalAs(UnmanagedType.LPStruct)]
private static extern CXString FormatDiagnostic(IntPtr diag, uint options);
public Diagnostic(IntPtr native)
{
_nativeObject = native;
}
public string GetString(DiagnosticDisplayOptions options = DiagnosticDisplayOptions.DisplaySourceLocation)
{
var cxstr = FormatDiagnostic(_nativeObject, (uint)options); //<-- I get the exception here
return cxstr.GetString();
}
}
The functions I need are also implemented in a C taste (global) so, I can make an impression of OO in my C# classes, but in fact I store the IntPtr representations of the C++ objects (_nativeObject). So I'm pretty sure, that the object stored in the _nativeObject is in fact an CXDiagnostic (I got the reference returned from another function of the same library).
The actual object that I'm trying to use with FormatDiagnostic method gets initialized from the constructor of another wrapper class (TranslationUnit):
public TranslationUnit(/*lots of params to init the unit*/)
{
//... there are some int conversions and initialization failsafe codes here
//this is a property of TranslationUnit
Diagnostics = new List<Diagnostic>();
//GetDiagnosticsCount is also PInvoke to count CXDiagnostic objects related to the TranslationUnit
var dgCnt = (int)GetDiagnosticsCount(_nativeObject);
for (int i = 0; i < dgCnt; i++)
{
//GetDiagnostic is also PInvoke, gets the CXDiagnostic at the given index
var diag_ptr = GetDiagnostic(_nativeObject, (uint)i);
Diagnostics.Add(new Diagnostic(diag_ptr));
}
//up until now, all the calls seem to work
//I get the expected count of diagnostics and none of the other
//PInvoke calls throw exceptions. They use IntPtrs, but none of them
//use structs.
}
So as the MSDN tutorial suggested, I've made a C# class to Marshal the CXString struct into, and it looks like this:
[StructLayout(LayoutKind.Sequential)]
public class CXString
{
public IntPtr data;
public uint private_flags;
}
I get the AccessViolationException when the code reaches FormatDiagnostic call.
Any tips where it could have gone wrong?
EDIT:
CXDiagnostic is a pointer type in the original C++ code:
typedef void *CXDiagnostic;
I think marshalling to LPStruct is not proper here and the CXString class needs to be struct.
Try the following code:
public class Diagnostic
{
...
[DllImport("libclang.dll", EntryPoint = "clang_formatDiagnostic")]
private static extern CXString FormatDiagnostic(IntPtr diag, uint options);
...
}
[StructLayout(LayoutKind.Sequential)]
public struct CXString
{
public IntPtr data;
public uint private_flags;
}
Also, you should take care of the calling convention (StdCall by default, but for example pure C use Cdecl) and the struct byte alignment.

Categories