当前位置:文档之家› 设备驱动接口函数DeviceIoControl使用实例

设备驱动接口函数DeviceIoControl使用实例

设备驱动接口函数DeviceIoControl使用实例
设备驱动接口函数DeviceIoControl使用实例

设备驱动接口函数DeviceIoControl使用实例

1.通过API访问设备驱动程序

Q 在NT/2000/XP中,我想用VC编写应用程序访问硬件设备,如获取磁盘参数、读写绝对扇区数据、测试光驱实际速度等,该从哪里入手呢?

A 在NT/2000/XP中,应用程序可以通过API函数DeviceIoControl来实现对设备的访问—获取信息,发送命令,交换数据等。利用该接口函数向指定的设备驱动发送正确的控制码及数据,然后分析它的响应,就可以达到我们的目的。

DeviceIoControl的函数原型为

BOOL DeviceIoControl(

HANDLE hDevice, // 设备句柄

DWORD dwIoControlCode, // 控制码

LPVOID lpInBuffer, // 输入数据缓冲区指针

DWORD nInBufferSize, // 输入数据缓冲区长度

LPVOID lpOutBuffer, // 输出数据缓冲区指针

DWORD nOutBufferSize, // 输出数据缓冲区长度

LPDWORD lpBytesReturned, // 输出数据实际长度单元长度

LPOVERLAPPED lpOverlapped // 重叠操作结构指针

);

设备句柄用来标识你所访问的设备。

发送不同的控制码,可以调用设备驱动程序的不同类型的功能。在头文件winioctl.h中,预定义的标准设备控制码,都以IOCTL或FSCTL开头。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数(介质类型、柱面数、每柱面磁道数、每磁道扇区数等)的控制码,FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。

输入输出数据缓冲区是否需要,是何种结构,以及占多少字节空间,完全由不同设备的不同操作类型决定。在头文件winioctl.h中,已经为标准设备预定义了一些输入输出数据结构。重叠操作结构指针设置为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计。

Q 设备句柄是从哪里获得的?

A 设备句柄可以用API函数CreateFile获得。它的原型为

HANDLE CreateFile(

LPCTSTR lpFileName, // 文件名/设备路径

DWORD dwDesiredAccess, // 访问方式

DWORD dwShareMode, // 共享方式

LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符指针

DWORD dwCreationDisposition, // 创建方式

DWORD dwFlagsAndAttributes, // 文件属性及标志

HANDLE hTemplateFile // 模板文件的句柄

);

CreateFile这个函数用处很多,这里我们用它“打开”设备驱动程序,得到设备的句柄。操作完成后用CloseHandle关闭设备句柄。

与普通文件名有所不同,设备驱动的“文件名”(常称为“设备路径”)形式固定为“\\.\DeviceName”(注意在C程序中该字符串写法为“\\\\.\\DeviceName”),DeviceName必须与设备驱动程序内定义的设备名称一致。

一般地,调用CreateFile获得设备句柄时,访问方式参数设置为0或GENERIC_READ|GENERIC_WRITE,共享方式参数设置为FILE_SHARE_READ|FILE_SHARE_WRITE,创建方式参数设置为OPEN_EXISTING,其它参数设置为0或NULL。

Q 可是,我怎么知道设备名称是什么呢?

A 一些存储设备的名称是微软定义好的,不可能有什么变化。大体列出如下软盘驱动器A:, B:

硬盘逻辑分区C:, D:, E:, ...

物理驱动器PHYSICALDRIVEx

CD-ROM, DVD/ROM CDROMx

磁带机TAPEx

……

其中,物理驱动器不包括软驱和光驱。逻辑驱动器可以是IDE/SCSI/PCMCIA/USB接口的硬盘分区(卷)、光驱、MO、CF卡等,甚至是虚拟盘。x=0,1,2 ……

其它的设备名称需通过驱动接口的GUID调用设备管理函数族取得,这里暂不讨论。

Q 请举一个简单的例子说明如何通过DeviceIoControl访问设备驱动程序。

A 这里有一个从MSDN上摘抄来的demo程序,演示在NT/2000/XP中如何通过DeviceIoControl获取硬盘的基本参数。

/* The code of interest is in the subroutine GetDriveGeometry. The

code in main shows how to interpret the results of the IOCTL call. */

#include

#include

BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)

{

HANDLE hDevice; // handle to the drive to be examined

BOOL bResult; // results flag

DWORD junk; // discard results

hDevice = CreateFile("\\\\.\\PhysicalDrive0", // drive to open

0, // no access to the drive

FILE_SHARE_READ | // share mode

FILE_SHARE_WRITE,

NULL, // default security attributes

OPEN_EXISTING, // disposition

0, // file attributes

NULL); // do not copy file attributes

if (hDevice == INV ALID_HANDLE_V ALUE) // cannot open the drive

{

return (FALSE);

}

bResult = DeviceIoControl(hDevice, // device to be queried

IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform

NULL, 0, // no input buffer

pdg, sizeof(*pdg), // output buffer

&junk, // # bytes returned

(LPOVERLAPPED) NULL); // synchronous I/O

CloseHandle(hDevice);

return (bResult);

}

int main(int argc, char *argv[])

