-
Declaring pointers
How can we declare and address pointer of this structure
public struct PASSTHRU_MSG
{
unsafe public System.Int32 ProtocolID;
unsafe public System.Int32 RxStatus;
unsafe public System.Int32 TxFlags;
unsafe public System.Int32 Timestamp;
unsafe public System.Int32 DataSize;
unsafe public System.Int32 ExtraDataIndex;
unsafe public System.Byte[] Data;
}
where Data is fixed array of 4000 bytes
When I try
unsafe
{
PASSTHRU_MSG1* test;
test = &ptrMsg;
}
It gives error
Error 1 Cannot take the address of, get the size of, or declare a pointer to a managed type ('project.Form1.PASSTHRU_MSG1')
-
Re: Declaring pointers
What are you trying to do with that unsafe code?
-
Re: Declaring pointers
Also, none of those 'unsafe' keywords are necessary, they should all be removed. Is is also possible to pass a struct to native code without using pointers, i.e. using completely safe code. So paste the signature of the native function you're trying to call.
-
Re: Declaring pointers
Revise your code like this:
Code:
unsafe
{
PASSTHRU_MSG ptrMsg = new PASSTHRU_MSG();
PASSTHRU_MSG *test;
test = &ptrMsg;
}
-
Re: Declaring pointers
(a) You have to do a 'fixed' to get a native pointer to a managed block of memory. This executes a 'pin' on the memory, preventing the GC from moving it after you've gained its native address e.g.
Code:
unsafe void Example(int [] values)
{
fixed (int *pValues = &values[0])
{
}
}
(b) You HAVE to tell the runtime how big the fixed-size byte[] array is :
Code:
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct PASSTHRU_MSG
{
public System.Int32 ProtocolID;
public System.Int32 RxStatus;
public System.Int32 TxFlags;
public System.Int32 Timestamp;
public System.Int32 DataSize;
public System.Int32 ExtraDataIndex;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4000)]
public System.Byte[] Data;
}
(c) Since your structure contains a byte array, you can't just pin it (which is what the above does). Because of the byte [] array the struct doesn't lie in contiguous memory - the byte [] array may not exist at the same address as the structure member variable.
So instead you must allocate a byte[] array of the same size as the structure then call Marshal.StructureToPtr. This will marshal the structure into the byte [] array and therefore make the "Data" member variable occupy the correct memory space.
Code:
static unsafe void Example()
{
PASSTHRU_MSG msg = new PASSTHRU_MSG();
byte [] msgData = new byte[Marshal.SizeOf(typeof(PASSTHRU_MSG))];
fixed (byte *pMsgData = &msgData[0])
{
Marshal.StructureToPtr(msg, (IntPtr)pMsgData, true);
}
}
You can do this WITHOUT using unsafe code (highly advisable as it will then produce verifyable code) by pinning the byte[] array using the GCHandle class :
Code:
static void SafeExample()
{
PASSTHRU_MSG msg = new PASSTHRU_MSG();
byte[] msgData = new byte[Marshal.SizeOf(typeof(PASSTHRU_MSG))];
GCHandle handle;
try
{
handle = GCHandle.Alloc(msgData, GCHandleType.Pinned);
IntPtr addressOfStruct = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(msg, handle.AddrOfPinnedObject(), true);
// use "addressOfStruct" in your interop calls.
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
}
}
You could also create the memory needed for the struct by using Marshal.AllocHGlobal or one of the other Marshal class memory allocation methods. However you have to destroy this memory manually so I prefer to use a pinned managed byte[] array since no cleanup is necessary (apart from the unpinning after use).
All of this is moot if you define your p/invoke method signature appropriately in the first place e.g. for
Code:
// C++
__declspec(dllexport) void PassMyStruct(PASSTHRU_MSG *pMsg);
use
Code:
[DllImport(...)]
public static extern void PassMyStruct([MarshalAs(UnmanagedType.LPStruct)] PASSTHRU_MSG msg);
Darwen.
-
Re: Declaring pointers
I feel incredibly stupid after reading darwen's post. More so than usual. You are one smart person.