我在使用 Marshal.PtrToStructure() 从指向 DEVMODE 类型结构的指针中提取数据时遇到问题。Here http://msdn.microsoft.com/en-us/library/dd183565%28v=vs.85%29.aspx是指向 DEVMODE 结构上的 MSDN 条目的链接。
我对该结构的 C# 实现如下:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;
public unsafe fixed char dmDeviceName [CCHDEVICENAME];
public Int16 dmSpecVersion;
public Int16 dmDriverVersion;
public Int16 dmSize;
public Int16 dmDriverExtra;
public DM_FIELD_TYPE dmFields;
public Int16 dmOrientation;
public Int16 dmPaperSize;
public Int16 dmPaperLength;
public Int16 dmPaperWidth;
public Int16 dmScale;
public Int16 dmCopies;
public Int16 dmDefaultSource;
public Int16 dmPrintQuality;
public POINTL dmPosition;
public Int32 dmDisplayOrientation;
public Int32 dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
public unsafe fixed char dmFormName [CCHFORMNAME];
public Int16 dmLogPixels;
public Int32 dmBitsPerPel;
public Int32 dmPelsWidth;
public Int32 dmPelsHeight;
public Int32 dmDisplayFlags;
public Int32 dmNup;
public Int32 dmDisplayFrequency;
public Int32 dmICMMethod;
public Int32 dmICMIntent;
public Int32 dmMediaType;
public Int32 dmDitherType;
public Int32 dmReserved1;
public Int32 dmReserved2;
public Int32 dmPanningWidth;
public Int32 dmPanningHeight;
public DEVMODE(byte[] data)
{
unsafe
{
fixed (byte* packet = &data[0])
{
this = *(DEVMODE*)packet;
}
}
}
}
[Flags()]
public enum DM_FIELD_TYPE : int
{
/* field selection bits */
DM_ORIENTATION = 0x00000001,
DM_PAPERSIZE = 0x00000002,
DM_PAPERLENGTH = 0x00000004,
DM_PAPERWIDTH = 0x00000008,
DM_SCALE = 0x00000010,
DM_POSITION = 0x00000020,
DM_NUP = 0x00000040,
DM_DISPLAYORIENTATION = 0x00000080,
DM_COPIES = 0x00000100,
DM_DEFAULTSOURCE = 0x00000200,
DM_PRINTQUALITY = 0x00000400,
DM_COLOR = 0x00000800,
DM_DUPLEX = 0x00001000,
DM_YRESOLUTION = 0x00002000,
DM_TTOPTION = 0x00004000,
DM_COLLATE = 0x00008000,
DM_FORMNAME = 0x00010000,
DM_LOGPIXELS = 0x00020000,
DM_BITSPERPEL = 0x00040000,
DM_PELSWIDTH = 0x00080000,
DM_PELSHEIGHT = 0x00100000,
DM_DISPLAYFLAGS = 0x00200000,
DM_DISPLAYFREQUENCY = 0x00400000,
DM_ICMMETHOD = 0x00800000,
DM_ICMINTENT = 0x01000000,
DM_MEDIATYPE = 0x02000000,
DM_DITHERTYPE = 0x04000000,
DM_PANNINGWIDTH = 0x08000000,
DM_PANNINGHEIGHT = 0x10000000,
DM_DISPLAYFIXEDOUTPUT = 0x20000000
}
public struct POINTL
{
public Int32 x;
public Int32 y;
}
在此结构中,有 2 个字符数组“dmDeviceName”和“dmFormName”。两者都是 32 个字符长。问题是,当我尝试从指针编组结构 DEVMODE 时,这些字符数组没有被正确填充。例如,dmDeviceName 将仅包含实际设备名称的第一个字符。数组的其余条目将只是“\0”。我正在执行封送处理的代码行如下:
DEVMODE devMode = (DEVMODE)Marshal.PtrToStructure(aData[i].NotifyData.Data.pBuf, typeof(DEVMODE));
“aData[i].NotifyData.Data.pBuf”是指向 DEVMODE 类型结构的有效指针。我知道这一点有两个原因。
此结构是从名为 FindNextPrinterChangeNotification() 的打印机驱动程序调用返回的信息的子集。我使用它来捕获打印作业信息,当我捕获打印作业并使用上面的代码封送 DEVMODE 对象时,“dmCopies”字段始终与该作业中打印的份数完全正确。因此,结构中的第 12 个成员如何正确编组,而它之前的一些成员似乎没有正确编组,这有点奇怪。
我使用 Marshal.ReadByte() 强制读取 aData[i].NotifyData.Data.pBuf 指向的前 100 个字节,果然,第一个字节集合是打印机设备的完整名称。所以我知道信息就在那里。
无论出于何种原因,当我使用 Marshal.PtrToStructure() 时,它似乎无法正确填充字符数组。我很确定大多数其他变量都是正确的,但由于数组问题,我有疑问。有谁知道这里发生了什么事。
- 编辑 -
根据要求,以下是填充 aData[] 数组的代码:
private PRINTER_NOTIFY_INFO_DATA[] MarshalOutPrinterNotifyInfoDataArray(IntPtr ppPrinterNotifyInfo)
{
//Dereferencing ppPrinterNotifyInfo and setting NotifyInfoStruct to it.
PRINTER_NOTIFY_INFO NotifyInfoStruct = (PRINTER_NOTIFY_INFO)Marshal.PtrToStructure(ppPrinterNotifyInfo, typeof(PRINTER_NOTIFY_INFO));
//Creating a point to point to the PRINTER_NOTIFY_INFO and then moving it to the end of the structure where the
//aData[] member would begin.
int paData = (int)ppPrinterNotifyInfo + Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO));
//Creating an array to hold all the elements of our aData array.
PRINTER_NOTIFY_INFO_DATA[] data = new PRINTER_NOTIFY_INFO_DATA[NotifyInfoStruct.Count];
//looping through all the PRINTER_NOTIFY_INFO_DATA elments in the aData member and adding them to our local array.
for (uint i = 0; i < NotifyInfoStruct.Count; i++)
{
//extracting out a single PRINTER_NOTIFY_INFO_DATA item and storing it in our local array
data[i] = (PRINTER_NOTIFY_INFO_DATA)Marshal.PtrToStructure((IntPtr)paData, typeof(PRINTER_NOTIFY_INFO_DATA));
//moving our pointer to the next PRINTER_NOTIFY_INFO_DATA item
paData += Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO_DATA));
}
return data;
}
private void SomeRoutine()
{
//////////////////
/// some code here
//////////////////
//retrieving information about the most recent change notification for a change notification object associated with the printer
FindNextPrinterChangeNotification(m_ManualResetEvent.SafeWaitHandle.DangerousGetHandle(), out pdwChangeFlags, null, out ppPrinterNotifyInfo);
//Need to extract our PRINTER_NOTIFY_INFO_DATA array out of the PRINTER_NOTIFY_INFO structure
PRINTER_NOTIFY_INFO_DATA[] aData = MarshalOutPrinterNotifyInfoDataArray(ppPrinterNotifyInfo);
//////////////////
/// some code here
//////////////////
}