{

DISK_GEOMETRY pdg; // disk drive geometry structure

BOOL bResult; // generic results flag

ULONGLONG DiskSize; // size of the drive, in bytes

bResult = GetDriveGeometry (&pdg);

if (bResult)

{

printf("Cylinders = %I64d\n", pdg.Cylinders);

printf("Tracks per cylinder = %ld\n", (ULONG) pdg.TracksPerCylinder);

printf("Sectors per track = %ld\n", (ULONG) pdg.SectorsPerTrack);

printf("Bytes per sector = %ld\n", (ULONG) pdg.BytesPerSector);

DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder *

(ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector;

printf("Disk size = %I64d (Bytes) = %I64d (Mb)\n", DiskSize,

DiskSize / (1024 * 1024));

}

else

{

printf("GetDriveGeometry failed. Error %ld.\n", GetLastError());

}

return ((int)bResult);

}

现在我们总结一下通过DeviceIoControl访问设备驱动程序的“三步曲”:首先用CreateFile取得设备句柄,然后用DeviceIoControl与设备进行I/O,最后别忘记用CloseHandle关闭设备句柄。

2.获取软盘/硬盘/光盘的参数

Q 在MSDN的那个demo中,将设备名换成“A:”取A盘参数,先用资源管理器读一下盘,再运行这个程序可以成功,但换一张盘后就失败;换成“CDROM0”取CDROM参数,无论如何都不行。这个问题如何解决呢?

A 取软盘参数是从软盘上读取格式化后的信息,也就是必须执行读操作,这一点与硬盘不同。将CreateFile中的访问方式改为GENERIC_READ就行了。

IOCTL_DISK_GET_DRIVE_GEOMETRY这个I/O控制码,对软盘和硬盘有效,但对一些可移动媒介如CD/DVD-ROM、TAPE等就不管用了。要取CDROM参数,还得另辟蹊径。IOCTL_STORAGE_GET_MEDIA_TYPES_EX能够帮我们解决问题。

Q 使用这些I/O控制码,需要什么样的输入输出数据格式呢?

A DeviceIoControl使用这两个控制码时,都不需要输入数据。

IOCTL_DISK_GET_DRIVE_GEOMETRY直接输出一个DISK_GEOMETRY结构:typedef struct _DISK_GEOMETRY {

LARGE_INTEGER Cylinders; // 柱面数

MEDIA_TYPE MediaType; // 介质类型

DWORD TracksPerCylinder; // 每柱面的磁道数

DWORD SectorsPerTrack; // 每磁道的扇区数

DWORD BytesPerSector; // 每扇区的字节数

} DISK_GEOMETRY;

IOCTL_STORAGE_GET_MEDIA_TYPES_EX输出一个GET_MEDIA_TYPES结构:typedef struct _GET_MEDIA_TYPES {

DWORD DeviceType; // 设备类型

DWORD MediaInfoCount; // 介质信息条数

DEVICE_MEDIA_INFO MediaInfo[1]; // 介质信息

} GET_MEDIA_TYPES;

让我们来看一下DEVICE_MEDIA_INFO结构的定义:typedef struct _DEVICE_MEDIA_INFO {

union {

struct {

LARGE_INTEGER Cylinders; // 柱面数

STORAGE_MEDIA_TYPE MediaType; // 介质类型

DWORD TracksPerCylinder; // 每柱面的磁道数

DWORD SectorsPerTrack; // 每磁道的扇区数

DWORD BytesPerSector; // 每扇区的字节数

DWORD NumberMediaSides; // 介质面数

DWORD MediaCharacteristics; // 介质特性

} DiskInfo; // 硬盘信息

struct {

LARGE_INTEGER Cylinders; // 柱面数

STORAGE_MEDIA_TYPE MediaType; // 介质类型

DWORD TracksPerCylinder; // 每柱面的磁道数

DWORD SectorsPerTrack; // 每磁道的扇区数

DWORD BytesPerSector; // 每扇区的字节数

DWORD NumberMediaSides; // 介质面数

DWORD MediaCharacteristics; // 介质特性

} RemovableDiskInfo; // “可移动盘”信息

struct {

STORAGE_MEDIA_TYPE MediaType; // 介质类型

DWORD MediaCharacteristics; // 介质特性

DWORD CurrentBlockSize; // 块的大小

} TapeInfo; // 磁带信息

} DeviceSpecific;

} DEVICE_MEDIA_INFO;

其中CD-ROM属于“可移动盘”的范围。请注意,GET_MEDIA_TYPES结构本身只定义了一条DEVICE_MEDIA_INFO,额外的DEVICE_MEDIA_INFO需要紧接此结构的另外的空间。

Q 调用方法我了解了,请用VC举个例子来实现我所期待已久的功能吧?

A 好,现在就演示一下如何取软盘/硬盘/光盘的参数。测试时,记得要有软盘/光盘插在驱动器里喔!

首先,用MFC AppWizard生成一个单文档的应用程序,取名为DiskGeometry,让它的View基于CEditView。

然后,添加以下的.h和.cpp文件。//////////////////////////////////////////////////////////////////////////////

// GetDiskGeometry.h

//////////////////////////////////////////////////////////////////////////////

#if !defined(GET_DISK_GEOMETRY_H__)

#define GET_DISK_GEOMETRY_H__

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#include

BOOL GetDriveGeometry(const char* filename, DISK_GEOMETRY *pdg);

#endif // !defined(GET_DISK_GEOMETRY_H__)

//////////////////////////////////////////////////////////////////////////////

// GetDiskGeometry.cpp

//////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "GetDiskGeometry.h"

// IOCTL_STORAGE_GET_MEDIA_TYPES_EX可能返回不止一条DEVICE_MEDIA_INFO,故定义足够的空间

#define MEDIA_INFO_SIZE sizeof(GET_MEDIA_TYPES)+15*sizeof(DEVICE_MEDIA_INFO)

// filename -- 用于设备的文件名

// pdg -- 参数缓冲区指针

BOOL GetDriveGeometry(const char* filename, DISK_GEOMETRY *pdg)

{

HANDLE hDevice; // 设备句柄

BOOL bResult; // DeviceIoControl的返回结果

GET_MEDIA_TYPES *pmt; // 内部用的输出缓冲区

DWORD dwOutBytes; // 输出数据长度

// 打开设备

hDevice = ::CreateFile(filename, // 文件名

GENERIC_READ, // 软驱需要读盘

FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式

NULL, // 默认的安全描述符

OPEN_EXISTING, // 创建方式

0, // 不需设置文件属性

NULL); // 不需参照模板文件

if (hDevice == INVALID_HANDLE_V ALUE)

{

// 设备无法打开...

return FALSE;

}

// 用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盘参数

bResult = ::DeviceIoControl(hDevice, // 设备句柄

IOCTL_DISK_GET_DRIVE_GEOMETRY, // 取磁盘参数

NULL, 0, // 不需要输入数据

pdg, sizeof(DISK_GEOMETRY), // 输出数据缓冲区

&dwOutBytes, // 输出数据长度

(LPOVERLAPPED)NULL); // 用同步I/O

// 如果失败,再用IOCTL_STORAGE_GET_MEDIA_TYPES_EX取介质类型参数

if (!bResult)

{

pmt = (GET_MEDIA_TYPES *)new BYTE[MEDIA_INFO_SIZE];

bResult = ::DeviceIoControl(hDevice, // 设备句柄

IOCTL_STORAGE_GET_MEDIA_TYPES_EX, // 取介质类型参数

NULL, 0, // 不需要输入数据

pmt, MEDIA_INFO_SIZE, // 输出数据缓冲区

&dwOutBytes, // 输出数据长度

(LPOVERLAPPED)NULL); // 用同步I/O

if (bResult)

{

// 注意到结构DEVICE_MEDIA_INFO是在结构DISK_GEOMETRY的基础上扩充的

// 为简化程序,用memcpy代替如下多条赋值语句:

// pdg->MediaType = (MEDIA_TYPE)pmt->MediaInfo[0].DeviceSpecific.DiskInfo.MediaType;

// pdg->Cylinders = pmt->MediaInfo[0].DeviceSpecific.DiskInfo.Cylinders;

// pdg->TracksPerCylinder = pmt->MediaInfo[0].DeviceSpecific.DiskInfo.TracksPerCylinder;

// ... ...

::memcpy(pdg, pmt->MediaInfo, sizeof(DISK_GEOMETRY));

}

delete pmt;

}

// 关闭设备句柄

::CloseHandle(hDevice);

return (bResult);

}

然后,在Toolbar的IDR_MAINFRAME上添加一个按钮,ID为ID_GET_DISK_GEOMETRY。打开ClassWizard,在DiskGeometryView中

添加ID_GET_DISK_GEOMETRY的映射函数OnGetDiskGeometry。打开DiskGeometryView.cpp,包含头文件GetDiskGeometry.h。

在OnGetDiskGeometry中,添加以下代码const char *szDevName[]=

{

"\\\\.\\A:",

"\\\\.\\B:",

"\\\\.\\PhysicalDrive0",

"\\\\.\\PhysicalDrive1",

"\\\\.\\PhysicalDrive2",

"\\\\.\\PhysicalDrive3",

"\\\\.\\Cdrom0",

"\\\\.\\Cdrom1",

};

DISK_GEOMETRY dg;

ULONGLONG DiskSize;

BOOL bResult;

CString strMsg;

CString strTmp;

for (int i = 0; i < sizeof(szDevName)/sizeof(char*); i++)

{

bResult = GetDriveGeometry(szDevName[i], &dg);

strTmp.Format("\r\n%s result = %s\r\n", szDevName[i], bResult ? "success" : "failure");

strMsg+=strTmp;

if (!bResult) continue;

strTmp.Format(" Media Type = %d\r\n", dg.MediaType);

strMsg+=strTmp;

strTmp.Format(" Cylinders = %I64d\r\n", dg.Cylinders);

strMsg+=strTmp;

strTmp.Format(" Tracks per cylinder = %ld\r\n", (ULONG) dg.TracksPerCylinder);

strMsg+=strTmp;

strTmp.Format(" Sectors per track = %ld\r\n", (ULONG) dg.SectorsPerTrack);

strMsg+=strTmp;

strTmp.Format(" Bytes per sector = %ld\r\n", (ULONG) dg.BytesPerSector);

strMsg+=strTmp;

DiskSize = dg.Cylinders.QuadPart * (ULONG)dg.TracksPerCylinder *

(ULONG)dg.SectorsPerTrack * (ULONG)dg.BytesPerSector;

strTmp.Format(" Disk size = %I64d (Bytes) = %I64d (Mb)\r\n", DiskSize, DiskSize / (1024 * 1024));

strMsg+=strTmp;

}

CEdit& Edit = GetEditCtrl();

Edit.SetWindowText(strMsg);

最后,最后干什么呢?编译,运行......

3.制作磁盘镜像文件

Q DOS命令DISKCOPY给我很深的印象,现在也有许多“克隆”软件,可以对磁盘进行全盘复制。我想,要制作磁盘镜像文件,DeviceIoControl应该很有用武之地吧?

A是的。这里举一个制作软盘镜像文件,功能类似于“DISKCOPY”的例子。

本例实现其功能的核心代码如下:

// 打开磁盘

HANDLE OpenDisk(LPCTSTR filename)

{

HANDLE hDisk;

// 打开设备

hDisk = ::CreateFile(filename, // 文件名

GENERIC_READ | GENERIC_WRITE, // 读写方式

FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式

NULL, // 默认的安全描述符

OPEN_EXISTING, // 创建方式

0, // 不需设置文件属性

NULL); // 不需参照模板文件

return hDisk;

}

// 获取磁盘参数

BOOL GetDiskGeometry(HANDLE hDisk, PDISK_GEOMETRY lpGeometry)

{

DWORD dwOutBytes;

BOOL bResult;

// 用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盘参数

bResult = ::DeviceIoControl(hDisk, // 设备句柄

IOCTL_DISK_GET_DRIVE_GEOMETRY, // 取磁盘参数

NULL, 0, // 不需要输入数据

lpGeometry, sizeof(DISK_GEOMETRY), // 输出数据缓冲区

&dwOutBytes, // 输出数据长度

(LPOVERLAPPED)NULL); // 用同步I/O

return bResult;

}

// 从指定磁道开始读磁盘

BOOL ReadTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD dwCylinderNumber)

{

DWORD VirtBufSize;

DWORD BytesRead;

// 大小

VirtBufSize = lpGeometry->TracksPerCylinder * lpGeometry->SectorsPerTrack * lpGeometry->BytesPerSector;

// 偏移

::SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN);

return ::ReadFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &BytesRead, NULL);

}

// 从指定磁道开始写磁盘

BOOL WriteTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD dwCylinderNumber)

{

DWORD VirtBufSize;

DWORD BytesWritten;

// 大小

VirtBufSize = lpGeometry->TracksPerCylinder * lpGeometry->SectorsPerTrack * lpGeometry->BytesPerSector;

// 偏移

::SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN);

return ::WriteFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &BytesWritten, NULL);

}

// 从指定磁道开始格式化磁盘

BOOL LowLevelFormatTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, DWORD dwStartCylinder, DWORD dwCylinderNumber)

{

FORMAT_PARAMETERS FormatParameters;

PBAD_TRACK_NUMBER lpBadTrack;

DWORD dwOutBytes;

DWORD dwBufSize;

BOOL bResult;

FormatParameters.MediaType = lpGeometry->MediaType;

FormatParameters.StartCylinderNumber = dwStartCylinder;

FormatParameters.EndCylinderNumber = dwStartCylinder + dwCylinderNumber - 1;

FormatParameters.StartHeadNumber = 0;

FormatParameters.EndHeadNumber = lpGeometry->TracksPerCylinder - 1;

dwBufSize = lpGeometry->TracksPerCylinder * sizeof(BAD_TRACK_NUMBER);

lpBadTrack = (PBAD_TRACK_NUMBER) new BYTE[dwBufSize];

// 用IOCTL_DISK_FORMAT_TRACKS对连续磁道进行低级格式化

bResult = ::DeviceIoControl(hDisk, // 设备句柄

IOCTL_DISK_FORMAT_TRACKS, // 低级格式化

&FormatParameters, sizeof(FormatParameters), // 输入数据缓冲区

lpBadTrack, dwBufSize, // 输出数据缓冲区

&dwOutBytes, // 输出数据长度

(LPOVERLAPPED)NULL); // 用同步I/O

delete lpBadTrack;

return bResult;

}

// 将卷锁定

BOOL LockVolume(HANDLE hDisk)

