How to correctly define PRINT_NOTIFY_INFO_DATA?
Asked Answered
U

2

12

I was playing with a project from codeproject which basically monitors the printing activity on the computer. However it does not work correctly for 64 bit configuration. The below portion of the code was the issue. This code is called whenever printing is done.

PRINTER_NOTIFY_INFO info = (PRINTER_NOTIFY_INFO)Marshal.PtrToStructure(pNotifyInfo, typeof(PRINTER_NOTIFY_INFO));                        
int pData = (int)pNotifyInfo + Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO));
PRINTER_NOTIFY_INFO_DATA[] data = new PRINTER_NOTIFY_INFO_DATA[info.Count];
for (uint i = 0; i < info.Count; i++)
{
    data[i] = (PRINTER_NOTIFY_INFO_DATA)Marshal.PtrToStructure((IntPtr)pData, typeof(PRINTER_NOTIFY_INFO_DATA));
    pData += Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO_DATA));
}

Debugging shows that the data[i].field value is always 0. In 32 bit however it works correctly. I think the PRINTER_NOTIFY_INFO_DATA is not defined correctly. Presently I am using the following code. Can anybody fix this to work correctly in 64 bit as well?

[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO
{
    public uint Version;
    public uint Flags;
    public uint Count;
}


[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO_DATA_DATA
{
    public uint cbBuf;
    public IntPtr pBuf;
}

[StructLayout(LayoutKind.Explicit)]
public struct PRINTER_NOTIFY_INFO_DATA_UNION
{
    [FieldOffset(0)]
    private uint adwData0;
    [FieldOffset(4)]
    private uint adwData1;
    [FieldOffset(0)]
    public PRINTER_NOTIFY_INFO_DATA_DATA Data;
    public uint[] adwData
    {
        get
        {
            return new uint[] { this.adwData0, this.adwData1 };
        }
    }
}

// Structure borrowed from http://lifeandtimesofadeveloper.blogspot.com/2007/10/unmanaged-structures-padding-and-c-part_18.html.
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO_DATA
{
    public ushort Type;
    public ushort Field;
    public uint Reserved;
    public uint Id;
    public PRINTER_NOTIFY_INFO_DATA_UNION NotifyData;
}

I was testing print using the MS XPS driver. The Code project article is Here

Ungenerous answered 9/10, 2012 at 3:39 Comment(0)
B
16

It doesn't work correctly for 64-bit configuration because of Data Alignment.

So I suggest you to change PRINTER_NOTIFY_INFO as follows:

[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO
{
    public uint Version;
    public uint Flags;
    public uint Count;
    public PRINTER_NOTIFY_INFO_DATA_UNION aData;
}

And then use Marshal.OffsetOf instead of Marshal.SizeOf:

PRINTER_NOTIFY_INFO info = (PRINTER_NOTIFY_INFO)Marshal.PtrToStructure(pNotifyInfo, typeof(PRINTER_NOTIFY_INFO));                        
long pData = (long)pNotifyInfo + (long)Marshal.OffsetOf(typeof(PRINTER_NOTIFY_INFO), "aData");
PRINTER_NOTIFY_INFO_DATA[] data = new PRINTER_NOTIFY_INFO_DATA[info.Count];
for (uint i = 0; i < info.Count; i++)
{
    data[i] = (PRINTER_NOTIFY_INFO_DATA)Marshal.PtrToStructure((IntPtr)pData, typeof(PRINTER_NOTIFY_INFO_DATA));
    pData += (long)Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO_DATA));
}
Burdette answered 9/10, 2012 at 8:7 Comment(4)
Additionally, make sure the projects involved are configured to build x86 executables!!!Glib
Couple of things to note on this. 1) Marshal.OffsetOf() returns a IntPtr object, which to my knowledge cannot be directly cast to a long. You must use the IntPtr.ToInt32() method. 2) This code can potentially crash your code if PRINTER_NOTIFY_INFO.Count is 0. It would mean that there are no structures in the aData[] list. By declaring it in the way described, you are guaranteeing at least 1 object in the array. If count is 0, than that's potential a segmentation fault. I would declare aData as a IntPtr instead, since really, in unmanaged code its just a pointer anyways.Rhonarhonchus
@Rhonarhonchus 1) There's an explicit conversion operator 2) No, it cannot. And no, aData is not a pointer in unmanaged code. Please learn what ANYSIZE_ARRAY is.Burdette
What is actually different that makes it work in 64 bit i see only property added aData and usage of offsetOf() what makes this work in 64bit?Starlight
S
0

Another solution is add 4 bytes after making: PRINTER_NOTIFY_INFO to fill up for data alignment (32int is only half a 64bit) + 4 bytes makes 64bit.

Also note: PRINTER_NOTIFY_INFO_DATA_DATA => IntPtr pBuf; is an inptr thus 64 bit in windows 64bit. which makes this whole class 128 bit worth of data. (so dont forget to add extra 4 bytes here as well.

Either the reserved or the Id also seems to have 4 extra bytes so i ended up adding 8 extra bytes per info_data in the end, to make it work.

Starlight answered 29/10, 2018 at 20:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.