-
2021-08-11 02:13:07
大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答。
佳能打印机连不上手机的原因是:
1、检查数据线和数据线接口,数据线和数据线接口是否损坏,数据接口是否连接到位。如果还是不行,可以尝试换一根数据线或者是换一个接口接上试试。
2、检查打印机电源,看看打印机的供电是否正常,也可以尝试着重新启动打印机和手机。
3、在电脑中检查打印设备是否工作正常,如果打印驱动损坏,可以尝试着重新安装打印机驱动。
4、查看打印机里的打印队列,试着将当前的打印任务暂停或取消,然后尝试重新打印。要去检查一下打印机的墨水盒的计数是否已满额,这种情况只能由厂商对计数进行重置来解决。
打印机(Printer)是计算机的输出设备之一,由约翰・沃特与戴夫・唐纳德于1976年合作发明。打印机主要用于将计算机处理结果打印在相关介质上,可分为激光式打印机、喷墨式打印机、击打式打印机等类型,知名打印机品牌有联想、惠普、爱普生、迈创等。打印机是由约翰・沃特、戴夫・唐纳德合作发明的。将计算机的运算结果或中间结果以人所能识别的数字、字母、符号和图形等,依照规定的格式印在纸上的设备。打印机正向轻、薄、短、小、低功耗、高速度和智能化方向发展。
更多相关内容 -
打印机无法打印原因?打印机设置共享的方法
2021-07-05 04:41:00小编给大家科普一下,打印机是计算机的输出设备之一,用于将计算机处理结果打印在相关介质上。衡量打印机好坏的指标有三项:打印分辨率,打印速度和噪声。将计算机的运算结果或中间结果以人所能识别的数字、字母、...我们平常也经常打印资料,那么打印机是什么呢?小编给大家科普一下,打印机是计算机的输出设备之一,用于将计算机处理结果打印在相关介质上。衡量打印机好坏的指标有三项:打印分辨率,打印速度和噪声。将计算机的运算结果或中间结果以人所能识别的数字、字母、符号和图形等,依照规定的格式印在纸上的设备。打印机正向轻、薄、短、小、低功耗、高速度和智能化方向发展。下面贤集网小编来为大家介绍一下打印机的相关知识点,包括:打印机无法打印原因?打印机设置共享的方法、打印机更换墨粉盒的方法、打印机扫描操作的方法、打印机使用技巧。
打印机无法打印原因
一、打印机没有处于联机状态导致打印机无法打印
在大多数打印机上,“OnLine”按钮旁边都有一个指示联机状态的小灯,正常情况该联机指示灯应处于常亮状态。如果该指示灯不亮或处于闪烁状态,说明联机不正常。请检查打印机电源是否接通、打印机电源开关是否打开、打印机电缆是否正确连接等。
二、重新开启打印机
如果联机指示灯显示联机正常,请先关掉打印机,然后再打开,重新打印文档试试。此操作能清除打印机内存中存放的打印文档数据并能解决许多问题,但有时这种方法会导致打印输出混乱,需重新启动系统后才可正常打印。
三、将打印机设置为默认打印机
步骤如下:
1、单击Windows“开始”菜单,指向“设置”,单击“打印机”,打开“打印机”窗口。
2、右键单击打印机图标,系统弹出快速菜单,单击其中的“设为默认值”。默认打印机有勾选,如果“打印机”窗口没有当前使用的打印机,请双击“添加打印机”图标,然后根据提示安装打印机。
四、打印机处于暂停打印状态导致无法打印
方法是:在“打印机”窗口,右键单击在用的打印机图标,然后单击以清除“暂停打印”选项前的对号“√”。
五、磁盘可用空间低于10MB导致打印机不能打印
如果可用硬盘空间小于10MB(一般指C盘),必须释放更多的空间才能完成打印任务。这时,请单击“磁盘清理”按钮,然后在“要删除的文件”列表框中,选中要删除的文件类型,单击“确定”按钮。
六、增加打印机的超时设置
检查或更改超时设置,步骤如下:
1、在“打印机”窗口,右键单击打印机图标,再单击“属性”。
2、单击“细致资料”选项卡,在“超时设置”下增加各项超时设置。“未选定”项是指定Windows等待打印机进入联机状态的时间,超过指定时间之后就将显示错误消息。
七、本地打印机端口设置不正确导致打印机无法打印
步骤如下:
1、在“打印机”窗口,右键单击打印机图标,再单击“属性”。
2、单击“细致资料”选项卡,在“打印到以下端口”框中,确保已将打印机设置到适当的端口。最常用的端口设置为“LPT1”,也有打印机使用USB端口。
八、程序生成的输出不正确导致无法打印
要确定程序生成的输出能否正确,能够采用通过其他程序打印文档的方法验证。我们新建记事本文件,键入几行文字,然后在“文件”菜单中,单击“打印”命令,如果能够打印测试文档,就是原来你使用进行打印的程序有问题,请重新安装程序。
九、打印机驱动程序损坏导致打印机无法打印
有时,打印机驱动程序可能被损坏,从而引发不能打印文档的错误。我们能够重新安装合适的驱动程序,然后再打印。
1、在“打印机”窗口,右键单击打印机图标,再单击“删除”,然后单击“是”按钮。如果系统提示“删除这台打印机的专用文件”,请单击“是”按钮。如果系统提示删除默认打印机,请单击“确定”按钮。
2、在“打印机”窗口,双击“添加打印机”图标,打开“添加打印机向导”,单击“下一步”按钮,然后执行屏幕指令。
十、BIOS中打印机端口设置为关闭导致无法打印
BIOS中打印机使用端口应设置为“Enable”,注意早期的有些打印机不支持ECP类型的打印端口信号,这时应将打印端口设置为“Normal”、“SPP”、“ECP+EPP”方式试试。
十一、系统感染病毒导致打印机无法打印
检查是否存在病毒,有的电脑中了病毒,也会导致打印机无法正常工作,比如打印机驱动被病毒破坏,所以最好的下载杀毒软件进行全面杀毒试试,杀毒软件一定要更新到最新版本。
十二、端口、打印机电缆存在问题导致打印机无法打印
1、打印机电缆连接能否牢靠如果计算机间接与打印机相连,要确保连接计算机和打印机的电缆两端都插对插牢。如果使用打印切换设备,请先绕过切换设备,将打印机间接与计算机相连,然后尝试进行打印。
2、测试端口连接将打印机连接到另一个可用端口,重试打印文档,如果能够打印则表明原端口损坏。
3、测试打印机电缆换上另一根打印机电缆,然后重试打印文档,如果能够打印则表明原电缆损坏。
打印机设置共享的方法
一、设置共享打印机
1、确认与共享打印机相连的计算机处于开机状态,而且已经安装过打印机驱动程序,并且可实现正常打印。
2、在电脑控制面板中寻找到打印机和传真机,也可以在开始菜单打印机和传真机找到 。
3、鼠标右键点击所要共享的打印机,然后鼠标左键点击“共享”。
4、点击“共享这台打印机”,并起一个共享名“xxxx”,然后点击“确定”按键,于是界面自动退出。这时我们看到打印机图标之前多出一个“小手”标志,此时,表明共享打开,这台打印机已经可以在局域网内被其他用户所使用。
二、如何找到共享的打印机
1、在需要连接共享的电脑上点击“开始-运行” 。
2、在运行对话框内输入,连接打印设备的那台电脑在局域网内的IP地址,如“\192.168.0.237”,找到共享在局域网内的打印设备. 。
3、在弹出的窗口中选择你已经共享的打印机名称“xxxx”双击(或者右键点连接) 。
4、弹出对话框提示可能有病毒什么的,直接点确定。
三、测试打印
1、在电脑控制面板中寻找到打印机和传真机,也可以在开始菜单打印机和传真机找到。打开后里面会有已经连接好了的共享打印机图标“xxxx”(还带有IP地址:192.168.0.237)
2、右键点击属性,点击打印测试页按钮测试文档就顺利的被打印出来了。
3、如何获取电脑的IP地址 如何获取和打印设备相连的那台电脑IP地址 :
①在运行中输入“cmd回车”,敲击回车按键,然后在弹出界面内输入“ipconfig”,然后敲击回车按键,第一行显示:“IP Addres......: xxx.xxx.xxx.xxx”就是这台电脑的IP地址。
②右键点击网上邻居属性,在弹出的窗口中找到本地连接,在本地连接图标上点击右键点状态。再选择支持也可以看见IP地址。
③在系统托盘里面点击本地连接再选择支持选项卡也可以看到IP地址。
4、在共享打印机时没有出现“共享这台打印机”的选项:
①在设置共享打印机第四步的时候,点击的共享并没有出现“共享这台打印机”的选项,而是出现了这样一段话“作为一个安全措施,windows不允许对这台计算机进行远程访问。但是您可以运行网络安装向导来启用远程访问并安全共享打印机” 。
②点击“网络安装向导”出现网络安装向导,点击下一步。选择“此计算机通过居民区的网关或网络上的其他计算机连接到Internet”下一步 。
③填写计算机名称和描述,下一步。填写工作组一般默认的就可以,下一步。选择“启用文件和打印机共享下一步。
④选择“完成该向导。我不需要在其他计算机上运行该向导”然后下一步到完成。提示重启点否。你就可以看见共享选项了!
打印机更换墨粉盒的方法
一、打印机前盖开门机型安装墨粉盒
1、打开前盖,拉出旧墨粉盒;
2、从包装袋中取出新墨粉盒;
3、彻底摇匀墨粉盒 5 至 6 次,使墨粉盒中的墨粉均匀分布;
4、撕开包装胶带,拆掉用于保护墨粉盒的包装纸;
5、握住墨粉盒把手,慢慢将墨粉盒插入打印机的开口处,最后关闭前盖,确保将盖板关紧。
二、打印机顶盖开门机型安装墨粉盒
1、打开顶盖,拉出墨粉盒;
2、打开新墨粉盒的包装,撕下墨粉盒上的保护盖;
3、慢慢摇动墨粉盒 5 至 6 次,使盒内墨粉均匀分布;
4、握住墨粉盒上的手柄,将其慢慢插入机器的开口中;
5、关上顶盖。
打印机扫描操作的方法
一、下载扫描软件和扫描驱动。
机器型号不同,所使用的扫描程序有可能不一样。扫描软件随机光盘里面就自带,如果您的光盘丢失了,需要扫描软件下载,您可以登陆三星网站www.samsung.com.cn下载。下载路径:进入网站后,在右上角搜索框中输入产品型号,然后搜索,选择【服务支持】里面的软件或驱动下载即可。三星自带扫描软件有:SmarThru Office、Samsung Easy Document Creatorr,请根据您的机型搜寻下载。
二、下载并安装扫描软件后,根据下载的软件进行操作:
使用SmarThru Office软件扫描,您也可以参考以下链接操作:
http://skp.samsungcsportal.com/integrated/popup/FaqDetailPopup3.jsp?seq=863493&cdsite=cn&status=A,使用Samsung Easy Document Creatorr软件扫描:
1、安装程序完毕后,将单页文档正面朝下放在文档玻璃板上,或者将文档正面朝上装入进稿器(多张扫描可以将纸张放入进稿器中)。
2、打开【Easy Document Creator】软件,点击【扫描】。
注意:
如果【扫描】显示灰色,不能选择,这时请点击【搜索】,再选择【开始搜索】,在搜索结果里面选择您的打印机,然后按【确认】就可以了。
3、选择【文档扫描】。(扫描里有多种扫描方式,您可以根据扫描文件的要求选择,下面以“文档扫描”为例介绍。)
4、机器会自动开始预扫描文件,预扫描完毕后,再点击【扫描】。(可以根据对扫描文件的要求进行扫描设置。)
5、扫描完毕后,根据您的需求设置红框中的项目,最后点击【保存】就可以了。
打印机使用技巧
1、我们新购置的打印机在使用前,可以用软绵或者棉纱蘸少量润滑油将打印头滑动杆擦上一层(注:动作要轻、小心;不要污染了机件)来回几次即可;最好能5、6个月加一次润滑油!
2、打印机应时常检查下色带是否移位,假如色带被卡住走不动,色带很容易损坏。
3、色带使用一段时间后,应该检查色带,发现表面开始起毛,或色带有破损,这时应立即更换色带,否则如果未及时发现会使打印头的针打断。
4、我们在更换色带时,因为打印色带的好坏会直接影响到打印的效果和打印头的寿命。
5、我们在安置打印机时要有良好的工作环境:打印机应避免阳光直接照射,不要把打印机放在高温、潮湿、灰尘较多的地方,以免影响性能。不要把打印机放置在有静电的环境中,同时打印机的插头最好不要与电功率大的电器(如空调机、吸取尘器等)共同使用一个电源插座。
6、我们在使用打印机打字时,不要让打印针直接击打在胶辊上,这样容易造成打印机针头的损坏,也会极大地磨蚀胶辊,影响打印效果,降低机器的使用寿命。同时也要保持打字胶辊的清洁,若表面同现凸痕或磨损比较厉害,就不要再继续使用,应及时更换打字胶辊,否则会造成打印头断针。
上述是贤集网小编为大家介绍的打印机无法打印原因?打印机设置共享的方法、打印机更换墨粉盒的方法、打印机扫描操作的方法、打印机使用技巧。在总体的打印机发展上看,无论是激光还是喷墨,现有技术的难以逾越,更多的表现在了硬件指标连年提高之上,因为即要做出升级,就要有这样的无奈。到是的据表明,它们在智能化方面连年得到提高,脱机打印、无线打印、包括云打印,以及具备的各种可存储、可管理性,都使我们可以更加方便的去应用,而在此的进一步提升,也将是今后打印机的发展方向。
注:文章内的所有配图皆为网络转载图片,侵权即删!
-
教你怎么使用打印机(api)
2021-02-05 10:44:24使用打印机为了处理文字和图形而使用视讯显示器时,设备无关的概念看来非常完美,但对于打印机,设备无关的概念又怎样呢?总的说来,效果也很好。在Windows程序中,用于视讯显示器的GDI函数一样可以在印表纸上打印...使用打印机
为了处理文字和图形而使用视讯显示器时,设备无关的概念看来非常完美,但对于打印机,设备无关的概念又怎样呢?
总的说来,效果也很好。在Windows程序中,用于视讯显示器的GDI函数一样可以在印表纸上打印文字和图形,在以前讨论的与设备无关的许多问题(多数都与平面显示的尺寸、分辨率以及颜色数有关)都可以用相同的方法解决。当然,一台打印机不像使用阴极射线管的显示器那么简单,它们使用的是印表纸。它们之间有一些比较大的差异。例如,我们从来不必考虑视讯显示器没有与显示卡连结好,或者显示器出现「屏幕空间不够」的错误,但打印机off line和缺纸却是经常会遇到的问题。
我们也不必担心显示卡不能执行某些图形操作,更不用担心显示卡能否处理图形,因为,如果它不能处理图形,就根本不能使用Windows。但有些打印机不能打印图形(尽管它们能在Windows环境中使用)。绘图机尽管可以打印向量图形,却存在位图块的传输问题。
以下是其它一些需要考虑的问题:
打印机比视讯显示器慢。尽管我们没有机会将程序性能调整到最佳状态,却不必担心视讯显示器更新所需的时间。然而,没有人想在做其它工作前一直等待打印机完成打印任务。
程序可以用新的输出覆盖原有的显示输出,以重新使用视讯显示器表面。这对打印机是不可能的,打印机只能用完一整页纸,然后在新一页的纸上打印新的内容。
在视讯显示器上,不同的应用程序都被窗口化。而对于打印机,不同应用程序的输出必须分成不同的文件或打印作业。
为了在GDI的其余部分中加入打印机支持功能,Windows提供几个只用于打印机的函数。这些限用在打印机上的函数(StartDoc、EndDoc、StartPage和EndPage)负责将打印机的输出组织打印到纸页上。而一个程序呼叫普通的GDI函数在一张纸上显示文字和图形,和在屏幕上显示的方式一样。
在第十五、十七和十八章有打印位图、格式化的文字以及metafile的其它信息。
打印入门
当您在Windows下使用打印机时,实际上启动了一个包含GDI32动态链接库模块、打印驱动程序动态连结模块(带.DRV扩展名)、Windows后台打印程序,以及有用到的其它相关模块。在写打印机打印程序之前,让我们先看一看这个程序是如何进行的。
打印和背景处理
当应用程序要使用打印机时,它首先使用CreateDC或PrintDlg来取得指向打印机设备内容的句柄,于是使得打印机设备驱动程序动态链接库模块被加载到内存(如果还没有加载内存的话)并自己进行初始化。然后,程序呼叫StartDoc函数,通知说一个新文件开始了。StartDoc函数是由GDI模块来处理的,GDI模块呼叫打印机设备驱动程序中的Control函数告诉设备驱动程序准备进行打印。
打印一个文件的程序以StartDoc呼叫开始,以EndDoc呼叫结束。这两个呼叫对于在文件页面上书写文字或者绘制图形的GDI命令来说,其作用就像分隔页面的书挡一样。每页本身是这样来划清界限的:呼叫StartPage来开始一页,呼叫EndPage来结束该页。
例如,如果应用程序想在一页纸上画出一个椭圆,它首先呼叫StartDoc开始打印任务,然后再呼叫StartPage通知这是新的一页,接着呼叫Ellipse,正如同在屏幕上画一个椭圆一样。GDI模块通常将程序对打印机设备内容做出的GDI呼叫储存在磁盘上的metafile中,该文件名以字符串~EMF(代表「增强型metafile」)开始,且以.TMP为扩展名。然而,我在这里应该指出,打印机驱动程序可能会跳过这一步骤。
当绘制第一页的GDI呼叫结束时,应用程序呼叫EndPage。现在,真正的工作开始了。打印机驱动程序必须把存放在metafile中的各种绘图命令翻译成打印机输出数据。绘制一页图形所需的打印机输出数据量可能非常大,特别是当打印机没有高级页面制作语言时,更是如此。例如,一台每英寸600点且使用8.5×11英寸印表纸的激光打印机,如果要定义一个图形页,可能需要4百万以上字节的数据。
为此,打印机驱动程序经常使用一种称作「打印分带」的技术将一页分成若干称为「输出带」的矩形。GDI模块从打印机驱动程序取得每个输出带的大小,然后设定一个与目前要处理的输出带相等的剪裁区,并为metafile中的每个绘图函数呼叫打印机设备驱动程序的Output函数,这个程序叫做「将metafile输出到设备驱动程序」。对设备驱动程序所定义的页面上的每个输出带,GDI模块必须将整个metafile「输出到」设备驱动程序。这个程序完成以后,该metafile就可以删除了。
对每个输出带,设备驱动程序将这些绘图函数转换为在打印机上打印这些图形所需要的输出数据。这种输出数据的格式是依照打印机的特性而异的。对点阵打印机,它将是包括图形序列在内的一系列控制命令序列的集合(打印机驱动程序也能呼叫在GDI模块中的各种「helper」辅助例程,用来协助这种输出的构造)。对于带有高阶页面制作语言(如PostScript)的激光打印机,打印机将用这种语言进行输出。
打印驱动程序将打印输出的每个输出带传送到GDI模块。随后,GDI模块将该打印输出存入另一个临时文件中,该临时文件名以字符串~SPL开始,带有.TMP扩展名。当处理好整页之后,GDI模块对后台打印程序进行一个程序间呼叫,通知它一个新的打印页已经准备好了。然后,应用程序就转向处理下一页。当应用程序处理完所有要打印的输出页后,它就呼叫EndDoc发出一个信号,表示打印作业已经完成。图13-1显示了应用程序、GDI模块和打印驱动程序的交互作用程序。
图13-1
应用程序、GDI模块、打印驱动程序和打印队列程序的交互作用过程
Windows后台打印程序实际上是几个组件的一种组合(见表13-1)。
表13-1
打印队列程序组件
说明
打印请求队列程序
将数据流传递给打印功能提供者
本地打印功能提供者
为本地打印机建立背景文件
网络打印功能提供者
为网络打印机建立背景文件
打印处理程序
将打印队列中与设备无关的数据转换为针对目的打印机的格式
打印端口监视程序
控件连结打印机的端口
打印语言监视程序
控件可以双向通讯的打印机,设定设备设定并检测打印机状态
打印队列程序可以减轻应用程序的打印负担。Windows在启动时就加载打印队列程序,因此,当应用程序开始打印时,它已经是活动的了。当程序行印一个文件时,GDI模块会建立包含打印输出数据的文件。后台打印程序的任务是将这些文件发往打印机。GDI模块发出一个消息来通知它一个新的打印作业开始,然后它开始读文件并将文件直接传送到打印机。为了传送这些文件,打印队列程序依照打印机所连结的并列端口或串行埠使用各种不同的通信函数。在打印队列程序向打印机发送文件的操作完成后,它就将包含输出数据的临时文件删除。这个交互作用过程如图13-2所示。
图13-2 后台打印程序的操作程序
这个程序的大部分对应用程序来说是透明的。从应用程序的角度来看,「打印」只发生在GDI模块将所有打印输出数据储存到磁盘文件中的时候,在这之后(如果打印是由第二个线程来操作的,甚至可以在这之前)应用程序可以自由地进行其它操作。真正的文件打印操作成了后台打印程序的任务,而不是应用程序的任务。通过打印机文件夹,使用者可以暂停打印作业、改变作业的优先级或取消打印作业。这种管理方式使应用程序能更快地将打印数据以实时方式打印,况且这样必须等到打印完一页后才能处理下一页。
我们已经描述了一般的打印原理,但还有一些例外情况。其中之一是Windows程序要使用打印机时,并非一定需要后台打印程序。使用者可以在打印机属性表格的详细数据属性页中关闭打印机的背景操作。
为什么使用者希望不使用背景操作呢?因为使用者可能使用了比Windows打印队列程序更快的硬件或软件后台打印程序,也可能是打印机在一个自身带有打印队列器的网络上使用。一般的规则是,使用一个打印队列程序比使用两个打印队列程序更快。去掉Windows后台打印程序可以加快打印速度,因为打印输出数据不必储存在硬盘上,而可以直接输出到打印机,并被外部的硬件打印队列器或软件的后台打印程序所接收。
如果没有启用Windows打印队列程序,GDI模块就不把来自设备驱动程序的打印输出数据存入文件中,而是将这些输出数据直接输出到打印输出埠。与打印队列程序进行的打印不同,GDI进行的打印一定会让应用程序暂停执行一段时间(特别是进行打印中的程序)直到打印完成。
还有另一个例外。通常,GDI模块将定义一页所需的所有函数存入一个增强型metafile中,然后替驱动程序定义的每个打印输出带输出一遍该metafile到打印驱动程序中。然而,如果打印驱动程序不需要打印分带的话,就不会建立这个metafile;GDI只需简单地将绘图函数直接送往驱动程序。进一步的变化是,应用程序也可能得承担起对打印输出数据进行打印分带的责任,这就使得应用程序中的打印程序代码更加复杂了,但却免去了GDI模块建立metafile的麻烦。这样,GDI只需简单地为每个输出带将函数传到打印驱动程序。
或许您现在已经发现了从一个Windows应用程序进行打印操作要比使用视讯显示器的负担更大,这样可能出现一些问题-特别是,如果GDI模块在建立metafile或打印输出文件时耗尽了磁盘空间。您可以更关切这些问题,并尝试着处理这些问题并告知使用者,或者您当然也可以置之不理。
对于一个应用程序,打印文件的第一步就是如何取得打印机设备的内容。
打印机设备内容
正如在视讯显示器上绘图前需要得到设备内容句柄一样,在打印之前,使用者必须取得一个打印机设备内容句柄。一旦有了这个句柄(并为建立一个新文件呼叫了StartDoc以及呼叫StartPage开始一页),就可以用与使用视讯显示设备内容句柄相同的方法来使用打印机设备内容句柄,该句柄即为各种GDI呼叫的第一个参数。
大多数应用程序经由呼叫PrintDlg函数打开一个标准的打印对话框(本章后面会展示该函数的用法)。这个函数还为使用者提供了一个在打印之前改变打印机或者指定其它特性的机会。然后,它将打印机设备内容句柄交给应用程序。该函数能够省下应用程序的一些工作。然而,某些应用程序(例如Notepad)仅需要取得打印机设备内容,而不需要那个对话框。要做到这一点,需要呼叫CreateDC函数。
在第五章中,您已知道如何通过如下的呼叫来为整个视讯显示器取得指向设备内容的句柄:
hdc = CreateDC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;
您也可以使用该函数来取得打印机设备内容句柄。然而,对打印机设备内容,CreateDC的一般语法为:
hdc = CreateDC (NULL, szDeviceName, NULL, pInitializationData) ;
pInitializationData参数一般被设为NULL。szDeviceName参数指向一个字符串,以告诉Windows打印机设备的名称。在设定设备名称之前,您必须知道有哪些打印机可用。
一个系统可能有不只一台连结着的打印机,甚至可以有其它程序,如传真软件,将自己伪装成打印机。不论连结的打印机有多少台,都只能有一台被认为是「目前的打印机」或者「内定打印机」,这是使用者最近一次选择的打印机。许多小型的Windows程序只使用内定打印机来进行打印。
取得内定打印机设备内容的方式不断在改变。目前,标准的方法是使用EnumPrinters函数来获得。该函数填入一个包含每个连结着的打印机信息的数组结构。根据所需的细节层次,您还可以选择几种结构之一作为该函数的参数。这些结构的名称为PRINTER_INFO_x,x是一个数字。
不幸的是,所使用的函数还取决于您的程序是在Windows 98上执行还是在Windows NT上执行。程序13-1展示了GetPrinterDC函数在两种操作系统上工作的用法。
程序13-1 GETPRNDC
GETPRNDC.C
/*----------------------------------------------------------------------
GETPRNDC.C -- GetPrinterDC function
-----------------------------------------------------------------------*/
#include
HDC GetPrinterDC (void)
{
DWORD dwNeeded, dwReturned ;
HDC hdc ;
PRINTER_INFO_4 * pinfo4 ;
PRINTER_INFO_5 * pinfo5 ;
if (GetVersion () & 0x80000000) // Windows 98
{
EnumPrinters (PRINTER_ENUM_DEFAULT, NULL, 5, NULL,
0, &dwNeeded, &dwReturned) ;
pinfo5 = malloc (dwNeeded) ;
EnumPrinters (PRINTER_ENUM_DEFAULT, NULL, 5, (PBYTE) pinfo5,
dwNeeded, &dwNeeded, &dwReturned) ;
hdc = CreateDC (NULL, pinfo5->pPrinterName, NULL, NULL) ;
free (pinfo5) ;
}
else
//Windows NT
{
EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 4, NULL,
0, &dwNeeded, &dwReturned) ;
pinfo4 = malloc (dwNeeded) ;
EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 4, (PBYTE) pinfo4,
dwNeeded, &dwNeeded, &dwReturned) ;
hdc = CreateDC (NULL, pinfo4->pPrinterName, NULL, NULL) ;
free (pinfo4) ;
}
return hdc ;
}
这些函数使用GetVersion函数来确定程序是执行在Windows 98上还是Windows NT上。不管是什么操作系统,函数呼叫EnumPrinters两次:一次取得它所需结构的大小,一次填入结构。在Windows 98上,函数使用PRINTER_INFO_5结构;在Windows NT上,函数使用PRINTER_INFO_4结构。这些结构在EnumPrinters文件(/Platform SDK/Graphics and Multimedia Services/GDI/Printing and Print Spooler/Printing and Print Spooler Reference/Printing and Print Spooler Functions/EnumPrinters,范例小节的前面)中有说明,它们是「容易而快速」的。
修改后的DEVCAPS程序
第五章的DEVCAPS1程序只显示了从GetDeviceCaps函数获得的关于视讯显示的基本信息。程序13-2所示的新版本显示了关于视讯显示和连结到系统之所有打印机的更多信息。
程序13-2 DEVCAPS2
DEVCAPS2.C
/*--------------------------------------------------------------------------
DEVCAPS2.C -- Displays Device Capability Information (Version 2)
(c) Charles Petzold, 1998
---------------------------------------------------------------------------*/
#include
#include "resource.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void DoBasicInfo (HDC, HDC, int, int) ;
void DoOtherInfo (HDC, HDC, int, int) ;
void DoBitCodedCaps (HDC, HDC, int, int, int) ;
typedef struct
{
int iMask ;
TCHAR * szDesc ;
}
BITS ;
#define IDM_DEVMODE 1000
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("DevCaps2") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
static TCHAR szDevice[32], szWindowText[64] ;
static int cxChar, cyChar,nCurrentDevice = IDM_SCREEN,
nCurrentInfo = IDM_BASIC ;
static DWORD dwNeeded, dwReturned ;
static PRINTER_INFO_4 * pinfo4 ;
static PRINTER_INFO_5 * pinfo5 ;
DWORD i ;
HDC hdc, hdcInfo ;
HMENU hMenu ;
HANDLE hPrint ;
PAINTSTRUCT ps ;
TEXTMETRIC tm ;
switch (message)
{
case WM_CREATE :
hdc = GetDC (hwnd) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cyChar = tm.tmHeight + tm.tmExternalLeading ;
ReleaseDC (hwnd, hdc) ;
// fall through
case WM_SETTINGCHANGE:
hMenu = GetSubMenu (GetMenu (hwnd), 0) ;
while (GetMenuItemCount (hMenu) > 1)
DeleteMenu (hMenu, 1, MF_BYPOSITION) ;
// Get a list of all local and remote printers
//
// First, find out how large an array we need; this
// call will fail, leaving the required size in dwNeeded
//
// Next, allocate space for the info array and fill it
//
// Put the printer names on the menu
if (GetVersion () & 0x80000000) // Windows 98
{
EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 5, NULL,
0, &dwNeeded, &dwReturned) ;
pinfo5 = malloc (dwNeeded) ;
EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 5, (PBYTE) pinfo5,
dwNeeded, &dwNeeded, &dwReturned) ;
for (i = 0 ; i < dwReturned ; i++)
{
AppendMenu (hMenu, (i+1) % 16 ? 0 : MF_MENUBARBREAK, i + 1,
pinfo5[i].pPrinterName) ;
}
free (pinfo5) ;
}
else
// Windows NT
{
EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 4, NULL,
0, &dwNeeded, &dwReturned) ;
pinfo4 = malloc (dwNeeded) ;
EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 4, (PBYTE) pinfo4,
dwNeeded, &dwNeeded, &dwReturned) ;
for (i = 0 ; i < dwReturned ; i++)
{
AppendMenu (hMenu, (i+1) % 16 ? 0 : MF_MENUBARBREAK, i + 1,
pinfo4[i].pPrinterName) ;
}
free (pinfo4) ;
}
AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ;
AppendMenu (hMenu, 0, IDM_DEVMODE, TEXT ("Properties")) ;
wParam = IDM_SCREEN ;
// fall through
case WM_COMMAND :
hMenu = GetMenu (hwnd) ;
if ( LOWORD (wParam) == IDM_SCREEN || // IDM_SCREEN & Printers
LOWORD (wParam) < IDM_DEVMODE)
{
CheckMenuItem (hMenu, nCurrentDevice, MF_UNCHECKED) ;
nCurrentDevice = LOWORD (wParam) ;
CheckMenuItem (hMenu, nCurrentDevice, MF_CHECKED) ;
}
else if (LOWORD (wParam) == IDM_DEVMODE) // Properties selection
{
GetMenuString (hMenu, nCurrentDevice, szDevice,
sizeof (szDevice) / sizeof (TCHAR), MF_BYCOMMAND);
if (OpenPrinter (szDevice, &hPrint, NULL))
{
PrinterProperties (hwnd, hPrint) ;
ClosePrinter (hPrint) ;
}
}
else
// info menu items
{
CheckMenuItem (hMenu, nCurrentInfo, MF_UNCHECKED) ;
nCurrentInfo = LOWORD (wParam) ;
CheckMenuItem (hMenu, nCurrentInfo, MF_CHECKED) ;
}
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_INITMENUPOPUP :
if (lParam == 0)
EnableMenuItem (GetMenu (hwnd), IDM_DEVMODE,
nCurrentDevice == IDM_SCREEMF_GRAYED : MF_ENABLED) ;
return 0 ;
case WM_PAINT :
lstrcpy (szWindowText, TEXT ("Device Capabilities: ")) ;
if (nCurrentDevice == IDM_SCREEN)
{
lstrcpy (szDevice, TEXT ("DISPLAY")) ;
hdcInfo = CreateIC (szDevice, NULL, NULL, NULL) ;
}
else
{
hMenu = GetMenu (hwnd) ;
GetMenuString (hMenu, nCurrentDevice, szDevice,
sizeof (szDevice), MF_BYCOMMAND) ;
hdcInfo = CreateIC (NULL, szDevice, NULL, NULL) ;
}
lstrcat (szWindowText, szDevice) ;
SetWindowText (hwnd, szWindowText) ;
hdc = BeginPaint (hwnd, &ps) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
if (hdcInfo)
{
switch (nCurrentInfo)
{
case IDM_BASIC :
DoBasicInfo (hdc, hdcInfo, cxChar, cyChar) ;
break ;
case IDM_OTHER :
DoOtherInfo (hdc, hdcInfo, cxChar, cyChar) ;
break ;
case IDM_CURVE :
case IDM_LINE :
case IDM_POLY :
case IDM_TEXT :
DoBitCodedCaps (hdc, hdcInfo, cxChar, cyChar,
nCurrentInfo - IDM_CURVE) ;
break ;
}
DeleteDC (hdcInfo) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
void DoBasicInfo (HDC hdc, HDC hdcInfo, int cxChar, int cyChar)
{
static struct
{
int nIndex ;
TCHAR * szDesc ;
}
info[] =
{
HORZSIZE, TEXT ("HORZSIZE Width in millimeters:"),
VERTSIZE, TEXT ("VERTSIZE Height in millimeters:"),
HORZRES, TEXT ("HORZRES Width in pixels:"),
VERTRES, TEXT ("VERTRES Height in raster lines:"),
BITSPIXEL, TEXT ("BITSPIXEL Color bits per pixel:"),
PLANES, TEXT ("PLANES Number of color planes:"),
NUMBRUSHES, TEXT ("NUMBRUSHES Number of device brushes:"),
NUMPENS, TEXT ("NUMPENS Number of device pens:"),
NUMMARKERS, TEXT ("NUMMARKERS Number of device markers:"),
NUMFONTS, TEXT ("NUMFONTS Number of device fonts:"),
NUMCOLORS, TEXT ("NUMCOLORS Number of device colors:"),
PDEVICESIZE, TEXT("PDEVICESIZESize of device structure:"),
ASPECTX, TEXT("ASPECTX Relative width of pixel:"),
ASPECTY, TEXT("ASPECTY Relative height of pixel:"),
ASPECTXY, TEXT("ASPECTXY Relative diagonal of pixel:"),
LOGPIXELSX, TEXT("LOGPIXELSX Horizontal dots per inch:"),
LOGPIXELSY, TEXT("LOGPIXELSY Vertical dots per inch:"),
SIZEPALETTE, TEXT("SIZEPALETTE Number of palette entries:"),
NUMRESERVED, TEXT("NUMRESERVED Reserved palette entries:"),
COLORRES, TEXT("COLORRES Actual color resolution:"),
PHYSICALWIDTH, TEXT("PHYSICALWIDTH Printer page pixel width:"),
PHYSICALHEIGHT,TEXT("PHYSICALHEIGHT Printer page pixel height:"),
PHYSICALOFFSETX,TEXT("PHYSICALOFFSETX Printer page x offset:"),
PHYSICALOFFSETY,TEXT("PHYSICALOFFSETY Printer page y offset:")
} ;
int i ;
TCHAR szBuffer[80] ;
for (i = 0 ; i < sizeof (info) / sizeof (info[0]) ; i++)
TextOut (hdc, cxChar, (i + 1) * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("%-45s%8d"), info[i].szDesc,
GetDeviceCaps (hdcInfo, info[i].nIndex))) ;
}
void DoOtherInfo (HDC hdc, HDC hdcInfo, int cxChar, int cyChar)
{
static BITS clip[] =
{
CP_RECTANGLE, TEXT ("CP_RECTANGLE Can Clip To Rectangle:")
} ;
static BITS raster[] =
{
RC_BITBLT, TEXT ("RC_BITBLT Capable of simple BitBlt:"),
RC_BANDING, TEXT ("RC_BANDING Requires banding support:"),
RC_SCALING, TEXT ("RC_SCALING Requires scaling support:"),
RC_BITMAP64, TEXT ("RC_BITMAP64 Supports bitmaps >64K:"),
RC_GDI20_OUTPUT, TEXT ("RC_GDI20_OUTPUT Has 2.0 output calls:"),
RC_DI_BITMAP, TEXT ("RC_DI_BITMAP Supports DIB to memory:"),
RC_PALETTE, TEXT ("RC_PALETTE Supports a palette:"),
RC_DIBTODEV, TEXT ("RC_DIBTODEV Supports bitmap conversion:"),
RC_BIGFONT, TEXT ("RC_BIGFONT Supports fonts >64K:"),
RC_STRETCHBLT,TEXT ("RC_STRETCHBLT Supports StretchBlt:"),
RC_FLOODFILL, TEXT ("RC_FLOODFILL Supports FloodFill:"),
RC_STRETCHDIB,TEXT ("RC_STRETCHDIB Supports StretchDIBits:")
} ;
static TCHAR * szTech[]= { TEXT ("DT_PLOTTER (Vector plotter)"),
TEXT ("DT_RASDISPLAY (Raster display)"),
TEXT ("DT_RASPRINTER (Raster printer)"),
TEXT ("DT_RASCAMERA (Raster camera)"),
TEXT ("DT_CHARSTREAM (Character stream)"),
TEXT ("DT_METAFILE (Metafile)"),
TEXT ("DT_DISPFILE (Display file)") } ;
int i ;
TCHAR szBuffer[80] ;
TextOut (hdc, cxChar, cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("%-24s%04XH"), TEXT ("DRIVERVERSION:"),
GetDeviceCaps (hdcInfo, DRIVERVERSION))) ;
TextOut (hdc, cxChar, 2 * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("%-24s%-40s"), TEXT ("TECHNOLOGY:"),
szTech[GetDeviceCaps (hdcInfo, TECHNOLOGY)])) ;
TextOut (hdc, cxChar, 4 * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("CLIPCAPS (Clipping capabilities)"))) ;
for (i = 0 ; i < sizeof (clip) / sizeof (clip[0]) ; i++)
TextOut (hdc, 9 * cxChar, (i + 6) * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("%-45s %3s"), clip[i].szDesc,
GetDeviceCaps (hdcInfo, CLIPCAPS) & clip[i].iMask ?
TEXT ("Yes") : TEXT ("No"))) ;
TextOut (hdc, cxChar, 8 * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("RASTERCAPS (Raster capabilities)"))) ;
for (i = 0 ; i < sizeof (raster) / sizeof (raster[0]) ; i++)
TextOut (hdc, 9 * cxChar, (i + 10) * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("%-45s %3s"), raster[i].szDesc,
GetDeviceCaps (hdcInfo, RASTERCAPS) & raster[i].iMask ?
TEXT ("Yes") : TEXT ("No"))) ;
}
void DoBitCodedCaps ( HDC hdc, HDC hdcInfo, int cxChar, int cyChar,int iType)
{
static BITS curves[] =
{
CC_CIRCLES, TEXT ("CC_CIRCLES Can do circles:"),
CC_PIE, TEXT ("CC_PIE Can do pie wedges:"),
CC_CHORD, TEXT ("CC_CHORD Can do chord arcs:"),
CC_ELLIPSES, TEXT ("CC_ELLIPSES Can do ellipses:"),
CC_WIDE, TEXT ("CC_WIDE Can do wide borders:"),
CC_STYLED, TEXT ("CC_STYLED Can do styled borders:"),
CC_WIDESTYLED, TEXT ("CC_WIDESTYLED Can do wide and styled borders:"),
CC_INTERIORS, TEXT ("CC_INTERIORS Can do interiors:")
} ;
static BITS lines[] =
{
LC_POLYLINE, TEXT ("LC_POLYLINE Can do polyline:"),
LC_MARKER, TEXT ("LC_MARKER Can do markers:"),
LC_POLYMARKER, TEXT ("LC_POLYMARKER Can do polymarkers"),
LC_WIDE, TEXT ("LC_WIDE Can do wide lines:"),
LC_STYLED, TEXT ("LC_STYLED Can do styled lines:"),
LC_WIDESTYLED, TEXT ("LC_WIDESTYLED Can do wide and styled lines:"),
LC_INTERIORS, TEXT ("LC_INTERIORS Can do interiors:")
} ;
static BITS poly[] =
{
PC_POLYGON,
TEXT ("PC_POLYGON Can do alternate fill polygon:"),
PC_RECTANGLE, TEXT ("PC_RECTANGLE Can do rectangle:"),
PC_WINDPOLYGON,
TEXT ("PC_WINDPOLYGON Can do winding number fill polygon:"),
PC_SCANLINE, TEXT ("PC_SCANLINE Can do scanlines:"),
PC_WIDE, TEXT ("PC_WIDE Can do wide borders:"),
PC_STYLED, TEXT ("PC_STYLED Can do styled borders:"),
PC_WIDESTYLED,
TEXT ("PC_WIDESTYLED Can do wide and styled borders:"),
PC_INTERIORS, TEXT ("PC_INTERIORS Can do interiors:")
} ;
static BITS text[] =
{
TC_OP_CHARACTER, TEXT ("TC_OP_CHARACTER Can do character output precision:"),
TC_OP_STROKE, TEXT ("TC_OP_STROKE Can do stroke output precision:"),
TC_CP_STROKE, TEXT ("TC_CP_STROKE Can do stroke clip precision:"),
TC_CR_90, TEXT ("TC_CP_90 Can do 90 degree character rotation:"),
TC_CR_ANY, TEXT ("TC_CR_ANY Can do any character rotation:"),
TC_SF_X_YINDEP, TEXT ("TC_SF_X_YINDEP Can do scaling independent of X and Y:"),
TC_SA_DOUBLE, EXT ("TC_SA_DOUBLE Can do doubled character for scaling:"),
TC_SA_INTEGER, TEXT ("TC_SA_INTEGER Can do integer multiples for scaling:"),
TC_SA_CONTIN, TEXT ("TC_SA_CONTIN Can do any multiples for exact scaling:"),
TC_EA_DOUBLE, TEXT ("TC_EA_DOUBLE Can do double weight characters:"),
TC_IA_ABLE, TEXT ("TC_IA_ABLE Can do italicizing:"),
TC_UA_ABLE, TEXT ("TC_UA_ABLE Can do underlining:"),
TC_SO_ABLE, TEXT ("TC_SO_ABLE Can do strikeouts:"),
TC_RA_ABLE, TEXT ("TC_RA_ABLE Can do raster fonts:"),
TC_VA_ABLE, TEXT ("TC_VA_ABLE Can do vector fonts:")
} ;
static struct
{
int iIndex ;
TCHAR * szTitle ;
BITS (*pbits)[] ;
int iSize ;
}
bitinfo[] =
{
CURVECAPS, TEXT ("CURVCAPS (Curve Capabilities)"),
(BITS (*)[]) curves, sizeof (curves) / sizeof (curves[0]),
LINECAPS, TEXT ("LINECAPS (Line Capabilities)"),
(BITS (*)[]) lines, sizeof (lines) / sizeof (lines[0]),
POLYGONALCAPS, TEXT ("POLYGONALCAPS (Polygonal Capabilities)"),
(BITS (*)[]) poly, sizeof (poly) / sizeof (poly[0]),
TEXTCAPS, TEXT ("TEXTCAPS (Text Capabilities)"),
(BITS (*)[]) text, sizeof (text) / sizeof (text[0])
} ;
static TCHAR szBuffer[80] ;
BITS (*pbits)[] = bitinfo[iType].pbits ;
int i, iDevCaps = GetDeviceCaps (hdcInfo, bitinfo[iType].iIndex) ;
TextOut (hdc, cxChar, cyChar, bitinfo[iType].szTitle,
lstrlen (bitinfo[iType].szTitle)) ;
for (i = 0 ; i < bitinfo[iType].iSize ; i++)
extOut (hdc, cxChar, (i + 3) * cyChar, szBuffer,
wsprintf (szBuffer, TEXT ("%-55s %3s"), (*pbits)[i].szDesc,
iDevCaps & (*pbits)[i].iMask ? TEXT ("Yes") : TEXT ("No")));
}
DEVCAPS2.RC (摘录)
//Microsoft Developer Studio generated resource script.
#include "resource.h"
#include "afxres.h"
/
// Menu
DEVCAPS2 MENU DISCARDABLE
BEGIN
POPUP "&Device"
BEGIN
MENUITEM "&Screen",IDM_SCREEN, CHECKED
END
POPUP "&Capabilities"
BEGIN
MENUITEM "&Basic Information",IDM_BASIC
MENUITEM "&Other Information",IDM_OTHER
MENUITEM "&Curve Capabilities",IDM_CURVE
MENUITEM "&Line Capabilities",IDM_LINE
MENUITEM "&Polygonal Capabilities",IDM_POLY
MENUITEM "&Text Capabilities",IDM_TEXT
END
END
RESOURCE.H (摘录)
// Microsoft Developer Studio generated include file.
// Used by DevCaps2.rc
#define IDM_SCREEN 40001
#define IDM_BASIC 40002
#define IDM_OTHER 40003
#define IDM_CURVE 40004
#define IDM_LINE 40005
#define IDM_POLY 40006
#define IDM_TEXT 40007
因为DEVCAPS2只取得打印机的信息内容,使用者仍然可以从DEVCAPS2的菜单中选择所需打印机。如果使用者想比较不同打印机的功能,可以先用打印机文件夹增加各种打印驱动程序。
PrinterProperties呼叫
DEVCAPS2的「Device」菜单中上还有一个称为「Properties」的选项。要使用这个选项,首先得从Device菜单中选择一个打印机,然后再选择Properties,这时弹出一个对话框。对话框从何而来呢?它由打印机驱动程序呼叫,而且至少还让使用者选择纸的尺寸。大多数打印机驱动也可以让使用者在「直印(portrait)」或「横印(landscape)」模式中进行选择。在直印模式(一般为内定模式)下,纸的短边是顶部。在横印模式下,纸的长边是顶部。如果改变该模式,则所作的改变将在DEVCAPS2程序从GetDeviceCaps函数取得的信息中反应出来:水平尺寸和分辨率将与垂直尺寸和分辨率交换。彩色绘图机的「Properties」对话框内容十分广泛,它们要求使用者输入安装在绘图机上之画笔的颜色和使用之绘图纸(或透明胶片)的型号。
所有打印机驱动程序都包含一个称为ExtDeviceMode的输出函数,它呼叫对话框并储存使用者输入的信息。有些打印机驱动程序也将这些信息储存在系统登录的自己拥有的部分中,有些则不然。那些储存信息的打印机驱动程序在下次执行Windows时将存取该信息。
允许使用者选择打印机的Windows程序通常只呼叫PrintDlg(本章后面我会展示用法)。这个有用的函数在准备打印时负责和使用者之间所有的通讯工作,并负责处理使用者要求的所有改变。当使用者单击「Properties」按钮时,PrintDlg还会启动属性表格对话框。
程序还可以通过直接呼叫打印机驱动程序的ExtDeviceMode或ExtDeveModePropSheet函数,来显示打印机的属性对话框,然而,我不鼓励您这样做。像DEVCAPS2那样,透过呼叫PrinterProperties来启动对话框会好得多。
PrinterProperties要求打印机对象的句柄,您可以通过OpenPrinter函数来得到。当使用者取消属性表格对话框时,PrinterProperties传回,然后使用者通过呼叫ClosePrinter,释放打印机句柄。DEVCAPS2就是这样做到这一点的。
程序首先取得刚刚在Device菜单中选择的打印机名称,并将其存入一个名为szDevice的字符数组中。
GetMenuString ( hMenu, nCurrentDevice, szDevice,
sizeof (szDevice) / sizeof (TCHAR), MF_BYCOMMAND) ;
然后,使用OpenPrinter获得该设备的句柄。如果呼叫成功,那么程序接着呼叫PrinterProperties启动对话框,然后呼叫ClosePrinter释放设备句柄:
if (OpenPrinter (szDevice, &hPrint, NULL))
{
PrinterProperties (hwnd, hPrint) ;
ClosePrinter (hPrint) ;
}
检查BitBlt支持
您可以用GetDeviceCaps函数来取得页中可打印区的尺寸和分辨率(通常,该区域不会与整张纸的大小相同)。如果使用者想自己进行缩放操作,也可以获得相对的图素宽度和高度。
打印机能力的大多数信息是用于GDI而不是应用程序的。通常,在打印机不能做某件事时,GDI会仿真出那项功能。然而,这是应用程序应该事先检查的。
以RASTERCAPS(「位映像支持」)参数呼叫GetDeviceCaps,它传回的RC_BITBLT位包含了另一个重要的打印机特性,该位标示设备是否能进行位块传送。大多数点阵打印机、激光打印机和喷墨打印机都能进行位块传送,而大多数绘图机却不能。不能处理位块传送的设备不支持下列GDI函数:CreateCompatibleDC、CreateCompatibleBitmap、PatBlt、BitBlt、StretchBlt、GrayString、DrawIcon、SetPixel、GetPixel、FloodFill、ExtFloodFill、FillRgn、FrameRgn、InvertRgn、PaintRgn、FillRect、FrameRect和InvertRect。这是在视讯显示器上使用GDI函数与在打印机上使用它们的唯一重要区别。
最简单的打印程序
现在可以开始打印了,我们尽可能简单地开始。事实上,我们的第一个程序只是让打印机走纸而已。程序13-3的FORMFEED程序,展示了打印所需的最小需求。
程序13-3 FORMFEED
FORMFEED.C
/*-----------------------------------------------------------------------
FORMFEED.C -- Advances printer to next page
(c) Charles Petzold, 1998
------------------------------------------------------------------------*/
#include
HDC GetPrinterDC (void) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int iCmdShow)
{
static DOCINFO di = { sizeof (DOCINFO), TEXT ("FormFeed") } ;
HDC hdcPrint = GetPrinterDC () ;
if (hdcPrint != NULL)
{
if (StartDoc (hdcPrint, &di) > 0)
if (StartPage (hdcPrint) > 0 && EndPage (hdcPrint) > 0)
EndDoc (hdcPrint) ;
DeleteDC (hdcPrint) ;
}
return 0 ;
}
这个程序也需要前面程序13-1中的GETPRNDC.C文件。
除了取得打印机设备内容(然后再删除它)外,程序只呼叫了我们在本章前面讨论过的四个打印函数。FORMFEED首先呼叫StartDoc开始一个新的文件,它测试从StartDoc传回的值,只有传回值是正数时,才继续下去:
if (StartDoc (hdcPrint, &di) > 0)
StartDoc的第二个参数是指向DOCINFO结构的指针。该结构在第一个字段包含了结构的大小,在第二个字段包含了字符串「FormFeed」。当文件正在被打印或者在等待打印时,这个字符串将出现在打印机任务队列中的「Document Name」列中。通常,该字符串包含进行打印的应用程序名称和被打印的文件名称。
如果StartDoc成功(由一个正的传回值表示),那么FORMFEED呼叫StartPage,紧接着立即呼叫EndPage。这一程序将打印机推进到新的一页,再次对传回值进行测试:
if (StartPage (hdcPrint) > 0 && EndPage (hdcPrint) > 0)
最后,如果不出错,文件就结束:
EndDoc (hdcPrint) ;
要注意的是,只有当没出错时,才呼叫EndDoc函数。如果其它打印函数中的某一个传回错误代码,那么GDI实际上已经中断了文件的打印。如果打印机目前未打印,这种错误代码通常会使打印机重新设定。测试打印函数的传回值是检测错误的最简单方法。如果您想向使用者报告错误,就必须呼叫GetLastError来确定错误。
如果您写过MS-DOS下的简单利用打印机走纸的程序,就应该知道,对于大多数打印机,ASCII码12启动走纸。为什么不简单地使用C的链接库函数open,然后用write输出ASCII码12呢?当然,您完全可以这么做,但是必须确定打印机连结的是串行端口还是并列埠。然后您还要确定另外的程序(例如,打印队列程序)是不是正在使用打印机。您并不希望在文件打印到一半时被别的程序把正在打印的那张纸送出打印机,对不对?最后,您还必须确定ASCII码12是不是所连结打印机的走纸字符,因为并非所有打印机的走纸字符都是12。事实上,在PostScript中的走纸命令便不是12,而是单字showpage。
简单地说,不要试图直接绕过Windows;而应该坚持在打印中使用Windows函数。
打印图形和文字
在一个Windows程序中,打印所需的额外负担通常比FORMFEED程序高得多,而且还要用GDI函数来实际打印一些东西。我们来写个打印一页文字和图形的程序,采用FORMFEED程序中的方法,并加入一些新的东西。该程序将有三个版本PRINT1、PRINT2和PRINT3。为避免程序代码重复,每个程序都用前面所示的GETPRNDC.C文件和PRINT.C文件中的函数,如程序13-4所示。
程序13-4 PRINT
PRINT.C
/*------------------------------------------------------------------------
PRINT.C -- Common routines for Print1, Print2, and Print3
--------------------------------------------------------------------------*/
#include
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
BOOL PrintMyPage (HWND) ;
extern HINSTANCE hInst ;
extern TCHAR szAppName[] ;
extern TCHAR szCaption[] ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hInst = hInstance ;
hwnd = CreateWindow (szAppName, szCaption,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
void PageGDICalls (HDC hdcPrn, int cxPage, int cyPage)
{
static TCHAR szTextStr[] = TEXT ("Hello, Printer!") ;
Rectangle (hdcPrn, 0, 0, cxPage, cyPage) ;
MoveToEx (hdcPrn, 0, 0, NULL) ;
LineTo (hdcPrn, cxPage, cyPage) ;
MoveToEx (hdcPrn, cxPage, 0, NULL) ;
LineTo (hdcPrn, 0, cyPage) ;
SaveDC (hdcPrn) ;
SetMapMode (hdcPrn, MM_ISOTROPIC) ;
SetWindowExtEx (hdcPrn, 1000, 1000, NULL) ;
SetViewportExtEx (hdcPrn, cxPage / 2, -cyPage / 2, NULL) ;
SetViewportOrgEx (hdcPrn, cxPage / 2, cyPage / 2, NULL) ;
Ellipse (hdcPrn, -500, 500, 500, -500) ;
SetTextAlign (hdcPrn, TA_BASELINE | TA_CENTER) ;
TextOut (hdcPrn, 0, 0, szTextStr, lstrlen (szTextStr)) ;
RestoreDC (hdcPrn, -1) ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
static int cxClient, cyClient ;
HDC hdc ;
HMENU hMenu ;
PAINTSTRUCT ps ;
switch (message)
{
case WM_CREATE:
hMenu = GetSystemMenu (hwnd, FALSE) ;
AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ;
AppendMenu (hMenu, 0, 1, TEXT ("&Print")) ;
return 0 ;
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
case WM_SYSCOMMAND:
if (wParam == 1)
{
if (!PrintMyPage (hwnd))
MessageBox (hwnd, TEXT ("Could not print page!"),
szAppName, MB_OK | MB_ICONEXCLAMATION) ;
return 0 ;
}
break ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
PageGDICalls (hdc, cxClient, cyClient) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
PRINT.C包括函数WinMain、WndProc以及一个称为PageGDICalls的函数。PageGDICalls函数接收打印机设备内容句柄和两个包含打印页面宽度及高度的变量。这个函数还负责画一个包围整个页面的矩形,有两条对角线,页中间有一个椭圆(其直径是打印机高度和宽度中较小的那个的一半),文字「Hello, Printer!」位于椭圆的中间。
处理WM_CREATE消息时,WndProc将一个「Print」选项加到系统菜单上。选择该选项将呼叫PrintMyPage,此函数的功能在程序的三个版本中将不断增强。当打印成功时,PrintMyPage传回TRUE值,如果遇到错误时则传回FALSE。如果PrintMyPage传回FALSE,WndProc就会显示一个消息框以告知使用者发生了错误。
打印的基本程序
打印程序的第一个版本是PRINT1,见程序13-5。经编译后即可执行此程序,然后从系统菜单中选择「Print」。接着,GDI将必要的打印机输出储存在一个临时文件中,然后打印队列程序将它发送给打印机。
程序13-5 PRINT1
PRINT1.C
/*---------------------------------------------------------------------
PRINT1.C -- Bare Bones Printing
(c) Charles Petzold, 1998
----------------------------------------------------------------------*/
#include
HDC GetPrinterDC (void) ; // in GETPRNDC.C
void PageGDICalls (HDC, int, int) ; // in PRINT.C
HINSTANCE hInst ;
TCHAR szAppName[] = TEXT ("Print1") ;
TCHAR szCaption[] = TEXT ("Print Program 1") ;
BOOL PrintMyPage (HWND hwnd)
{
static DOCINFO di = { sizeof (DOCINFO), TEXT ("Print1: Printing") } ;
BOOL bSuccess = TRUE ;
HDC hdcPrn ;
int xPage, yPage ;
if (NULL == (hdcPrn = GetPrinterDC ()))
return FALSE ;
xPage = GetDeviceCaps (hdcPrn, HORZRES) ;
yPage = GetDeviceCaps (hdcPrn, VERTRES) ;
if (StartDoc (hdcPrn, &di) > 0)
{
if (StartPage (hdcPrn) > 0)
{
PageGDICalls (hdcPrn, xPage, yPage) ;
if (EndPage (hdcPrn) > 0)
EndDoc (hdcPrn) ;
else
bSuccess = FALSE ;
}
}
else
bSuccess = FALSE ;
DeleteDC (hdcPrn) ;
return bSuccess ;
}
我们来看看PRINT1.C中的程序代码。如果PrintMyPage不能取得打印机的设备内容句柄,它就传回FALSE,并且WndProc显示消息框指出错误。如果函数成功取得了设备内容句柄,它就通过呼叫GetDeviceCaps来确定页面的水平和垂直大小(以图素为单位)。
xPage = GetDeviceCaps (hdcPrn, HORZRES) ;
yPage = GetDeviceCaps (hdcPrn, VERTRES) ;
这不是纸的全部大小,只是纸的可打印区域。呼叫后,除了PRINT1在StartPage和EndPage呼叫之间呼叫PageGDICalls,PRINT1的PrintMyPage函数中的程序代码在结构上与FORMFEED中的程序代码相同。仅当呼叫StartDoc、StartPage和EndPage都成功时,PRINT1才呼叫EndDoc打印函数。
使用放弃程序来取消打印
对于大型文件,程序应该提供使用者在应用程序行印期间取消打印任务的便利性。也许使用者只要打印文件中的一页,而不是打印全部的537页。应该要能在印完全部的537页之前纠正这个错误。
在一个程序内取消一个打印任务需要一种被称为「放弃程序」的技术。放弃程序在程序中只是个较小的输出函数,使用者可以使用SetAbortProc函数将该函数的地址传给Windows。然后GDI在打印时,重复呼叫该程序,不断地问:「我是否应该继续打印?」
我们看看将放弃程序加到打印处理程序中去需要些什么,然后检查一些旁枝末节。放弃程序一般命名为AbortProc,其形式为:
BOOL CALLBACK AbortProc (HDC hdcPrn, int iCode)
{
//其它行程序
}
打印前,您必须通过呼叫SetAbortProc来登记放弃程序:
SetAbortProc (hdcPrn, AbortProc) ;
在呼叫StartDoc前呼叫上面的函数,打印完成后不必清除放弃程序。
在处理EndPage呼叫时(亦即,在将metafile放入设备驱动程序并建立临时打印文件时),GDI常常呼叫放弃程序。参数hdcPrn是打印机设备内容句柄。如果一切正常,iCode参数是0,如果GDI模块在生成临时文件时耗尽了磁盘空间,iCode就是SP_OUTOFDISK。
如果打印作业继续,那么AbortProc必须传回TRUE(非零);如果打印作业异常结束,就传回FALSE(零)。放弃程序可以被简化为如下所示的形式:
BOOL CALLBACK AbortProc (HDC hdcPrn, int iCode)
{
MSG msg ;
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return TRUE ;
}
这个函数看起来有点特殊,其实它看起来像是消息循环。使用者会注意到,这个「消息循环」呼叫PeekMessage而不是GetMessage。我在第五章的RANDRECT程序中讨论过PeekMessage。应该还记得,PeekMessage将会控制权返回给程序,而不管程序的消息队列中是否有消息存在。
只要PeekMessage传回TRUE,那么AbortProc函数中的消息循环就重复呼叫PeekMessage。TRUE值表示PeekMessage已经找到一个消息,该消息可以通过TranslateMessage和DispatchMessage发送到程序的窗口消息处理程序。若程序的消息队列中没有消息,则PeekMessage的传回值为FALSE,因此AbortProc将控制权返回给Windows。
Windows如何使用AbortProc
当程序进行打印时,大部分工作发生在要呼叫EndPage时。呼叫EndPage前,程序每呼叫一次GDI绘图函数,GDI模块只是简单地将另一个记录加到磁盘上的metafile中。当GDI得到EndPage后,对打印页中由设备驱动程序定义的每个输出带,GDI都将该metafile送入设备驱动程序中。然后,GDI将打印机驱动程序建立的打印输出储存到一个文件中。如果没有启用后台打印,那么GDI模块必须自动将该打印输出写入打印机。
在EndPage呼叫期间,GDI模块呼叫您设定的放弃程序。通常iCode参数为0,但如果由于存在未打印的其它临时文件,而造成GDI执行时磁盘空间不够,iCode参数就为SP_OUTOFDISK(通常您不会检查这个值,但是如果愿意,您可以进行检查)。放弃程序随后进入PeekMessage循环从自己的消息队列中找寻消息。
如果在程序的消息队列中没有消息,PeekMessage会传回FALSE,然后放弃程序跳出它的消息循环并给GDI模块传回一个TRUE值,指示打印应该继续进行。然后GDI模块继续处理EndPage呼叫。
如果有错误发生,那么GDI将中止打印程序,这样,放弃程序的主要目的是允许使用者取消打印。为此,我们还需要一个显示「Cancel」按钮的对话框,让我们采用两个独立的步骤。首先,我们在建立PRINT2程序时增加一个放弃程序,然后在PRINT3中增加一个带有「Cancel」按钮的对话框,使放弃程序可用。
实作放弃程序
现在快速复习一下放弃程序的机制。可以定义一个如下所示的放弃程序:
BOOL CALLBACK AbortProc (HDC hdcPrn, int iCode)
{
MSG msg ;
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return TRUE ;
}
当您想打印什么时,使用下面的呼叫将指向放弃程序的指针传给Windows:
SetAbortProc (hdcPrn, AbortProc) ;
在呼叫StartDoc之前进行这个呼叫就行了。
不过,事情没有这么简单。我们忽视了AbortProc程序中PeekMessage循环这个问题,它是个很大的问题。只有在程序处于打印程序时,AbortProc程序才会被呼叫。如果在AbortProc中找到一个消息并把它传送给窗口消息处理程序,就会发生一些非常令人讨厌的事情:使用者可以从菜单中再次选择「Print」,但程序已经处于打印例程之中。程序在打印前一个文件的同时,使用者也可以把一个新文件加载到程序里。使用者甚至可以退出程序!如果这种情况发生了,所有使用者程序的窗口都将被清除。当打印例程执行结束时,除了退到不再有效的窗口例程之外,您无处可去。
这种东西会把人搞得晕头转向,而我们的程序对此并未做任何准备。正是由于这个原因,当设定放弃程序时,首先应禁止程序的窗口接受输入,使它不能接受键盘和鼠标输入。可以用以下的函数完成这项工作:
EnableWindow (hwnd, FALSE) ;
它可以禁止键盘和鼠标的输入进入消息队列。因此在打印程序中,使用者不能对程序做任何工作。当打印完成时,应重新允许窗口接受输入:
EnableWindow (hwnd, TRUE) ;
您可能要问,既然没有键盘或鼠标消息进入消息队列,为什么我们还要进行AbortProc中的TranslateMessage和DispatchMessage呼叫呢?实际上并不一定非得需要TranslateMessage,但是,我们必须使用DispatchMessage,处理WM_PAINT消息进入消息队列中的情况。如果WM_PAINT消息没有得到窗口消息处理程序中的BeginPaint和EndPaint的适当处理,由于PeekMessage不再传回FALSE,该消息就会滞留在队列中并且妨碍工作。
当打印期间阻止窗口处理输入消息时,您的程序不会进行显示输出。但使用者可以切换到其它程序,并在那里进行其它工作,而后台打印程序则能继续将输出文件送到打印机。
程序13-6所示的PRINT2程序在PRINT1中增加了一个放弃程序和必要的支持-呼叫AbortProc函数并呼叫EnableWindow两次(第一次阻止窗口接受输入消息,第二次启用窗口)。
程序13-6 PRINT2
PRINT2.C
/*---------------------------------------------------------------------
PRINT2.C -- Printing with Abort Procedure
(c) Charles Petzold, 1998
----------------------------------------------------------------------*/
#include
HDC GetPrinterDC (void) ; // in GETPRNDC.C
void PageGDICalls (HDC, int, int) ; // in PRINT.C
HINSTANCE hInst ;
TCHAR szAppName[] = TEXT ("Print2") ;
TCHAR szCaption[] = TEXT ("Print Program 2 (Abort Procedure)") ;
BOOL CALLBACK AbortProc (HDC hdcPrn, int iCode)
{
MSG msg ;
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return TRUE ;
}
BOOL PrintMyPage (HWND hwnd)
{
static DOCINFO di = { sizeof (DOCINFO), TEXT ("Print2: Printing") } ;
BOOL bSuccess = TRUE ;
HDC hdcPrn ;
short xPage, yPage ;
if (NULL == (hdcPrn = GetPrinterDC ()))
return FALSE ;
xPage = GetDeviceCaps (hdcPrn, HORZRES) ;
yPage = GetDeviceCaps (hdcPrn, VERTRES) ;
EnableWindow (hwnd, FALSE) ;
SetAbortProc (hdcPrn, AbortProc) ;
if (StartDoc (hdcPrn, &di) > 0)
{
if (StartPage (hdcPrn) > 0)
{
PageGDICalls (hdcPrn, xPage, yPage) ;
if (EndPage (hdcPrn) > 0)
EndDoc (hdcPrn) ;
else
bSuccess = FALSE ;
}
}
else
bSuccess = FALSE ;
EnableWindow (hwnd, TRUE) ;
DeleteDC (hdcPrn) ;
return bSuccess ;
}
增加打印对话框
PRINT2还不能令人十分满意。首先,这个程序没有直接指示出何时开始打印和何时结束打印。只有将鼠标指向程序并且发现它没有反应时,才能断定它仍然在处理PrintMyPage例程。PRINT2在进行背景处理时也没有给使用者提供取消打印作业的机会。
您可能注意到,大多数Windows程序都为使用者提供了一个取消目前正在进行打印操作的机会。一个小的对话框出现在屏幕上,它包括一些文字和「Cancel」按键。在GDI将打印输出储存到磁盘文件或(如果停用打印队列程序)打印机正在打印的整个期间,程序都显示这个对话框。它是一个非系统模态对话框,您必须提供对话程序。
通常称这个对话框为「放弃对话框」,称这种对话程序为「放弃对话程序」。为了更清楚地把它和「放弃程序」区别开来,我们称这种对话程序为「打印对话程序」。放弃程序(名为AbortProc)和打印对话程序(将命名为PrintDlgProc)是两个不同的输出函数。如果想以一种专业的Windows式打印方式进行打印工作,就必须拥有这两个函数。
这两个函数的交互作用方式如下:AbortProc中的PeekMessage循环得被修改,以便将非系统模态对话框的消息发送给对话框窗口消息处理程序。PrintDlgProc必须处理WM_COMMAND消息,以检查「Cancel」按钮的状态。如果「Cancel」钮被按下,就将一个叫做bUserAbort的整体变量设为TRUE。AbortProc传回的值正好和bUserAbort相反。您可能还记得,如果AbortProc传回TRUE会继续打印,传回FALSE则放弃打印。在PRINT2中,我们总是传回TRUE。现在,使用者在打印对话框中按下「Cancel」按钮时将传回FALSE。程序13-7所示的PRINT3程序实作了这个处理方式。
程序13-7 PRINT3
PRINT3.C
/*-----------------------------------------------------------------
PRINT3.C -- Printing with Dialog Box
(c) Charles Petzold, 1998
-------------------------------------------------------------------*/
#include
HDC GetPrinterDC (void) ; // in GETPRNDC.C
voidPageGDICalls (HDC, int, int) ; // in PRINT.C
HINSTANCE hInst ;
TCHAR szAppName[] = TEXT ("Print3") ;
TCHAR szCaption[] = TEXT ("Print Program 3 (Dialog Box)") ;
BOOL bUserAbort ;
HWND hDlgPrint ;
BOOL CALLBACK PrintDlgProc (HWND hDlg, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
SetWindowText (hDlg, szAppName) ;
EnableMenuItem (GetSystemMenu (hDlg, FALSE), SC_CLOSE, MF_GRAYED) ;
return TRUE ;
case WM_COMMAND:
bUserAbort = TRUE ;
EnableWindow (GetParent (hDlg), TRUE) ;
DestroyWindow (hDlg) ;
hDlgPrint = NULL ;
return TRUE ;
}
return FALSE ;
}
BOOL CALLBACK AbortProc (HDC hdcPrn, int iCode)
{
MSG msg ;
while (!bUserAbort && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
if (!hDlgPrint || !IsDialogMessage (hDlgPrint, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
return !bUserAbort ;
}
BOOL PrintMyPage (HWND hwnd)
{
static DOCINFO di = { sizeof (DOCINFO), TEXT ("Print3: Printing") } ;
BOOL bSuccess = TRUE ;
HDC hdcPrn ;
int xPage, yPage ;
if (NULL == (hdcPrn = GetPrinterDC ()))
return FALSE ;
xPage = GetDeviceCaps (hdcPrn, HORZRES) ;
yPage = GetDeviceCaps (hdcPrn, VERTRES) ;
EnableWindow (hwnd, FALSE) ;
bUserAbort = FALSE ;
hDlgPrint = CreateDialog (hInst, TEXT ("PrintDlgBox"),
hwnd, PrintDlgProc) ;
SetAbortProc (hdcPrn, AbortProc) ;
if (StartDoc (hdcPrn, &di) > 0)
{
if (StartPage (hdcPrn) > 0)
{
PageGDICalls (hdcPrn, xPage, yPage) ;
if (EndPage (hdcPrn) > 0)
EndDoc (hdcPrn) ;
else
bSuccess = FALSE ;
}
}
else
bSuccess = FALSE ;
if (!bUserAbort)
{
EnableWindow (hwnd, TRUE) ;
DestroyWindow (hDlgPrint) ;
}
DeleteDC (hdcPrn) ;
return bSuccess && !bUserAbort ;
}
PRINT.RC (摘录)
//Microsoft Developer Studio generated resource script.
#include "resource.h"
#include "afxres.h"
/
// Dialog
PRINTDLGBOX DIALOG DISCARDABLE 20, 20, 186, 63
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,67,42,50,14
CTEXT "Cancel Printing",IDC_STATIC,7,21,172,8
END
如果您使用PRINT3,那么最好临时暂停使用后台打印;否则,只有在打印队列程序从PRINT3中接收数据时才可见到的「Cancel」按钮可能会很快消失,让您根本没有机会去按它。如果您按「Cancel」按钮时打印并不立即终止(特别是在一个慢速打印机上),不要惊讶。打印机有一个内部缓冲区,在打印机停止之前其中的数据必须全部送出,按「Cancel」只是告诉GDI不要向打印机的缓冲区发送更多的数据而已。
PRINT3增加了两个整体变量:一个是叫做bUserAbort的布尔变量,另一个是叫做hDlgPrint的对话框窗口句柄。PrintMyPage函数将bUserAbort初始化为FALSE。与PRINT2一样,程序的主窗口是不接收输入消息的。指向AbortProc的指标用于SetAbortProc呼叫中,而指向PrintDlgProc的指标用于CreateDialog呼叫中。CreateDialog传回的窗口句柄储存在hDlgPrint中。
现在,AbortProc中的消息循环如下:
while (!bUserAbort && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
if (!hDlgPrint || !IsDialogMessage (hDlgPrint, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
return !bUserAbort ;
只有在bUserAbort为FALSE,也就是使用者还没有终止打印工作时,这段程序代码才会呼叫PeekMessage。IsDialogMessage函数用来将消息发送给非系统模态对话框。和普通的非系统模态对话框一样,对话框窗口的句柄在这个呼叫之前受到检查。AbortProc的传回值正好与bUserAbort相反。开始时,bUserAbort为FALSE,因此AbortProc传回TRUE,表示继续进行打印;但是bUserAbort可能在打印对话程序中被设定为TRUE。
PrintDlgProc函数是相当简单的。处理WM_INITDIALOG时,该函数将窗口标题设定为程序名称,并且停用系统菜单上的「Close」选项。如果使用者按下了「Cancel」钮,PrintDlgProc将收到WM_COMMAND消息:
case WM_COMMAND :
bUserAbort = TRUE ;
EnableWindow (GetParent (hDlg), TRUE) ;
DestroyWindow (hDlg) ;
hDlgPrint = NULL ;
return TRUE ;
将bUserAbort设定为TRUE,则说明使用者已经决定取消打印操作,主窗口被启动,而对话框被清除(按顺序完成这两项活动是很重要的,否则,在Windows中执行其它程序之一将变成活动程序,而您的程序将消失到背景中)。与通常的情况一样,将hDlgPrint设定为NULL,防止在消息循环中呼叫IsDialogMessage。
只有在AbortProc用PeekMessage找到消息,并用IsDialogMessage将它们传送给对话框窗口消息处理程序时,这个对话框才接收消息。只有在GDI模块处理EndPage函数时,才呼叫AbortProc。如果GDI发现AbortProc的传回值是FALSE,它将控制权从EndPage传回到PrintMyPage。它不传回错误码。至此,PrintMyPage认为打印页已经发完了,并呼叫EndDoc函数。但是,由于GDI模块还没有完成对EndPage呼叫的处理,所以不会打印出什么东西来。
有些清除工作尚待完成。如果使用者没在对话框中取消打印作业,那么对话框仍然会显示着。PrintMyPage重新启用它的主窗口并清除对话框:
if (!bUserAbort)
{
EnableWindow (hwnd, TRUE) ;
DestroyWindow (hDlgPrint) ;
}
两个变量会通知您发生了什么事:bUserAbort可以告诉您使用者是否终止了打印作业,bSuccess会告诉您是否出了故障,您可以用这些变量来完成想做的工作。PrintMyPage只简单地对它们进行逻辑上的AND运算,然后把值传回给WndProc:
return bSuccess && !bUserAbort ;
为POPPAD增加打印功能
现在准备在POPPAD程序中增加打印功能,并且宣布POPPAD己告完毕。这需要第十一章中的各个POPPAD文件,此外,还需要程序13-8中的POPPRNT.C文件。
程序13-8 POPPRNT
POPPRNT.C
/*---------------------------------------------------------------------
POPPRNT.C -- Popup Editor Printing Functions
-----------------------------------------------------------------------*/
#include
#include
#include "resource.h"
BOOL bUserAbort ;
HWND hDlgPrint ;
BOOL CALLBACK PrintDlgProc ( HWND hDlg, UINT msg, WPARAM wParam,LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG :
EnableMenuItem (GetSystemMenu (hDlg, FALSE), SC_CLOSE, MF_GRAYED) ;
return TRUE ;
case WM_COMMAND :
bUserAbort = TRUE ;
EnableWindow (GetParent (hDlg), TRUE) ;
DestroyWindow (hDlg) ;
hDlgPrint = NULL ;
return TRUE ;
}
return FALSE ;
}
BOOL CALLBACK AbortProc (HDC hPrinterDC, int iCode)
{
MSG msg ;
while (!bUserAbort && PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
if (!hDlgPrint || !IsDialogMessage (hDlgPrint, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
return !bUserAbort ;
}
BOOL PopPrntPrintFile (HINSTANCE hInst, HWND hwnd, HWND hwndEdit,
PTSTR szTitleName)
{
static DOCINFO di = { sizeof (DOCINFO) } ;
static PRINTDLG pd ;
BOOL bSuccess ;
int yChar, iCharsPerLine, iLinesPerPage, iTotalLines,
iTotalPages, iPage, iLine, iLineNum ;
PTSTR pstrBuffer ;
TCHAR szJobName [64 + MAX_PATH] ;
TEXTMETRIC tm ;
WORD iColCopy, iNoiColCopy ;
// Invoke Print common dialog box
pd.lStructSize = sizeof (PRINTDLG) ;
pd.hwndOwner = hwnd ;
pd.hDevMode = NULL ;
pd.hDevNames = NULL ;
pd.hDC = NULL ;
pd.Flags = PD_ALLPAGES | PD_COLLATE |
PD_RETURNDC | PD_NOSELECTION ;
pd.nFromPage = 0 ;
pd.nToPage = 0 ;
pd.nMinPage = 0 ;
pd.nMaxPage = 0 ;
pd.nCopies = 1 ;
pd.hInstance = NULL ;
pd.lCustData = 0L ;
pd.lpfnPrintHook = NULL ;
pd.lpfnSetupHook = NULL ;
pd.lpPrintTemplateName = NULL ;
pd.lpSetupTemplateName = NULL ;
pd.hPrintTemplate = NULL ;
pd.hSetupTemplate = NULL ;
if (!PrintDlg (&pd))
return TRUE ;
if (0 == (iTotalLines = SendMessage (hwndEdit, EM_GETLINECOUNT, 0, 0)))
return TRUE ;
// Calculate necessary metrics for file
GetTextMetrics (pd.hDC, &tm) ;
yChar = tm.tmHeight + tm.tmExternalLeading ;
iCharsPerLine = GetDeviceCaps (pd.hDC, HORZRES) / tm.tmAveCharWidth ;
iLinesPerPage = GetDeviceCaps (pd.hDC, VERTRES) / yChar ;
iTotalPages = (iTotalLines + iLinesPerPage - 1) / iLinesPerPage ;
// Allocate a buffer for each line of text
pstrBuffer = malloc (sizeof (TCHAR) * (iCharsPerLine + 1)) ;
// Display the printing dialog box
EnableWindow (hwnd, FALSE) ;
bSuccess = TRUE ;
bUserAbort = FALSE ;
hDlgPrint = CreateDialog (hInst, TEXT ("PrintDlgBox"),
hwnd, PrintDlgProc) ;
SetDlgItemText (hDlgPrint, IDC_FILENAME, szTitleName) ;
SetAbortProc (pd.hDC, AbortProc) ;
// Start the document
GetWindowText (hwnd, szJobName, sizeof (szJobName)) ;
di.lpszDocName = szJobName ;
if (StartDoc (pd.hDC, &di) > 0)
{
// Collation requires this loop and iNoiColCopy
for (iColCopy = 0 ;
iColCopy < ((WORD) pd.Flags & PD_COLLATE ? pd.nCopies : 1) ;
iColCopy++)
{
for (iPage = 0 ; iPage < iTotalPages ; iPage++)
{
for (iNoiColCopy = 0 ;
iNoiColCopy < (pd.Flags & PD_COLLATE ? 1 : pd.nCopies);
iNoiColCopy++)
{
// Start the page
if (StartPage (pd.hDC) < 0)
{
bSuccess = FALSE ;
break ;
}
// For each page, print the lines
for (iLine = 0 ; iLine < iLinesPerPage ; iLine++)
{
iLineNum = iLinesPerPage * iPage + iLine ;
if (iLineNum > iTotalLines)
break ;
*(int *) pstrBuffer = iCharsPerLine ;
TextOut (pd.hDC, 0, yChar * iLine, pstrBuffer,
(int) SendMessage (hwndEdit, EM_GETLINE,
(WPARAM) iLineNum, (LPARAM) pstrBuffer));
}
if (EndPage (pd.hDC) < 0)
{
bSuccess = FALSE ;
break ;
}
if (bUserAbort)
break ;
}
if (!bSuccess || bUserAbort)
break ;
}
if (!bSuccess || bUserAbort)
break ;
}
}
else
bSuccess = FALSE ;
if (bSuccess)
EndDoc (pd.hDC) ;
if (!bUserAbort)
{
EnableWindow (hwnd, TRUE) ;
DestroyWindow (hDlgPrint) ;
}
free (pstrBuffer) ;
DeleteDC (pd.hDC) ;
return bSuccess && !bUserAbort ;
}
与POPPAD尽量利用Windows高阶功能来简化程序的方针一致,POPPRNT.C文件展示了使用PrintDlg函数的方法。这个函数包含在通用对话框链接库(common dialog box library)中,使用一个PRINTDLG型态的结构。
通常,程序的「File」菜单中有个「Print」选项。当使用者选中「Print」选项时,程序可以初始化PRINTDLG结构的字段,并呼叫PrintDlg。
PrintDlg显示一个对话框,它允许使用者选择打印页的范围。因此,这个对话框特别适用于像POPPAD这样能打印多页文件的程序。这种对话框同时也给出了一个确定副本份数的编辑区和名为「Collate(逐份打印)」的复选框。「逐份打印」影响着多个副本页的顺序。例如,如果文件是3页,使用者要求打印三份副本,则这个程序能以两种顺序之一打印它们。选择逐份打印后的副本的页码顺序为1、2、3、1、2、3、1、2、3,未选择逐份打印的副本的页码顺序是1、1、1、2、2、2、3、3、3。程序在这里应负起的责任就是以正确的顺序打印副本。
这个对话框也允许使用者选择非内定打印机,它包括一个标记为「Properties」的按钮,可以启动设备模式对话框。这样,至少允许使用者选择直印或横印。
从PrintDlg函数传回后,PRINTDLG结构的字段指明打印页的范围和是否对多个副本进行逐份打印。这个结构同时也给出了准备使用的打印机设备内容句柄。
在POPPRNT.C中,PopPrntPrintFile函数(当使用者在「File」菜单里选中「Print」选项时,它由POPPAD呼叫)呼叫PrintDlg,然后开始打印文件。PopPrntPrintFile完成某些计算,以确定一行能容纳多少字符和一页能容纳多少行。这个程序涉及到呼叫GetDeviceCaps来确定页的分辨率,呼叫GetTextMetrics来确定字符的大小。
这个程序通过发送一条EM_GETLINECOUNT消息给编辑控件来取得文件中的总行数(在变量iTotalLines中)。储存各行内容的缓冲区配置在局部内存中。对每一行,缓冲区的第一个字被设定为该行中字符的数目。把EM_GETLINE消息发送给编辑控件可以把一行复制到缓冲区中,然后用TextOut把这一行送到打印机设备内容中(POPPRNT.C还没有聪明到对超出打印宽度的文字换到下一行去处理。在第十七章我们会讨论这种文字绕行的技术)。
为了确定副本份数,应注意打印文字的处理方式包括两个for循环。第一个for循环使用了一个叫作iColCopy的变量,当使用者指定将副本逐份打印时,它将会起作用。第二个for循环使用了一个叫作iNonColCopy的变量,当不对副本进行逐份打印时,它将起作用。
如果StartPage或EndPage传回一个错误,或者如果bUserAbort为TRUE,那么这个程序退出增加页号的那个for循环。如果放弃程序的传回值是FALSE,则EndPage不传回错误。正是由于这个原因,在下一页开始之前,要直接测试bUserAbort。如果没有报告错误,则进行EndDoc呼叫:
if (!bError)
EndDoc (hdcPrn) ;
您可能想通过打印多页文件来测试POPPAD。您可以从打印任务窗口中监视打印进展情况。在GDI处理完第一个EndPage呼叫之后,首先打印的文件将显示在打印任务窗口中。此时,后台打印程序开始把文件发送到打印机。然后,如果在POPPAD中取消打印作业,那么后台打印程序将终止打印,这也就是放弃程序传回FALSE的结果。当文件出现在打印任务窗口中,您也可以透过从「Document」菜单中选择「Cancel Printing」来取消打印作业,在这种情况下, POPPAD中的EndPage呼叫会传回一个错误。
Windows的程序设计的新手经常会抱住AbortDoc函数不放,但实际上这个函数几乎不在打印中使用。像在POPPAD中看到的那样,使用者几乎随时可以取消打印作业,或者通过POPPAD的打印对话框及通过打印任务窗口。这两种方法都不需要程序使用AbortDoc函数。 POPPAD中允许AbortDoc的唯一时刻是在对StartDoc的呼叫和对EndPage的第一个呼叫之间,但是程序很快就会执行过去,以至不再需要AbortDoc。
图13-3显示出正确打印多页文件之打印函数的呼叫顺序。检查bUserAbort的值是否为TRUE的最佳位置是在每个EndPage函数之后。只有当对先前的打印函数的呼叫没有产生错误时,才使用EndDoc函数。实际上,如果任何一个打印函数的呼叫出现错误,那么表演就结束了,同时您也可以回家了。
图13-3 打印一个文件时的函数呼叫顺序
-
输入输出
2021-07-18 00:27:12输入输出指的是产品输入输出视频信号的端口,比较常见的是S端子和复合视频端口。中文名输入输出外文名in-out定义产品输入输出视频信号的端口常见类型S端子和...一般的输入输出设备有打印机、硬盘、键盘和鼠标。实际...输入输出指的是产品输入输出视频信号的端口,比较常见的是S端子和复合视频端口。
中文名
输入输出
外文名
in-out定 义
产品输入输出视频信号的端口
常见类型
S端子和复合视频端口
S端子
Separate Video
输入输出简介
编辑
语音
输入输出(input/output,I/O),读作“eye-oh”,描述的是在计算机上输入输出数据的操作系统、程序或设备。一般的输入输出设备有打印机、硬盘、键盘和鼠标。实际上,有些设备只有输入功能,如键盘和鼠标;有些设备只有输出功能,如打印机;还有些设备具有输入输出2种功能,如硬盘、磁碟和可写性只读光盘(CD-ROM)。[1]
输入输出S端子
编辑
语音
S端子也就是Separate Video,而“Separate”的中文意思就是“分离”。它是在AV接口的基础上将色度信号C 和亮度信号Y进行分离,再分别以不同的通道进行传输,减少影像传输过程中的“分离”、“合成”的过程,减少转化过程中的损失,以得到最佳的显示效果。但S-Video仍要将两路色差信号混合为一路色度信号C进行传输,然后再在显示设备内解码进行处理,这样多少仍会带来一定信号损失而产生失真(这种失真很小) ,而且由于混合导致色度信号的带宽也有一定的限制。S-Video虽不是最好的,但考虑到目前的市场状况和综合成本等其它因素,它还是应用最普遍的视频接口。
输入输出RCA接口
编辑
语音
复合视频接口采用RCA接口,RCA接口是目前电视设备上应用最广泛的接口,几乎每台电视上都提供了此类接口,用于视频输入。虽然AV接口实现了音频和视频的分离传输,这就避免了因为音/视频混合干扰而导致的图像质量下降,但由于AV接口传输的仍然是一种亮度/色度(Y/C)混合的视频信号,仍然需要显示设备对其进行亮/色分离和色度解码才能成像,这种先混合再分离的过程必然会造成色彩信号的损失,色度信号和亮度信号也会有很大的机会相互干扰,从而影响最终输出的图像质量。
输入输出输入输出设备
编辑
语音
输入输出设备(IO设备),是数据处理系统的关键外部设备之一,可以和计算机本体进行交互使用。如:键盘、写字板、麦克风、音响、显示器等。因此输入输出设备起了人与机器之间进行联系的作用。
输入输出输入设备
编辑
语音
输入设备是向计算机输入数据和信息的设备,是计算机与用户或其他设备通信的桥梁,是用户和计算机系统之间进行信息交换的主要装置之一。输入设备的任务是把数据、指令及某些标志信息等输送到计算机中去。键盘、鼠标、摄像头、扫描仪、光笔、手写输入板、游戏杆、语音输入装置等都属于输入设备(Input Device ),是人或外部与计算机进行交互的一种装置,用于把原始数据和处理这些数据的程序输入到计算机中。
计算机能够接收各种各样的数据,既可以是数值型的数据,也可以是各种非数值型的数据,如图形、图像、声音等都可以通过不同类型的输入设备输入到计算机中,进行存储、处理和输出。计算机的[1]输入设备按功能可分为下列几类:
●字符输入设备:键盘;
● 光学阅读设备:光学标记阅读机、光学字符阅读机;
● 图形输入设备:鼠标器、操纵杆、光笔;
● 图像输入设备:数码像机、扫描仪、传真机;
● 模拟输入设备:语言模数转换识别系统。
(如光电纸带输入器、卡片输入器、光学字符读出器、磁带输入装备、汉字输入装备、鼠标等)将数据、程序和控制信息送入计算机内。
输入输出输出设备
编辑
语音
输出设备(Output Device)是把计算或处理的结果或中间结果以人能识别的各种形式,如数字、符号、字母等表示出来,因此输入输出设备起了人与机器之间进行联系的作用。常见的有显示器、打印机、绘图仪、影像输出系统、语音输出系统、磁记录设备等。
显示器是计算机必不可少的一种图文[2]输出设备,它的作用是将数字信号转换为光信号,使文字与图形在屏幕上显示出来;打印机也是PC机上的一种主要输出设备,它把程序、数据、字符图形打印在纸上。
控制台打字机、光笔、显示器等既可作输入设备、也可作输出设备。
输入输出设备(I/O)起着人和计算机、设备和计算机、计算机和计算机的联系作用。
输入输出即插即用
编辑
语音
说道I/O,就不得不说起即插即用。随着新技术的发展,人们已经厌倦使用驱动光盘来安装新设备的驱动。而即插即用的新技术的来临,则解决了这个问题。
随着微软推出WINDOWS95/98,它也宣传了WINDOWS95/98为用户提供的底层硬件资源的智能管理能力,即WINDOWS95/98具有即插即用(PNP:PlugandPlay)的功能,由此即插即用才为人们广泛重视。
输入输出标准输入输出
编辑
语音
执行一个shell命令行时通常会自动打开三个标准文件,即标准输入文件(stdin),通常对应终端的键盘;标准输出文件(stdout)和标准错误输出文件(stderr),这两个文件都对应终端的屏幕。进程将从标准输入文件中得到输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。
输入输出输入输出系统
编辑
语音
输入输出系统是计算机系统中的主机与外部进行通信的系统。它由外围设备和输入输出控制系统两部分组成,是计算机系统的重要组成部分。外围设备包括输入设备、输出设备和磁盘存储器、磁带存储器、光盘存储器等。从某种意义上也可以把磁盘、磁带和光盘等设备看成一种输入输出设备,所以输入输出设备与外围设备这两个名词经常是通用的。在计算机系统中,通常把处理机和主存储器之外的部分称为输入输出系统,输入输出系统的特点是异步性、实时性和设备无关性。
输入输出原理
编辑
语音
从信息传输速率来讲,相差也很悬殊。如果把高速工作的主机同不同速度工作的外围设备相连接,保证主机与外围设备在时间上同步要讨论的外围设备的定时问题。
输入/输出设备同CPU交换数据的过程:
输入过程:
(1)CPU把一个地址值放在地址总线上,这一步将选择某一输入设备;
(2)CPU等候输入设备的数据成为有效;
(3)CPU从数据总线读入数据,并放在一个相应的寄存器中。
输出过程:
(1)CPU把一个地址值放在地址总线上,选择输出设备;
(2)CPU把数据放在数据总线上;
(3)输出设备认为数据有效,从而把数据取走。
输入输出定时方式
编辑
语音
由于输入/输出设备本身的速度差异很大,因此,对于不同速度的外围设备,需要有不同的定时方式,总的说来,CPU与外围设备之间的定时,有以下三种情况。
1.速度极慢或简单的外围设备
对这类设备,如机械开关、显示二极管等等,CPU总是能足够快地作出响应。换句话说,对机械开关来讲,CPU可以认为输入的数据一直有效,因为机械开关的动作相对CPU的速度来讲是非常慢的,对显示二极管来讲,CPU可以认为输出一定准备就绪,因为只要给出数据,显示二极管就能进行显示,所以,在这种情况下,CPU只要接收或发送数据就可以了。
2.慢速或中速的外围设备
由于这类设备的速度和CPU的速度并不在一个数量级,或者由于设备(如键盘)本身是在不规则时间间隔下操作的,因此,CPU与这类设备之间的数据交换通常采用异步定时方式。其定时过程如下:
如果CPU从外设接收一个字,则它首先询问外设的状态,如果该外设的状态标志表明设备已“准备就绪”,那么CPU就从总线上接收数据。CPU在接收数据以后,发出输入响应信号,告诉外设已经把数据总线上的数据取走。然后,外设把“准备就绪”的状态标志复位,并准备下一个字的交换。如果CPU起先询问外设时,外设没有“准备就绪”,那么它就发出表示外设“忙”的标志。于是,CPU将进入一个循环程序中等待,并在每次循环中询问外设的状态,一直到外设发出“准备就绪”信号以后,才从外设接收数据。
CPU发送数据的情况也与上述情况相似,外设先发出请求输出信号,而后,CPU询问外设是否准备就绪。如果外设已准备就绪,CPU便发出准备就绪信号,并送出数据。外设接收数据以后,将向CPU发出“数据已经取走”的通知。
通常,把这种在CPU和外设间用问答信号进行定时的方式叫做应答式数据交换。
3.高速的外围设备
由于这类外设是以相等的时间间隔操作的,而CPU也是以等间隔的速率执行输入/输出指令的,因此,这种方式叫做同步定时方式。一旦CPU和外设发生同步,它们之间的数据交换便靠时钟脉冲控制来进行。
输入输出控制方式
编辑
语音
程序查询方式和程序中断方式适用于数据传输率比较低的外围设备,而DMA方式、通道方式和PPU方式适用于数据传输率比较高的设备。在单片机和微型机中多采用程序查询方式、程序中断方式和DMA方式。通道方式和PPU方式大都用在中、大型计算机中。
在计算机系统中,CPU管理外围设备也有几种类似的方式:
程序查询方式是早期计算机中使用的一种方式。数据在CPU和外围设备之间的传送完全靠计算机程序控制,查询方式的优点是CPU的操作和外围设备的操作能够同步,而且硬件结构比较简单。但问题是,外围设备动作很慢,程序进入查询循环时将白白浪费掉CPU很多时间。这种情况同上述例子中第一种方法相仿,CPU此时只能等待,不能处理其他业务。即使CPU采用定期地由主程序转向查询设备状态的子程序进行扫描轮询的办法,CPU宝贵资源的浪费也是可观的。因此当前除单片机外,很少使用程序查询方式。
中断是外围设备用来“主动”通知CPU,准备送出输入数据或接收输出数据的一种方法。通常,当一个中断发生时,CPU暂停它的现行程序,而转向中断处理程序,从而可以输入或输出一个数据。当中断处理完毕后,CPU又返回到它原来的任务,并从它停止的地方开始执行程序。这种方式和我们前述例子的第二种方法相类似。可以看出,它节省了CPU宝贵的时间,是管理I/O操作的一个比较有效的方法。中断方式一般适用于随机出现的服务,并且一旦提出要求,应立即进行。同程序查询方式相比,硬件结构相对复杂一些,服务开销时间较大。
3直接内存访问(DMA)方式
用中断方式交换数据时,每处理一次I/O交换,约需几十微秒到几百微秒。对于一些高速的外围设备,以及成组交换数据的情况,仍然显得速度太慢。直接内存访问(DMA)方式是一种完全由硬件执行I/O交换的工作方式。这种方式既考虑到中断响应,同时又要节约中断开销。此时,DMA控制器从CPU完全接管对总线的控制,数据交换不经过CPU,而直接在内存和外围设备之间进行,以高速传送数据。这种方式和前述例子的第三种方法相仿,主要优点是数据传送速度很高,传送速率仅受到内存访问时间的限制。与中断方式相比,需要更多的硬件。DMA方式适用于内存和高速外围设备之间大批数据交换的场合。
DMA方式的出现已经减轻了CPU对I/O操作的控制,使得CPU的效率有显著的提高,而通道的出现则进一步提高了CPU的效率。这是因为,CPU将部分权力下放给通道。通道是一个具有特殊功能的处理器,某些应用中称为输入输出处理器(IOP),它可以实现对外围设备的统一管理和外围设备与内存之间的数据传送。这种方式与前述例子的第四种方法相仿,大大提高了CPU的工作效率。然而这种提高CPU效率的办法是以花费更多硬件为代价的。
外围处理机(PPU)方式是通道方式的进一步发展。由于PPU基本上独立于主机工作,它的结构更接近一般处理机,甚至就是微小型计算机。在一些系统中,设置了多台PPU,分别承担I/O控制、通信、维护诊断等任务。从某种意义上说,这种系统已变成分布式的多机系统[2]
。
参考资料
1.
输入输出
.TechTarget信息化[引用日期2015-07-23]
2.
刘国海, 戴先中. 交流电机输入输出自适应解耦控制[J]. 电工技术学报, 2002, 17(1):12-16.
-
佳能LBP2900+打印机驱动
2021-05-18 13:34:08佳能LBP2900+打印机驱动官方版佳能LBP2900+打印机驱动官方版是款由佳能官方为大家打造的LBP2900型号的打印机驱动程序。佳能LBP2900+打印机驱动操作简单、功能实用,能够有效解决打印机无法识别设备的问题。佳能... -
excle正在访问打印机!连接局域网打印机在excel中预览,excel自动就关闭是怎么回事
2021-08-08 10:51:41excel2010自动访问打印机怎样取消1,把默认打印机设置成虚拟打印机试试。2.到文件——选项——加载项看下,是不是打印机随机启动了。希望帮到你!自己到文件——选项——加载项看下,是不是打印机随机启动了。Excel ... -
佳能lbp2900打印机驱动下载
2021-05-18 12:14:49佳能LBP2900打印机驱动是Canon佳能LBP2900 激光打印机的驱动程序。佳能LBP2900打印机驱动可以有效的解决佳能lbp2900打印机无法打印的问题,可以让你快速简便的打印所需要的文件,功能实用,操作简单,有需要的小伙伴... -
HP5000打印机控制面板菜单解释
2021-07-15 04:53:05这是控制打印机的最方便途径,并将取代打印机控制面板的设置。参见软件相关的帮助文件,或者通过访问打印机驱动程序来获得更多信息。也可以通过更改打印机控制面板的设置来控制打印机。使用控制面板可访问打印机驱动... -
佳能LBP2900打印机驱动安装使用 USB无法识别的解决方法步骤
2021-05-14 18:14:41佳能LBP2900打印机驱动可以有效的解决佳能lbp2900打印机无法打印的问题,可以让你快速简便的打印所需要的文件,功能实用,操作简单佳能LBP2900打印机驱动功能特色1、佳能LBP2900打印机驱动操作简单,打印速度快,... -
插上无线翅膀 兄弟1218W激光打印机评测
2021-08-12 03:21:591旧貌换新颜 插上无线的翅膀【中关村在线办公打印频道原创】在智能移动设备越来越...那么有没有一个“经济实惠量又足”的打印机,能满足这些场景的应用需求呢?答案是有的。●旧貌换新颜插上无线的翅膀用过兄弟1118... -
打印虎原创RepRapPrusai33D打印机校准图解教程系列之二.pdf
2020-12-29 05:46:45【打印虎原创】RepRap_Prusa_i3_3D 打印机校准图解教程系列之二在打印虎的上一篇教程, 【打印虎原创】Prusa_i3_3D 打印机校准图解教程-基础篇中,我们介绍了最重要、最基础的RepRap Prusa i3 3D 打印机校准步骤。... -
计算机组成原理——输入/输出系统(I/O接口&外设&中断)
2022-04-06 20:03:55提示:文章写完后,目录可以自动...③显示器、打印机――输出设备 ④可统称“外部设备” 2.主机如何与l/O设备进行交互? (1)I/O接口:又称I/O控制器(I/O Controller)、设备控制器,负责协调主机与外部设备之间的数据. -
关于java操作zebraZT230打印机
2021-11-17 12:02:03使用java调用斑马打印机打印条码 -
3D打印机 G代码解释
2022-02-20 08:23:59执行G代码: G0 – 快速定位 G1 – 直线运动X,Y,Z,E G2 - CW ARC G3 - CCW ARC G4 - 暂停s或p ...G10 - 缩回根据M207的设置长丝 ...G30 - 单个Z探头,探头床上当前的XY位置。...G92 - 设置当前位置cordinates给出 -
3D打印机改装雕刻机经验分享
2019-03-02 23:28:16/* 大概是大二的时候,为了参加学校的比赛,组装了一台无人机,只有外壳是自己用PRO/E设计的,然后用3D打印机打出来,其他的电子设备都是买的现成的,包括螺旋桨。无人机飞起来了,但是一轮面试都没有通过。虽然有些... -
c#代码实现打印机打印文件
2020-02-27 22:12:16在windows应用程序中文档的打印是一项非常重要的功能,在以前一直是一个非常复杂的工作,...1 打印设置 设置打印机的一些参数比如更改打印机驱动程序等 2 页面设置 设置页面大小纸张类型等 3 打印预览 类似... -
C/C++之标准输入输出
2017-03-10 18:02:39开销小,因为puts()只能输出字符串,不能输出数字或进行格式转换,因而puts()用的空间少且速度比printf()快。因此函数puts()经常用于代码优化。操作失败时返回EOF,正常返回非负值。 int printf(const char* ... -
3D打印机DIY之六------G代码命令
2019-12-14 10:59:42G代码是用于指导3D打印机怎么动作的文件,其实最主要就是指导打印机的3轴电机和挤出机如何动作,比如某个电机正转多少、速度多少。 3D打印机本质就是gcode的执行器,同样的雕刻机、激光雕刻机也是。我们把一张平面... -
首款能充粉的打印机来了 惠普1005w一体机评测
2020-12-31 13:08:371回顶部[PConline 专业评测] 不可否认,对于那些每个月有大批量打印...难道就没有一款可以给打印机补充碳粉并且打印成本可以得到控制的打印产品吗?的确,给打印机充粉这件事在我们的固有思维中确实很难想象。不过... -
第 5 章 输入输出管理
2021-03-05 22:48:04第 5 章 输入输出管理 1、I/O设备的基本概念和分类 1.1、思维导图 操作系统需要管理的资源 I/O 设备的基本概念和分类 1.2、什么是I/O设备 “I/O”就是“输入/输出”(Input/Output) I/O设备就是可以将数据... -
电脑打印机显示没有安装驱动怎么办啊
2021-07-16 00:58:341. 为什么已安装打印机,电脑显示没有装,怎么解决1、你的安装...扩展资料:打印机(Printer) 是计算机的输出设备之一,用于将计算机处理结果打印在相关介质上。衡量打印机好坏的指标有三项:打印分辨率,打印速度和噪... -
计算机基础:7、计算机的输入输出设备
2020-09-13 13:49:33显示器、打印机、投影仪 2、输入输出接口的通用设计 2.1、输入输出接口需要完成的工作 读取数据功能 向设备发送数据 设备有没有被占用(被占用如何处理未被占用如何处理) 设备是否已经连接 设备是否已经启动 2.2、... -
c++输入输出流
2022-03-23 10:15:11在输出操作中,字节从内存流向设备(例如,显示屏、打印机、磁盘驱动器、网络连接等)。 应用程序通过字节传达信息。字节可以组成字符、原始数据、图形图像、数字语音、数字视频或者任何应用程序所需要的其他信息。... -
操作系统—输入输出系统
2021-12-24 21:31:56输入输出系统 1 I/O系统的功能、模型和接口 I/O系统管理对象主要是I/O设备和相应的设备控制器,主要任务是完成用户提出的I/O请求,提高I/O速率,提高设备利用率 1.1 I/O系统的基本功能 第一第二功能为了方便用户... -
打印机编程
2015-03-23 16:44:48在大多数的Internet及多媒体应用软件中,为使画面下载速度增快,大都是采 用72dpi或75dpi的低解析度影像,如果仅从屏幕上观看,画质还可以,但是当 使用解析度较高的打印机打印出来时,就会发生如锯齿状或是... -
计算机操作系统(第四版)之输入输出系统要点梳理
2018-08-01 22:39:14输入输出系统 输入输出系统 -
超硬核!操作系统学霸笔记,考试复习面试全靠它
2021-03-22 18:43:49再释放PCB 进程管理中的数据结构 操作系统中用于管理控制的数据结构:内存表,设备表,文件表,进程表(程序控制快PCB) 进程控制块PCB的作用: 1)作为独立运行基本单位的标志 2)能实现间断性的运行方式 3)提供... -
python 3d打印机_如何使用Python构建自己的CNC控制器和3D打印机
2020-07-15 18:55:19python 3d打印机by Nikolay Khabarov 通过尼古拉·哈巴罗夫(Nikolay Khabarov) 如何使用Python构建自己的CNC控制器和3D打印机 (How you can use Python to build your own CNC controller and 3D printer) This ... -
串口控制惠普打印机
2018-11-15 13:35:42在项目开发中,经常要使用打印机,打印出检测的结果,在本项目中使用的是惠普打印机,输出结果打印在A4纸上面。 本次使用U2P-016串口打印模块和惠普激光打印机P1106,编写了600行代码就搞定了,哈哈。先上图 程序... -
超硬核!数据结构学霸笔记,考试面试吹牛就靠它
2021-03-26 11:11:21取自于某个特定对象的集合 输出:一个或多个输出 设计要求:正确性、可读性、健壮性、效率与低存储量需求。 执行频度概念:是指通过统计算法的每一条指令的执行次数得到的。 执行频度=算法中每一条语句执行次数的和 ...