我正在从托管代码(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以接收作业信息1,但我遇到的问题是,上面的函数返回nbytesneded作为每个结构的240,而Marshal.SizeOf(typeof(JOB_INFO_1))是64个字节,在我运行Marshal.PtrToStructure时会导致内存异常。手动计算结构的字节数得到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.

此外,它还写道:
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:
作业2:
编辑
似乎您需要比我做的更健壮的检查,因为当队列中没有作业时,EnumJobs似乎会返回true。在我的示例中,断言将失败,但这并不意味着代码是错误的;只需确保队列中有一些用于测试函数的作业。

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

10-13 05:20