【问题标题】:Monitoring a print job using Print Spooler API使用 Print Spooler API 监控打印作业
【发布时间】:2018-01-04 00:34:41
【问题描述】:

我正在尝试使用 C# 中的 Print Spooler API 获取打印作业的一些数据。为此,我使用了以下代码 sn-p。 (错误处理被移除)

public static int GetJobInfo(string printerUncName, int jobId)
{
    var printerInfo2 = new PrinterInfo2();
    var pHandle = new IntPtr();
    var defaults = new PrinterDefaults();
    try
    {
        //Open a handle to the printer
        bool ok = OpenPrinter(printerUncName, out pHandle, IntPtr.Zero);
        //Here we determine the size of the data we to be returned
        //Passing in 0 for the size will force the function to return the size of the data requested
        int actualDataSize = 0;
        GetJobs(pHandle, jobId, 2, IntPtr.Zero, 0, ref actualDataSize);
        int err = Marshal.GetLastWin32Error();
        if (err == 122)// ERROR_INSUFFICIENT_BUFFER 
        {
            if (actualDataSize > 0)
            {
                //Allocate memory to the size of the data requested
                IntPtr printerData = Marshal.AllocHGlobal(actualDataSize);
                //Retrieve the actual information this time
                GetJobs(pHandle, jobId, 2, printerData, actualDataSize, ref actualDataSize);
                //Marshal to our structure
                printerInfo2 = (PrinterInfo2)Marshal.PtrToStructure(printerData, typeof(PrinterInfo2));
                //We've made the conversion, now free up that memory
                Marshal.FreeHGlobal(printerData);
            }
        }
}
finally
{
    //Always close the handle to the printer
    ClosePrinter(pHandle);
}
}

