文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>API编程系列之一:窥豹——API调用优美代码赏析

API编程系列之一:窥豹——API调用优美代码赏析

时间:2011-01-19  来源:南子

 

像所有的明星上台,都必须有显赫的背景介绍,我们这一系列的编写也必须要有深刻而迫切的原因的:一、市场上对设备编程的迫切需要,一般的设备系统通常都是用C或C++编写的;二、网络上中文资料的残缺(要么不实用、要么不完整、而且整个GOOGLE实际就两、三篇文章,打开一看一字不差、百度则更差,经常气的半死);三、便于以后复习,好不容易凑全了,万一忘了怎么办?四、如钱钟书所说:愛情這功課,就像麻疹,一生中,總得出上那麼一、兩回,據說從沒出過麻疹的人,免疫力特差;不管麻疹的程度怎么样,好歹也出过,面试经理也经常这样想,所以出麻疹的人被逼的越来越多。

     会不会有担心:连WINDOWS函数都看不懂,学也是白学!这个是不用担心的,因为API是很成熟的技术,.NET也是比较成熟的技术,.NET对API兼容调用依然是比较成熟的技术。所以只要掌握了C#对API编程的关键要点,一样能把API玩的顺风顺水。当然如果你能把C++WINDOWS编程原理摸透,那自然是更好。你就不必看微软的脸色去做那怪模怪样的八股文章了,你甚至可以自己写好要用的API,肯定不会比徐娘半老又勉强整容的COM+们差吧?再则知识是有层次和深度的,这是自然规律,就算你精通的WINDOWS编程,操作系统原理又在等着你,精通了操作系统原理编译器原理又在等着你、等你精通编译器原理芯片设计原理又在等着你,等你精通芯片设计原理激光切片芯片设计技术INTEL公司早已发挥到极致了,这时候你对你写的程序的性能和效率又产生了莫大怀疑?等一路追寻下去就算你有幸全都了然,你的黄花菜已早已不只是凉了。那结果只得像天龙八部中萧远山与慕容博两位大侠一样!武功通神,正好出家!所以我们应该学会享受过程的精彩,该出手时就出手!

 

     下面代码对API的调用具备极为精湛的掌握,也包含了绝大部分关键技术要点,尤其是对DocumentProperties函数阳关三叠的传神调用,极为形象深刻地说明了API编程的技术特点。让我们用仰慕和向往的心情来阅读它吧,再次强调这种代码是不多见的。

  #region  为制定打印机加载自定义纸型_____________________________________________

        /// <summary>     

        /// </summary>

        /// <param name="printerName">The printer name</param>

        /// <param name="paperName">Name of the printer form</param>

        /// <param name="widthMm">Width given in millimeters</param>

        /// <param name="heightMm">Height given in millimeters</param>

        public static void AddCustomPaperSize(string printerName, string paperName, float

            widthMm, float heightMm)

        {

            if (PlatformID.Win32NT == Environment.OSVersion.Platform)

            {   

              // 为什么要进行操作系统类型的判断是因为这个函数在

                //因为操作函数在WINDOWS早期版本时不支持的,else后会给出早期版本的操作方式

                //打印机访问方式,后面打开打印机操作句柄会用到

                const int PRINTER_ACCESS_USE = 0x00000008; 

                const int PRINTER_ACCESS_ADMINISTER = 0x00000004;//打印机访问方式

                const int FORM_PRINTER = 0x00000002;

                //定义打印机这一结构体,不然程序不知道要分配多少内存给执行函数

                PrinterSetting.PRINTER_DEFAULTSdefaults = new PrinterSetting.PRINTER_DEFAULTS();

                defaults.pDatatype = 0;

                defaults.pDevMode = 0;

                //打印机访问方式,前面定义过的

                defaults.DesiredAccess = PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE;

                IntPtr hPrinter = IntPtr.Zero;

                // 打开打印机,这就是为什么前一行语句为什么要声明一个托管句柄

                // 参数有打印机名前面传入的,刚声明的句柄用于承载函数输出的打印机变量体

                // 最后一个参数就是所定义的打印机结构类型,让函数知道输出个什么结构的东东

                //  API就真的是烦躁,直接搞个打印机类型的输出型变量不就OK了吗?还非得要搞个

                // 句柄变量来输出,后面还的告诉它是个什么类型的变量,有时间真要好好看看微软以 

                // 前怎么那么笨,要有C++高手就好鸟,这也就是我们C#优雅矜持之处了,时代在进步啊!

                if (PrinterSetting.OpenPrinter(printerName, out hPrinter, ref defaults))

                {

                    try

                    {                 

                        //直接就删了,要是本来没得出错咋办,该try就果断的try住了,果然高手哇!

                        //不像菜鸟,满篇都TRY的,也不像我们这样的菜鸟,该TRY得地方也不敢TRY

                        DeleteForm(hPrinter, paperName);                   

                        FormInfo1 formInfo = new FormInfo1();                   

                        //这个地方要注意了哦,这个Flags是大有文章的啊,很多老外都哭喊着说我定

                        //制了FORM可就是在打印机属性设置里头找不着它,可我没机会告诉他们,真

                        //的,CSDN里面也有个2007年的帖子在喊,可后面都没得正解。看来高手也不

                        //是我们想像的那样多,嘿嘿!因此,API编程贵在入微,入微则滋味不一样

                        formInfo.Flags =0;

                        formInfo.pName = paperName;

                        // all sizes in 1000ths of millimeters

                        formInfo.Size.width = (int)(widthMm * 1000.0);

                        formInfo.Size.height = (int)(heightMm * 1000.0);

                        formInfo.ImageableArea.left = 0;

                        formInfo.ImageableArea.right = formInfo.Size.width;

                        formInfo.ImageableArea.top = 0;

                        formInfo.ImageableArea.bottom = formInfo.Size.height;

                        //这里的第一个参数就是前面打开的打印机句柄,很多API刚接触的程序员,按

                        //习惯给个空值,以为"无用则空",连API常识都不了解,不说也罢

                        //添加定制的纸型给指定的打印机,按道理说做到这步也就够了。不过这都是猜

                        //鸟的思维,不过网上很多就做到这里的。

                        if (!AddForm(hPrinter, 1, ref formInfo))

                        {

                            //高手的素养就体现在这里,在这拒绝使用string而用StringBuilder 

                             //套用某句名言:懂string与StringBuilder的不像我们想象中的那么多,也

                            //也不像我们想象中的那么少

                            StringBuilder strBuilder = new StringBuilder();

                            strBuilder.AppendFormat("Failed to add the custom paper size {0} to the printer {1}, System error number: {2}",

                                paperName, printerName, GetLastError());

                            throw new ApplicationException(strBuilder.ToString());

                        }                     

                        const int DM_OUT_BUFFER = 2;

                        const int DM_IN_BUFFER = 8;

                        structDevMode devMode = new structDevMode();

                        //好!真正精彩的地方开始了,这里声明了一大堆变量,有必要这么多吗?

                        IntPtr hPrinterInfo, hDummy;

                        PRINTER_INFO_9 printerInfo;

                        printerInfo.pDevMode = IntPtr.Zero;

                        int iPrinterInfoSize, iDummyInt;                        

                        //半夜听着美酒加咖啡的确让人心醉,不过好困了!美好的时光总过的特别快

                        //阳光三叠的第一叠,取打印机属性对象pDevMode 的内存大小,注意啊,她

                        //跟后面两次调用的区别,唉,算了,还是直接说吧,玄机就是参数的赋值

                        //仅仅一个参数的值,整个函数的作用就翻天覆地的变化,API真不可思议啊!

                        int iDevModeSize = DocumentProperties(IntPtr.Zero, hPrinter, printerName, IntPtr.Zero, IntPtr.Zero, 0);

                        if (iDevModeSize < 0)

                            throw new ApplicationException("Cannot get the size of the DEVMODE structure.");                    

                        //这个Marshal类很风骚的哦,用词香艳了一点,估计看到这的人也不多,

                        //如果要真正掌握API调用,这个必须掌握不可她的内涵绝不只是一般的丰富

                        //不过依葫芦画瓢也就可以了,呵呵!这里是为非托管对象分派内存

                        //API就这样麻烦,虽然功能强大,但又很脆弱,什么都要为她照顾好

                         

                        IntPtr hDevMode = Marshal.AllocCoTaskMem(iDevModeSize + 100);                    

                        //注意了注意了,仔细才能看出区别,阳光二叠!DM_OUT_BUFFER

                        int iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName, hDevMode, IntPtr.Zero, DM_OUT_BUFFER);

                        if (iRet < 0)

                            throw new ApplicationException("Cannot get the DEVMODE structure.");                      

                         //这里是把非托管对象的数据封装到托管对象中来!API 编程必备绝技

                        devMode=(structDevMode)Marshal.PtrToStructure(hDevMode, devMode.GetType());                     

                        devMode.dmFields = 0x10000; // DM_FORMNAME 

                        devMode.dmFormName = paperName;

                                      //取出打印机本身属性更改过后,再塞进去,保证不会出问题

                        Marshal.StructureToPtr(devMode, hDevMode, true);                   

                        //阳关三叠!设置打印机指定属性

                        iRet = DocumentProperties(IntPtr.Zero, hPrinter, printerName,

                                 printerInfo.pDevMode, printerInfo.pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);

                        if (iRet < 0)

                            //如果出错则提示不能设置指定属性

                            throw new ApplicationException("Unable to set the orientation setting for this printer.");

                        // GET THE PRINTER INFO SIZE

                        GetPrinter(hPrinter, 9, IntPtr.Zero, 0, out iPrinterInfoSize);

                        if (iPrinterInfoSize == 0)

                            throw new ApplicationException("GetPrinter failed. Couldn't get the # bytes needed for shared PRINTER_INFO_9 structure");                 

                        hPrinterInfo = Marshal.AllocCoTaskMem(iPrinterInfoSize + 100);                   

                        bool bSuccess = GetPrinter(hPrinter, 9, hPrinterInfo, iPrinterInfoSize, out iDummyInt);

                        if (!bSuccess)

                            throw new ApplicationException("GetPrinter failed. Couldn't get the shared PRINTER_INFO_9 structure");                     

                        printerInfo = (PRINTER_INFO_9)Marshal.PtrToStructure(hPrinterInfo, printerInfo.GetType());

                        printerInfo.pDevMode = hDevMode;                 

                        Marshal.StructureToPtr(printerInfo, hPrinterInfo, true);                 

                        //在这里才是真正的设置打印机纸型信息,好麻烦啊!

                        //windows操作系统就是这样,一个打印机属性设置有默认选项,首选选项,

                        //更邪恶的是,它还有高级设置,搞个自定义纸型设置不容易啊!

                        //这就是API 高手编程的手法!精湛,优雅,正点!不像菜鸟那样残缺也不像

                         //老菜鸟那样进退失据!

                        bSuccess = SetPrinter(hPrinter, 9, hPrinterInfo, 0);

                        if (!bSuccess)

                            throw new Win32Exception(Marshal.GetLastWin32Error(), "SetPrinter() failed.  Couldn't set the printer settings");

                        //Tell all open programs that this change occurred.

                        SendMessageTimeout(

                           new IntPtr(HWND_BROADCAST),

                           WM_SETTINGCHANGE,

                           IntPtr.Zero,

                           IntPtr.Zero,

                           CustomPrintForm.SendMessageTimeoutFlags.SMTO_NORMAL,

                           1000,

                           out hDummy);

                    }

                    finally

                    {

                        ClosePrinter(hPrinter);

                    }

                }

                else

                {

                    StringBuilder strBuilder = new StringBuilder();

                    strBuilder.AppendFormat("Failed to open the {0} printer, System error number: {1}",

                        printerName, GetLastError());

                    throw new ApplicationException(strBuilder.ToString());

                }

            }

            else

            {

                //这里是WIN95,WIN98的操作,完整,优雅!

                structDevMode pDevMode = new structDevMode();

                IntPtr hDC = CreateDC(null, printerName, null, ref pDevMode);

                if (hDC != IntPtr.Zero)

                {

                    const long DM_PAPERSIZE = 0x00000002L;

                    const long DM_PAPERLENGTH = 0x00000004L;

                    const long DM_PAPERWIDTH = 0x00000008L;

                    pDevMode.dmFields = (int)(DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH);

                    pDevMode.dmPaperSize = 256;

                    pDevMode.dmPaperWidth = (short)(widthMm * 1000.0);

                    pDevMode.dmPaperLength = (short)(heightMm * 1000.0);

                    ResetDC(hDC, ref pDevMode);

                    DeleteDC(hDC);

                }

            }

        }     

   好!能坚持看到这里的朋友很不容易了!因为高手是不会看的,白菜也看不到这里,能看到这里都对API比较投缘了。纵上所述,可见API编程的确有许多特殊的地方:一、函数的机构比C#函数奇怪很多 二、数据类型与C#的差别很大,尤其是结构类型和指针类型,搞死人 三、数据在托管与非托管代码间的封装有特色,必须熟练 四:对所调用的API原型必须细致入微,因为一个参数值的差别就是地狱与天堂的差别。下一章,就专门讲述这些特点,以及他们产生的原因和处理的办法。                                       

相关阅读 更多 +
排行榜 更多 +
找茬脑洞的世界安卓版

找茬脑洞的世界安卓版

休闲益智 下载
滑板英雄跑酷2手游

滑板英雄跑酷2手游

休闲益智 下载
披萨对对看下载

披萨对对看下载

休闲益智 下载