Complicated (?) pinvoke situation - avoid function overloading - c#

Summary:
I have a bunch of C functions I have to call from C#. My current working solution is based on function overloading and I'm wondering if there is a more elegant solution.
The C stuff:
somewhere in a header file
typedef struct _unknown_stuff * handle; // a opaque pointer
an example of the function
func( uint num_entries,
handle * objects,
uint * n)
{ ... }
in C the function should be used similar to this:
// warning: i bet that the syntax is not correct but you should get the idea...
uint n;
func(0, null, &n);
handle * objects = malloc(n * sizeof(handle));
func(n, objects, null);
The C# stuff:
right now I'm doing the following in C#:
public struct handle
{
public IntPtr Pointer;
}
// version to get number of objects
[DllImport(dll, ...]
private static extern void
func( uint must_be_zero,
object must_be_null,
out uint n);
// version to get the actual data
[DllImport(dll, ...]
private static extern void
func( uint num_entries,
[Out] handle[] objects,
int must_be_zero);
and then:
handle[] objects;
uint n = 42;
func(0, null, out n);
objects = new handle[n];
func(n, objects, 0);
The Question
Since I'm a C# noob, I was wondering if this is the best way to do this. Especially I would like to know if there is a way around overloading the function.

First of all, your code as is is wrong, because in the second C# signature your third argument is int, while the corresponding C argument is still int*. It will work when targetting 32-bit Windows, because sizeof(int)==sizeof(int*) there - so when you pass a 0 int you end up passing a null pointer - but it is not 64-bit safe. If you still want to do it that way, you need to use IntPtr there.
Aside from that, you could try:
[DllImport(dll, ...]
private static extern void func(
uint num_entries,
[Out] handle[] objects,
[Out] int[] num_devices);
making the third argument an array so that you could pass null there.
Or just use pointers. There's nothing bad about it, and don't be afraid of unsafe keyword - any kind of P/Invoke is inherently unsafe anyway, so you might as well be explicit about it.

Wouldn't the C dll look at 0, and process that as null?
It only has one function, correct? That is, the C function is defined once, and not separately for a null case, and would have an if( objects == NULL ) check somewhere in there. On most systems (check the stdio.h of the target system) this is 0.
So calling from pinvoke with a 0 value in this place should do what you wanted overloading to do.

Related

Function with multiple return parameter

I am new to C#. For a motor driver that we use, I want to see its current and there is a BOOL function in motor driver's guide. But I have no idea how I can use it.
VCS_GetCurrentIsAveraged(KeyHandle,NodeId, short* pCurrentIsAveraged,
pErrorCode)
for this function KeyHandle and NodeId are parameters and pCurrentIsAveraged and pErrorCode are return parameters.
I have all parameters already in my code except pCurrentIsAveraged, which is what I want to see. So how can I get this value as a return.
//Initialize
short* pCurrentIsAveraged;
double current;
current=VCS_GetCurrentIsAveraged(KeyHandle,NodeId,pCurrentIsAveraged,
pErrorCode)
Would this work, I want to get current but what should I enter for pCurrentIsAveraged value as return parameter?
Thanks.
Using the function signature VCS_GetCurrentIsAveraged(KeyHandle,NodeId, short* pCurrentIsAveraged, pErrorCode) requires you to use unsafe code, which, if you are new to C#, you should avoid. That looks like a p/invoke wrapper around a C-style API.
As it stands, you would need to call it like this (from an unsafe block) (fixing may not be needed if currentIsAveraged happens to be allocated on the stack):
short currentIsAveraged = 0;
double current;
fixed (short* pCurrentIsAveraged = &currentIsAveraged) {
current = VCS_GetCurrentIsAveraged(KeyHandle,NodeId,pCurrentIsAveraged, pErrorCode);
}
bool isCurrentAveraged = (currentIsAveraged != 0);
If you have control of the p/invoke definition of VCS_GetCurrentIsAveraged, you could change that signature to use an ref short pCurrentIsAveraged rather than short* pCurrentIsAveraged. This would let you use that method like this (without the need for unsafe code):
short currentIsAveraged = 0;
double current;
current = VCS_GetCurrentIsAveraged(KeyHandle,NodeId,ref currentIsAveraged, pErrorCode);
bool isCurrentAveraged = (currentIsAveraged != 0);
You can optimize the p/invoke signature further by using [Out] attribute on that value to tell the marshaller to skip a probably unneeded copy, and you could also convice the compiler to treat it as a bool directly rather than going back and forth with a short, but that's all advanced stuff. Check out the System.Runtime.InteropServices namespace if you are interested in that.

Marshalling array of structures that contain a std::vector

I am trying to marshal a vector of the following C++ class to C# that is used in a tree:
class CFileNode
{
private:
std::string m_name;
std::vector<CFileNode*> m_children;
bool m_isDirectory;
CFileNode* m_parent;
};
And:
extern DATAACCESSLAYERDLL_API size_t __stdcall Get_FileSystemNodeCount()
{
// GetFileNodeList() returns a std::vector<CFileNode*>&
return CFileSystem::GetFileNodeList().size();
}
extern DATAACCESSLAYERDLL_API void __stdcall Get_FileSystemNodes(size_t count, CFileNode** nodes)
{
nodes = CFileSystem::GetFileNodeList().data();
}
And in C#:
[SuppressUnmanagedCodeSecurity]
[DllImport("MyDll.dll")]
static private extern int Get_FileSystemNodeCount();
[SuppressUnmanagedCodeSecurity]
[DllImport("MyDll.dll")]
static private extern void Get_FileSystemNodes(int count, [In,Out] IntPtr[] outNodes);
And my struct:
[StructLayout(LayoutKind.Sequential, Pack = 1), Serializable]
public struct CFileNode
{
[MarshalAs(UnmanagedType.LPStr)]
string m_name;
[MarshalAs(UnmanagedType.SafeArray)]
IntPtr[] m_children;
[MarshalAs(UnmanagedType.Bool)]
bool m_isDirectory;
[MarshalAs(UnmanagedType.LPStruct)]
IntPtr m_parent;
}
And where I use it:
int count = Get_FileSystemNodeCount();
IntPtr[] results = new IntPtr[count];
Get_FileSystemNodes(count, results);
foreach (IntPtr ptr in results)
{
m_fileSystemNodes.Add(Marshal.PtrToStructure<CFileNode>(ptr));
}
In its current state, the IntPtr[] results is just a big array of zeroes.
If I modify C++ GetFileSystemNodes() to go through the vector and add everything to the array nodes, results will have a load of numbers that look like memory addresses (although I have no idea if they are garbage or not), but then the Marshal.PtrToStructure<CFileNode>(ptr) fails with an AccessViolationException.
I would like to transfer this whole tree structure from C++ to C# in as few PInvoke calls as possible, for speed. This is why, before any of this code is executed, my entire tree is flattened out into a list. Each node still has a pointer to its parent and children, so it's easy to reconstruct it. If it makes it easier, I could also just pass the root node.
Here are my questions:
1) Is marshalling vectors of objects that contain other vectors even possible? Should I make my C++ classes simpler, using arrays or something instead?
2) Is there a way to pass the vector of nodes through GetFileSystemNodes without moving everything in the vector to the array argument?
3) What is the correct way to write my C# struct so it matches my C++ one?
This can be done, I would recommend using SWIG. SWIG makes interop between C++ and other languages (including C#) much easier. It has built in support to handle some of the C++ STL containers, such as vectors.
Yes, SWIG can handle vectors of objects, you just have to build them up in the interface file.

How do I call a C++ function that sets the values of an int array from C#?

I have the following function in C++ inside a .dll:
extern "C"
{
__declspec(dllexport) void ColorRamps_getColorRampTable(int n, int *table);
}
It takes in an array with n elements and sets the array values.
How do I call this from C#?
I understand that I must first DllImport the function (?):
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int[] table);
public static void getColorRampTable(int[] table)
{
int n = table.Length;
ColorRamps_getColorRampTable(n, table);
}
Is this correct?
When I call getColorRampTable(int[] table) must I pin the array?
How do I do this?
I tried:
int[] table = new int[164];
GCHandle handle = GCHandle.Alloc(table, GCHandleType.Pinned);
getColorRampTable(table);
handle.Free();
You have two options. One is to rely on the .NET marshaller, another is to use unsafe code.
Both are actually quite simple for your case. The marshaller one:
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, [In, Out] int[] table);
public static void getColorRampTable(int[] table)
{
int n = table.Length;
ColorRamps_getColorRampTable(n, table);
}
This has a slight overhead in that the marshaller copies the array to unmanaged memory first, and then copies it back into your array again. EDIT: As Xanatos correctly noted, since int[] is a blittable type, the marshaller actually cheats and passes a pointer to your actual array. Or at least that's the documented behaviour.
If this is a performance problem for you (and it's a real, measured problem - don't do this otherwise), you can pass a pointer to the .NET array directly using unsafe code:
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int* table);
public unsafe static void getColorRampTable(int[] table)
{
int n = table.Length;
fixed (int* pTable = &table)
{
ColorRamps_getColorRampTable(n, pTable);
}
}
The class has to be marked with the unsafe keyword, and you have to enable unsafe code in the project settings.
EDIT:
As above, this doesn't actually apply for the int[] case. I'm going to leave it in here because it's true for non-blittable types, but int[] is blittable, and the marshaller should just pass the pointer directly, while handling the pinning for you. You should still use the [In, Out] attribute, though - even though it will usually behave as In/Out by default, the contract should be clearly specified.
The main difference is that in the first case, the C(++) code never has access to any of the managed memory of yours - it always works on copies of the data. By adding the [In, Out] attributes, you're telling the marshaller to not only copy the data in, but also to copy it out after the call is done - otherwise your table array would never change.
On the other hand, the second variant passes a pointer to the actual array you're using on the C# side - this may or may not be faster, depending on way too many things, and it's generally slightly more unsafe (be very careful about specifying the correct length of the array, for example).
Another EDIT:
There's also an option to use IntPtr table in the P/Invoke method declaration, which allows you additional flexibility. The easiest example would be just pinning the array, which is equivalent to the fixed case:
var hTable = GCHandle.Alloc(table, GCHandleType.Pinned);
try
{
ColorRamps_getColorRampTable(n, hTable.AddrOfPinnedObject());
}
finally
{
hTable.Free();
}
And even allowing you to explicitly manage the copying quite easily:
var pTable = Marshal.AllocHGlobal(sizeof(int) * table.Length);
try
{
Marshal.Copy(table, 0, pTable, table.Length);
ColorRamps_getColorRampTable(n, pTable);
Marshal.Copy(pTable, table, 0, table.Length);
}
finally
{
Marshal.FreeHGlobal(pTable);
}
This is a decent workaround if you can't use unsafe code for some reason, but do remember that it's just as "unsafe" as unsafe code in most scenarios. The truth is, since you're interacting with native code, you're always (somewhat) unsafe - a bug in the native code can easily corrupt your whole application.