{

DWORD dwOutBytes;

BOOL bResult;

// 用FSCTL_LOCK_VOLUME锁卷

bResult = ::DeviceIoControl(hDisk, // 设备句柄

FSCTL_LOCK_VOLUME, // 锁卷

NULL, 0, // 不需要输入数据 NULL, 0, // 不需要输出数据 &dwOutBytes, // 输出数据长度 (LPOVERLAPPED)NULL); // 用同步I/O

return bResult;

}

// 将卷解锁

BOOL UnlockVolume(HANDLE hDisk)

{

DWORD dwOutBytes;

BOOL bResult;

// 用FSCTL_UNLOCK_VOLUME开卷锁

bResult = ::DeviceIoControl(hDisk, // 设备句柄

FSCTL_UNLOCK_VOLUME, // 开卷锁

NULL, 0, // 不需要输入数据 NULL, 0, // 不需要输出数据 &dwOutBytes, // 输出数据长度 (LPOVERLAPPED)NULL); // 用同步I/O

return bResult;

}

// 将卷卸下

// 该操作使系统重新辨识磁盘,等效于重新插盘

BOOL DismountVolume(HANDLE hDisk)

{

DWORD dwOutBytes;

BOOL bResult;

// 用FSCTL_DISMOUNT_VOLUME卸卷

bResult = ::DeviceIoControl(hDisk, // 设备句柄

FSCTL_DISMOUNT_VOLUME, // 卸卷

NULL, 0, // 不需要输入数据 NULL, 0, // 不需要输出数据 &dwOutBytes, // 输出数据长度 (LPOVERLAPPED)NULL); // 用同步I/O

return bResult;

}

将软盘保存成镜像文件的步骤简单描述为:

1、创建空的镜像文件。

2、调用OpenDisk打开软盘。成功转3,失败转8。

3、调用LockVolume将卷锁定。成功转4,失败转7。

4、调用GetDiskGeometry获取参数。成功转5,失败转6。

5、将磁盘参数写入镜像文件作为文件头。调用ReadTracks按柱面读出数据,保存在镜像文件中。循环次数等于柱面数。

6、调用UnlockVolume将卷解锁。

7、调用CloseDisk关闭软盘。

8、关闭镜像文件。

将镜像文件载入软盘的步骤简单描述为:

1、打开镜像文件。

2、调用OpenDisk打开软盘。成功转3,失败转11。

3、调用LockVolume将卷锁定。成功转4,失败转10。

4、调用GetDiskGeometry获取参数。成功转5,失败转9。

5、从镜像文件中读出文件头,判断两个磁盘参数是否一致。不一致转6,否则转7。

6、调用LowLevelFormatTracks按柱面格式化软盘。循环次数等于柱面数。成功转7,失败转8。

7、从镜像文件中读出数据,并调用WriteTracks按柱面写入磁盘。循环次数等于柱面数。

8、调用DismountVolume将卷卸下。

9、调用UnlockVolume将卷解锁。

10、调用CloseDisk关闭软盘。

11、关闭镜像文件。

Q我注意到,磁盘读写和格式化是按柱面进行的,有什么道理吗?

A没有特别的原因,只是因为在这个例子中可以方便地显示处理进度。

有一点需要特别提及,按绝对地址读写磁盘数据时,“最小单位”是扇区,地址一定要与扇区对齐,长度也要等于扇区长度的整数倍。比如,每扇区512字节,那么起始地址和数据长度都应能被512整除才行。

Q我忽然产生了一个伟大的想法,用绝对地址读写的方式使用磁盘,包括U盘啦,MO啦,而不是用现成的文件系统,那不是可以将数据保密了吗?

A当然,只要你喜欢。可千万别在你的系统盘上做试验,否则......可别怪bhw98没有提醒过你喔!

Q我知道怎么测试光驱的传输速度了,就用上面的方法,读出一定长度数据,除以所需时间,应该可以吧?

A可以。但取光盘参数时要用IOCTL_STORAGE_GET_MEDIA_TYPES_EX,我们已经探讨过的

4.获取硬盘的详细信息

Q 用IOCTL_DISK_GET_DRIVE_GEOMETRYIOCTL_STORAGE_GET_MEDIA_TYPES_EX只能

得到很少的磁盘参数,我想获得包括硬盘序列号在内的更加详细的信息,有什么办法呀?

A 确实,用你所说的I/O控制码,只能得到最基本的磁盘参数。获取磁盘出厂信息的I/O控制码,微软在VC/MFC环境中没有开放,在DDK中可以发现一些线索。早先,Lynn McGuire写了一个很出名的获取IDE硬盘详细信息的程序DiskID32,下面的例子是在其基础上经过增删和改进而成的。

本例中,我们要用到ATA/APAPI的IDENTIFY DEVICE指令。ATA/APAPI是国际组织T13起草和发布的IDE/EIDE/UDMA硬盘及其它可移动存储设备与主机接口的标准,至今已经到了ATA/APAPI-7版本。该接口标准规定了ATA/ATAPI 设备的输入输出寄存器和指令集。欲了解更详细的ATA/ATAPI技术资料,可访问T13的站点。

用到的常量及数据结构有以下一些:

// IOCTL控制码

// #define DFP_SEND_DRIVE_COMMAND0x0007c084

#define DFP_SEND_DRIVE_COMMAND CTL_CODE(IOCTL_DISK_BASE, 0x0021,

METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

// #define DFP_RECEIVE_DRIVE_DATA0x0007c088

#define DFP_RECEIVE_DRIVE_DATA CTL_CODE(IOCTL_DISK_BASE, 0x0022,

METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define FILE_DEVICE_SCSI0x0000001b

#define IOCTL_SCSI_MINIPORT_IDENTIFY((FILE_DEVICE_SCSI << 16) + 0x0501)

#define IOCTL_SCSI_MINIPORT 0x0004D008//see NTDDSCSI.H for definition

// ATA/ATAPI指令

#define IDE_ATA_IDENTIFY0xEC//ATA的ID指令(IDENTIFY DEVICE)

// IDE命令寄存器

typedef struct _IDEREGS

{

BYTE bFeaturesReg;// 特征寄存器(用于SMART 命令)BYTE bSectorCountReg;// 扇区数目寄存器BYTE bSectorNumberReg;// 开始扇区寄存器

BYTE bCylLowReg;// 开始柱面低字节寄存器

BYTE bCylHighReg;// 开始柱面高字节寄存器

BYTE bDriveHeadReg;// 驱动器/磁头寄存器

BYTE bCommandReg;// 指令寄存器

BYTE bReserved;// 保留

} IDEREGS, *PIDEREGS, *LPIDEREGS;

// 从驱动程序返回的状态

typedef struct _DRIVERSTATUS

{

BYTE bDriverError;// 错误码

BYTE bIDEStatus;// IDE状态寄存器

BYTE bReserved[2];// 保留

DWORD dwReserved[2];// 保留

} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;

// IDE设备IOCTL输入数据结构

typedef struct _SENDCMDINPARAMS

{

DWORD cBufferSize;// 缓冲区字节数

IDEREGS irDriveRegs;// IDE寄存器组

BYTE bDriveNumber;// 驱动器号

BYTE bReserved[3];// 保留

DWORD dwReserved[4]; // 保留

BYTE bBuffer[1];// 输入缓冲区(此处象征性地包含1字节)

} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;

// IDE设备IOCTL输出数据结构

typedef struct _SENDCMDOUTPARAMS

{

DWORD cBufferSize;// 缓冲区字节数

DRIVERSTATUS DriverStatus;// 驱动程序返回状态

BYTE bBuffer[1];// 输入缓冲区(此处象征性地包含1字节)

} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;

// IDE的ID命令返回的数据

// 共512字节(256个WORD),这里仅定义了一些感兴趣的项(基本上依据ATA/ATAPI-4)

typedef struct _IDINFO

{

USHORT wGenConfig;// WORD 0: 基本信息字

USHORT wNumCyls;// WORD 1: 柱面数

USHORT wReserved2;// WORD 2: 保留

USHORT wNumHeads;// WORD 3: 磁头数USHORT wReserved4;// WORD 4: 保留

USHORT wReserved5;// WORD 5: 保留

USHORT wNumSectorsPerTrack;// WORD 6: 每磁道扇区数

USHORT wVendorUnique[3];// WORD 7-9: 厂家设定值

CHAR sSerialNumber[20];// WORD 10-19:序列号

USHORT wBufferType;// WORD 20: 缓冲类型

USHORT wBufferSize;// WORD 21: 缓冲大小

USHORT wECCSize;// WORD 22: ECC校验大小

CHAR sFirmwareRev[8];// WORD 23-26: 固件版本

CHAR sModelNumber[40];// WORD 27-46: 内部型号

USHORT wMoreVendorUnique;// WORD 47: 厂家设定值

USHORT wReserved48;// WORD 48: 保留

struct {

USHORT reserved1:8;

USHORT DMA:1;// 1=支持DMA

USHORT LBA:1;// 1=支持LBA

USHORT DisIORDY:1;// 1=可不使用IORDY

USHORT IORDY:1;// 1=支持IORDY

USHORT SoftReset:1;// 1=需要ATA软启动

USHORT Overlap:1;// 1=支持重叠操作

USHORT Queue:1;// 1=支持命令队列

USHORT InlDMA:1;// 1=支持交叉存取DMA

} wCapabilities;// WORD 49: 一般能力

USHORT wReserved1;// WORD 50: 保留

USHORT wPIOTiming;// WORD 51: PIO时序

USHORT wDMATiming;// WORD 52: DMA时序

struct {

USHORT CHSNumber:1;// 1=WORD 54-58有效

USHORT CycleNumber:1;// 1=WORD 64-70有效

USHORT UnltraDMA:1;// 1=WORD 88有效

USHORT reserved:13;

} wFieldValidity;// WORD 53: 后续字段有效性标志

USHORT wNumCurCyls;// WORD 54: CHS可寻址的柱面数

USHORT wNumCurHeads;// WORD 55: CHS可寻址的磁头数

USHORT wNumCurSectorsPerTrack;// WORD 56: CHS可寻址每磁道扇区数USHORT wCurSectorsLow;// WORD 57: CHS可寻址的扇区数低位字

USHORT wCurSectorsHigh;// WORD 58: CHS可寻址的扇区数高位字

struct {

USHORT CurNumber:8;// 当前一次性可读写扇区数

USHORT Multi:1;// 1=已选择多扇区读写

USHORT reserved1:7;

} wMultSectorStuff;// WORD 59: 多扇区读写设定

ULONG dwTotalSectors;// WORD 60-61: LBA可寻址的扇区数

USHORT wSingleWordDMA;// WORD 62: 单字节DMA支持能力struct { USHORT Mode0:1;// 1=支持模式0 (4.17Mb/s)

USHORT Mode1:1;// 1=支持模式1 (13.3Mb/s)

USHORT Mode2:1;// 1=支持模式2 (16.7Mb/s)

USHORT Reserved1:5;

USHORT Mode0Sel:1;// 1=已选择模式0

USHORT Mode1Sel:1;// 1=已选择模式1

USHORT Mode2Sel:1;// 1=已选择模式2

USHORT Reserved2:5;

} wMultiWordDMA;// WORD 63: 多字节DMA支持能力

struct {

USHORT AdvPOIModes:8;// 支持高级POI模式数

USHORT reserved:8;

} wPIOCapacity;// WORD 64: 高级PIO支持能力

USHORT wMinMultiWordDMACycle;// WORD 65: 多字节DMA传输周期的最小值USHORT wRecMultiWordDMACycle;// WORD 66: 多字节DMA传输周期的建议值USHORT wMinPIONoFlowCycle;// WORD 67: 无流控制时PIO传输周期的最小值

USHORT wMinPOIFlowCycle;// WORD 68: 有流控制时PIO传输周期的最小值

USHORT wReserved69[11];// WORD 69-79: 保留

struct {

USHORT Reserved1:1;

USHORT ATA1:1;// 1=支持ATA-1

USHORT ATA2:1;// 1=支持ATA-2

USHORT ATA3:1;// 1=支持ATA-3

USHORT ATA4:1;// 1=支持ATA/ATAPI-4

USHORT ATA5:1;// 1=支持ATA/ATAPI-5

USHORT ATA6:1;// 1=支持ATA/ATAPI-6

USHORT ATA7:1;// 1=支持ATA/ATAPI-7

USHORT ATA8:1;// 1=支持ATA/ATAPI-8

USHORT ATA9:1;// 1=支持ATA/ATAPI-9

USHORT ATA10:1;// 1=支持ATA/ATAPI-10

USHORT ATA11:1;// 1=支持ATA/ATAPI-11

USHORT ATA12:1;// 1=支持ATA/ATAPI-12

USHORT ATA13:1;// 1=支持ATA/ATAPI-13

USHORT ATA14:1;// 1=支持ATA/ATAPI-14

USHORT Reserved2:1;

} wMajorVersion;// WORD 80: 主版本

USHORT wMinorVersion;// WORD 81: 副版本

USHORT wReserved82[6];// WORD 82-87: 保留

struct {

USHORT Mode0:1;// 1=支持模式0 (16.7Mb/s)

USHORT Mode1:1;// 1=支持模式1 (25Mb/s)

USHORT Mode2:1;// 1=支持模式2 (33Mb/s)

USHORT Mode3:1;// 1=支持模式3 (44Mb/s)USHORT Mode4:1;// 1=支持模式4 (66Mb/s)

USHORT Mode5:1;// 1=支持模式5 (100Mb/s)

USHORT Mode6:1;// 1=支持模式6 (133Mb/s)

USHORT Mode7:1;// 1=支持模式7 (166Mb/s) ???

USHORT Mode0Sel:1;// 1=已选择模式0

USHORT Mode1Sel:1;// 1=已选择模式1

USHORT Mode2Sel:1;// 1=已选择模式2

USHORT Mode3Sel:1;// 1=已选择模式3

USHORT Mode4Sel:1;// 1=已选择模式4

USHORT Mode5Sel:1;// 1=已选择模式5

USHORT Mode6Sel:1;// 1=已选择模式6

USHORT Mode7Sel:1;// 1=已选择模式7

} wUltraDMA;// WORD 88:Ultra DMA支持能力

USHORT wReserved89[167];// WORD 89-255

} IDINFO, *PIDINFO;

// SCSI驱动所需的输入输出共用的结构

typedef struct _SRB_IO_CONTROL

{

ULONG HeaderLength;// 头长度

UCHAR Signature[8];// 特征名称

ULONG Timeout;// 超时时间

ULONG ControlCode;// 控制码

ULONG ReturnCode;// 返回码

ULONG Length;// 缓冲区长度

} SRB_IO_CONTROL, *PSRB_IO_CONTROL;

需要引起注意的是IDINFO第57-58 WORD (CHS可寻址的扇区数),因为不满足32位对齐的要求,不可定义为一个ULONG 字段。Lynn McGuir e的程序里正是由于定义为一个ULONG字段,导致该结构不可用。

以下是核心代码:

// 打开设备

// filename: 设备的“文件名”

HANDLE OpenDevice(LPCTSTR filename)

{

HANDLE hDevice;

// 打开设备

hDevice= ::CreateFile(filename,// 文件名

GENERIC_READ | GENERIC_WRITE,// 读写方式

FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式

NULL,// 默认的安全描述符

OPEN_EXISTING,// 创建方式0,// 不需设置文件属性

NULL);// 不需参照模板文件

return hDevice;

}

// 向驱动发“IDENTIFY DEVICE”命令,获得设备信息

// hDevice: 设备句柄

// pIdInfo:设备信息结构指针

BOOL IdentifyDevice(HANDLE hDevice, PIDINFO pIdInfo)

{

PSENDCMDINPARAMS pSCIP;// 输入数据结构指针PSENDCMDOUTPARAMS pSCOP; // 输出数据结构指针

DWORD dwOutBytes;// IOCTL输出数据长度

BOOL bResult;// IOCTL返回值

// 申请输入/输出数据结构空间

pSCIP = (PSENDCMDINPARAMS)::GlobalAlloc(LMEM_ZEROINIT,

sizeof(SENDCMDINPARAMS)-1);

pSCOP = (PSENDCMDOUTPARAMS)::GlobalAlloc(LMEM_ZEROINIT,

sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1);

// 指定ATA/ATAPI命令的寄存器值

// pSCIP->irDriveRegs.bFeaturesReg = 0;

// pSCIP->irDriveRegs.bSectorCountReg = 0;

// pSCIP->irDriveRegs.bSectorNumberReg = 0;

// pSCIP->irDriveRegs.bCylLowReg = 0;

// pSCIP->irDriveRegs.bCylHighReg = 0;

// pSCIP->irDriveRegs.bDriveHeadReg = 0;

pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;

// 指定输入/输出数据缓冲区大小

pSCIP->cBufferSize = 0;

pSCOP->cBufferSize = sizeof(IDINFO);

// IDENTIFY DEVICE

bResult = ::DeviceIoControl(hDevice,// 设备句柄

DFP_RECEIVE_DRIVE_DATA,// 指定IOCTL

pSCIP, sizeof(SENDCMDINPARAMS) - 1, // 输入数据缓冲区

pSCOP, sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) - 1, // 输出数据缓冲区&dwOutBytes,// 输出数据长度

(LPOVERLAPPED)NULL);// 用同步I/O

// 复制设备参数结构

::memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO));// 释放输入/输出数据空间::GlobalFree(pSCOP);

