I've got a managed program, which cooperates with unmanaged DLL library.
Library constructs an object, which asks (by callback function converted to delegate) managed host to fill unmanaged array. The array itself is passed via pointer (IntPtr) along with information about its size. The type is known to both sides. The point is, how can I safely fill the unmanaged array with data in managed code? Two restrictions apply: no unsafe code and preferably no additional arrays created. The array might be passed in another way if such exists.
Let the callback have the following prototype:
typedef void (__stdcall * FillData)(double * array, int count);
Let the delegate have the following prototype:
protected delegate void FillData(IntPtr array, int count);
If you want no unsafe code then you'll have to let the pinvoke marshaller copy the array. Declare the delegate type like this:
private delegate MyUnmanagedCallback(
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] double[] array,
int count);
Be sure to store the delegate object so it can't be garbage collected.
Write managed callback according to your FillData prototype. Create unmanaged function pointer from it using Marshal.GetFunctionPointerForDelegate Method. Pass it to unmanaged code as callback function pointer.
Related
I have a .NET program that uses a DLL export to get a name of a user.
public static extern string Name(byte[] buf);
This is the export, and I would really like to not change it, as a lot of code relies on it. So, I would like to know, in C++, how would I convert a char* array to the byte buffer?
I have tried this:
void Name(std::byte buf[256])
{
std::string s{ "test" };
std::byte* ptr = reinterpret_cast<std::byte*>(s.data());
buf = ptr;
return;
}
When I print out the string that I convert, I get nothing, it is empty.
Your C++ function implementation does not match the expectations of the C# function declaration.
The C# byte array is marshalled into the function as a pinned pointer to the array's raw memory. The syntax you are using in the C++ code for the parameter (std::byte buf[256]) is just syntax sugar, the compiler actually treats it as a pointer (std::byte* buf). Which is fine in this situation. However, your C++ function is not actually copying anything into the memory that the pointer is pointing at. You are simply changing the pointer itself to point at a different memory address. And the pointer itself is a local variable, so when the function exits, the pointer will no longer exist, and it won't matter what it is pointing at.
Also, the C# declaration is expecting the function to return something that can be marshalled to a .NET string, but the C++ function is not actually return'ing anything at all. The default marshalling behavior for a string return value is as UnmanagedType.LPStr, so the C++ function needs to return a pointer to a null-terminated char* string. The memory for that string must be allocated with CoTaskMemAlloc(), as the marshaller will take ownership of the memory and free it with CoTaskMemFree() after converting the char data to a string.
Also, the C++ function has no calling convention defined, so it is going to use the compiler's default (which is usually __cdecl, unless you change your compiler's settings). However, the default calling convention that .NET's DllImport expects the C++ function to use is __stdcall instead (for compatibility with Win32 APIs). This mismatch won't matter in 64bit, but it matters alot in 32bit.
Without changing your C# declaration, try this on the C++ side:
char* __stdcall Name(std::byte buf[256])
{
std::string s{ "test" };
size_t size = s.size() + 1;
memcpy(buf, s.c_str(), size);
void *ptr = CoTaskMemAlloc(size);
memcpy(ptr, s.c_str(), size);
return ptr;
}
That being said, it is weird to have a function that returns a string in both a byte array output parameter and in a return value. Are you sure the byte array is not meant to be used as an input parameter instead, where the function parses its content to extract amd a string? That would make more sense. It would really help if you would update your question to show how your .NET code is actually calling the C++ function and using the byte array.
When you write
void Name(std::byte buf[256])
that declares buf as pointer to 256 bytes, not an array. So when you later write
buf = ptr;
all you are doing is changing the local variable buf to now point at ptr. And at the end of the function the local variable dies.
Use std::array<std::byte, 256>& and copy the string contents into that. Or even better return a freshly made array instead of an in/out parameter.
Assume the following:
Let us have a C++ class:
class ExampleClass
{
private:
int var1;
public:
void DoSomething();
}
Assume also following C++ function which we want to call from .NET Core app with PInvoke or using EmitCalli:
extern "C" ExampleClass CreateObject()
So C++ function returns instance of ExampleClass by value. Is there any way to get this instance on managed part as byte array (assuming that size of ExampleClass is known).
As I remember, in most native x86(x64) calling conventions C++ functions which return structures actully have pointer to the structure to fill as one of the parameters. Will this hint work with NET Core: allocate byte array on managed part and pass pointer as first parameter to unmanaged call?
Thanks!
in C++
ExampleClass CreateClass();
will return a block of bytes of sizeof(ExampleClass) and in absence of virtual members and inheritance it will be plain object and equivalent to same struct definition (see more about this here: Structure of a C++ Object in Memory Vs a Struct )
P/Invoke marshaller should be able to deal with this without a problem.
It it not an IntPtr, it's an actual memory block for C and C++.
IntPtr equivalent would be
ExampleClass * CreateClass();
I am not sure why it returns the instance instead of a pointer but they probably had their reasons.
so if you define it
public struct ExampleClass
{
public int Var1;
}
you should be fine (except if class structure is more complex you would need to check what was layout in C++ compilation, specifically padding. For example class with 2 bools may yield size of 16 in some padding).
see more here:
https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-classes-structures-and-unions
https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute
I have an array of arrays of this struct (shown here in C#, but existing in C++ as well):
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
IntPtr name; //pointer to string, char* on C++ side
long pValues;
long jValues;
long eValues;
long kValues;
int cost;
};
and an algorithm in a C++ DLL that does work on it, being called from managed C# code. It's CPU-heavy, which is what necessitates this as it runs much faster in C++ than C#. The managed (C#) side never has to know the contents of the struct data, as the algorithm only returns a single array of ints.
So, how would I go about storing this data in the most efficient way (ie with the least overhead), for the lifetime of the application? I think I have it narrowed down to two options:
Initialize structs and set values in C#, pin memory with GCHandle and pass reference to C++ whenever I want to do work (see this post on Unity forums)
Initialize structs and set values in C++, have structs persist in memory on unmanaged side
So my questions are very specific:
With 1, I'm confused as to how marshalling works. It looks like in MSDN: Copying and Pinning that you are able to pass arrays of structures by pinning and passing a reference to the pinned data, without having to copy or convert any of it (and as long as the struct looks the same on both sides). Am I reading that correctly, is that how it actually works? Referring to the Unity3d forum post, I see Marshal.PtrToStructure being called; I thought that performs copying operations? As the data would be stored on the managed side in this instance, having to copy and/or convert the data every time the C++ function is called would cause a lot of overhead, unless I'm thinking that those type of operations are a lot more expensive than they actually are.
With 2, I'm wondering if it's possible to have persistence between C++ calls. To the best of my knowledge, if you're P/Invoking from a DLL, you can't have persistent data on the unmanaged side, so I can't just define and store my struct arrays there, making the only data transferred between managed and unmanaged the int array resulting from the unmanaged algorithm. Is this correct?
Thank you very much for taking the time to read and help!
If the C# code does not need to know the internals of the array and the structure, don't expose it to the C# code. Do all the work on this type in the unmanaged code and avoid marshalling overhead.
Essentially, you want to follow this basic pattern. I'm sure the details will differ, but this should give you the basic concept.
C++
MyStruct* newArray(const int len)
{
return new MyStruct[len];
}
void workOnArray(MyStruct* array, const int len)
{
// do stuff with the array
}
void deleteArray(const MyStruct* array)
{
delete[] array;
}
C#
[DllImport(dllname)]
static extern IntPtr newArray(int len);
[DllImport(dllname)]
static extern void workOnArray(IntPtr array int len);
[DllImport(dllname)]
static extern void deleteArray(IntPtr array);
I have structure:
public struct MyStruct
{
public int a;
public int b;
public byte[] mass;
}
I need:
Pass poiner to "mass" array to C++ unmanaged function.
And after it done all work it will return me pointer to "mass".
So I have the list of MyStruct. And I need to know what the MyStruct in the list contains returned "mass"(wich pointer to I have).
If I know pointer to "mass" can I reduce pointer to 8 bytes and take pointer to MyStruct?
HOW TO :
1.Get IntPtr to "mass" array?
2.Get IntPtr to MyStruct structure?
3.Get MyStruct from IntPtr?
But, do not using any copy procedure, like Marshal.Copy...
Or is there a better way to do what I need ? Can I use pointers like in C++ or IntPtr is enought, and how can I do that?
Assuming that the memory for the array is allocated by the managed code:
When you pass an array to an unmanaged function via P/Invoke, then by default the array is generally pinned in memory by the marshaller so that the memory used by the array does not have to be copied.
You should not need to use an IntPtr at all - you just need to declare the P/Invoke so that it is accepting an array parameter.
However, things are different if the unmanaged code is allocating memory to be returned to the managed code; then things get MUCH more difficult.
Assuming that's not the case, then if you can show us the "C" function declaration we might be able to come up with a P/Invoke declaration.
(I do have a feeling that your situation may be a bit more complicated though...)
Some useful links for you:
http://msdn.microsoft.com/en-us/library/z6cfh6e6%28v=vs.80%29.aspx
http://msdn.microsoft.com/en-us/library/zah6xy75.aspx
How can I pass a pointer to an array using p/invoke in C#?
And some lower level information about the optimizations that the marshaller makes when calling unmanaged code and passing arrays. Essentially, if it can it doesn't make a copy of the data at all:
http://msdn.microsoft.com/en-us/library/23acw07k%28v=vs.80%29.aspx
Take care as the structure may have some specific memory alignment.
You may be interested by this link
I'm trying to pass an array of objects from C# to unmanaged C++, and nothing seems to work.
The compiler won't let me pretend the array is an IntPtr. Casting the array to an IntPtr doesn't work. I've tried to pass the address of pinned data, but this didn't work either.
I just need to pass a pointer to the beginning of the array, and this is turning out to be incredibly difficult.
Any suggestions or links? Thanx!
What finally worked:
Passing an array of structs instead of an array of objects (references).
Putting "[StructLayout(LayoutKind.Sequential, Pack = 1)]" just before the struct definition.
Putting "[MarshalAs(UnmanagedType.LPWStr)]" before the string (in the struct definition) to cause the string to appear as a wide-character string on the C++ side.
Declaring an array of structs for the argument in the DllImport declaration: "VariableObject[] varObj".
Declaring a pointer to the class as the parameter on the C++ side. (The C++ class mirrors the C# struct.): "VariableObject* varObj".
Can you cast to a void pointer? Be sure the array of objects is pinned.
In your C#/Managed method signature, mark the input parameter with [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]...
[DllImport(...)]
public void DoTask
(
...,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] results,
...
);
Then call it as you did always. Also, inside the unmanaged code, you can modify this array. I suggest that you send in an extra int telling the unmanaged code what the size of the array is to prevent “array out of bound” modification.