Import C dll in C# , how to convert short** type

I want to import a dll in C# which is coded in C language. The following is the formate of the function I want to call.
/*
*ReadAnswer
*#param objectID The answer object ID
*#param answerBuf The answer buffer.This buffer is automatically allocated by
the function.
It is automatically recycled with each call. A call to this
function with an empty answer or a new request will
automatically free the allocated buffer.
*#param answerBufferSize The answer buffer size.This function return the size of the
allocated buffer in this parameter.
*#return 0 if error occurs
1 if success
*/
int ReadAnswer(unsigned short *objectID,
unsigned short **answerBuf, unsighed short *answerBufferSize )
Please help me with it. I'm stucked by this function. Thank you in advance.
The C side-of-things is usually not enough to be sure, but after reading the comment, it should be something like this:
[DllImport("my.dll")]
private extern static int ReadAnswer(ref ushort objectID, out IntPtr answerBuf, out ushort answerBufferSize);
The function should be declared like this:
[DllImport(dllname)]
private extern static int ReadAnswer(
out ushort objectID,
out IntPtr answerBuf,
out ushort answerBufferSize
);
Call it like this:
ushort objectID, answerBufSize;
IntPtr answerBufPtr;
int retval = ReadAnswer(out objectID, out answerBufPtr,
out answerBufSize);
if (retval == 0)
// handle error
ushort[] answerBuf = new ushort[answerBufSize/2];
Marshal.Copy(answerBufPtr, (Int16[])answerBuf, 0, answerBuf.Length);
My assumption is that answerBufSize is the size in bytes.
First of all, there is no unsigned keyword in C#, use ushort.
Second, in order to declare pointers you need to write * after the type, example: ushort*.
And finally to import dlls which are written in C, use this:
[System.Runtime.InteropServices.DllImport(requiredDll)]
extern static int ReadAnswer(ushort* objectID, ushort** answerBuf, out ushort* answerBufferSize);
Also, since the function put the buffer size in answerBufferSize, this parameter requires out.