::GlobalFree(pSCIP);

return bResult;

}

// 向SCSI MINI-PORT 驱动发“IDENTIFY DEVICE”命令,获得设备信息

// hDevice: 设备句柄

// pIdInfo:设备信息结构指针

BOOL IdentifyDeviceAsScsi(HANDLE hDevice, int nDrive, PIDINFO pIdInfo)

{

PSENDCMDINPARAMS pSCIP;// 输入数据结构指针

PSENDCMDOUTPARAMS pSCOP; // 输出数据结构指针

PSRB_IO_CONTROL pSRBIO;// SCSI输入输出数据结构指针

DWORD dwOutBytes;// IOCTL输出数据长度

BOOL bResult;// IOCTL返回值

// 申请输入/输出数据结构空间

pSRBIO = (PSRB_IO_CONTROL)::GlobalAlloc(LMEM_ZEROINIT,

sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1);

pSCIP = (PSENDCMDINPARAMS)((char *)pSRBIO+sizeof(SRB_IO_CONTROL));

pSCOP = (PSENDCMDOUTPARAMS)((char *)pSRBIO+sizeof(SRB_IO_CONTROL));

// 填充输入/输出数据

pSRBIO->HeaderLength = sizeof(SRB_IO_CONTROL);

pSRBIO->Timeout = 10000;

pSRBIO->Length = sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1;

pSRBIO->ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;

::strncpy ((char *)pSRBIO->Signature, SCSIDISK, 8);

// 指定ATA/ATAPI命令的寄存器值

// pSCIP->irDriveRegs.bFeaturesReg = 0;

// pSCIP->irDriveRegs.bSectorCountReg = 0;

// pSCIP->irDriveRegs.bSectorNumberReg = 0;

// pSCIP->irDriveRegs.bCylLowReg = 0;

// pSCIP->irDriveRegs.bCylHighReg = 0;

// pSCIP->irDriveRegs.bDriveHeadReg = 0;

pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;

pSCIP->bDriveNumber = nDrive;

// IDENTIFY DEVICE

bResult = ::DeviceIoControl(hDevice,// 设备句柄

IOCTL_SCSI_MINIPORT,// 指定IOCTL

pSRBIO, sizeof(SRB_IO_CONTROL) +sizeof(SENDCMDINPARAMS) - 1, // 输入数据缓冲区pSRBIO, sizeof(SRB_IO_CONTROL) +sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) - 1, // 输出

数据缓冲区

&dwOutBytes,// 输出数据长度

(LPOVERLAPPED)NULL); // 用同步I/O

// 复制设备参数结构

::memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO));

// 释放输入/输出数据空间

::GlobalFree(pSRBIO);

return bResult;

}

// 将串中的字符两两颠倒

// 原因是ATA/ATAPI中的WORD,与Windows采用的字节顺序相反

// 驱动程序中已经将收到的数据全部反过来,我们来个负负得正

void AdjustString(char* str, int len)

{

char ch;

int i;

// 两两颠倒

for(i=0;i

{

ch = str[i];

str[i] = str[i+1];

str[i+1] = ch;

}

// 若是右对齐的,调整为左对齐(去掉左边的空格)

i=0;

while(i

::memmove(str, &str[i], len-i);

// 去掉右边的空格

i = len - 1;

while(i>=0 && str[i]==' ')

{

str[i] = '';

i--;

}

} // 读取IDE硬盘的设备信息,必须有足够权限

// nDrive: 驱动器号(0=第一个硬盘,1=0=第二个硬盘,......)

// pIdInfo: 设备信息结构指针

BOOL GetPhysicalDriveInfoInNT(int nDrive, PIDINFO pIdInfo)

{

HANDLE hDevice;// 设备句柄

BOOL bResult;// 返回结果

char szFileName[20]; // 文件名

::sprintf(szFileName,.PhysicalDrive%d, nDrive);

hDevice = ::OpenDevice(szFileName);

if(hDevice == INVALID_HANDLE_VALUE)

{

return FALSE;

}

// IDENTIFY DEVICE

bResult = ::IdentifyDevice(hDevice, pIdInfo);

if(bResult)

{

// 调整字符串

::AdjustString(pIdInfo->sSerialNumber, 20);

::AdjustString(pIdInfo->sModelNumber, 40);

::AdjustString(pIdInfo->sFirmwareRev, 8);

}

::CloseHandle (hDevice);

return bResult;

}

// 用SCSI驱动读取IDE硬盘的设备信息,不受权限制约

// nDrive: 驱动器号(0=Primary Master, 1=Promary Slave, 2=Secondary master, 3=Secondary slave) // pIdInfo: 设备信息结构指针

BOOL GetIdeDriveAsScsiInfoInNT(int nDrive, PIDINFO pIdInfo)

{

HANDLE hDevice;// 设备句柄

BOOL bResult;// 返回结果

char szFileName[20]; // 文件名

::sprintf(szFileName,.Scsi%d:, nDrive/2);hDevice = ::OpenDevice(szFileName);

if(hDevice == INVALID_HANDLE_VALUE)

{

return FALSE;

}

// IDENTIFY DEVICE

bResult = ::IdentifyDeviceAsScsi(hDevice, nDrive%2, pIdInfo);

// 检查是不是空串

if(pIdInfo->sModelNumber[0]=='')

{

bResult = FALSE;

}

if(bResult)

{

// 调整字符串

::AdjustString(pIdInfo->sSerialNumber, 20);

::AdjustString(pIdInfo->sModelNumber, 40);

::AdjustString(pIdInfo->sFirmwareRev, 8);

}

return bResult;

}

Q 我注意到ATA/ATAPI里,以及DiskID32里,有一个“IDENTIFY PACKET DEVICE”指令,与“I DENTIFYDEVICE”有什么区别?

A IDENTIFY DEVICE专门用于固定硬盘,而IDENTIFY PACKET DEVICE用于可移动存储设备如CDROM、CF、MO、ZIP、TAPE等。因为驱动程序的原因,实际上用本例的方法,不管是IDEN TIFY DEVICE也好,IDENTIFY PACKET DEVICE也好,获取可移动存储设备的详细信息,一般是做不到的。而且除了IDE硬盘,对SCSI、USB等接口的硬盘也不起作用。除非厂商提供的驱动支持这样的功能

Q ATA/ATAPI有很多指令,如READ SECTORS, WRITE SECTORS, SECURITY, SLEEP, STAN DBY等,利用上述方法,是否可进行相应操作?

A 应该没问题。但切记,要慎重慎重再慎重!

Q 关于权限问题,请解释一下好吗?

A 在NT/2000/XP下,administrator可以管理设备,上述两种访问驱动的方法都行。但在user身份下,或者登录到域后,用户无法访问PhysicalDrive驱动的核心层,但SCSI MINI-PORT 驱动却可以。目前是可以,不知道Windows以后的版本是否支持。因为这肯定是一个安全隐患。

另外,我们着重讨论NT/2000/XP 中DeviceIoControl的应用,如果需要在98/ME中得到包括硬盘序列号在内的更加详细的信息,请参考DiskID32。

5.列举已安装的存储设备

Q 前几次我们讨论的都是设备名比较清楚的情况,有了设备名(路径),就可以直接调用CreateFile 打开设备,进行它所支持的I/O操作了。如果事先并不能确切知道设备名,如何去访问设备呢?

A 访问设备必须用设备句柄,而得到设备句柄必须知道设备路径,这个套路以你我之力是改变不了的。每个设备都有它所属类型的GUID,我们顺着这个GUID就能获得设备路径。GUID是同类或同种设备的全球唯一识别码,它是一个128 bit(16字节)的整形数,真实面目为:

typedef struct _GUID

{

unsigned long Data1;

unsigned short Data2;

unsigned short Data3;

unsigned char Data4[8];

} GUID, *PGUID;

例如,Disk类的GUID为“53f56307-b6bf-11d0-94f2-00a0c91efb8b”,在我们的程序里可以定义为const GUID DiskClassGuid = {0x53f56307L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1 e, 0xfb,0x8b)};

或者用一个宏来定义

DEFINE_GUID(DiskClassGuid, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb,0x8b);

通过GUID找出设备路径,需要用到一组设备管理的API函数SetupDiGetClassDevs, SetupDiEnum DeviceInterfaces,SetupDiGetInterfaceDeviceDetail,SetupDiDestroyDeviceInfoList,以及结构SP_DEVICE_INT ERFACE_DATA, SP_DEVICE_INTERFACE_DETAIL_DATA。

有关信息请查阅MSDN,这里就不详细介绍了。实现GUID到设备路径的代码如下:// SetupDiGetInterfaceDeviceDetail所需要的输出长度,定义足够大

#define INTERFACE_DETAIL_SIZE (1024)

// 根据GUID获得设备路径

// lpGuid: GUID指针

// pszDevicePath: 设备路径指针的指针

// 返回: 成功得到的设备路径个数,可能不止1个

int GetDevicePath(LPGUID lpGuid, LPTSTR* pszDevicePath)

{

HDEVINFO hDevInfoSet;

SP_DEVICE_INTERFACE_DATA ifdata;

PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;

int nCount;

BOOL bResult;

// 取得一个该GUID相关的设备信息集句柄

hDevInfoSet = ::SetupDiGetClassDevs(lpGuid,// class GUID

NULL,// 无关键字

NULL,// 不指定父窗口句柄

DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); // 目前存在的设备

// 失败...

if(hDevInfoSet == INVALID_HANDLE_VALUE)

{

return 0;

}

// 申请设备接口数据空间

pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT,

INTERFACE_DETAIL_SIZE);

pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

nCount = 0;

bResult = TRUE;

// 设备序号=0,1,2... 逐一测试设备接口,到失败为止

while (bResult)

{

ifdata.cbSize=sizeof(ifdata);

// 枚举符合该GUID的设备接口

bResult = ::SetupDiEnumDeviceInterfaces(hDevInfoSet,// 设备信息集句柄

NULL,// 不需额外的设备描述

lpGuid,// GUID

(ULONG)nCount,// 设备信息集里的设备序号

&ifdata);// 设备接口信息

if(bResult)

{

// 取得该设备接口的细节(设备路径)

bResult = SetupDiGetInterfaceDeviceDetail(

hDevInfoSet,// 设备信息集句柄

&ifdata,// 设备接口信息

pDetail,// 设备接口细节(设备路径)

INTERFACE_DETAIL_SIZE, // 输出缓冲区大小

NULL,// 不需计算输出缓冲区大小(直接用设定值)

NULL);// 不需额外的设备描述

if(bResult)

{

// 复制设备路径到输出缓冲区

::strcpy(pszDevicePath[nCount], pDetail->DevicePath);

// 调整计数值

nCount++;

}

}

}

// 释放设备接口数据空间

::GlobalFree(pDetail);

// 关闭设备信息集句柄

::SetupDiDestroyDeviceInfoList(hDevInfoSet);

return nCount;

}

