gpt4 book ai didi

c# - 结构 DEVMODE 中的 Marshal.PtrToStructure() 和字符数组出现问题

转载 作者:太空狗 更新时间:2023-10-30 00:44:39 26 4
gpt4 key购买 nike

我在使用 Marshal.PtrToStructure() 从指向 DEVMODE 类型结构的指针中提取数据时遇到问题。 Here是指向 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”。我正在执行编码(marshal)处理的代码行如下:

DEVMODE devMode = (DEVMODE)Marshal.PtrToStructure(aData[i].NotifyData.Data.pBuf, typeof(DEVMODE));

“aData[i].NotifyData.Data.pBuf”是指向 DEVMODE 类型结构的有效指针。我知道这有两个原因。

  1. 此结构是从名为 FindNextPrinterChangeNotification() 的打印机驱动程序调用返回的信息的子集。我用它来捕获打印作业信息,当我捕获打印作业并使用上面的代码编码 DEVMODE 对象时,“dmCopies”字段始终与该作业中打印的份数完全正确。因此,当结构中的第 12 个成员似乎没有正确编码时,结构中的第 12 个成员如何正确编码,这有点奇怪。

  2. 我使用 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
//////////////////
}

最佳答案

使用字符串:

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string dmDeviceName;

参见:http://pinvoke.net/default.aspx/Structures/DEVMODE.html

编辑: 在发布我的快速回复时我没有注意到,因为我专注于字符串(并且在结构的最开头有一个,这意味着其余的结构元素不能影响事情),但正如其他人所指出的那样, DEVMODE 结构中的联合存在很大问题。联合中的元素不是按顺序排列的,而是占据内存中的相同空间:一次只能使用一个联合元素。例如:

union {
DWORD dmDisplayFlags;
DWORD dmNup;
};

意味着 dmDisplayFlags 和 dmNup 本质上是同一 block 内存的不同名称。 IE。 dmDisplayFlags 和 dmNup 都存储在距结构开头 116 字节的偏移处。更改 dmNup 会导致 dmDisplayFlags 的值也发生更改,反之亦然。使用 C# 代码,如:

public Int32 dmDisplayFlags;
public Int32 dmNup;

意味着它们按顺序存储,即在偏移量 116 和 120 处。这打乱了整个结构的布局。要解决这个问题,您需要使用显式布局并手动定义字段偏移量。查看我之前在 pinvoke.net 上提供的链接,了解如何在此特定结构上执行此操作的示例。请注意 dmDisplayFlags 和 dmNup 如何具有相同 字段偏移量。由于 C# 本身不支持联合,因此对于需要它的特殊互操作场景(如本例),这是一种处理联合的有点笨拙的方法。

我建议解决联合问题,然后按照最初的建议将字符串与 ByValTStr 一起使用(总而言之,使用 pinvoke.net 上的内容)。看看它是否能让您获得更好的结果。

其他人认为这是一个 Unicode 问题,但我认为事实并非如此。文档说 FindNextPrinterChangeNotification 在 Unicode 中不可用。如果您在字节级别检查了结构并说它不是 Unicode - 我绝对相信您。

来自文档:

"ByValTStr:用于出现在结构中的行内固定长度字符数组。与 ByValTStr 一起使用的字符类型由 System.Runtime.InteropServices.CharSet 参数确定应用于包含结构的 System.Runtime.InteropServices.StructLayoutAttribute。始终使用 MarshalAsAttribute.SizeConst 字段来指示数组的大小。

.NET Framework ByValTStr 类型在结构(例如,char s[5])中的行为类似于 C 样式、固定大小的字符串。托管代码中的行为不同于 Microsoft Visual Basic 6.0 的行为,后者不是空终止的(例如,MyString As String * 5)。”

我很清楚这应该是普遍接受的“正确”方法。

关于c# - 结构 DEVMODE 中的 Marshal.PtrToStructure() 和字符数组出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7354762/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com