C# 关于打印机的监视

由于工作原因,需要监视打印机状态(windows 上spooler),然后做取得一些关于打印机操作。

在VC++平台,问题很好解决,也有很好的平台解决方案,但是苦于要做成Windows Service和大量平台多线程处理(线程池),还有Tcp通信,xml配置等等,在C++上实现,我的编码效率低,用C#利用微软成熟的.net 4.0平台,时装比较快,我就采用了C# 来实现,但是问题来了,大量的spooler API 都是VC时代winAPI的非托管代码,怎用托管代码调用成了我一个棘手问题,因为业务是和打印机相关,C#关于打印机交互的接口我又不是很了解(不知道能不能实现),我就选择了折中方案,也是个危险的方案,就是用Marshal来解决问题。一旦解决不好,C#再好没鸟用,还得用C++,下面就是我查找和学习过程。

首先找到我基本要的调用的C++关于打印机winAPI都有哪些

在这里找:C++

#include <winspool.h>
#pragma comment(lib, "winspool.lib")

主要有一下进入winspool.h,有以下东东

BOOL
WINAPI
OpenPrinterW(
_In_opt_    LPWSTR             pPrinterName,
_Out_       LPHANDLE            phPrinter,
_In_opt_    LPPRINTER_DEFAULTSW pDefault
);

HANDLE
WINAPI
FindFirstPrinterChangeNotification(
_In_     HANDLE hPrinter,
         DWORD  fdwFilter,
         DWORD  fdwOptions,
_In_opt_ PVOID  pPrinterNotifyOptions
    );

BOOL
WINAPI
FindNextPrinterChangeNotification(
_In_        HANDLE hChange,
_Out_opt_   PDWORD pdwChange,
_In_opt_    LPVOID pvReserved,
_Out_opt_   LPVOID *ppPrinterNotifyInfo
    );

BOOL
WINAPI
FindClosePrinterChangeNotification(
_In_   HANDLE hChange
    );

BOOL
WINAPI
ClosePrinter(
_In_ HANDLE hPrinter
);

这都是啥呢,参见微软:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd162723(v=vs.85).aspx

也就是说,打印机一旦有作业,我们知道windows spooler 怎么 来通知我们,哦,对了我们还需要这个

WINBASEAPI
DWORD
WINAPI
WaitForSingleObject(
    _In_ HANDLE hHandle,
    _In_ DWORD dwMilliseconds
    );

//调用
 if (WaitForSingleObject(hChange, INFINITE) != 0x00000000L) {
T.B.D

哎呀,好好用啊,C++ 搞定了啊,一打印通知我啊,可惜我们还要取些信息,还要用好多结构体。。。


PRINTER_NOTIFY_OPTIONS_TYPE

PPRINTER_NOTIFY_INFO

//重要
PRINTER_NOTIFY_OPTIONS
//重要
PRINTER_NOTIFY_INFO_DATA

几乎所有信息都在PRINTER_NOTIFY_INFO中,我们得读啊。。。。

来吧,微软给你机会读:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd162853(v=vs.85).aspx

我的天,我们还是安心的先整C# 调用的上面函数把,结构体下篇文章在说。

然后废了九牛二虎之力:

重点来了

        #region DLL Import Functions

        /// <summary>
        /// 指定的打印机句柄取得
        /// </summary>
        /// <param name="szPrinter">打印机名字</param>
        /// <param name="hPrinter">获得句柄</param>
        /// <param name="pDefault">A pointer to a PRINTER_DEFAULTS structure. This value can be NULL.</param>
        /// <returns></returns>
        [DllImport("winspool.drv", EntryPoint = "OpenPrinterW", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPWStr)] string szPrinter, out IntPtr hPrinter, IntPtr pDefault);

        //变更通知结束,关闭句柄
        [DllImport("winspool.drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool ClosePrinter(IntPtr hPrinter);

        //变更通知句柄获得
        [DllImport("winspool.drv", EntryPoint = "FindFirstPrinterChangeNotification", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr FindFirstPrinterChangeNotification
                            ([InAttribute()] IntPtr hPrinter,
                            [InAttribute()] Int32 fwFlags,
                            [InAttribute()] Int32 fwOptions,
                            [InAttribute(), MarshalAs(UnmanagedType.LPStruct)] PRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions);

        //变更句柄发生动作时候,取得详细信息
        [DllImport("winspool.drv", EntryPoint = "FindNextPrinterChangeNotification", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
        public static extern bool FindNextPrinterChangeNotification
                            ([InAttribute()] IntPtr hChangeObject,
                             [OutAttribute()] out Int32 pdwChange,
                             [InAttribute(), MarshalAs(UnmanagedType.LPStruct)] PRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
                            [OutAttribute()] out IntPtr lppPrinterNotifyInfo);

        //变更通知句柄关闭
        [DllImport("winspool.drv", EntryPoint = "FindClosePrinterChangeNotification", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool FindClosePrinterChangeNotification([InAttribute] IntPtr hChange);

        //The EnumJobs function retrieves information about a specified set of print jobs for a specified printer.
        [DllImport("winspool.drv", EntryPoint = "EnumJobsW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EnumJobs([InAttribute] IntPtr hPrinter, int firstJob, int noJobs, int level, IntPtr pJob, int cdBuf, out int pcbNeeded, out int pcReturned);

        //The GetJob function retrieves information about a specified print job.
        [DllImport("winspool.drv", EntryPoint = "GetJobW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool GetJob([InAttribute] IntPtr hPrinter, int jobId, int level, IntPtr pJob, int cdBuf, out int pcbNeeded);

        #endregion

然后,剩下一些问题,就是内存对齐了,你的参数和返回值数据统统在各种复杂结构体中,你怎么取C#和C++数据了

[StructLayout(LayoutKind.Sequential)]
    public struct PRINTER_NOTIFY_INFO
    {
        public uint Version;
        public uint Flags;
        public uint Count;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public PRINTER_NOTIFY_INFO_DATA[] aData;
    }
    public struct PRINTER_NOTIFY_INFO_DATA
    {
        public ushort Type;
        public ushort Field;
        public uint Reserved;
        public uint Id;
        public PRINTER_NOTIFY_INFO_DATA_UNION NotifyData;
    }
   [StructLayout(LayoutKind.Explicit)]
    public struct PRINTER_NOTIFY_INFO_DATA_UNION
    {
        [FieldOffset(0)]
        private uint adwData0;
        [FieldOffset(4)]
        private uint adwData1;
        [FieldOffset(0)]
        public PRINTER_NOTIFY_INFO_DATA_DATA Data;
        public uint[] adwData
        {
            get
            {
                return new uint[] { this.adwData0, this.adwData1 };
            }
        }
    }

这个最重要的结构体(PRINTER_NOTIFY_INFO)是不是我就完成了呢?当然没有,我们还是看大神怎么做的把。

转发codeproject. 印度大神(这里不能埋汰人家阿三,人家比你强的地方你得学啊。。。。)

https://www.codeproject.com/Articles/51085/Monitor-jobs-in-a-printer-queue-NET

和我贴的代码大同小异,业务流程我不表示,

但是以上代码有问题,测试过程中发现x64,出现问题,怎么解决和我们最关键的(PRINTER_NOTIFY_INFO)数据

在一片文章讨论

如果哪位大神看出那里写错,希望留言指正,必须感激涕零。一起讨论

 

展开阅读全文

没有更多推荐了,返回首页