调用GetDevicePath函数时要注意,pszDevicePath是个指向字符串指针的指针,例如可以这样

int i;

char* szDevicePath[MAX_DEVICE];// 设备路径

// 分配需要的空间

for(i=0; i

nDevice = ::GetDevicePath((LPGUID)&DiskClassGuid, szDevicePath);

// 逐一获取设备信息

for(i=0; i

{

// 打开设备

hDevice = ::OpenDevice(szDevicePath[i]);

if(hDevice != INVALID_HANDLE_VALUE)

{

... ...// I/O操作

::CloseHandle(hDevice);

}

}

// 释放空间

for(i=0;i

本例的Project中除了要包含winioctl.h外,还要包含initguid.h,setupapi.h,以及连接setupapi.lib。

Q 得到设备路径后,就可以到下一步,用CreateFile打开设备,然后用DeviceIoControl 进行读写了吧?

A 是的。尽管该设备路径与以前我们接触的那些不太一样。本是“.PhysicalDrive0”,现在鸟枪换炮,变成了类似这样的一副尊容:“?ide#diskmaxtor_2f040j0__________________________vam51jj0#314656344 7534558202020202020202020202020#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}”。

其实这个设备名在注册表的某处可以找到,例如在Win2000中这个名字位于HKEY_LOCAL_MAC HINESystemCurrentControlSetServicesDiskEnum,只不过“#”换成了“”。分析一下这样的设备路径,你会发

字符设备驱动程序课程设计报告

中南大学 字符设备驱动程序 课程设计报告 姓名:王学彬 专业班级:信安1002班 学号:0909103108 课程:操作系统安全课程设计 指导老师:张士庚 一、课程设计目的 1.了解Linux字符设备驱动程序的结构; 2.掌握Linux字符设备驱动程序常用结构体和操作函数的使用方法; 3.初步掌握Linux字符设备驱动程序的编写方法及过程; 4.掌握Linux字符设备驱动程序的加载方法及测试方法。 二、课程设计内容 5.设计Windows XP或者Linux操作系统下的设备驱动程序; 6.掌握虚拟字符设备的设计方法和测试方法;

7.编写测试应用程序,测试对该设备的读写等操作。 三、需求分析 3.1驱动程序介绍 驱动程序负责将应用程序如读、写等操作正确无误的传递给相关的硬件,并使硬件能够做出正确反应的代码。驱动程序像一个黑盒子,它隐藏了硬件的工作细节,应用程序只需要通过一组标准化的接口实现对硬件的操作。 3.2 Linux设备驱动程序分类 Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。虽然Linux内核的不断升级,但驱动程序的结构还是相对稳定。 Linux系统的设备分为字符设备(char device),块设备(block device)和网络设备(network device)三种。字符设备是指在存取时没有缓存的设备,而块设备的读写都有缓存来支持,并且块设备必须能够随机存取(random access)。典型的字符设备包括鼠标,键盘,串行口等。块设备主要包括硬盘软盘设备,CD-ROM等。 网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket 机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据传递。系统有支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。 3.3驱动程序的结构 驱动程序的结构如图3.1所示,应用程序经过系统调用,进入核心层,内核要控制硬件需要通过驱动程序实现,驱动程序相当于内核与硬件之间的“系统调用”。

MT明泰_读卡器_API接口函数库使用说明书

API接口函数库使用说明 部文件:V1.0.20 发布时间:2015-04-29

版本更新记录

目录 API接口函数库使用说明 (1) 1. 文档概述 (6) 1.1. 文档围 (6) 1.2. 面向对象 (6) 1.3. 参考资料 (6) 2. 函数库介绍 (6) 2.1. 功能 (6) 2.2. 性能 (7) 3. 运行环境 (7) 3.1. 硬设备 (7) 3.2. 软件的运行平台 (7) 3.3. 函数调用方法 (8) 3.3.1. Delphi调用32位动态库的方法 (8) 3.3.2. VB调用32位动态库的方法 (9) 3.3.3. VC调用32位动态库的方法 (11) 4. API介绍 (11) 4.1. 函数调用流程 (11) 4.1.1. 非接触式存储卡API调用流程 (11) 4.1.2. 非接触式CPU卡片API调用流程 (12) 4.1.3. 接触式CPU卡片API调用流程 (12) 4.1.4. 接触式存储卡片API调用流程 (12) 4.1.5. API调用流程 (12) 4.1.6. 函数操作结果信息表 (16) 4.2. 设备操作函数组 (19) 4.2.1 打开读写器device_open (19) 4.2.2 关闭读写器device_close (19) 4.2.3 判断设备通讯类型device_gettype (19) 4.2.4 设置通讯波特率device_setbaud (20) 4.2.5 获取读写器版本信息device_version (20)

4.2.6 读写器蜂鸣device_beep (21) 4.2.7 LED灯控制 device_ledctrl (21) 4.2.8 获取读写器生产序列号 device_readsnr (22) 4.2.9 获取设备状态 get_device_status (22) 4.2.10 读取读卡器的EEPROM (23) 4.2.11更新读卡器的EEPROM (23) 4.2.12 复位串口配置信息 ReSetupComm (24) 4.2.13 读卡器软复位 device_reset (24) 4.2.14 获取设备状态扩展 get_device_statusEx (25) 4.2.15 获取非接触式CPU卡卡片状态 dev_cardstate (25) 4.2.16 获取接触式CPU卡到位状态 ICC_GetStatus (26) 4.2.17 读EMID号Dev_GetEMID (26) 4.3 接触式卡片操作函数 (27) 4.3.1 判断接触式卡片状态sam_slt_getstate (27) 4.3.2 接触式卡片上电复位sam_slt_reset (28) 4.3.3 接触式卡设置复位波特率sam_slt_reset_baud (28) 4.3.4 接触式卡片下电sam_slt_powerdown (29) 4.4. 非接触 CPU 卡函数 (29) 4.4.1 激活非接触式卡open_card (29) 4.4.2 设置非接触式卡片为halt状态 rf_halt (30) 4.4.3 应用层传输命令card_APDU (31) 4.5 非接触式存储卡操作函数 (31) 4.5.1 激活非接触式存储卡rf_card (31) 4.5.2 非接触式存储卡认证扇区 rf_authentication (33) 4.5.3 非接触式存储卡读数据rf_read (33) 4.5.4 非接触式存储卡写数据rf_write (34) 4.5.5 非接触式存储卡读值块rf_readval (34) 4.5.6 非接触式存储卡写值块rf_initval (35) 4.5.7 非接触式存储卡加值rf_increment (35) 4.5.8 非接触式存储卡减值rf_decrement (36) 4.5.9 非接触式存储卡值传送 rf_transfer (36) 4.6 二代操作函数 (37) 4.6.1 卡操作指令-读卡IDCard_Read (37) 4.6.2 卡操作指令-读卡IDCard_ReadCard (39) 4.6.3卡操作指令根据索引获取数据IDCard_GetCardInfo (40) 4.6.4 获取二代证模块ID IDCard_GetModeID (40) 4.6.5 读卡模块扩展一IDCard_ReadCard_Extra (41) 4.6.6 设置二代证照片存储路径 IDCard_SetPhotoPath (41) 4.6.7 设置二代证照片名字 IDCard_SetPhotoName (42) 4.6.8 读二代证信息扩展二 IDCard_ReadCard_Ex (42) 4.6.9 删除所有二代证照片文件delete_all_photofile (44) 4.6.10 获取卡ID号IDCard_Read_IDNUM (44) 4.6.11 获取卡IDCard_Name (45) 4.6.12 获取卡性别IDCard_Sex (45)

电子营业执照驱动程序使用说明

电子营业执照由上海市工商行政管理局颁发。它作为企业在互联网上的身份标识,为企业上网办事(如:“网上年检”等)提供安全服务。因此,用户应予妥善保存。初次使用电子营业执照时,请先安装驱动程序,并使用驱动光盘内电子营业执照密码修改工具更改初始密码,初始密码为123456。 电子营业执照驱动安装说明 一.驱动程序安装 1、用户用鼠标双击打开安装文件:Setup.exe,进行电子营业执照驱动程序安装,将出现如下欢迎画面: 2、单击“下一步”选择目的安装地址;

3、用户单击“下一步”,创建快捷方式文件夹,单击“取消”,则取消驱动程序安装; 4、单击“下一步”,准备驱动程序安装;

5.单击“下一步”继续安装文件,画面如下; 6.确认是否将证书添加到根存储区中,单击“是”,添加证书,该证书将作为用户信任证书导入到系统证书库,请单击“是”继续安装。

7.若在此次安装前系统已经安装过本驱动程序,则在本次安装时需先删除原有证书,界面如下图,请单击“是”删除原有证书; 8.用户单击“是”,重新将证书添加到根存储区中;

9.电子营业执照驱动程序安装完成,并出现如下界面,单击“完成”,退出安装程序。 二.驱动程序卸载 1.用户进入「开始」菜单,选择程序\电子营业执照驱动程序,单击“卸载电子营业执照驱动程序”;或进入“控制面版”中的“添加或删除程序”,选中电子营业执照驱动程序,单击“更改/删除”按钮,出现如下确认对话框,单击“是”继续卸载,“否”取消卸载。 2.确认将证书从根证书中删除,单击“是”删除证书。

3.卸载程序,出现如下画面,卸载完成后单击“关闭”。完成卸载。

未能成功安装设备驱动程序MTPUSB设备安装失败的解决办法

未能成功安装设备驱动程序M T P U S B设备安装失败的解决办法 文件编码(GHTU-UITID-GGBKT-POIU-WUUI-8968)

M T P U S B设备安装失败未能成功安装设备驱动程序 终极解决方法 环境介绍:电脑系统win7(32位)已安装摩托罗拉手机驱动版本(其他版本应该也行,不行的话去摩托罗拉官网下载最新驱动) 手机型号:摩托罗拉defy mb525(系统) 备注:其他电脑操作系统和不同型号手机可参考此方法,找到相应设置项即可。 问题简介: 1.当我们把手机连接至电脑,把模式调制成“摩托罗拉手机门户”时,出现下列情况 2.过一会之后便会弹出提示说:未能成功安装设备驱动程序

3.单击查看详情便弹出窗口如下图所示: 4.此时桌面右下角图标出现黄色三角号,如图所示: 5.于是我们就开始不淡定了,怎么看怎么别扭、抓狂、按耐不住。下面介绍问题解决方案 解决方法: 1.我的电脑——右键单击——管理——设别管理器,之后会看到如图所示:在便携设备下有黄色三角号提示,即是我们纠结的MTP USB设备安装不成功的展示。

MIUI手机操作系统为例,其他手机操作系统需将USB绑定服务开启即可)。选择设置——系统——共享手机网络——USB绑定,将该选项设置为“开”,这是你会发现如图所示变化,在设备管理器面板中没有了便携设备选项及黄色三角号提醒,如图所示:(但桌面右下角的黄色三角警示还在)

3.在完成以上步骤后,用手机打开WIFI并登录无线WLAN,手机打开网页检验连接是否正常,若正常则如下图所示,黄色三角号警示消失,问题解决;若以上步骤没有解决问题,请先连接WIFI并登录WLAN之后,再按步骤操作。 4.完成以上步骤并解决问题后,选择电脑桌面网络——右键单击——属性,如下图所示:此时不仅手机能上网,而且电脑也能正常连接网络,正常上网。(我的体验是连接数据不稳定,时不时的要手机重新登陆WIFI,才有数据传输,可能是高校WLAN的问题,在家网速快的可以尝试一下) 5.通过这个问题的解决,我才知道原来MTP USB设备安装失败,未能成 功安装设备驱动程序的原因是我们手机里面没有启用该设备服务。今天 才知道MTP USB设备是与手机里的共享手机网络中“USB绑定”服务相关 联的,是电脑用来使用手机WIFI网络连接进行上网的工具。

接口使用说明文档

中国移动短信网关 SP端接口使用手册 China Mobile Shot Message Gateway Interface for SP Manual 作者:沈岗 日期:2004年1月 版本:V1.2

一、CMSMIF.CMPPApp 简要说明: 该类采用CMPP协议(V2.0)实现了SP端与移动短信网关的连接处理。 本类中,采用长连接方式与ISMG通讯。通信双方以客户-服务器方式建立TCP连接,用于双方信息的相互提交。当信道上没有数据传输时,通信双方应每隔时间C发送链路检测包以维持此连接,当链路检测包发出超过时间T后未收到响应,立即再发送链路检测包,再连续发送N-1次后仍未得到响应则断开此连接。参数C、T、N可通过属性配置。 消息发送时采用并发方式,即发送一条消息不等待网关回复确认,继续向网关发送短信,这样发送消息速度非常快,完全取决于网关的处理速度及网络速度。为避免消息丢失,同时采用了滑动窗口流量控制,窗口大小可通过属性设置。 消息接收、网络断开等采用事件触发方式,不需应用程序轮询,在此接口基础之上编程方便。 类中运用了多线程技术,如一条线程处理发送网络包,而另一条线程处理从网关上接收网络包,其他还有一些线程处理检测包、网络连接情况监测等,使程序思路明确、执行效率很高、运行非常稳定。 (一)属性 1.ActiveInterval 说明:检测包发送时间间隔,单位:毫秒。默认值为120000,即120秒。为上 述类说明中的C参数。 2.MaxNetworkPackSize 说明:与ISMG通讯时最大网络包大小,单位:字节。默认值为512Byte。 3.MaxRetryTimes 说明:网络超时最大重发次数,单位:次。默认值为3次。为类说明中的N。 4.OverTime 说明:网络包发送超时时间,单位:毫秒,超过此值还未收到回复则重发。默认值 为60000,即60秒。为类说明中的T。 5.QueueLength 说明:网络队列大小,单位:个,默认值为20。为类说明中的滑动窗口大小, 以控制发送流量。 (二)方法 1.ConnectToIsmg 方法说明: 连接到远程短信网关ISMG上,只有连接到远程短信网关上,才可进行短信收发操作。 在本操作中,自动初始化本地Socket,以连接到指定IP服务器的指定端口上。 声明原型:int ConnectToIsmg(string ServerIP,int Port,string SP_ID,string Secret,string SN) 参数说明: ServerIP:远程短信网关服务器的IP地址,如211.138.200.51 Port:远程短信网关服务器的端口号,如7890 SP_ID:企业服务代码

C++中所有函数的使用说明

C++函数大全 2010-04-10 10:11 数学函数,所在函数库为math.h、stdlib.h、string.h、float.h int abs(int i) 返回整型参数i的绝对值 double cabs(struct complex znum) 返回复数znum的绝对值 double fabs(double x) 返回双精度参数x的绝对值 long labs(long n) 返回长整型参数n的绝对值 double exp(double x) 返回指数函数ex的值 double frexp(double value,int *eptr) 返回value=x*2n中x的值,n存贮在eptr中 double ldexp(double value,int exp); 返回value*2exp的值 double log(double x) 返回logex的值 double log10(double x) 返回log10x的值 double pow(double x,double y) 返回xy的值 double pow10(int p) 返回10p的值 double sqrt(double x) 返回+√x的值 double acos(double x) 返回x的反余弦cos-1(x)值,x为弧度 double asin(double x) 返回x的反正弦sin-1(x)值,x为弧度 double atan(double x) 返回x的反正切tan-1(x)值,x为弧度 double atan2(double y,double x) 返回y/x的反正切tan-1(x)值,y的x为弧度 double cos(double x) 返回x的余弦cos(x)值,x为弧度 double sin(double x) 返回x的正弦sin(x)值,x为弧度 double tan(double x) 返回x的正切tan(x)值,x为弧度 double cosh(double x) 返回x的双曲余弦cosh(x)值,x为弧度 double sinh(double x) 返回x的双曲正弦sinh(x)值,x为弧度 double tanh(double x) 返回x的双曲正切tanh(x)值,x为弧度 double hypot(double x,double y) 返回直角三角形斜边的长度(z), x和y为直角边的长度,z2=x2+y2 double ceil(double x) 返回不小于x的最小整数 double floor(double x) 返回不大于x的最大整数 void srand(unsigned seed) 初始化随机数发生器 int rand() 产生一个随机数并返回这个数 double poly(double x,int n,double c[])从参数产生一个多项式 double modf(double value,double *iptr)将双精度数value分解成尾数和阶double fmod(double x,double y) 返回x/y的余数 double frexp(double value,int *eptr) 将双精度数value分成尾数和阶double atof(char *nptr) 将字符串nptr转换成浮点数并返回这个浮点数double atoi(char *nptr) 将字符串nptr转换成整数并返回这个整数 double atol(char *nptr) 将字符串nptr转换成长整数并返回这个整数 char *ecvt(double value,int ndigit,int *decpt,int *sign) 将浮点数value转换成字符串并返回该字符串 char *fcvt(double value,int ndigit,int *decpt,int *sign) 将浮点数value转换成字符串并返回该字符串

设备驱动程序

驱动程序 驱动程序一般指的是设备驱动程序(Device Driver),是一种可以使计算机和设备通信的特殊程序。相当于硬件的接口,操作系统只有通过这个接口,才能控制硬件设备的工作,假如某设备的驱动程序未能正确安装,便不能正常工作。 因此,驱动程序被比作“硬件的灵魂”、“硬件的主宰”、和“硬件和系统之间的桥梁”等。 中文名 驱动程序 外文名 Device Driver 全称 设备驱动程序 性质 可使计算机和设备通信的特殊程序 目录 1定义 2作用 3界定 ?正式版 ?认证版 ?第三方 ?修改版 ?测试版 4驱动程序的开发 ?微软平台 ?Unix平台 5安装顺序 6inf文件 1定义 驱动程序(Device Driver)全称为“设备驱动程序”,是一种可以使计算机和设备通信的特殊程序,可以说相当于硬件的接口,操作系统只能通过这个接口,才能控制硬件设备的工作,假如某设备的驱动程序未能正确安装,便不能正常工作。 惠普显卡驱动安装 正因为这个原因,驱动程序在系统中的所占的地位十分重要,一般当操作系统安装完毕后,首要的便是安装硬件设备的驱动程序。不过,大多数情况下,我们并不需要安装所有硬件设备的驱动程序,例如硬盘、显示器、光驱等就不需要安装驱动程序,而显卡、声卡、扫描仪、摄像头、Modem等就需要安装驱动程序。另外,不同版本的操作系统对硬件设

备的支持也是不同的,一般情况下版本越高所支持的硬件设备也越多,例如笔者使用了Windows XP,装好系统后一个驱动程序也不用安装。 设备驱动程序用来将硬件本身的功能告诉操作系统,完成硬件设备电子信号与操作系统及软件的高级编程语言之间的互相翻译。当操作系统需要使用某个硬件时,比如:让声卡播放音乐,它会先发送相应指令到声卡驱动程序,声卡驱动程序接收到后,马上将其翻译成声卡才能听懂的电子信号命令,从而让声卡播放音乐。 所以简单的说,驱动程序提供了硬件到操作系统的一个接口以及协调二者之间的关系,而因为驱动程序有如此重要的作用,所以人们都称“驱动程序是硬件的灵魂”、“硬件的主宰”,同时驱动程序也被形象的称为“硬件和系统之间的桥梁”。 戴尔电脑驱动盘 驱动程序即添加到操作系统中的一小块代码,其中包含有关硬件设备的信息。有了此信息,计算机就可以与设备进行通信。驱动程序是硬件厂商根据操作系统编写的配置文件,可以说没有驱动程序,计算机中的硬件就无法工作。操作系统不同,硬件的驱动程序也不同,各个硬件厂商为了保证硬件的兼容性及增强硬件的功能会不断地升级驱动程序。如:Nvidia显卡芯片公司平均每个月会升级显卡驱动程序2-3次。驱动程序是硬件的一部分,当你安装新硬件时,驱动程序是一项不可或缺的重要元件。凡是安装一个原本不属于你电脑中的硬件设备时,系统就会要求你安装驱动程序,将新的硬件与电脑系统连接起来。驱动程序扮演沟通的角色,把硬件的功能告诉电脑系统,并且也将系统的指令传达给硬件,让它开始工作。 当你在安装新硬件时总会被要求放入“这种硬件的驱动程序”,很多人这时就开始头痛。不是找不到驱动程序的盘片,就是找不到文件的位置,或是根本不知道什么是驱动程序。比如安装打印机这类的硬件外设,并不是把连接线接上就算完成,如果你这时候开始使用,系统会告诉你,找不到驱动程序。怎么办呢参照说明书也未必就能顺利安装。其实在安装方面还是有一定的惯例与通则可寻的,这些都可以帮你做到无障碍安装。 在Windows系统中,需要安装主板、光驱、显卡、声卡等一套完整的驱动程序。如果你需要外接别的硬件设备,则还要安装相应的驱动程序,如:外接游戏硬件要安装手柄、方向盘、摇杆、跳舞毯等的驱动程序,外接打印机要安装打印机驱动程序,上网或接入局域网要安装网卡、Modem甚至ISDN、ADSL的驱动程序。说了这么多的驱动程序,你是否有一点头痛了。下面就介绍Windows系统中各种的不同硬件设备的驱动程序,希望能让你拨云见日。 在Windows 9x下,驱动程序按照其提供的硬件支持可以分为:声卡驱动程序、显卡驱动程序、鼠标驱动程序、主板驱动程序、网络设备驱动程序、打印机驱动程序、扫描仪驱动程序等等。为什么没有CPU、内存驱动程序呢因为CPU和内存无需驱动程序便可使用,不仅如此,绝大多数键盘、鼠标、硬盘、软驱、显示器和主板上的标准设备都可以用Windows 自带的标准驱动程序来驱动,当然其它特定功能除外。如果你需要在Windows系统中的DOS 模式下使用光驱,那么还需要在DOS模式下安装光驱驱动程序。多数显卡、声卡、网卡等内置扩展卡和打印机、扫描仪、外置Modem等外设都需要安装与设备型号相符的驱动程序,否则无法发挥其部分或全部功能。驱动程序一般可通过三种途径得到,一是购买的硬件附

输入法接口函数说明

输入法编程(转贴)(快看) IME输入法编程 第一章Windows9x系统下汉字输入法的基本原理 Windows系统下汉字输入法实际上是将输入的标准ascii字符串按照一定的编码规则转换为汉字或汉字串,进入到目的地。由于应用程序各不相同,用户不可能自己去设计转换程序,因此,汉字输入自然而然落到WINDOWS系统管理中。 一、输入法与系统的关系 键盘事件应用程序 || Windows的USER.EXE | 输入法管理器 | 输入法 系统的键盘事件有windows的user.exe软件接收后,user.exe在将键盘事件传导输入法管理器(Input Method Manager,简称IMM)中,管理器再将键盘事件传到输入法中,输入法根据用户编码字典,翻译键盘事件为对应的汉字(或汉字串),然后再反传到user.exe 中,user.exe再将翻译后的键盘事件传给当前正运行的应用程序,从而完成汉字的输入。 二、汉字输入法的组成 微软Windows9x系统中汉字输入法的名称是"Input Method Editor " ,简称IME,输入法的程序名称为:*.ime,数据文件名称为*.MB,即通常说的输入法编码表(字典). 实际上IME文件是一个动态连接库程序(DLL),它与dll文件没有区别,只是名称不同而已。 一般汉字输入法都由三个窗口组成: 状态窗口(Status Windows)-显示当前的输入法状态(中文还是英文等站环信息); 编码输入窗口(Composition Windows)-显示当前击键情况; 汉字选择窗口(Candidates Windows)-列出当前编码的全部汉字(串),供用户选择或查询。 上述三个窗口由基本的用户接口(User Interface )函数管理着。 现在我们用Dumpbin.exe打开微软提供的拼音输入法(WINDOWS\SYSTEM\WINPY.IME)看看它有什么组成(这里一WINDOWS98为例,并假定windows系统安装在c:盘下):

Linux网络设备驱动

嵌入式培训专家
Linux网络设备驱动
主讲:宋宝华
https://www.doczj.com/doc/de5547988.html,

华清远见
今天的内容
vLinux网络设备驱动架构 vLinux网络设备驱动数据流程
? NON-NAPI模式数据接收流程 ? NAPI模式数据接收流程 ? 数据发送流程
vLinux网络协议栈的实现
? TCP/UDP/IP/MAC各层数据传递 ? 网络系统调用与socket

华清远见
Linux网络设备驱动架构

华清远见
net_device
struct net_device_ops { int (*ndo_open)(struct net_device *dev); int (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); int (*ndo_set_mac_address)(struct net_device *dev, void *addr); int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); void (*ndo_tx_timeout) (struct net_device *dev); ... }
struct net_device { struct net_device_stats stats; const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; ... }
struct ethtool_ops { int (*get_settings)(struct net_device *, struct ethtool_cmd *); int (*set_settings)(struct net_device *, struct ethtool_cmd *); void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); int (*get_regs_len)(struct net_device *); ... }

