C#监视打印机(接上篇)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xiaogaokun/article/details/82262594

关于上一篇,由于有了基本函数,也有了大神给的几乎直接可以用的源码,视乎问题就解决了。

但是问题远远没有那么简单,在测试x64时候出现了比较棘手的问题。

前文我们用到一些有价值的系统函数

在Win32 上我们用  微软的例子用到WaitForSingleObject(hChange, INFINITE)

我们在C# 。.net4.0平台用一个回调函数来完成

 _changeHandle = FindFirstPrinterChangeNotification(_printerHandle,
                                                                   (int) PRINTER_CHANGES.PRINTER_CHANGE_ADD_JOB, 0,
                                                                   _notifyOptions);
                //_mrEvent.Handle = _changeHandle;
                _mrEvent.SafeWaitHandle = new SafeWaitHandle(_changeHandle, false);

                //use the waitOne
                //like C++ smaple if (WaitForSingleObject(hChange, INFINITE) != 0x00000000L) {
                //new Thread(PirnterWaitOne).Start();

                //Register for the ThreadPool Use callBack
                _waitHandle = ThreadPool.RegisterWaitForSingleObject(_mrEvent, new WaitOrTimerCallback(PrinterNotifyWaitCallback), _mrEvent, -1, true);

FindFirstPrinterChangeNotification触发事件的时候,回调我们的函数:

当那么回调函数呢

        private void PrinterNotifyWaitCallback(Object state, bool timedOut)
        {
            try
            {
                if (_printerHandle != IntPtr.Zero)
                {
                    #region read notification details
                    _notifyOptions.Count = 1;
                    int pdwChange;
                    IntPtr pNotifyInfo;
                    var bResult = FindNextPrinterChangeNotification(_changeHandle, out pdwChange, _notifyOptions, out pNotifyInfo);
                    //If the Printer Change Notification Call did not give data, exit code
                    if ((bResult == false) || (((int)pNotifyInfo) == 0))
                    {

                    }
                    else
                    {
                        if ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_PRINTER) > 0 || (pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_PORT) > 0)
                        {

                        }

                        //If the Change Notification was not relgated to job, exit code
                        bool bJobRelatedChange = ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_ADD_JOB) == PRINTER_CHANGES.PRINTER_CHANGE_ADD_JOB) ||
                                                 ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_SET_JOB) == PRINTER_CHANGES.PRINTER_CHANGE_SET_JOB) ||
                                                 ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_DELETE_JOB) == PRINTER_CHANGES.PRINTER_CHANGE_DELETE_JOB) ||
                                                 ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_WRITE_JOB) == PRINTER_CHANGES.PRINTER_CHANGE_WRITE_JOB);

                        if (bJobRelatedChange)
                        {
                            #region populate Notification Information
                            //PRINTER_NOTIFY_INFOの数据的取得
                            var info = (PRINTER_NOTIFY_INFO)Marshal.PtrToStructure(pNotifyInfo, typeof(PRINTER_NOTIFY_INFO));
                            var pData = (int)pNotifyInfo + Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO));
                            var data = new PRINTER_NOTIFY_INFO_DATA[info.Count];
                            for (uint i = 0; i < info.Count; i++)
                            {
                                data[i] = (PRINTER_NOTIFY_INFO_DATA)Marshal.PtrToStructure((IntPtr)pData, typeof(PRINTER_NOTIFY_INFO_DATA));
                                pData += Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO_DATA));
                            }
                            #endregion

                            var intJobID = -1;
                            try
                            {
                                #region iterate through all elements in the data array
                                for (var i = 0; i < data.Count(); i++)
                                {

                                    if ((data[i].Field == (ushort)PRINTERJOBNOTIFICATIONTYPES.JOB_NOTIFY_FIELD_STATUS) &&
                                         (data[i].Type == (ushort)PRINTERNOTIFICATIONTYPES.JOB_NOTIFY_TYPE)
                                        )
                                    {
                                        //var jStatus = (JOBSTATUS)Enum.Parse(typeof(JOBSTATUS), data[i].NotifyData.Data.cbBuf.ToString(CultureInfo.InvariantCulture));
                                        intJobID = (int)data[i].Id;
                                    }
                                }
                                #endregion
                            }
                            catch (Exception e)
                            {
                                intJobID = -1;
                            }
}

回调函数中FindNextPrinterChangeNotification中取得PRINTER_NOTIFY_INFO

再在它内部结构体数组PRINTER_NOTIFY_INFO_DATA中获得当前打印的JobId,然后我们用JobId来做进一步的数据取得。

问题出现了,当在x64系统上,PRINTER_NOTIFY_INFO_DATA 中

 [StructLayout(LayoutKind.Sequential)]
    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;
    }

Type, Field,Rsevered,Id通通为零,数据错误。

第一个反应一定是x64编译器和x86编译中内存对齐有问题啊,不能共用一套结构体。

然后去查了好多C++,C# 内存对齐机制的变量匹配,发现PRINTER_NOTIFY_INFO_DATA在64中确实比32中要大

但是经过分析,和看别人论坛的讨论,觉得没问题,这几份内存对齐代码代码最初由这个团队

http://lifeandtimesofadeveloper.blogspot.com/2007/10/unmanaged-structures-padding-and-c-part.html 

http://lifeandtimesofadeveloper.blogspot.com/2007/10/unmanaged-structures-padding-and-c-part_18.html 

这两篇文章,是2007新西兰Ben做的一个博客,因该经过测试,并且完美解决的。经过x86 x64 系统的兼容性测试。

(这两篇文章纯英文,并且不翻墙上不去,不知道为啥。。。。等我转发)

然后代码查阅

var info = (PRINTER_NOTIFY_INFO)Marshal.PtrToStructure(pNotifyInfo, typeof(PRINTER_NOTIFY_INFO));
var pData = (int)pNotifyInfo + Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO));
var data = new PRINTER_NOTIFY_INFO_DATA[info.Count];
                           

这时候源代码为了找到PRINTER_NOTIFY_INFO_DATA[] 其实位置,提前用Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO))

提前把指针偏移到unit Count 之后

   [StructLayout(LayoutKind.Sequential)]
    public struct PRINTER_NOTIFY_INFO
    {
        public uint Version;
        public uint Flags;
        public uint Count;
    }

 后来发现这并不是微软推荐的,微软有更好的结构体内指针移动函数,Marshal.OffsetOf

It doesn't work correctly for 64-bit configuration because of Data Alignment.

找到一个解决问题的方案

https://stackoverflow.com/questions/12792508/how-to-correctly-define-print-notify-info-data

You saved my life, thanx a million. 

看到这句话我知道基本好用了,恢复结构体

   [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;
    }
var info = (PRINTER_NOTIFY_INFO)Marshal.PtrToStructure(pNotifyInfo, typeof(PRINTER_NOTIFY_INFO));
//var pData = (int)pNotifyInfo + Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO));
long pData = (long)pNotifyInfo + (long)Marshal.OffsetOf(typeof(PRINTER_NOTIFY_INFO), "aData");

这样经过测试,好用了,微软的x86,x64到底之间出现了什么内存对齐指令,还是很好奇

因为之前我在PRINTER_NOTIFY_INFO前添加一个uint 也是好用的,说明内存对齐,x64还是有自己内存对齐算法。

以后希望能继续学习和深入了解,如果有大神指点,也是很感谢。

 

展开阅读全文

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