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.
Related
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'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.
I have tried to do this in many ways, but none is working. Does anyone have a correct example for this? I just want to move the wchar_t* value from a function to the C# level.
This isn't as difficult as you think it is... What is wchar_t*? What value does that type typically represent? A string. It's the equivalent to the LPWSTR type defined in windows.h.
So, you marshal it as a string type. However, since it's an out parameter (or a return value), you'll need to use the StringBuilder class on the C# end, rather than the string type.
The P/Invoke syntax would look something like this:
[DllImport("MyLib.dll")]
public static extern void MyFunction(StringBuilder str);
And to use it, you first declare an instance of the StringBuiler class with the appropriate capacity, and then call the function:
StringBuilder myString = new StringBuilder(255);
MyFunction(myString);
Remember that the unmanaged C++ code must free the string in order to prevent a memory leak. It's the only one with access to the unmanaged memory area where the string was allocated.
is there any way through which we can get the collection of string from c++ to c#
C# Code
[DllImport("MyDLL.dll")]
private static extern List<string> GetCollection();
public static List<string> ReturnCollection()
{
return GetCollection();
}
C++ Code
std::vector<string> GetCollection()
{
std::vector<string> collect;
return collect;
}
The code above is only for sample, the main aim is to get collection in C# from C++, and help would be appreciated
//Jame S
There are a multitude of ways to tackle this, but they are all rather more complex than what you currently have.
Probably the easiest way to pass a string allocated in C++ to C# is as a BSTR. That allows you to allocate the string down in your C++ and let the C# code deallocate it. This is the biggest challenge you face and marshalling as BSTR solves it trivially.
Since you want a list of strings you could change to marshalling it as an array of BSTR. That's one way, it's probably the route I would take, but there are many other approaches.
I think you have to convert that to something more C# friendly, like C-style array of char or wchar_t C-style strings. Here you can find an example of std::string marshaling. And here you'll find a discussion on how to marshal an std::vector.
Try to use instead
C# part
[DllImport("MyDLL.dll")]
private static extern void GetCollection(ref string[] array, uint size);
C++ part
void GetCollection(string * array , uint size)
and fill array in GetCollection function
I suggest you change it to array and then marshal it. Marshalling arrays is much easier in PInvoke and in fact I do not believe C++ classes classes can be marshalled at all.
I would return a SAFEARRAY of BSTR in C++, and marshal it as an array of strings in C#. You can see how to use safearray of BSTR here How to build a SAFEARRAY of pointers to VARIANTs?, or here http://www.roblocher.com/whitepapers/oletypes.aspx.