Best Way To Marshal A Pointer of Array of Struct

I'm calling functions from C++ that returns a pointer to an array of struct and I'm having problems since I'm new to this operation/implementation.
My C++ codes:
// My C++ Structs
typedef struct _MainData {
double dCount;
DataS1 *DS1;
int iCount1;
DataS2 *DS2;
int iCount2;
}MainData;
typedef struct _DataS1 {
unsigned int uiCount1;
unsigned int uiCount2;
int iCount;
void *pA;
void *pB;
} DataS1;
typedef struct _DataS2 {
unsigned int uiCount1;
unsigned int uiCount2;
unsigned int uiCount3;
unsigned int uiCount4;
double dCount;
int iCount1;
char strLbl[64];
} DataS2;
// My C++ Function
MainData* GetData(const int ID)
{
MainData* mData;
int iLength = Get_Count();
mData = new MainData[iLength];
for(int x = 0;x < VarCounter; x++)
{
// Codes here assign data to mData[x]
}
return mData;
}
Question:
How can I call the C++ function GetData to C#?
My current codes in C# are:
[DllImport(".\\sdata.dll")]
[return: MarshalAs(UnmanagedType.LPArray)]
private static unsafe extern MainData[] GetData(int ID);
// The struct MainData in my C# side is already "Marshalled"...
//My function call is here:
MainData[] SmpMapData = GetData(ID);
When I compiled it, there's an exception:
"Cannot marshal 'return value': Invalid managed/unmanaged type combination."
Sorry for the poor coding... Please help...
I tried to do exactly the same thing but didn't succeed due to lack of time but I learned something in process:
Allocate memory in C#
To pass array of structs, struct must be blittable.
Good luck with that, I couldn't make it to work.
One problem is that the marshaller doesn't know how many items are in the array returned by the C++ code. An alternative approach could be to have two C++ methods - one which returns the number of items, and one which returns a single MainData given an index.
What do your structures look like on the C# side?
Since you are coding both the C++ and C# side, it may be easier to use C++/CLI to interface them instead of PInvoke.

Categories

Resources