安装WIN7时提示缺少所需的CDDVD驱动器设备驱动程序的解决办法

安装WIN7时提示 “缺少所需的CD/DVD驱动器设备驱动程序” 的解决办法 问题描述:安装win7时提示:“缺少所需的CD/DVD驱动器设备驱动程序。…………”然后找遍整个硬盘/光盘也找不到合适的驱动,安装无法继续。 ————————————————————————————————————————————————————————————— 现在安装系统肯定用光驱的人不多,一般最简单的方法就是通过U盘来安装WINDOWS7这个操作系统,实际操作过程中我们用到最多的工具要数UltraISO这个软件了,现在的最新版本应该是9.5。通过插入U盘,之后在UltraISO的启动标签中,我们选择写入硬盘就可以制作我们想要的U盘WINDOWS7安装盘。 但实际操作过程中,我们会碰到一个比较麻烦的问题就是出现:缺少所需的CD/DVD驱动器设备驱动程序,这时我们的安装是没有办法继续的如下图:

其实从表面看,好像真像他提示的一样,可能是缺少驱动了,其实不然,这是微软的提示误导了我们,我们可以看一下下图 我们点击浏览按扭之后会出现如上的图片,在上图中我们可以清楚地看到硬盘的分区情况,那证明不是驱动的问题,但就是在这个窗口其实细心的朋友应该可以看出情况来,先买一个关子。这时我们没有办法继续安装,那怎么办呢?这时我们关掉上图中要我们找驱动的界面,我们回到开始安装界面,然后同时按下Shift+F10键,这时会弹出命令窗口,如下图: 在这个界面同时按下Shift+F10,出现如下窗口

