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

标签 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)) 是当我运行 Marshal.PtrToStructure 时,64 字节导致内存异常。手动计算结构的字节数为 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;
}

最佳答案

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.

另外它是这样写的:

缓冲区必须足够大以接收结构数组以及结构成员指向的任何字符串或其他数据。

所以除了结构本身之外,这里还有用于额外数据的字节。

解决方案:

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

完整示例:

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。在我的示例中,断言会失败,但这并不意味着代码是错误的;只需确保队列中有一些作业用于测试功能即可。

关于c# - EnumJobs 返回与 Marshal.SizeOF 不同的 JOB_INFO_1 大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26857875/

相关文章:

c# - C#中两个问号在一起是什么意思?

windows - 重启docker后docker compose无法启动service network not found

c++ - 如何获取打印机类别(类型)?

c++ - GPU 去隔行扫描

c# - 在 C# 中使用 PrintSpoolerAPI 函数 SetForm()

java - 将控制数据添加到纯文本打印作业

c# - 在扩展类型中访问扩展方法?

c# - 使用可为空的枚举属性和具有空字符串值的附加选项呈现单选按钮

c# - 在 WPF/C# 中,如何检查中心按钮是否被单击或释放?

c++ - 如何获取使用标准库创建的线程的 winapi id?