本文转载于http://www.cnblogs.com/wz19860913/archive/2008/08/16/1269460.html,原作写的更加详细并在此谢谢。我转载至我的博客以方便查询学习,并加了自己的一点理解。
设备的输入输出,即设备I/O,可以分为“同步”和“异步”两种方式。同步的设备I/O,调用的API函数总是等到设备I/O完成才返回。而异步的设备I/O,可以通过多种方法来实现,但是其根本原理是得到“设备I/O的完成通知”。目前我的理解说的其实就在于调用函数(即设备IO的API函数,设备输入输出的API函数),该API函数调用之后线程或者程序处于阻塞状态,并直到设备I/O完成才返回的称为同步I/O,相反的如果设备IO的API函数调用之后立即返回的称为异步I/O。
本篇主要讨论如何打开和关闭一个设备。注意,这里的设备,不是指像键盘、显示器那种实体。而是一种抽象的概念,指一种与外界通信的对象,可以接受外界的输入,也可以对外界的请求作出响应,称之为设备I/O。这个概念比较抽象,这些设备往往和某个内核对象关联。要打开这些设备,就要创建相关的内核对象。
这些设备包括文件、目录、逻辑磁盘驱动、物理磁盘驱动、串行端口、并行端口、邮槽、管道、套接字、控制台(如下表):
设备 | 主要用途 |
文件 | 保存数据 |
目录 | 属性和文件压缩设置 |
逻辑磁盘驱动 | 磁盘格式化 |
物理磁盘驱动 | 访问分区表 |
串行端口 | 串行传输数据 |
并行端口 | 多位数据同时传输,主要是将数据传输给打印机 |
油槽 | 一对多传输数据,往往适用于一个网络中的一台计算机向其他机器发送数据 |
命名管道 | 一对一传输数据,往往适用于一个网络中的一台计算机向其他机器发送数据 |
匿名管道 | 一对一传输数据,适用于简单的数据传输,不适用于网络 |
套接字 | 以流或数据报的形式发送数据,适用于一个网络中的通信 |
控制台 | 一个文字窗口显示缓冲区 |
要使用这些设备,你首先应该打开这些设备。
Windows努力隐藏这些设备的差异,所以,很多设备的打开的I/O工作可以通过同一个API函数完成,如下表:
设备 | 经常用来打开设备的API函数和用法 |
文件 | CreateFile —— 打开设备的函数。 将参数pszName是一个文件路径名。 |
目录 | CreateFile —— 打开设备的函数。 将参数pszName是一个目录名。 Windows允许你打开一个目录,通过使用参数FILE_ FLAG_BACKUP_SEMANTICS旗标来呼叫CreateFile函数。 这是目录属性,即文件夹属性,比如正常、隐藏、系统、只读等。 |
逻辑磁盘驱动 | CreateFile--打开设备的函数 将参数pszName设置为字符串“\\ . \x:”。比如要打开C盘,就将其设置为“\\.\C:”。 |
物理磁盘驱动 | CreateFile---打开设备的函数。 将参数pszName设置为“\\.\PHYSICALDRIVEx”。比如打开第一个物理硬盘扇区:可以这么调用CreateFile函数: CreateFile(TEXT("\\.\PHYSICALDRIVE0"), ...); 这样就可以打开一个物理磁盘驱动,并且可以直接访问硬盘分区表。 但是打开物理磁盘驱动是存在潜在危险的,特别是当错误的写入,会造成物理磁盘内容的破坏。 |
串行端口 | CreateFile--打开设备的函数。 将参数pszName设置为“COMx”,比如打开COM1串口设备,只要将其设置为“COM1”。 |
并行端口 | CreateFile —— 打开设备的函数。 将参数pszName设置为“LPTx”,比如打开LPT1并行端口,将其设置为“LPT1”。 |
邮槽(服务器端) | CreateMailslot —— 打开设备的函数。 将参数pszName设置为“\\.\mailslot\mailslotname”,其中,“mailsoltname”是为邮槽取的名字,可以任意,前面的字符串是固定的。 |
邮槽(客户端) | CreateFile —— 打开设备的函数。 将参数pszName设置为“\\servername\mailslot\mailslotname”,其中,“mailsoltname”是为邮槽取的名字,可以任意,前面的字符串是固定的。 |
命名管道(服务器端) | CreateFile —— 打开设备的函数。 将参数pszName设置为“\\.\pipe\pipename”,其中,“pipename”是为命名管道取的名字,可以任意,前面的字符串是固定的。 |
命名管道(客户端) | CreateFile —— 打开设备的函数。 将参数pszName设置为“\\servername\pipe\pipename”,其中,“pipename”是为命名管道取的名字,可以任意,前面的字符串是固定的。 |
匿名管道 | CreatePipe--打开设备的函数。 无论是客户端还是服务器端都以该函数创建或打开匿名管道 |
套接字 | socket--创建一个套接字描述符accept,或AcceptEx。 |
控制台 | CreateConsoleScreenBuffer,GetStdHandle--打开设备的函数 |
从上表可以发现,很多设备都以CreateFile函数来创建和打开。这个函数以后会讲。
打开了设备,你得到了一个设备的句柄,你就可以通过该句柄使用其他函数,来对相关设备进行设置。
比如,现在打开了一个串行端口,然后要设置它的传输波特率:
BOOL SetCommConfig(
HANDLE hCommDev,
LPCOMMCONFIG pCC,
DWORD dwSize);
或者,你获得了一个邮槽句柄,可以设置读取数据的等待时间:
BOOL SetMailslotInfo(
HANDLE hMailslot,
DWORD dwReadTimeout);
最后,不要忘记关闭句柄,从而正确地关闭设备:
BOOL CloseHandle(HANDLE hObject);
int closesocket(SOCKET s); //套接字的关闭
如果你有了一个设备句柄,你可以调查它的设备类型,通过使用GetFileType函数,该函数的返回值表明了它是一个什么类型的设备,可以参考MSDN。
DWORD GetFileType(HANDLE hDevice);
好了,现在让我们来讨论一下CreateFile函数:
HANDLE CreateFile(
PCTSTR pszName, //指明设备类型或一个特定的设备实体
DWORD dwDesiredAccess, //访问限制
DWORD dwShareMode, //共享方式
PSECURITY_ATTRIBUTES psa, //安全描述结构
DWORD dwCreationDisposition, //创建和打开方式
DWORD dwFlagsAndAttributes, //属性旗标,与缓冲区和文件操作属性有关
HANDLE hFileTemplate); //设备模版,一个设备句柄
该函数成功,返回句柄,失败返回INVALID_HANDLE_VALUE(值为-1)。
如果设置了最后一个参数hFileTemplate,那么就照着这个参数所代表的设备,创建一个属性相同的设备,当然,这个参数所表示的设备要具有“可读”的权限,即有GENERIC_READ访问权限。
至于该函数的具体用法,可以参看本书或MSDN。