I have a DLL which exports a function that returns a float*, that I would like to use it in my C# code. I am not sure how to Marshal my float* so that I can safely use it in C#.
So, in my C++ DLL, I have declared:
static float* GetSamples(int identifier, int dataSize);
In my C# script, I have:
[DllImport ("__Internal")]
public static extern float[] GetSamples (int identifier, int dataSize);
The C++ GetSamples(int,int) allocates memory and return a pointer t the float array. How do I declare the C# GetSamples to Marshal my float array, and how do I access the data (either by iteration or Marshal.Copy)?
Also, can I delete the float* from C# or do I have to call another C++ function to delete the allocated memory?
EDIT:
So this is what I have tried up to now.
First, on the C# side:
Declaration:
[DllImport ("__Internal")]
public static extern int GetSamples ([In, Out]IntPtr buffer,int length, [Out] out IntPtr written);
Trying to call it:
IntPtr dataPointer = new IntPtr();
IntPtr outPtr;
GetSamples(dataPointer, data.Length, out outPtr);
for (var i = 0; i < data.Length; i++){
copiedData[i] = Marshal.ReadByte(dataPointer, i);
}
Then in my C++ lib:
int AudioReader::RetrieveSamples(float * sampleBuffer, size_t dataLength, size_t * /* out */ written)
{
float* mydata = new float[dataLength];
//This is where I copy the actual data into mydata
memcpy(sampleBuffer, mydata, dataLength*sizeof(float));
delete data;
return dataLength;
}
I don't really know what outPtr is for... And I know I have some additional copying steps that I can removes, I just want to get it working for now.
So this is a bit of a complicated answer...
.NET doesn't know how to handle C++ memory allocation, so regardless returning a float * is dangerous at best for this. Furthermore the .NET memory model is based on COM so it is CoTaskMemAlloc based, not that it really helps you here. So here is what I would suggest:
int AudioReader::RetrieveSamples(
float * sampleBuffer,
int dataLength,
int * /* out */ written)
{
// assuming mydata is already defined
if(sampleBuffer == NULL || dataLength == 0)
{
*written = sizeof(mydata);
return -1;
}
ZeroMemory(sampleBuffer, dataLength);
int toCopy = min(dataLength, sizeof(myData));
//This is where I copy the actual data into mydata
memcpy(sampleBuffer, mydata, toCopy);
*written = toCopy;
return 0;
}
[DLLImport("__internal")]
private static extern int GetSamples(
[In, Out]IntPtr buffer,
[In] int length,
[Out] out int written);
float[] RetrieveFloats()
{
int bytesToAllocate = 0;
GetSamples(IntPtr.Zero, 0, out bytesToAllocate);
if(bytesToAllocate == 0)
return null;
int floatCount = bytesToAllocate/ sizeof(float);
float[] toReturn = new float[floatCount];
IntPtr allocatedMemory = Marshal.AllocHGlobal(bytesToAllocate);
int written = 0;
if(GetSamples(allocatedMemory, bytesToAllocate, out written) != -1)
{
floatCount = written/sizeof(float);
Marshal.Copy(allocatedMemory, toReturn, 0, floatCount);
}
Marshal.FreeHGlobal(allocatedMemory);
return toReturn;
}
Passing a bufferLength of zero would return the space required for the buffer, which can then be allocated and passed in.
You will need to allocate the memory for the buffer in C#, you cannot allocate it in C++
Related
I have the following export from a dll made with Delphi2006.
procedure ScSetMRStatus(StatusType: TStatusType; active_mr_ids: TLongIntArray; id_dst: Integer; id_src_list: TLongIntArray; IsComplete: Boolean); stdcall; export;
where TLongIntArray is defined as:
TLongIntArray = array of LongInt;
and TStatusType is just an enum:
TStatusType = ( stPhysical, stMaster );
Now I tried to call this method from a c# application.
[DllImport(DelphiDLLName, EntryPoint = "ScSetMRStatus", CallingConvention = CallingConvention.StdCall)]
private static extern void ScSetMRStatus(
Int32 statusType,
IntPtr activeMrIds,
Int32 IdDst,
IntPtr idSrcList,
[MarshalAs(UnmanagedType.U1)] bool isComplete);
Using it this way from c#:
ScSetMRStatus((Int32) statusType, ConvertManagedArrayToDelphiDynIntArray(activeMrIds), idDst, ConvertManagedArrayToDelphiDynIntArray(idSrcList), isComplete);
ConvertManagedArrayToDelpiDynIntArray looks like:
public static IntPtr ConvertManagedArrayToDelphiDynIntArray(int[] array)
{
if (array == null) return IntPtr.Zero;
int elementSize = sizeof(int);
int arrayLength = array.Length;
int allocatedMemSize = 8 + elementSize * arrayLength;
IntPtr delphiArrayPtr = Marshal.AllocHGlobal(allocatedMemSize);
Marshal.WriteInt32(delphiArrayPtr, 0, 1);
Marshal.WriteInt32(delphiArrayPtr, 4, arrayLength);
for (int k = 0; k < arrayLength; k++) {
Marshal.WriteInt32(delphiArrayPtr, 8 + k*elementSize, array[k]);
}
return delphiArrayPtr+8;
}
But this doesn't work!
How do I send c# arrays to delphi?
It's finally working!
We did a few changes around the call to free the allocated memory.
var activeMrIdsNative = ConvertManagedArrayToDelphiDynIntArray(activeMrIds);
var idSrcListNative = ConvertManagedArrayToDelphiDynIntArray(idSrcList);
ScSetMRStatus((Int32) statusType, activeMrIdsNative, idDst, idSrcListNative, isComplete);
Marshal.FreeHGlobal(activeMrIdsNative-8);
Marshal.FreeHGlobal(idSrcListNative-8);
We just thought it would not work, as we have not looked at what the Delphi side does with it. All data gets it's way into the delphi dll and it's working good.
Maybe there is a memory issue, but we will examine this.
I am using FFMPEG in C# and have the following function prototpe:
public static extern AVIOContext* avio_alloc_context(byte* buffer, int buffer_size, int write_flag, void* opaque, IntPtr read_packet, IntPtr write_packet, IntPtr seek);
In C/C++ this function is declared as follows:
avio_alloc_context (unsigned char *buffer, int buffer_size, int write_flag, void *opaque, int(*read_packet)(void *opaque, uint8_t *buf, int buf_size), int(*write_packet)(void *opaque, uint8_t *buf, int buf_size), int64_t(*seek)(void *opaque, int64_t offset, int whence))
In C/C++ I can do the following to call this function:
int readFunction(void* opaque, uint8_t* buf, int buf_size)
{
// Do something here
int numBytes = CalcBytes();
return numBytes;
}
int64_t seekFunction(void* opaque, int64_t offset, int whence)
{
// Do seeking here
return pos;
}
AVIOContext * avioContext = avio_alloc_context(ioBuffer, ioBufferSize, 0, (void*)(&fileStream), &readFunction, NULL, &seekFunction);
Where the readFunction and seekFunction are callback functions that are used in reading/seeking etc.
I am unsure how to copy this behaviour in the C# version of the code when it expects an IntPtr. How can I create the callback functions and pass them in the C# version?
Turns out you can do this, however it is not entirely intuitive.
First you need to create a delegate with the UnmanagedFunctionPointer and ensure the params can be passed
back from the callee to the caller after being modified using [In, Out]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int av_read_function_callback(IntPtr opaque, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), In, Out] byte[] endData, int bufSize);
In the function we can then marshal this delegate as follows:
private av_read_function_callback mReadCallbackFunc;
mReadCallbackFunc = new av_read_function_callback(ReadPacket);
mAvioContext = FFmpegInvoke.avio_alloc_context(mReadBuffer, mBufferSize, 0, null, Marshal.GetFunctionPointerForDelegate(mReadCallbackFunc), IntPtr.Zero, IntPtr.Zero);
where ReadPacket looks something like
public int ReadPacket(IntPtr opaque, byte[] endData, int bufSize)
{
// Do stuff here
}
This results in the same behaviour as a function pointer in C++.
I want to write a C# method with prototype like this:
void memcpy(byte[] dst, int dstOffset, byte[] src, int srcOffset, int len);
I have 2 options for this method:
1.
void memcpy(byte[] dst, int dstOffset, byte[] src, int srcOffset, int len)
{
for (int i = 0; i < len; i++)
{
dst[dstOffset + i] = src[srcOffset + i];
}
}
2.
void memcpy(byte[] dst, int dstOffset, byte[] src, int srcOffset, int len)
{
IntPtr intPtr = getIntPtr(dst, dstOffset);
System.Runtime.InteropServices.Marshal.Copy(src, srcOffset, intPtr, len);
}
IntPtr getIntPtr(byte[] buffer, int offset)
{
IntPtr intPtr;
unsafe
{
fixed (byte* p1 = buffer)
{
byte* p2 = p1 + offset;
intPtr = (IntPtr)p2;
}
}
return intPtr;
}
Questions:
A. I guess option 2 is faster than option 1, is it right?
B. Is there another faster method?
Thanks a lot.
Option #2 is broken, since you're using the pointer after the object it points to is no longer fixed. Pointers obtained within a fixed block may only be used inside that same fixed block. And it seems like you should have used Marshal.UnsafeAddrOfPinnedArrayElement anyway (and use it only inside a fixed block pinning the array).
Have a look at Buffer.BlockCopy.
I am trying to create a Win32 DLL exposes some functions which are called in C# as follows
__declspec(dllexport) int GetData(unsigned char* *data, int* size)
{
try
{
int tlen = 3;
unsigned char* tchr = new unsigned char[5];
tchr[0] = 'a';
tchr[1] = 'b';
tchr[2] = 'c';
*size = tlen;
*data = tchr;
return 1;
}
catch (char *p)
{
return 0;
}
}
And on C# side
[DllImport("MyDll.dll")]
static extern int GetData(ref byte[] data, ref int size);
static void Main()
{
try
{
int hr = 0;
byte[] gData = null;
int gSize = 0;
hr = GetData(ref gData, ref gSize);
Console.WriteLine(gSize);
for (int i = 0; i < gSize; i++)
Console.WriteLine((char)gData[i]);
}
catch (Exception p)
{
Console.WriteLine(p.ToString());
}
}
When I run C# code, AccessViolationException happens on GetData function which is a sign of exception in C++ code however, following C++ code snippet works fine without any error.
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char* data = NULL;
int size = NULL;
GetData(&data, &size);
printf("%d", size);
for (int i = 0; i < size; i++)
printf("%c,", data[i]);
return 0;
}
If you compare C# main function and C++ _tmain, they are almost analoguous so where I may make a mistake?
You are returning an array allocated by a call to C++ new and hoping that the marshaler will turn it into a C# byte[]. That won't happen.
You'll need to pass a pointer by reference and then marshal it by hand. Your p/invoke should look like this:
[DllImport("MyDll.dll")]
static extern int GetData(out IntPtr data, out int size);
When the function returns data will point to the array and you can read the contents using the Marshal class. I guess you would copy it to a new byte array.
var arr = new byte[size];
Marshal.Copy(data, arr, 0, size);
Some other points:
The calling conventions do not match. The native side is cdecl and the managed is stdcall.
You'll need to export a deallocator to delete the memory returned by the native function. Consider a re-design where the caller allocates the buffer.
I created a cross-platform DLL in C++ that compiles on both Windows and Mac OSX. On Windows, I have a C# app that calls the DLL using P/Invoke and on Mac OSX, an objective C app calls the DLL. I have simple functions working just fine but I need a new function that returns an array of integers.
The best example I can find is at Marshal C++ int array to C# and I was able to make it work. However, I would like to modify this example to pass the integer array back as a reference argument instead. The size of the array has to be set at runtime.
Here's what I've tried. The pSize is coming back correctly but the list is empty.
In unmanaged c++:
bool GetList(__int32* list, __int32* pSize)
{
// Some dummy data
vector<int> ret;
ret.push_back(5);
ret.push_back(6);
list = (__int32*)malloc(ret.size());
for (unsigned int i = 0; i < ret.size(); i++)
{
list[i] = ret.at(i);
}
*pSize = ret.size();
return true;
}
In C#:
[DllImport(#"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetList(out IntPtr arrayPtr, out int size);
public static int[] GetList() {
IntPtr arrayValue = IntPtr.Zero;
int size = 0;
bool b = GetFrames(out arrayValue, out size);
// arrayValue is 0 here
int[] result = new int[size];
Marshal.Copy(arrayValue, result, 0, size);
return result;
}
"Caller-allocates" is the only way to make code portable while keeping it maintainable. Not only does your code not change the caller's pointer, but the C# code has no way to free the memory you allocated (malloc-ed memory won't be cleaned up by garbage collection).
If finding the size is quick (doesn't require generating all the output data), just add a second function to return the size.
If you can't get the size until you generate the data, then make one function return the size and a pointer to the content (int**, on the C# side it will be ref IntPtr). And a second function that copies that data to the C# array and frees the native buffer.
Your problem is the definition of list, it really needs to be an __int32** in order to pass back the address of the allocated array. To breeze through the interop difficulties of pointers-to-pointers, how about you instead return the address of list or null if it fails:
__int32* GetList(__int32* pSize)
{
// Some dummy data
vector<int> ret;
ret.push_back(5);
ret.push_back(6);
// per #David's catch, you'll need to allocate the right amount
__int32* list = (__int32*)malloc(ret.size() * sizeof(__int32));
for (unsigned int i = 0; i < ret.size(); i++)
{
list[i] = ret.at(i);
}
*pSize = ret.size();
return list;
}
void RemoveList(__int32* list)
{
free(list);
}
With the appropriate modifications to your C#:
[DllImport(#"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetList(out int size);
[DllImport(#"MyDll.dll",
CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern void RemoveList(IntPtr array);
public static int[] GetList()
{
int[] result = null;
int size;
IntPtr arrayValue = IntPtr.Zero;
try
{
arrayValue = GetList(out size);
if (arrayValue != IntPtr.Zero)
{
result = new int[size];
Marshal.Copy(arrayValue, result, 0, size);
}
}
finally
{
// don't forget to free the list
RemoveList(arrayValue);
}
return result;
}