I have a dll which has arguments with double*, eg xyz(double* a, double* b). I am using this DLL to pass two double arrays to function xyz. The problem is when I'm passing a double array by reference using a, the array is being updated but the values are rounded off which happens when you typecast something to and from int. Can you tell me an efficient way of sending this double array to c dll and getting the desired results in decimal. (Changing the DLL is not an option). Also I tried Marshal, I am not entirely sure if I did the right thing, but whenever I used to change the argument of xyz to xyz(IntPtr a, double* b) or something I used to get AccessViolationException, corrupt memory.
Try following :
double[] a = { 1.23, 2.34, 5.45 };
IntPtr aptr = Marshal.AllocHGlobal(a.Length * sizeof(double));
Marshal.Copy(a, 0, aptr, a.Length);
double[] b = { 1.23, 2.34, 5.45 };
IntPtr bptr = Marshal.AllocHGlobal(b.Length * sizeof(double));
Marshal.Copy(b, 0, bptr, b.Length);
xyz(aptr, bptr)
If I understand correctly, you are passing an array from C# to C (or C++) code. The problem with the approach of sending in the array directly to the method like xyz(double* a, double* b) is that the the code has no way of knowing the sizes of the arrays. This can get you access violation exceptions, once the C/C++ code tries to access an element that is out of the bounds of the array.
There are multiple ways around this - either using SafeArray from C# (that already contains the size attribute), sending the arrays to some bridging method with the sizes, or passing the data in a struct, like this:
internal struct ArraysStruct
{
public int floatArraySize;
public IntPtr floatArray;
public int uintArraySize;
public IntPtr uintArray;
}
Which would then be matched on the C/C++ side by something like:
typedef struct ArraysStruct
{
size_t floatArraySize;
float* floatArray;
size_t uintArraySize;
unsigned int* uintArray;
}
You would need to use Marshal.AllocHGlobal() to allocate the arrays to the sizes first (in C#) and then Marshal.FreeHGlobal() once you're done with them.
Related
I create a SAFEARRAY storing VARIANTs that are BYTEs in C++.
When this structure is marshaled to C#, a weird thing happens.
If I print the content of this structure in C# to a WinForms ListBox, e.g.:
byte data[]
TestSafeArray(out data);
lstOutput.Items.Clear();
foreach (byte x in data)
{
lstOutput.Items.Add(x); // Strange numbers
}
I get some numbers that seem unrelated to the original ones. Moreover, each time I run the C# client for a new test, I get a different set of numbers.
Note that if I inspect the content of that data array with the Visual Studio debugger, I get the correct numbers, as the following screenshot shows:
However, if I CopyTo the marshaled data array to a new one, I get the correct numbers:
byte[] data;
TestSafeArray(out data);
// Copy to a new byte array
byte[] byteData = new byte[data.Length];
data.CopyTo(byteData, 0);
lstOutput.Items.Clear();
foreach (byte x in byteData)
{
lstOutput.Items.Add(x); // ** WORKS! **
}
This is the C++ repro code I use to build the SAFEARRAY (this function is exported from a native DLL):
extern "C" HRESULT __stdcall TestSafeArray(/* [out] */ SAFEARRAY** ppsa)
{
HRESULT hr = S_OK;
try
{
const std::vector<BYTE> v{ 11, 22, 33, 44 };
const int count = static_cast<int>(v.size());
CComSafeArray<VARIANT> sa(count);
for (int i = 0; i < count; i++)
{
CComVariant var(v[i]);
hr = sa.SetAt(i, var);
if (FAILED(hr))
{
return hr;
}
}
*ppsa = sa.Detach();
}
catch (const CAtlException& e)
{
hr = e;
}
return hr;
}
And this is the C# P/Invoke I used:
[DllImport("NativeDll.dll", PreserveSig = false)]
private static extern void TestSafeArray(
[Out, MarshalAs(UnmanagedType.SafeArray,
SafeArraySubType = VarEnum.VT_VARIANT)]
out byte[] result);
Note that if in C++ I create a SAFEARRAY storing BYTEs directly (instead of a SAFEARRAY(VARIANT)), I get the correct values immediately in C#, without the intermediate CopyTo operation.
[Out, MarshalAs(UnmanagedType.SafeArray,
SafeArraySubType = VarEnum.VT_VARIANT)]
out byte[] result);
You fibbed. You told the marshaller that you wanted an array of variants, indeed compatible with what the C++ compiler produced. It will dutifully produce an object[], object is the standard marshaling for a VARIANT. The elements of the array are boxed bytes.
That did not fool the debugger, it ignored the program declaration and looked at the array type and discovered object[], readily visible in your screenshot. So it properly accessed the boxed bytes. And it did not fool Array.CopyTo(), it takes an Array argument so is forced to look at the element type. And properly converted the boxed bytes to bytes, it knows how to do that.
But the C# compiler is fooled badly. It has no idea that it needs to emit an unbox instruction. Not actually sure what goes wrong, you are probably getting the low byte of the object address. Pretty random indeed :)
Fibbing in pinvoke declarations can be very useful. Works just fine in this particular case if the array contains actual objects, like strings, arrays or interface pointers. The probable reason the marshaller doesn't scream bloody murder. Not here, the boxing trips it up bad. You'll have to fix the declaration, use object[].
I'm attempting to follow the answer at this question
My struct looks like this in C
typedef struct drive_info_t {
unsigned char drive_alias[32];
} drive_info_t;
My function looks like this in C
unsigned int get_drive_info_list(drive_info_t **list, unsigned int *item_count) {
//fill list in native C
//print out in native C
printf("list.alias - %s\r\n",list[i]->drive_alias);
}
My C# struct looks like this
[StructLayout(LayoutKind.Sequential)]
public struct drive_info_t
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] drive_alias;
}
My C# function declaration looks like this
[DllImport("mydll.dll", EntryPoint = "get_drive_info_list", CallingConvention = CallingConvention.Cdecl)]
public static extern uint GetDriveInfoList(out System.IntPtr ptr_list_info, out System.IntPtr ptr_count);
I'm calling C# function like this
IntPtr ptr_list_info = IntPtr.Zero;
IntPtr ptr_cnt = IntPtr.Zero;
ret = api.GetDriveInfoList(out ptr_list_info, out ptr_cnt);
I'm marshaling the returned pointers like this
nAlloc = ptr_cnt.ToInt32();
int szStruct = Marshal.SizeOf(typeof(api.drive_info_t));
api.drive_info_t[] localStructs = new api.drive_info_t[nAlloc];
for (int i = 0; i < nAlloc; i++)
{
localStructs[i] = (api.drive_info_t)Marshal.PtrToStructure(ptr_list_info, typeof(api.drive_info_t));
ptr_list_info = new IntPtr(ptr_list_info.ToInt32() + (szStruct));
}
Printing the struct alias like this
for (uint i = 0; i < localStructs.Length; i++)
{
Console.WriteLine("list.alias - {0}", System.Text.Encoding.Default.GetString(localStructs[i].drive_alias));
}
Thanks for staying with me..
This is what my output looks like on a console application in C#. You can see the native C dll printing to the console it's values, but my marshaling is messing up somewhere:
======================== C values ============================
list.alias - drv1
list.alias - drv2
list.alias - drv3
list.alias - drv4
======================== C# values ============================
list.alias - drv1
list.alias - o£Q95drv2
list.alias - o£Q95drv3
list.alias - o£Q95drv4
I have no clue where this garbage text and offset is coming from.
I'm responsible for the .Net side, other team members can change the native C as needed if required, but native C changes need to be cross-platform OSX/Windows/Linux.
Thanks in advance.
This is a perfect example of why the types of the arguments do not define the interface. Consider
drive_info_t **list
This could be a pointer to an array of structs. In which case presumably the array is allocated by the callee. Which is why a pointer to array is used as opposed to an array.
Or it could be an array of pointers to struct. Here the array would be allocated by caller and the structs could be allocated by either callee or caller. No way for us to tell.
We can discern that your interface accepts an array of pointers to struct. Look at this code:
list[i]->drive_alias
Quite clearly list is an array of pointers to struct.
This tells you that your code is wrong. You have followed the wrong template because you mis-identified the semantics. Your next step is to return to the documentation for the interface and any example calling code to learn precisely what the semantics are. Who allocates and deallocated what.
Almost certainly you will use IntPtr[] to marshal the array but beyond that it is impossible for us to say what happens. I would guess that the caller allocates the structs but don't rely on guesswork. Read the documentation and example calling code.
Beyond that the second argument should be ref uint or perhaps out uint depending on the semantics of the function. Again, that's something that we cannot work out.
I have the following c#/c code, where I am doing stuff in a C dll. Am using pinvoke/marshal as the black magic that enables me to dynamically allocate/free stuff in the dll, without c# code knowing anything untoward is going on.
In this snippet, you will see that I am using 2 different ways to alloc/use/free an array of doubles. My question is, what does the "MarshalAs(UnmanagedType..." line do, because both incantations (ie, using or not using the MarshalAs statement) work fine? I should add that I have a poor understanding of C, even less understanding of C#, and I understand the whole pinvoke/marshal about as well as I understand supersymmetric quantum mechanics.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class row
{
public int a;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
IntPtr[] b;
IntPtr [] c;
}
// c code
struct row
{
int a;
double *b;
double *c;
}
void fooe(void)
{
row.b[4] = (double *) malloc(54000);
row.c[4] = (double *) malloc(54000);
free(row.b[4]);
free(row.c[4]);
}
This particular use of ByValArray changes everything.
First, let's consider the without ByValArray case. Here the array is marshalled as a pointer to the first element. The matching C struct is
struct row
{
int a;
void* *b; // more likely you would use a typed pointer
void* *c;
}
No for the case where you use ByValArray. Here the array is marshalled inline and the equivalent C struct is:
struct row
{
int a;
void* b[12];
void* *c;
}
These two versions are very different.
I also wonder if you really meant to use IntPtr[] in the C# code. Did you perhaps really mean to write double[]? So, perhaps you actually meant:
[StructLayout(LayoutKind.Sequential)]
public class row
{
public int a;
double[] b;
double[] c;
}
In which case the matching C struct would be:
struct row
{
int a;
double *b;
double *c;
}
I've no idea what is meant by the fooe function, but it makes no sense at all and does not compile. It looks like you are trying to assign a pointer to a double which is quite meaningless.
My question has to do with trying to call a function written in C from C#. I've looked in a header file that came with the C library to understand the functions as they exist in the C dll. Here's what I see:
C code (for a function called "LocGetLocations"):
typedef enum {
eLocNoError,
eLocInvalidCriteria,
eLocNoMatch,
eLocNoMoreLocations,
eLocConnectionError,
eLocContextError,
eLocMemoryError
} tLocGetStatus;
typedef void *tLocFindCtx;
typedef void *tLocation;
PREFIX unsigned int POSTFIX LocGetLocations
(
tLocFindCtx pCtx,
tLocation *pLoc,
unsigned int pNumLocations,
tLocGetStatus *pStatus
);
In C#, I have this:
[DllImport(#"VertexNative\Location.dll")]
public static extern uint LocGetLocations(IntPtr findContext, out byte[] locations, uint numberLocations, out int status);
The problem is that I don't quite know how to handle the pLoc parameter in C#. I'm bringing it over as a byte array, although I'm not sure if that is correct. The C library's documentation says that that parameter is a pointer to an array of handles.
How can I get an array back on the C# side and access its data?
The example I was given in C, looks like this:
tLocation lLocation[20];
// other stuff
LocGetLocations(lCtx, lLocation, 20, &lStatus)
Any help would be much appreciated!
Generally, the only thing that matters is the size of the parameters. As I recall enums are integers in C, so you can simply use that. Or better, recreate the same enum in C#, I think it would work. One thing to remember is that when dealing with complex structs, one needs to use attributes to tell the framework about the desired alignment of members.
In the end, this signature works:
[DllImport(#"VertexNative\Location.dll")]
public static extern uint LocGetLocations(IntPtr findContext, [Out] IntPtr[] locations, uint numberLocations, out int status);
And I can call it like this (some refactoring needed):
IntPtr[] locations = new IntPtr[20];
int status;
// findContext is gotten from another method invocation
uint result = GeoCodesNative.LocGetLocations(findContext, locations, 20, out status);
Thanks for the help!
I have the following C++ method :
__declspec(dllexport) void __stdcall getDoubles(int *count, double **values);
the method allocates and fills an array of double and sets *count to the size of the array.
The only way i managed to get this to work via pinvoke is :
[System.Runtime.InteropServices.DllImportAttribute("xx.dll")]
public static extern void getDoubles(ref int count, ref System.IntPtr values);
and usage is :
int count = 0;
IntPtr doubles = new IntPtr();
Nappy.getDoubles(ref count, ref doubles);
double[] dvs = new double[count];
for(int i = 0;i < count;++{
dvs[i] = (double)Marshal.PtrToStructure(doubles, typeof(System.Double));
doubles = new IntPtr(doubles.ToInt64()+Marshal.SizeOf(typeof(System.Double)));
}
the values end up in the dvs array.
Is there a better way ti do this not invloving pointer arithmetic in a managed language...
I think you can use
Marshal.Copy( source, destination, 0, size );
You'll need to change the unmanaged method signature so it reads like this:
__declspec(dllexport) void __stdcall getDoubles(SAFEARRAY *array);
Then you should be able to use the following managed version of the function:
[System.Runtime.InteropServices.DllImportAttribute("xx.dll")]
public static extern void getDoubles(
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_R8)]
double[] array
);
Of course that you'll also have to rewrite your unmanaged code to work with SAFEARRAYs.
More about this topic can be found at MSDN.
One question though, I recall working with ZLib in C# which is able, without any wrapper, to work with byte[] while the unmanaged counterpart is BYTE*, have you tried working directly with double* / double[] ?