Ultratrunks
Ultratrunks

Reputation: 2574

Can't get around compile error about fixed sized buffers in C#

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

Answers (2)

svick
svick

Reputation: 244948

You just can't declare fixed-size array of custom structs 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

David Heffernan
David Heffernan

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

Related Questions