Visual C# Developer Center >
Visual C# Forums
>
Visual C# General
>
Callback from unmanaged code with pointer to array of different types
Callback from unmanaged code with pointer to array of different types
I'm having troubles with a callback from a C dll. The C callback function prototype looks like this:
__stdcall int Callback(void* pData, SomeStruct* infoAboutData)
where pData is a pointer to a buffer in the C dll which the C#/VB.NET etc user is allowed to read and write. It's a stdcall function, and pData can be a pointer to 8 bit data, or 16 bit data. The size of the array, unfortunately, is not passed with the callback, but can be determined from fields in SomeStruct, but not using simple field1 + field2 math (rather more like 'if field1 is X then blah blah blah why oh why don't they just pass the array size') The 8 vs 16 array type can be easily determined from a field in SomeStruct.
What I want is something like
public delegate int Callback( byte[] buffer, ref SomeStruct infoAboutData)
with by needing 8 and 16 bits, I'm guessing I need to declare two delegates, one for 8 bit data, one for 16 bit data, and that's fine, because I know before the callback is called whether to expect 8 or 16 bit data. I'm happy to do something like this
public delegate int Callback8(byte[] buffer, ref SomeStruct moreData)
public delegate int Callback16(short[] buffer, ref SomeStruct moreData)
But the question is: how can I marshall that first parameter so that I can read from and write to the buffer of data in the DLL? I've tried
[MarshalAs(UnmanagedType.LPArray, SizeConst=64)] byte[] buffer
but when (I'm assuming) the callback is invoked, I get "unhandled exception System.OverflowException occurred in UnknownModule (Arithmetic operation resulted in an overflow)" and I don't know how to find out more info about that.
Is there something wrong with the declaration? Can I just say the SizeConst is some huge number and require the user to not go beyond the correct bounds? Does .NET copy the data from unmanaged memory into managed memory and then write it back or what?
I know the callback from the C dll is working properly 'cause if I change the first param to System.IntPtr, the memory pointed to is the data I'm expecting.
I'm considering writing a class which wraps/uses the System.IntPtr and Marshal.ReadInt8 etc. Is that the make-work way?
Any and all help is greatly appreciated!
M.
Answers
I still don't know how to declare the marshal for a void*, but I've found that the following at least lets me access and modify the unmanaged data if I declare the void* as an IntPtr in the delegate
public class WrapperArray8
{
protected IntPtr m_pData;protected int m_length;
public WrapperArray8(IntPtr pData, int length){m_pData = pData;m_length = length;}
public int Length
{get
{return m_length;}}
public IntPtr DataPointer{get{return m_pData;}}
protected void CheckIndex(int index){if ((index < 0) || (index >= m_length)){throw new IndexOutOfRangeException();}}
public int this[int index]{get{CheckIndex(index);return Marshal.ReadByte(m_pData, index);}
set{CheckIndex(index);Marshal.WriteByte(m_pData, index, (byte)value);}}
// Copy data to an existing arraypublic void CopyTo(byte[] dst, int srcStartIndex, int length){CheckIndex(srcStartIndex + length - 1);Marshal.Copy(m_pData, dst, srcStartIndex, length);}
}
This will allow you to access the data as an array of bytes. You can use Marshal.WriteInt16 do to something similar for shorts. Check Marshal for other useful members. Your needs and choices may vary.
I've posted this in case someone out there stumbles across this and is equally stuck and needs to make progress.- if you want the marshaller to allow the native code to write to the array, you'll have to use "ref". Also, if it is variable size array the array size must be a parameter of the delegate to use the SizeParamIndex property.
Michael T. Brown wrote:
I'm having troubles with a callback from a C dll. The C callback function prototype looks like this:
__stdcall int Callback(void* pData, SomeStruct* infoAboutData)
where pData is a pointer to a buffer in the C dll which the C#/VB.NET etc user is allowed to read and write. It's a stdcall function, and pData can be a pointer to 8 bit data, or 16 bit data. The size of the array, unfortunately, is not passed with the callback, but can be determined from fields in SomeStruct, but not using simple field1 + field2 math (rather more like 'if field1 is X then blah blah blah why oh why don't they just pass the array size') The 8 vs 16 array type can be easily determined from a field in SomeStruct.
What I want is something like
public delegate int Callback( byte[] buffer, ref SomeStruct infoAboutData)
with by needing 8 and 16 bits, I'm guessing I need to declare two delegates, one for 8 bit data, one for 16 bit data, and that's fine, because I know before the callback is called whether to expect 8 or 16 bit data. I'm happy to do something like this
public delegate int Callback8(byte[] buffer, ref SomeStruct moreData)
public delegate int Callback16(short[] buffer, ref SomeStruct moreData)
But the question is: how can I marshall that first parameter so that I can read from and write to the buffer of data in the DLL? I've tried
[MarshalAs(UnmanagedType.LPArray, SizeConst=64)] byte[] buffer
Yes, the marshaller copies the data so that the native code can access the data without modifying managed memory. Upon return from the call any modified data is copied back.Michael T. Brown wrote:
but when (I'm assuming) the callback is invoked, I get "unhandled exception System.OverflowException occurred in UnknownModule (Arithmetic operation resulted in an overflow)" and I don't know how to find out more info about that.
Is there something wrong with the declaration? Can I just say the SizeConst is some huge number and require the user to not go beyond the correct bounds? Does .NET copy the data from unmanaged memory into managed memory and then write it back or what?
If the data you'e passing can be 8-bit or 16-bit and is viewed as void * on the native side, I'd suggest you view it as opaque and use IntPtr.Michael T. Brown wrote:
I know the callback from the C dll is working properly 'cause if I change the first param to System.IntPtr, the memory pointed to is the data I'm expecting.
I'm considering writing a class which wraps/uses the System.IntPtr and Marshal.ReadInt8 etc. Is that the make-work way?
Any and all help is greatly appreciated!
All Replies
I still don't know how to declare the marshal for a void*, but I've found that the following at least lets me access and modify the unmanaged data if I declare the void* as an IntPtr in the delegate
public class WrapperArray8
{
protected IntPtr m_pData;protected int m_length;
public WrapperArray8(IntPtr pData, int length){m_pData = pData;m_length = length;}
public int Length
{get
{return m_length;}}
public IntPtr DataPointer{get{return m_pData;}}
protected void CheckIndex(int index){if ((index < 0) || (index >= m_length)){throw new IndexOutOfRangeException();}}
public int this[int index]{get{CheckIndex(index);return Marshal.ReadByte(m_pData, index);}
set{CheckIndex(index);Marshal.WriteByte(m_pData, index, (byte)value);}}
// Copy data to an existing arraypublic void CopyTo(byte[] dst, int srcStartIndex, int length){CheckIndex(srcStartIndex + length - 1);Marshal.Copy(m_pData, dst, srcStartIndex, length);}
}
This will allow you to access the data as an array of bytes. You can use Marshal.WriteInt16 do to something similar for shorts. Check Marshal for other useful members. Your needs and choices may vary.
I've posted this in case someone out there stumbles across this and is equally stuck and needs to make progress.- if you want the marshaller to allow the native code to write to the array, you'll have to use "ref". Also, if it is variable size array the array size must be a parameter of the delegate to use the SizeParamIndex property.
Michael T. Brown wrote:
I'm having troubles with a callback from a C dll. The C callback function prototype looks like this:
__stdcall int Callback(void* pData, SomeStruct* infoAboutData)
where pData is a pointer to a buffer in the C dll which the C#/VB.NET etc user is allowed to read and write. It's a stdcall function, and pData can be a pointer to 8 bit data, or 16 bit data. The size of the array, unfortunately, is not passed with the callback, but can be determined from fields in SomeStruct, but not using simple field1 + field2 math (rather more like 'if field1 is X then blah blah blah why oh why don't they just pass the array size') The 8 vs 16 array type can be easily determined from a field in SomeStruct.
What I want is something like
public delegate int Callback( byte[] buffer, ref SomeStruct infoAboutData)
with by needing 8 and 16 bits, I'm guessing I need to declare two delegates, one for 8 bit data, one for 16 bit data, and that's fine, because I know before the callback is called whether to expect 8 or 16 bit data. I'm happy to do something like this
public delegate int Callback8(byte[] buffer, ref SomeStruct moreData)
public delegate int Callback16(short[] buffer, ref SomeStruct moreData)
But the question is: how can I marshall that first parameter so that I can read from and write to the buffer of data in the DLL? I've tried
[MarshalAs(UnmanagedType.LPArray, SizeConst=64)] byte[] buffer
Yes, the marshaller copies the data so that the native code can access the data without modifying managed memory. Upon return from the call any modified data is copied back.Michael T. Brown wrote:
but when (I'm assuming) the callback is invoked, I get "unhandled exception System.OverflowException occurred in UnknownModule (Arithmetic operation resulted in an overflow)" and I don't know how to find out more info about that.
Is there something wrong with the declaration? Can I just say the SizeConst is some huge number and require the user to not go beyond the correct bounds? Does .NET copy the data from unmanaged memory into managed memory and then write it back or what?
If the data you'e passing can be 8-bit or 16-bit and is viewed as void * on the native side, I'd suggest you view it as opaque and use IntPtr.Michael T. Brown wrote:
I know the callback from the C dll is working properly 'cause if I change the first param to System.IntPtr, the memory pointed to is the data I'm expecting.
I'm considering writing a class which wraps/uses the System.IntPtr and Marshal.ReadInt8 etc. Is that the make-work way?
Any and all help is greatly appreciated!
Thanks for confirming I have to use IntPtr and the Marshal.WriteByte etc to access/modify the unmanaged memory in situ.

