I'm working on a managed C++ wrapper for unmanaged C++ code and have a question.
Just for simplicity let's say that I need to pass a 2D array from C# code to Managed C++ to Unmanaged C++. I have no problem with 1D array but stuck with 2D version. Converting it to 1D is the option, but I want to take a look if there are other ways.
For simplicity let say I want to send 2D array to unmanaged code using intermediate wrapper and change its values.
so here is C# code with a call to managed C++:
MNumeric wrapper = new MNumeric(); //managed C++ object
int[,] dArr = new int[10, 10];
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
dArr[i, j] = 10;
}
}
wrapper.ChangeArray(dArr, Convert.ToInt32(Math.Sqrt(dArr.Length)))
Managed C++:
void MNumeric::ChangeArray(cli::array<int,2> ^arr, int mySize)
{
//some code to call Unmanaged C++ passing managed 2D array ???
}
Unmanaged C++
void UNumeric::ChangeArray(int** arr, int mySize)
{
for(int i=0;i<mySize;i++)
{
for(int j=0;j<mySize;j++)
{
arr[i][j]=i;
}
}
}
Thanks a lot for your help.
Looks like I fix one error but got another. My C++ Managed code looks like this now.
void MNumeric::ChangeArray(cli::array<int,2> ^arr, int mySize)
{
pin_ptr<int> p_arr = &arr[0,0];
u_num->ChangeArray((int**)p_arr, mySize);
}
where u_num is just a pointer to UNumeric class.
The error I got now is the following:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Any ideas appreciated.
void MNumeric::ChangeArray(cli::array<int,2> ^arr, int mySize)
{
pin_ptr<int> p = &arr[0,0]; // pin pointer to first element in arr
int* np = p; // pointer to the first element in arr
UNumeric::ChangeArray((int**)np, mySize);
}
You should not use a cast here, it could hide potentially important warnings. Notably, a 2D array is not convertible to an int**. An int**, is a pointer to an array of pointers. An int*[] is a pointer to an array of arrays. You have a function that takes an array of pointers. It wants a managed int[][], not an int[,]. Your compiler would thoroughly have shouted at you for attempting this, if you hadn't casted.
Related
I'm trying to copy an array of floats from my C# application to an array in a C-coded DLL.
Im used to programming in C#, not so much with C. However I have no problem doing the reverse procedure ie. reading an array of floats from a C coded DLL into my C# application. I've read several threads on this site but cant work out where Im going wrong.
C# CODE
[DllImport(#"MyDll")]
static extern int CopyArray(double[] MyArray);
double[] myValues = new double[100]
int a = 0;
while(a < 100)
{
myValues[a] = a;
a++;
}
CopyArray(myValues);
C++ DLL
This is the function header;
__declspec(dllexport) int CopyArray(float* Wavelengths);
This is the function code;
float theValues[100];
int CopyArray(float* theArray)
{
status = 0;
int count = 0;
while (count < 100)
{
theValues[count] = theArray[count];
++count;
}
return(status);
}
I'm expecting my C# array to end up in the C array "theValues" but that's not happening. There is nothing getting into "theValues" array.
A couple of things.
You are mixing data types and they are different lengths (float is 32bit and double is 64bit). Both types exist in both languages, but your caller and callee need to agree on the data type. Here is a list of data types and their managed/unmanaged equivalents.
The parameter you are sending is not a pointer. It might be translated to that automatically by the compiler, but there are several options. It is entirely possible that the compiler will pick one you don't want (more info here). The one you are probably looking for is [In]. If you want to get data back from C to C#, you will also want [Out]:
[DllImport(#"MyDll")]
static extern int CopyArray([In, Out]double[] MyArray);
I'm creating a .NET wrapper class to a C++/CLI function. Internally the function uses an array of ints (int*), but I'd like to expose a clean List<int>^ on the .NET side. I'm using the following code to convert a C# List to a C++ unmanaged list of int.
Apart from the fact that I'm not freeing the allocated memory using Marshal::FreeHGlobal, is there any problem with the function? For example, should I be allocating ((count * 4) + 4) for the array length bytes?
static int* ListToArray(List<int>^ list){
// new array
int count = list->Count;
int* arr = (int*)(Marshal::AllocHGlobal(count * 4).ToPointer());
// convert list to array
for(int a = 0; a < count; a++){
arr[a] = list[a];
}
return arr;
}
Your code is correct. You allocate the correct amount of memory. Instead of 4 I would use sizeof int, which is more expressive.
I do wonder why you are using AllocHGlobal. I think new would be more appropriate in C++ code. And I also wonder why you are using raw pointers. Wouldn't std::vector<int> makes more sense in C++ code?
I have spent the last day searching documentation, reviewing forum posts, and googling to try to do something that I am guessing could be easily done with the right information.
I have a very large existing C++ application that has an already defined COM server with many methods exposed. I am trying to use those COM methods in a C# application (I am experienced in C++, but a C# newbie).
So in my VS2010 C# application, I add the COM server as a reference. COM Methods are visible in the object browser, and passing single-valued strings, floats, and ints seems to work fine.
But I am stumped trying to read SAFEARRAY values passed out of the C++ COM server into the C# application. Eventually I need to pass string arrays from C++ server to the C# app, but in just testing passing an array of floats, I have code that builds but fails with the following exception when I try to cast the System.Object containing the array of floats to (float[]) ,
"exception {System.InvalidCastException: Unable to cast object of type 'System.Object[]' to type 'System.Single[]'."
With intellisence I can see that the Object contains the correct 8760 long array of floats, but I am unable to access that data in C#.
Here is the code on the C# side (d2RuleSet is an interface defined in the DOE2Com COM server). The exception above is thrown on the last line below.
DOE2ComLib.DOE2Com d2RuleSet;
d2RuleSet = new DOE2ComLib.DOE2Com();
System.Int32 i_Series =0;
System.Object pv_WeatherData;
float[] faWeatherData;
iOut = d2RuleSet.GetWeatherData(i_Series, out pv_WeatherData);
Type typeTest;
typeTest = pv_WeatherData.GetType();
int iArrayRank = typeTest.GetArrayRank();
Type typeElement = typeTest.GetElementType();
faWeatherData = (float[])pv_WeatherData;
Below is the section in the idl file defining the C++ COM method
HRESULT GetWeatherData( [in] int iSeries, [out] VARIANT* pvWeatherData, [out,retval] int * piErrorCode);
Below is the C++ code where the VARIANT data is loaded.
void CDOE2BaseClass::GetWeatherData( int iSeries, VARIANT* pvWeatherData, int* piErrorCode)
{
*piErrorCode = 0;
if (iSeries < 0 || iSeries >= D2CWS_NumSeries)
*piErrorCode = 1;
else if (m_faWeatherData[iSeries] == NULL)
*piErrorCode = 3;
else
{
SAFEARRAYBOUND rgsaBound;
rgsaBound.lLbound = 0;
rgsaBound.cElements = 8760;
// First lets create the SafeArrays (populated with VARIANTS to ensure compatibility with VB and Java)
SAFEARRAY* pSAData = SafeArrayCreate( VT_VARIANT, 1, &rgsaBound );
if( pSAData == NULL ) {
#ifndef _DOE2LIB
_com_issue_error( E_OUTOFMEMORY);
#else
//RW_TO_DO - Throw custom Lib-version exception
OurThrowDOE2LibException(-1,__FILE__,__LINE__,0,"OUT OF MEMORY");
#endif //_DOE2LIB
}
for (long hr=0; hr<8760; hr++)
{
COleVariant vHrResult( m_faWeatherData[iSeries][hr] );
SafeArrayPutElement( pSAData, &hr, vHrResult );
}
// Now that we have populated the SAFEARRAY, assign it to the VARIANT pointer that we are returning to the client.
V_VT( pvWeatherData ) = VT_ARRAY | VT_VARIANT;
V_ARRAY( pvWeatherData ) = pSAData;
}
}
Thanks in advance for help with this problem, I feel like I spent too much time on what should be a simple problem. Also please post up any links or books that that cover interop between native C++ and C# well (I think I have already ping-ponged through most of the Visual Studio/MSDN documentation, but maybe I missed something there too).
-----------------End Of Original Question------------------------------------------------------
I am editing to post the code from phoog's successful solution below, so others can read and use it.
int iOut = 0;
System.Int32 i_Series =0;
System.Object pv_WeatherData = null;
iOut = d2RuleSet.GetWeatherData(i_Series, out pv_WeatherData);
Type typeTest;
typeTest = pv_WeatherData.GetType();
int iArrayRank = typeTest.GetArrayRank();
Type typeElement = typeTest.GetElementType();
//float[] faWeatherData = (float[])pv_WeatherData;
float[] faWeatherData = ConvertTheArray((object[])pv_WeatherData);
....
float[] ConvertTheArray(object[] inputArray)
{
float[] result = new float[inputArray.Length];
for (var index = 0; index < result.Length; index++)
result[index] = (float)inputArray[index];
return result;
}
The marshalled SAFEARRAY is a System.Object[]; you can't reference-convert that to a System.Single[]. You have to cast the individual elements. This would work, assuming that all the elements of the argument array are in fact boxed floats:
float[] ConvertTheArray(object[] inputArray)
{
float[] result = new float[inputArray.Length];
for (var index = 0; index < result.Length; index++)
result[index] = (float)inputArray[index];
return result;
}
You could do that with a lot less typing using Linq, but as you're a C# newbie I thought a more basic solution might be better.
EDIT
Since you indicated in your comment that your object[] is referenced as an object, here's the usage example:
object obj = GetMarshalledArray();
float[] floats = ConvertTheArray((object[])obj);
EDIT 2
A shorter solution using Linq:
float[] ConvertTheArray(object[] inputArray)
{
return inputArray.Cast<float>().ToArray();
}
I would like to do the following:
Call Unamanaged method that returns the array of type MyStruct[] that it allocated.
Example of c code:
MyStruct[] foo(int size)
{
Mystruct* st = (MyStruct*)malloc(size * sizeof(MyStruct));
return st;
}
How should Implement c# calling method?
Thank you!
The Marshal class in the System.Runtime.InteropServices namespace has a lot of methods to help you marshal data to and from unmanaged code.
You need to declare your native method:
[DllImport("myclib.dll")]
public static extern IntPtr Foo(Int32 size);
And also your C struct as a managed value type (you can use attributes on the fields to control exactly how they are mapped to native memory when they are marshalled):
[StructLayout(LayoutKind.Sequential)]
struct MyStruct {
public Char Character;
public Int32 Number;
}
Then you can use Marshal.PtrToStructure to marshal each element of the array into a managed value:
var n = 12;
var pointer = Foo(n);
var array = new MyStruct[n];
var structSize = Marshal.SizeOf(typeof(MyStruct));
for (var i = 0; i < n; ++i) {
array[i] = (MyStruct) Marshal.PtrToStructure(pointer, typeof(MyStruct));
pointer += structSize;
}
Note that you are using malloc to allocate the memory in the C code. C# us unable to free that memory and you will have to provide another method to free the allocated memory.
This should help you http://msdn.microsoft.com/en-us/library/aa288468%28v=vs.71%29.aspx
Refer to this thread, it has some information about how to return a dynamically allocated struct from C++ to C# as an array.
My C# method needs to be invoked from C++
Originally my C# method takes a parameter of type double[], but when calling from C++ it becomes a SAFEARRAY
In C++ I need to take data from an array of doubles, and populate a SAFEARRAY. I have not found any sample code to do this.
Any help is appreciated
Following is the code to create a safearray in C++.
#include<oaidl.h>
void CreateSafeArray(SAFEARRAY** saData)
{
double data[10]; // some sample data to write into the created safearray
SAFEARRAYBOUND Bound;
Bound.lLbound = 0;
Bound.cElements = 10;
*saData = SafeArrayCreate(VT_R8, 1, &Bound);
double HUGEP *pdFreq;
HRESULT hr = SafeArrayAccessData(*saData, (void HUGEP* FAR*)&pdFreq);
if (SUCCEEDED(hr))
{
// copy sample values from data[] to this safearray
for (DWORD i = 0; i < 10; i++)
{
*pdFreq++ = data[i];
}
SafeArrayUnaccessData(*saData);
}
}
Free the pointer when you are finished like the following code-
SAFEARRAY* saData;
CreateSafeArray(&saData); // Create the safe array
// use the safearray
...
...
// Call the SafeArrayDestroy to destroy the safearray
SafeArrayDestroy(saData);
saData = NULL; // set the pointer to NULL
If you use ATL for C++, then better use CComSafeArray declared in "atlsafe.h". This is wrapper for SAFEARRAY. link text
Continuing on #Liton's answer, I want to stress his last sentence, i.e. ATL's CComSafeArray. It really can save you a lot of typing. CComSafeArray has C++ constructors, destructors, operator overloads including one for [ ] that gives you an read / write reference to any element in the SAFEARRAY. In short, you can really focus on your business logic and needn't worry about the SAFEARRAY plumbing:
#include <atlbase.h>
#include <atlsafe.h>
// ...
CComSafeArray<double> arr(10);
arr[0] = 2.0;
arr[1] = 3.0;
arr[2] = 5.0;
// ...
At the very least, even if you're not going to use CComSafeArray it's worthwhile to deconstruct its source code in <atlsafe.h> giving you better insight on the what, when, why and how on SAFEARRAY functions.
Passing SAFEARRAYs is not recommended. It is recommended to place the SAFEARRAY into a VARIANT. Further, the SAFEARRAY should hold VARIANT data. This gives the best of all worlds and makes passing VARIANT SAFEARRAY of VARIANTs more useful to other languages. E.g. C++ to VB / C# (Note it is up to the caller to free/destroy the SAFEARRAY)
Building on the previous code
// A VARIANT holding a SAFEARRAY of VARIANTs
VARIANT vRet;
SAFEARRAYBOUND Bound;
Bound.lLbound = 0;
Bound.cElements = 10;
SAFEARRAY * psaData = SafeArrayCreate(VT_VARIANT, 1, &Bound);
VARIANT HUGEP * pData = NULL;
HRESULT hr = SafeArrayAccessData(psaData, (void HUGEP * FAR *)&pData);
if (SUCCEEDED(hr))
{
for (short i = 0; i < 10; ++i,++pData)
{
::VariantInit(pData);
pData->vt = VT_I2;
pData->iVal = i;
}
SafeArrayUnaccessData(psaData);
}
vRet.vt = VT_ARRAY | VT_VARIANT;
vRet.parray = psaData;