(取自https://stackoverflow.com/a/3283918/3079364

为了解析从 GetJobs 返回的指针(printerData),我使用以下类。

public struct PrinterInfo2
{
    public uint JobID;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string ServerName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string PrinterName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string ShareName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string PortName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string DriverName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string Comment;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string Location;
    public IntPtr DevMode;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string SepFile;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string PrintProcessor;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string Datatype;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string Parameters;
    public IntPtr SecurityDescriptor;
    public uint Attributes;
    public uint Priority;
    public uint DefaultPriority;
    public uint StartTime;
    public uint UntilTime;
    public uint Status;
    public uint Jobs;
    public uint AveragePpm;
}

(PrinterInfo2)Marshal.PtrToStructure(printerData, typeof(PrinterInfo2)); 行返回以下对象。 我不熟悉 Windows apis 和 dll。可能我在创建解析器类时做错了。如何从 PtrToStructure 方法返回有意义的信息?

编辑:GetJobs 定义

[DllImport("winspool.drv", SetLastError = true, EntryPoint = "GetJobA", CharSet = CharSet.Auto)]
public static extern bool GetJobs(
                IntPtr printerHandle,
                int jobId,
                int Level,
                IntPtr printerData,
                int bufferSize,
                ref int printerDataSize);

编辑 2:

我应用了 NetMage 的解决方案,但它也没有返回正确数量的 PagesPrinted 和 TotalPages。 (它返回 PagesPrinted = 2,TotalPages = 0 应该是 PagesPrinted = 1,TotalPages = 2) 然后我意识到,当我运行以下代码 sn-p 时,WMI 给出了相同的数字。

string searchQuery = "SELECT * FROM Win32_PrintJob";
ManagementObjectSearcher searchPrintJobs = new ManagementObjectSearcher(searchQuery);
ManagementObjectCollection prntJobCollection = searchPrintJobs.Get();
foreach (ManagementObject prntJob in prntJobCollection)
{
char[] splitArr = new char[1];
splitArr[0] = Convert.ToChar(",");
string prnterName = jobName.Split(splitArr)[0];
int prntJobID = Convert.ToInt32(jobName.Split(splitArr)[1]);
string documentName = prntJob.Properties["Document"].Value.ToString();
UInt32 jobSatus = (UInt32)prntJob.Properties["StatusMask"].Value;
UInt32 pagesPrinted = (UInt32)prntJob.Properties["PagesPrinted"].Value;
UInt32 totalPages = (UInt32)prntJob.Properties["TotalPages"].Value;
}

【问题讨论】:

  • 我假设这些字符串都是宽字符串,这将要求结构上的UnmanagedType.LPWStr
  • 感谢您的关注。我将 LPTStr 切换为 LPWStr 但它是一样的
  • GetJobs 是如何定义的?
  • 我想我会称之为GetJob,就像微软在这种情况下所做的那样......它只返回有关单个工作的信息。我还建议您仅在从 OpenPrinter 获得有效值时才调用 ClosePrinter

标签: c# printing print-spooler-api


【解决方案1】:

您的问题是您复制了PRINTER_INFO_2 结构来创建JOB_INFO_2 结构,但它们根本不一样。 JOB_INFO_2 需要以下结构:

[StructLayout(LayoutKind.Sequential)]
public struct SystemTime {
    [MarshalAs(UnmanagedType.U2)] public short Year;
    [MarshalAs(UnmanagedType.U2)] public short Month;
    [MarshalAs(UnmanagedType.U2)] public short DayOfWeek;
    [MarshalAs(UnmanagedType.U2)] public short Day;
    [MarshalAs(UnmanagedType.U2)] public short Hour;
    [MarshalAs(UnmanagedType.U2)] public short Minute;
    [MarshalAs(UnmanagedType.U2)] public short Second;
    [MarshalAs(UnmanagedType.U2)] public short Milliseconds;

    public SystemTime(DateTime dt) {
        dt = dt.ToUniversalTime();  // SetSystemTime expects the SYSTEMTIME in UTC
        Year = (short)dt.Year;
        Month = (short)dt.Month;
        DayOfWeek = (short)dt.DayOfWeek;
        Day = (short)dt.Day;
        Hour = (short)dt.Hour;
        Minute = (short)dt.Minute;
        Second = (short)dt.Second;
        Milliseconds = (short)dt.Millisecond;
    }
}

public struct JobInfo2 {
    public uint JobID;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string PrinterName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string ServerName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string UserName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string DocumentName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string NotifyName;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string DataType;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string PrintProcessor;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string Parameters;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string DriverName;
    public IntPtr DevMode;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string strStatus;
    public IntPtr SecurityDescriptor;
    public uint Status;
    public uint Priority;
    public uint Position;
    public uint StartTime;
    public uint UntilTime;
    public uint TotalPages;
    public uint Size;
    public SystemTime Submitted;
    public uint Time;
    public uint PagesPrinted;
}

【讨论】:

  • 感谢您的回答。在这种情况下,我至少从提交时间字段中获得了有意义的信息。但是对我来说,重要的是 totalPages 和 PagesPrinted 字段。显然它们是不正确的。当我使用 WMI 检查打印假脱机时,我也看到了相同的错误数量。我将编辑关于这些新进展的问题imgur.com/a/a0lRF
  • 很遗憾,该信息由打印机驱动程序提供,并非所有驱动程序都能正确提供。你用的是什么驱动?当您从打印机的 P{打印管理器打开后台打印程序打印机队列时,您会看到哪些页面信息?
  • 我使用的是 HP OfficejetPro 8100 打印机,驱动程序名称与打印机名称相同。我将使用另一个驱动程序(HP Universal printing pcl 6)对此进行测试。如果这解决了问题,我将编辑问题。谢谢
  • 有趣 - 除了 TotalPages 之外,我的所有卡住的工作都没有。不幸的是,您的问题似乎并不少见:PagesPrinted field of job_info_2 not reliable for hp printers on windows 7 注意:您显然需要查看 DevMode 以获取副本数。
  • 感谢您以这种方式帮助我。正如您所说,当 Windows 打印机队列正确显示时,我从 win32_printjob api 获得了不正确的信息,这很奇怪。我可能需要提出另一个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-21
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
相关资源
最近更新 更多