Here is my code:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Foo
{
UInt32 StartAddr;
UInt32 Type;
}
[DllImport(DllName, EntryPoint="_MyFunc", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe IntPtr MyFunc([MarshalAs(UnmanagedType.LPArray)] Foo[] Foos);
List<Foo> Foos = new List<Foo>();
Foo1 = new Foo();
Foo1.StartAddr = 1;
Foo1.Type = 2;
Foos.Add(Foo1);
MyFunc(Foos.ToArray());
In the C-based DLL I print out the value of Foos[0].StartAddr and Foos[0].Type. This works great.
Now I want to add a parameterless constructor to the struct which means I have to switch to a class. By only changing the C# declaration from "struct" to "class" results in corrupted values being passed to the C-based DLL.
I believe this should work but I presume I am missing a step. How can I pass an array of C# classes as a array of structs to C code?
Thanks! Andy
If you need default item in your struct you can add static property to it
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Foo
{
UInt32 StartAddr;
UInt32 Type;
public static Foo Default
{
get
{
Foo result = new Foo();
result.StartAddr = 200;
result.Type = 10;
return result;
}
}
}
And when you need to create new Foo struct just call Foo.Default
Related
I am facing the following issue.
I have a C++ API passed to C# through DLL. Here is how the api looks like:
header.h
extern "C" __declspec( dllexport) bool f(CustomClass* myclass);
where:
struct CustomClass
{
double* grades;
int* ages;
char name[16];
}
So I intend to call this f from C# and face difficulties to allocate the pointers:
To be more precise, I can allocate the CustomClass* but cannot the underlying types:
struct CustomClass
{
public unsafe char* name;
public unsafe double* grades;
public unsafe int* ages;
};
fixed(CustomClass* instances = new CustomClass[5]) // this is fine
{
instances[0].grades = new double[10];// no good, , cannot implicitly convert from double[] to double*
fixed(instances[0].grades = new double[10]) // no good, instances is a variable but used as a type
{
}
}
Can anyone help to understand how to allocate a pointer inside a class?
Or maybe any other workaround to satisfy the C++ DLL api?
You need to define the struct using fixed-size arrays. Fill in the necessary size for each
struct CustomClass
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
double[] grades = new double[10];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
int[] ages = new int[10];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
char[] name = new char[16];
}
I have a c++ struct as follow:
struct Vehicle
{
u32 something;
Info *info;
u8 something2[ 0x14 ];
Vector3f location;
Template* data;
};
struct Info
{
u32 speed;
std::string name;
BuffCollection** buffCollection;
void* sectionPtr;
};
struct Template
{
u32 templateID;
};
From this question, I figured out the meaning of the u32, u8, and so on, or so I think I did.
Then I tried to make my own C# struct out of it:
[StructLayout(LayoutKind.Sequential)]
public struct Vehicle
{
public uint Something;
public Info Info;
public byte Something2;
public Vector3f Location;
public Template Data;
}
[StructLayout(LayoutKind.Sequential)]
public struct Info
{
public uint Speed;
public string Name;
public byte[] BuffCollection;
public IntPtr SectionPointer;
}
[StructLayout(LayoutKind.Sequential)]
public struct Template
{
public uint TemplateId;
}
public struct Vector3f
{
public float X, Y, Z;
public Vector3f(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
}
However, when I try to read the Vehicle:
[DllImport("Core.dll")]
static extern Vehicle GetVehicle();
static void Main()
{
var vehicle = GetVehicle();
Console.WriteLine(vehicle.Info.Name);
Console.ReadKey();
}
I get the following error:
System.Runtime.InteropServices.MarshalDirectiveException: Method's type signature is not PInvoke compatible
From the search I did on it, it lead me to believe that my structure conversion is wrong.
What is wrong with my converted structures?
Regarding structures:
Vehicle.Info is a pointer, so you need to declare it as IntPtr Info, and then use Marshal.PtrToStructure / Marshal.StructureToPtr to read/write its value in managed code;
Vehicle.something2 is a byte array, not a byte, so you need to declare it this way:
[MarshalAs(UnmanagedType.ByValArray, SizeConst=20)]
byte[] something2=new byte[20];
Vehicle.Data - see #1, the same problem
Info.Name - .NET does not provide marshalling for std::string, so you will either need to write your own marshaler (see this: Custom Marshaler for PInvoke with std::string) or change the type to something like char* in your c++ library.
Info.BuffCollection should also be an IntPtr or BuffCollection[] (depending on what the BuffCollection type is about - it's not provided in your question)
Regarding the signature and invocation of GetVehicle(); method:
it is likely that the method returns the pointer to the structure, not the structure itself (just speculating, please double check). If so, you need to declare it as
static extern IntPtr GetVehicle();
and then use Marshal.PtrToStructure to convert it to your structure like this:
var vehiclePtr=GetVehicle();
var vehicle = (Vehicle)Marshal.PtrToStructure(vehiclePtr, typeof(Vehicle));
I'd like to create Foo objects in C# from an unmanaged array of Foo structs created in C++.
This is how I think it should work:
On the C++ side:
extern "C" __declspec(dllexport) void* createFooDetector()
{
return new FooDetector();
}
extern "C" __declspec(dllexport) void releaseFooDetector(void* fooDetector)
{
FooDetector *fd = (FooDetector*)fooDetector;
delete fd;
}
extern "C" __declspec(dllexport) int detectFoo(void* fooDetector, Foo **detectedFoos)
{
FooDetector *fd = (FooDetector*)fooDetector;
vector<Foo> foos;
fd->detect(foos);
int numDetectedFoos = foos.size();
Foo *fooArr = new Foo[numDetectedFoos];
for (int i=0; i<numDetectedFoos; ++i)
{
fooArr[i] = foos[i];
}
detectedFoos = &fooArr;
return numDetectedFoos;
}
extern "C" __declspec(dllexport) void releaseFooObjects(Foo* fooObjects)
{
delete [] fooObjects;
}
On C# side:
(I ommitted some fancy code making it possible to call the C++ functions from within C# for better readability);
List<Foo> detectFooObjects()
{
IntPtr fooDetector = createFooDetector();
IntPtr detectedFoos = IntPtr.Zero;
detectFoo(fooDetector, ref detectedFoos);
// How do I get Foo objects from my IntPtr pointing to an unmanaged array of Foo structs?
releaseFooObjects(detectedFoos);
releaseFooDetector(fooDetector);
}
But I don't know how to retrieve the objects from the IntPtr detectedFoos. It should be possible somehow...
Any hints?
UPDATE
Let's assume, Foo is a simple detection rectangle.
C++:
struct Foo
{
int x;
int y;
int w;
int h;
};
C#:
[StructLayout(LayoutKind.Sequential)]
public struct Foo
{
public int x;
public int y;
public int width;
public int height;
}
Is it possible to read from unmanaged memory and create new managed objects from it before releasing the unmanaged memory?
I don't know how may Foo objects will be detected, so I don't know, how much memory to allocate in C# before calling detectFoo(). That's why I alloc/free memory in C++ and just pass a pointer to it. But somehow I can't retrieve the detectedFoos pointer address under C#. How do I do that?
You must re-declare Foo in your C# project. Assuming you know the count of Foos and the value of sizeof(Foo) you should be able to use System.Runtime.Interopservices.Marshal.PtrToStructure() to retrieve your Foo structures one at a time.
You have to define your struct again in C#, and it depends on your stuct. Your struct have to be blitable (the memory layout of the C# struct ave to be the same as it is for the C struct)
Have a look at "Marshalling structs"
Or post your real "Foo" struct and i can show you the C# version
UPDATE:
Because your struct seems to be blitable, you can simple cast the pointer to unmanaged memory to a pointer to the struct defined in c#:
If it is ok for your application to use unsafe code you can write:
unsafe List<Foo> detectFooObjects()
{
List<Foo> res = new List<Foo>()
IntPtr fooDetector = createFooDetector();
IntPtr detectedFoos = IntPtr.Zero;
int nNumFoos = detectFoo(fooDetector, ref detectedFoos );
for(int i=0;i<nNumFoos;i++)
{
Foo** ppDetectedFoos = detectedFoos.ToPointer();
Foo* pFoo = *ppDetectedFoos
res.Add(*pFoo); //copies the struct because is a struct
ppDetectedFoos++:
}
releaseFooObjects(detectedFoos);
releaseFooDetector(fooDetector);
return res;
}
I ended up solving my problem by using a C++/CLI wrapper class.
I want to pass a structure to C function and I write the following code.
When I run it, the first function - Foo1 is working and then function Foo gets an exception. Can you help me to understand what the problem is?...
The C code:
typedef struct
{
int Size;
//char *Array;
}TTest;
__declspec(dllexport) void Foo(void *Test);
__declspec(dllexport) int Foo1();
void Foo(void *Test)
{
TTest *X = (TTest *)Test;
int i = X->Size;
/*for(int i=0;i<Test->Size;Test++)
{
Test->Array[i] = 127;
}*/
}
int Foo1()
{
return 10;
}
The C# code:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential)]
public class TTest
{
public int Size;
}
class Program
{
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern void Foo(
[MarshalAs(UnmanagedType.LPStruct)]
TTest lplf // characteristics
);
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern int Foo1();
static void Main(string[] args)
{
TTest Test = new TTest();
Test.Size = 25;
int XX = Program.Foo1();
Program.Foo(Test);
}
}
}
To the downvoters: This answer solves two issues: the immediate issue of the calling convention/the MarhsalAs attribute, and the issue he will soon find where his TTest parameter won't work if he takes my suggestion of turning TTest into a struct.
Your native code is asking for a void*, which in C# is an IntPtr. First you should define TTest as a struct and not a class. Second, you should change the declaration of Foo to:
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(IntPtr lplf);
And third, you should pin the TTest using the fixed keyword and pass it's pointer to Foo. If you're using a class, you can use Marhsal.StructureToPtr to get an IntPtr from your TTest.
This provides the same functionality on both sides, where a pointer to any type can be passed in. You can also write overloads with all the class types that you want to use since they all equate to void* on the native side. With a struct, your parameters would be prepended with a ref.
What I'm curious about is why your native code wants a void* instead of a TTest* when the first thing you do in the unmanaged code is cast to a TTest*. If you switched the parameter to a TTest*, then providing identical functionality becomes simpler. You declaration would become:
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(ref TTest lplf);
And you would call the function as Program.Foo(ref Test);
If you're using the class, the ref isn't necessary as classes are reference types.
You are using C call so you need to specify CallingConvention.Cdecl
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
By default its stdcall in C# pinvoke as i remember; You can also do change C code instead and leave your C# code as is like in below
__declspec(dllexport) void __stdcall Foo(void *Test);
But for me best is to both declare __cdecl (or stdcall) in your C export and CallingConvention.Cdecl (or stdcall) in your C# code to keep convenience. You can check https://learn.microsoft.com/en-gb/cpp/cpp/argument-passing-and-naming-conventions?view=vs-2017 and https://learn.microsoft.com/en-gb/dotnet/api/system.runtime.interopservices.callingconvention?view=netframework-4.7.2 for further info
I am trying to build a struct in C# to pass to unmanaged C++, I was wondering what is the correct type of variable to use for a unichar array in my struct and what it should be marshalled as.
I have already figured this out for an unsigned char array
C/C++
typedef struct _foo {
void *fileId;
unsigned char fileName[15];
} foo;
C#
[StructLayout(LayoutKind.Sequential)]
public struct foo
{
public IntPtr fileId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string fileName;
}
So if I have the following in C++
typedef struct _foo {
void *fileId;
unichar fileName[15]; // UTF-16LE
} foo;
What would be the correct struct to use in C#?
Specify the structure as a unicode structure:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct foo
{
public IntPtr fileId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string fileName;
}
I'm guessing the same struct would do, but you need to set the DllImportAttribute.CharSet property to Auto or it will default to Ansi. Unicode would do too, but unless you are using Windows 98 or Me (no comments) Auto will marshal strings as Unicode.