EnumJobs возвращают размер JOB_INFO_1, отличный от Marshal.SizeOF

1

Я вызываю функцию EnableJobs Win32 (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. Подсчет байтов вручную для struct дает 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;
}
  • 2
    Размер структуры составляет 80 байтов. Вы получаете массив 4 x 80 = 240 байт. EnumJobs () упакован System.Printing в .NET.
  • 0
    Вы прибили это в одном ряду, я не должен был пропустить .Net Framework. Небольшая заметка, 4 * 80 равняется 320, а не 240: D
Теги:
print-spooler-api

1 ответ

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:

Изображение 174551

Работа 2:

Изображение 174551

РЕДАКТИРОВАТЬ

Кажется, вам нужно будет получить более надежную проверку, чем я, потому что EnumJobs кажется, возвращает true когда в очереди нет заданий. В случае моего примера утверждение не удастся, но это не означает, что код неверен; просто убедитесь, что у вас есть несколько заданий в очереди для тестирования функции.

  • 0
    спасибо за ваш ответ, он предупредил меня о проблеме в коде. Ваши структуры возвращают мои размеры того же размера, но ошибка памяти была вызвана моим использованием EnumJobsA, а не EnumJobsW.
  • 0
    Пожалуйста. : D

Ещё вопросы

Сообщество Overcoder
Наверх
Меню