我们用WINDODWS 7提供的硬盘分区命令:diskpart 来查看我们的驱动器情况,先在默认窗口中输入:diskpart 回车,提到下图 再输入:list disk 回车,图如下

USB设备驱动程序设计

USB设备驱动程序设计 引言 USB 总线是1995 年微软、IBM 等公司推出的一种新型通信标准总线, 特点是速度快、价格低、独立供电、支持热插拔等,其版本从早期的1.0、1.1 已经发展到目前的2.0 版本,2.0 版本的最高数据传输速度达到480Mbit/s,能 满足包括视频在内的多种高速外部设备的数据传输要求,由于其众多的优点,USB 总线越来越多的被应用到计算机与外设的接口中,芯片厂家也提供了多种USB 接口芯片供设计者使用,为了开发出功能强大的USB 设备,设计者往往 需要自己开发USB 设备驱动程序,驱动程序开发一直是Windows 开发中较难 的一个方面,但是通过使用专门的驱动程序开发包能减小开发的难度,提高工 作效率,本文使用Compuware Numega 公司的DriverStudio3.2 开发包,开发了基于NXP 公司USB2.0 控制芯片ISP1581 的USB 设备驱动程序。 USB 设备驱动程序的模型 USB 设备驱动程序是一种典型的WDM(Windows Driver Model)驱动程序,其程序模型如图1 所示。用户应用程序工作在Windows 操作系统的用户模式层,它不能直接访问USB 设备,当需要访问时,通过调用操作系统的 API(Application programming interface)函数生成I/O 请求信息包(IRP),IRP 被传输到工作于内核模式层的设备驱动程序,并通过驱动程序完成与UBS 外设通 信。设备驱动程序包括两层:函数驱动程序层和总线驱动程序层,函数驱动程 序一方面通过IRP 及API 函数与应用程序通信,另一方面调用相应的总线驱动 程序,总线驱动程序完成和外设硬件通信。USB 总线驱动程序已经由操作系统 提供,驱动程序开发的重点是函数驱动程序。 USB 设备驱动程序的设计

CAN应用接口函数

CAN应用接口函数库使用手册V1.0

目录 1.接口卡设备类型定义 (3) 2.错误码定义 (4) 3.函数库中的数据结构定义 (5) 3.1.VCI_BOARD_INFO (5) 3.2.VCI_CAN_OBJ (6) 3.3.VCI_CAN_STATUS (7) 3.4.VCI_ERR_INFO (8) 3.5.VCI_INIT_CONFIG (9) 3.6.VCI_RxAcptMask (10) 3.7.VCI_RxAcptFilter (11) 4.接口库函数说明 (12) 4.1.FD_OpenDevice (12) 4.2.FD_CloseDevice (13) 4.3.FD_InitCan (14) 4.4.FD_ReadBoardInfo (15) 4.5.FD_GetReceiveNum (16) 4.6.FD_ClearBuffer (17) 4.7.FD_StartCAN (18) 4.8.FD_ResetCAN (19) 4.9.FD_Transmit (20) 4.10.FD_Receive (21) 4.11.FD_RxAcptMaskInit (22) 4.12.FD_DisableRxAcptFilter (23) 4.13.FD_RxAcptFilterInit (24) 4.13.FD_SetOperationMode (25) 5.接口库函数使用方法 (26) 5.1.VC调用动态库的方法 (26) 5.2.VB调用动态库的方法 (26)

1.接口卡设备类型定义 各个接口卡的类型定义如下: 设备名称设备类型号备注 USBCAN11第一代USBCAN USBCAN22第二代USBCAN CAN2323待开发PCICAN4待开发

常用网络设备设备

