Reputation: 2574
I'm trying to create some structures in C# to mimic ones from some C++ Microsoft header files. My code is as follows:
[StructLayout(LayoutKind.Sequential)]
unsafe public struct _NotifyData
{
fixed uint adwData[2];
public struct Data
{
uint cbBuf;
IntPtr pBuff;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO_DATA
{
public ushort Type;
public ushort Field;
public uint Reserved;
public uint Id;
public _NotifyData NotifyData;
}
[StructLayout(LayoutKind.Sequential)]
unsafe public struct PRINTER_NOTIFY_INFO
{
public uint Version;
public uint Flags;
public uint Count;
fixed PRINTER_NOTIFY_INFO_DATA aData[1]; //Compiler complains here!!!
}
The compiler complains about my aData[1]
variable in my declaration of struct PRINTER_NOTIFY_INFO
. I've encountered a handfull of these and adding fixed
to the variable in question and unsafe
to the structure declaration seemed to work. Except for this structure that is. The error I get is this:
Fixed size buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double
Now I can see that the type I'm using is not one of the listed types, but according to this, putting unsafe
in front of the struct declaration should allow me to use types other than the ones listed. For some reason it is not letting me.
Upvotes: 0
Views: 1512
Reputation: 244948
You just can't declare fixed-size array of custom struct
s in C#, as the error message and the page you linked to (I suggest you reread it) say.
EDIT: Removed incorrect info. See David Heffernan's answer for a way to solve this.
Upvotes: 1
Reputation: 613311
I have a couple of suggestions.
First of all _NotifyData
is a union and should look like this:
[StructLayout(LayoutKind.Explicit)]
public struct _NotifyData
{
[FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
public uint[] adwData;
[FieldOffset(0)]
public struct Data
{
uint cbBuf;
IntPtr pBuff;
}
}
Secondly, PRINTER_NOTIFY_INFO
simply can't be handled by the P/invoke marshaller. You will have to use manual marshalling, i.e. Marshal.PtrToStructure()
to get anywhere. The documentation for the ppPrinterNotifyInfo
parameter of FindNextPrinterChangeNotification()
states:
A pointer to a pointer variable that receives a pointer to a system-allocated, read-only buffer. Call the FreePrinterNotifyInfo function to free the buffer when you are finished with it. This parameter can be NULL if no information is required.
You should pass an IntPtr
as an out
parameter and then use Marshal.PtrToStructure()
to read out the contents into your own data structures. Something like this:
IntPtr PrinterNotifyInfo;
FindNextPrinterChangeNotification(..., out PrinterNotifyInfo);
IntPtr pCount = PrinterNotifyInfo + 2*Marshal.SizeOf(typeof(uint));
uint Count = (uint)Marshal.ReadInt32(pCount);
IntPtr pData = pCount + Marshal.SizeOf(typeof(uint));
for (int i=0; i<Count; i++)
{
PRINTER_NOTIFY_INFO_DATA Data = (PRINTER_NOTIFY_INFO_DATA)Marshal.PtrToStructure(pData, typeof(PRINTER_NOTIFY_INFO_DATA));
pData += Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO_DATA));
}
I've not attempted to compile this but hopefully it gets the idea across.
Upvotes: 4