EnumJobs返回与Marshal.SizeOF不同的JOB_INFO_1大小

时间:2014-11-11 05:05:40

标签: c# windows print-spooler-api

我从托管代码(C#)调用Win32函数EnumJobs(http://msdn.microsoft.com/en-us/library/windows/desktop/dd162625(v=vs.85).aspx)。

    [DllImport("Winspool.drv", SetLastError = true, EntryPoint = "EnumJobsA")]
    public static extern bool EnumJobs(
       IntPtr hPrinter,                    // handle to printer object
       UInt32 FirstJob,                // index of first job
       UInt32 NoJobs,                // number of jobs to enumerate
       UInt32 Level,                    // information level
       IntPtr pJob,                        // job information buffer
       UInt32 cbBuf,                    // size of job information buffer
       out UInt32 pcbNeeded,    // bytes received or required
       out UInt32 pcReturned    // number of jobs received
    );

EnumJobs(_printerHandle, 0, 99, 1, IntPtr.Zero, 0, out nBytesNeeded, out pcReturned);

我指定级别1接收JOB_INFO_1,但我遇到的问题是上面的函数返回nBytesNeeded为每个结构240,而Marshal.SizeOf(typeof(JOB_INFO_1))是64字节,当我运行{Marshal.PtrToStructure时导致内存异常1}}。手动为结构计算字节给出了64,所以我有点不知道为什么我从函数接收240字节结构,任何见解都会受到赞赏。

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Unicode)]
public struct JOB_INFO_1
{
    public UInt32 JobId;
    public string pPrinterName;
    public string pMachineName;
    public string pUserName;
    public string pDocument;
    public string pDatatype;
    public string pStatus;
    public UInt32 Status;
    public UInt32 Priority;
    public UInt32 Position;
    public UInt32 TotalPages;
    public UInt32 PagesPrinted;
    public SYSTEMTIME Submitted;
}

1 个答案:

答案 0 :(得分:5)

64的大小确实对JOB_INFO_1是正确的,但如果你仔细查看文档,它会讨论一组结构:

pJob [out]
A pointer to a buffer that receives an array of JOB_INFO_1, JOB_INFO_2, or JOB_INFO_3 structures.

另外写道:

The buffer must be large enough to receive the array of structures and any strings or other data to which the structure members point.

因此,结构本身旁边有额外数据的字节。

<强>解决方案:

填充结构,递增下一个结构的指针并忽略剩余的字节。

完整示例:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;

namespace WpfApplication3
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // Get handle to a printer
            var hPrinter = new IntPtr();
            bool open = NativeMethods.OpenPrinterW("Microsoft XPS Document Writer", ref hPrinter, IntPtr.Zero);
            Debug.Assert(open);

            /* Query for 99 jobs */
            const uint firstJob = 0u;
            const uint noJobs = 99u;
            const uint level = 1u;

            // Get byte size required for the function
            uint needed;
            uint returned;
            bool b1 = NativeMethods.EnumJobsW(
                hPrinter, firstJob, noJobs, level, IntPtr.Zero, 0, out needed, out returned);
            Debug.Assert(!b1);
            uint lastError = NativeMethods.GetLastError();
            Debug.Assert(lastError == NativeConstants.ERROR_INSUFFICIENT_BUFFER);

            // Populate the structs
            IntPtr pJob = Marshal.AllocHGlobal((int) needed);
            uint bytesCopied;
            uint structsCopied;
            bool b2 = NativeMethods.EnumJobsW(
                hPrinter, firstJob, noJobs, level, pJob, needed, out bytesCopied, out structsCopied);
            Debug.Assert(b2);

            var jobInfos = new JOB_INFO_1W[structsCopied];
            int sizeOf = Marshal.SizeOf(typeof (JOB_INFO_1W));
            IntPtr pStruct = pJob;
            for (int i = 0; i < structsCopied; i++)
            {
                var jobInfo_1W = (JOB_INFO_1W) Marshal.PtrToStructure(pStruct, typeof (JOB_INFO_1W));
                jobInfos[i] = jobInfo_1W;
                pStruct += sizeOf;
            }
            Marshal.FreeHGlobal(pJob);

            // do something with your structs
        }
    }

    public class NativeConstants
    {
        public const int ERROR_INSUFFICIENT_BUFFER = 122;
    }

    public partial class NativeMethods
    {
        [DllImport("kernel32.dll", EntryPoint = "GetLastError")]
        public static extern uint GetLastError();
    }

    public partial class NativeMethods
    {
        [DllImport("Winspool.drv", EntryPoint = "OpenPrinterW")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool OpenPrinterW([In] [MarshalAs(UnmanagedType.LPWStr)] string pPrinterName,
            ref IntPtr phPrinter, [In] IntPtr pDefault);

        [DllImport("Winspool.drv", EntryPoint = "EnumJobsW")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumJobsW([In] IntPtr hPrinter, uint FirstJob, uint NoJobs, uint Level, IntPtr pJob,
            uint cbBuf, [Out] out uint pcbNeeded, [Out] out uint pcReturned);
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct JOB_INFO_1W
    {
        public uint JobId;
        [MarshalAs(UnmanagedType.LPWStr)] public string pPrinterName;
        [MarshalAs(UnmanagedType.LPWStr)] public string pMachineName;
        [MarshalAs(UnmanagedType.LPWStr)] public string pUserName;
        [MarshalAs(UnmanagedType.LPWStr)] public string pDocument;
        [MarshalAs(UnmanagedType.LPWStr)] public string pDatatype;
        [MarshalAs(UnmanagedType.LPWStr)] public string pStatus;
        public uint Status;
        public uint Priority;
        public uint Position;
        public uint TotalPages;
        public uint PagesPrinted;
        public SYSTEMTIME Submitted;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SYSTEMTIME
    {
        public ushort wYear;
        public ushort wMonth;
        public ushort wDayOfWeek;
        public ushort wDay;
        public ushort wHour;
        public ushort wMinute;
        public ushort wSecond;
        public ushort wMilliseconds;
    }
}

<强>结果:

工作1:

enter image description here

工作2:

enter image description here

修改

似乎你必须得到比我更强大的检查,因为EnumJobs似乎在队列中没有作业时返回true。在我的例子中,断言将失败,但这并不意味着代码是错误的;只需确保队列中有一些作业用于测试函数。