物理层设备 1.调制解调器 调制解调器的英文名称为modem,来源于Modulator/Demodulator,即调制器/解调器。 ⑴工作原理 调制解调器是由调制器与解调器组合而成的,故称为调制解调器。调制器的基本职能就是把从终端设备和计算机送出的数字信号转变成适合在电话线、有线电视线等模拟信道上传输的模拟信号;解调器的基本职能是将从模拟信道上接收到的模拟信号恢复成数字信号,交给终端计算机处理。 ⑵调制与解调方式 调制,有模拟调制和数字调制之分。模拟调制是对载波信号的参量进行连续地估值;而数字调制使用载波信号的某些离散状态来表征所传送的信息,在接收端对载波信号的离散参量进行检测。调制是指利用载波信号的一个或几个参数的变化来表示数字信号的一种过程。 调制方式相应的有:调幅、调频和调相三种基本方式。 调幅:振幅调制其载波信号将随着调制信号的振幅而变化。 调频:载波信号的频率随着调制信号而改变。 调相:相位调制有两相调制、四相调制和八相调制几种方式。 ⑶调制解调器的分类 按安装位置:调解解调器可以分为内置式和外置式 按传输速率分类:低速调制解调器,其传输速率在9600bps以下;中速调制解调器,其传输速率在9.6~19.2kbps之间;高速调制解调器,传输速率达到19.2~56kbps。 ⑷调制解调器的功能 ?差错控制功能:差错控制为了克服线路传输中出现的数据差错,实现调制解调器至远端调制解调器的无差错数据传送。 ?数据压缩功能:数据压缩功能是为了提高线路传输中的数据吞吐率,使数据更快地传送至对方。 ⑸调制解调器的安装 调制解调器的安装由两部分组成,线路的连接和驱动程序的安装。 线路连接: ?将电话线引线的一端插头插入调制解调器后面LINE端口。

API函数手册

POSTEK PPLⅠAPI函数手册 G Series 条码标签打印机 Version 2.00 深圳市博思得通信发展有限公司 二○○四年

API函数库文件说明 名称:CDFPSK.dll 中文版本编号:1.X.X.X 英文版本编号:2.X.X.X 版权所有:?2004深圳市博思得通信发展有限公司。保留所有权利。 用途 本API函数库为深圳市博思得通信发展有限公司条码标签打印机的用户提供一组命令,为他们编写基于Windows9X,NT,2000,XP等操作系统的应用程序提供便利。 本API函数库仅支持本公司产品。 缩略语对照 PPLⅠ:深圳市博思得通信发展有限公司的第一套打印机编程语言(Printer Porgram Language Ⅰ)。 API:应用程序编程接口(Application Program Interface)。 Dots:像素(pixel)是一种计算机科学技术尺寸单位,原指电视图像成像的最小单位,在打印机领域表示打印机的最小打印成像单位:1dot等于一英寸除以打印机的最大分辨率。 - 对于203DPI的打印机来说, 1dot = 25.4mm/203 = 0.125mm(1dot = 1000 / 203 = 5mil); - 对于300DPI的打印机来说, 1dot = 25.4mm/300 = 0.085mm(1dot = 1000 / 300 = 3mil)。 TrueType Font:是基于Windows操作系统使用,可装卸的字体。 - 已经安装的TrueType Font,都可以被本函数使用。 使用前须知 字符串 * 字符串以双引号(“)作为起始和结束标记; *

驱动程序安装方法

驱动程序安装方法 初识电脑的人,可能为安装驱动程序而头疼。因为对驱动程序了解得不多就会在安装过程中走不少弯路,下面就给大家介绍一下安装驱动程序的两种常用方法和一些实用技巧。 一、安装即插即用设备的驱动程序 安装前的准备工作很重要,一般我们拿到要安装的新硬件时,首先要查看外包装盒,了解产品的型号、盒内部件及产品对系统的最低要求等信息。紧接着就要打开包装盒,取出硬件产品、说明书和驱动盘(光盘或软盘),认真阅读说明书或驱动盘上的ReadMe 文件,一般说明书上写有安装方法和步骤,以及安装注意事项。除了阅读说明书外,还应记得硬件产品上印刷的各种信息以及板卡产品使用的主要芯片的型号。这些信息就是确定产品型号及厂家的重要依据,只有知道这些,才能在网上查找最新的驱动程序。最后按照说明书上介绍的方法来安装硬件。通常安装内置板卡、内置驱动器,使用串口或PS /2接口的设备都应关机断电后再操作,而安装USB设备、笔记本电脑的PC卡时可以带电热插拔。当然,如果是Win2000系统则均可热插拔。完成前面的准备工作之后,就可以启动Windows 来安装驱动程序了。通常情况下,Windows 能够自动检测到PCI 卡、AGP卡、ISA卡、USB设备以及多数打印机和扫描仪等外设,并提示用户插入安装盘。以YAMAHA724声卡为例,其在Win98下安装驱动程序的详细步骤如下。 1.Win98在启动过程中会自动检测即插即用设备,一旦发现了新设备,并且在INF目录下有该设备的.inf 文件,系统将自动安装驱动程序;如果这是一个新设备,INF目录下没有相应的.inf 文件,那么系统就会启动硬件向导。我们单击“下一步”让安装向导自动搜索设备驱动程序,然后再单击“下一步”。 2.在图3中只选中“指定位置”,插入驱动光盘,并单击“浏览”,根据说明书的介绍,选择简体中文版驱动程序所在的目录“E:\Lx_so u n d /Yamaha /Win9X”,点“确定”后单击“下一步”。需要注意的是:Win95的安装向导没有自动搜索功能,我们必须选择“从磁盘安装”,并指定驱动程序所在的位置。驱动程序所在的目录通常是驱动盘上的“Win95”、“Win9X”或“Windows98”目录。 3.硬件安装向导会在指定目录下查找与设备相符的.inf 文件,此例中,硬件向导将在指定目录下找到并向作户报告发现YAMAHA724声卡驱动程序,继续按“下一步”。 4.硬件安装向导显示Windows 准备安装的驱动程序的信息,单击“下一步”后,硬件向导便会根据.inf 文件的内容把指定的文件拷贝到相应的目录下,并在注册表中写入相应的信息,安装成功后显示出对话框。 5.对多数设备而言,到这里驱动程序就算安装完毕了。但如果你安装的是声卡那就还未结束,因为刚才的步骤只能装完声卡的主体部分。单击“完成”后,Windows 又会报告发现了两个新硬件,分别是声卡的DOS 仿真部件和声卡上的游戏控制端口。由于此时SBPCI9X.inf 文件已经被拷到“Windows /INF /Other”子目录下,所以Windows 能够自动安装好这两种设备的驱动程序。 6.驱动程序安装完毕后,我们需要检查设备能否正常工作。检查前还要进行额外的设置,例如使用网卡之前必须先安装和设置网络协议,用调制解调器上网之前要先“新建连接”等。此例中,在“控制面板”里打开“系统”→“设备管理器”→“声音、视频和游戏控制器”,可以看见下面多了三个设备,只要设备的小图标上没有黄色惊叹号,就表示驱动程序运行正常。 二、安装非即插即用设备的驱动程序

安装Windows7时电脑提示缺少所需的CDDVD驱动器设备驱动程序的原因

安装Windows7时电脑提示缺少所需的CD/DVD驱动器设备驱动程序的原因 以及解决方案 来源:互联网作者:佚名时间:09-03 15:16:35【大中小】问题描述:安装win7时提示:“缺少所需的CD/DVD驱动器设备驱动程序。…………”然后找遍整个硬盘/光盘也找不到合适的驱动,安装无法继续。 现在安装系统肯定用光驱的人不多,一般最简单的方法就是通过U盘来安装WINDOWS7这个操作系统,实际操作过程中我们用到最多的工具要数UltraISO这个软件了,现在的最新版本应该是9.5。通过插入U盘,之后在UltraISO的启动标签中,我们选择写入硬盘就可以制作我们想要的U盘WINDOWS7安装盘。 但实际操作过程中,我们会碰到一个比较麻烦的问题就是出现:缺少所需的CD/DVD驱动器设备驱动程序,这时我们的安装是没有办法继续的如下图:

其实从表面看,好像真像他提示的一样,可能是缺少驱动了,其实不然,这是微软的提示误导了我们,我们可以看一下下图 我们点击浏览按扭之后会出现如上的图片,在上图中我们可以清楚地看到硬盘的分区情况,那证明不是驱动的问题,但就是在这个窗口其实细心的朋友应该可以看出情况来,先买一个关子。这时我们没有办法继续安装,那怎么办呢?这时我们关掉上图中要我们找驱动的界面,我们回到开始安装界面,然后同时按下Shift+F10键,这时会弹出命令窗口,如下图:

在这个界面同时按下Shift+F10,出现如下窗口 我们用WINDODWS 7提供的硬盘分区命令:diskpart 来查看我们的驱动器情况,先在默认窗口中输入:diskpart 回车,提到下图

DLL函数接口说明

typedef void __stdcall (*fun_AddResult)(char pDataStr[10][255],double pDataDouble[100]); extern "C" void __declspec(dllimport) __stdcall SetBackColor(TColor pBkColor); extern "C" void __declspec(dllimport) __stdcall FanSelect( void *pSeriesNames,//系列名称YLDStr * (typedef struct{char Data[255];} YLDStr;) const int pSeriesNameCount,//系列名称个数,-1时表示全部选择 void *pSubSeriesNames,//系列名称YLDStr * (typedef struct{char Data[255];} YLDStr;) const int pSubSeriesNameCount,//子系列名称个数,-1时表示全部选择 const double &pFlow, //风量 const int &pFlowUnitType, //风量单位类型0-m^3/h 1-m^3/s 2-l/s 3-cfm const double &pPres, //风压 const int &pPresUnitType, //风压单位类型0-Pa 1-mmH2O 2-kgf/cm^2 3-inH2O const int &pPresType, //风压类型0-全压1-静压 const int &pOutFanType,//出风方式0-管道出风1-自由出风 const double &pAirDensity,//空气密度 const double &pMotorSafeCoff,//电机容量安全系数(%) const bool &pUserSetMotorSafeCoff,//用户设定了电机容量安全系数 double &rFlow_STDUnit,//标准单位下的风量 fun_AddResult pAddResult//函数指针,用于回传数据 ); extern "C" void __declspec(dllimport) __stdcall Belt_Selection( const char *pSeriesName, const char *pSubTypeName, const char *pModelName, const int &pSped_DataType,//电机转速类型0-标准数据1-用户数据默认为0(界面选择) const int &pHz_DataType,//频率选择0-50HZ 1-60HZ(界面选择) const int &pRotation,//出风旋转角度R0,R90,R180,R270(参考常量定义) const double &pFanSped,//风机转速(第一步计算得到) const double &pMotorPow,//电机功率(第一步计算得到) const double &pFlow_STDUnit,//标准单位下的风量 const double &pTPres,//全压(第一步计算得到) const double &pSPres,//静压(第一步计算得到) const double &pFTEff,//全压内效率(第一步计算得到) const double &pSdbA,//噪声(第一步计算得到) const int &pV olt,//用户选择的电压(参考电压常量声明V380 V400) , bool pPole[4],//用户选择的极数2,4,6,8 bool pBeltType[4],//用户选择的皮带类型SPZ SPA SPB SPC const bool &pBeSetFanSped,//用户设定了风机转速 const double &pFanSpedRt_User,//用户设定的风机转速差 const bool &pBeSetBeltMoveSped,//用户设定最大皮带运动速度 const double &pBeltMoveSped_User,//用户设定的最大皮带运动速度

相关主题
文本预览
相关文档 最新文档