﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-iniwf-随笔分类-API应用</title><link>http://www.cppblog.com/iniwf/category/9825.html</link><description>风是温柔的，雨是伤心的，云是快乐的，月是多情的，爱是迷失的，恋是醉人的，情是难忘的，天是长久的，地是永恒的</description><language>zh-cn</language><lastBuildDate>Tue, 14 Apr 2009 11:27:45 GMT</lastBuildDate><pubDate>Tue, 14 Apr 2009 11:27:45 GMT</pubDate><ttl>60</ttl><item><title>在NT中直接访问物理内存</title><link>http://www.cppblog.com/iniwf/archive/2009/04/13/79827.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 13 Apr 2009 13:17:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/13/79827.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79827.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/13/79827.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79827.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79827.html</trackback:ping><description><![CDATA[转自<a href="http://blog.csdn.net/bhw98/archive/2003/11/13/19677.aspx">http://blog.csdn.net/bhw98/archive/2003/11/13/19677.aspx</a><br><br>
<p class=bhw98>我们知道，在NT/2K/XP中，操作系统利用虚拟内存管理技术来维护地址空间映像，每个进程分配一个4GB的虚拟地址空间。运行在用户态的应用程序，不能直接访问物理内存地址；而运行在核心态的驱动程序，能将虚拟地址空间映射为物理地址空间，从而访问物理内存地址。</p>
<p class=bhw98>如果要在应用程序中以物理地址方式访问内存，自然而然的办法，是编写一个专用的驱动程序（如大家熟悉的WinIO），里面设置一定的IOCTL码，应用程序通过调用DeviceIoCtrol()来实现这样的功能。</p>
<p class=bhw98>那么，有没有一种方法，省去编写专用驱动程序这一步，很方便地就能访问物理内存呢？答案是肯定的。实际上，微软早就给我们准备好了一套办法，只是他们秘而不宣罢了。系统内建一个叫做PhysicalMemory的内核对象，可以通过系统核心文件NTDLL.DLL中的有关API进行操纵，从而实现物理内存的直接访问。微软声称这些API是用于驱动程序开发的，在VC/.NET中未提供原型说明和库文件，然而事实证明在应用程序中调用它们是没有问题的。我们感兴趣的API主要包括：</p>
<li class=bhw98>ZwOpenSection 或 NtOpenSection - 打开内核对象
<li class=bhw98>ZwMapViewOfSection 或 NtMapViewOfSection - 映射虚拟地址空间
<li class=bhw98>ZwUnmapViewOfSection 或 NtUnmapViewOfSection - 取消地址空间映射
<li class=bhw98>RtlInitUnicodeString - 用UNICODE串初始化UNICODE描述的结构
<p class=bhw98>以下的代码描述了如何利用NTDLL.DLL中的上述几个API，实现对物理内存的读取。需要指出的是，只有system拥有读写权限，administrator只有读权限，而user连读权限都没有。这一点，是不能与专用驱动程序方法向相比的。</p>
<p class=bhw98>在VC/.NET中，由于没有相应的原型说明和库文件，我们用GetProcAddress()进行DLL显式调用。前面大段的代码，用于说明必需的类型和结构。读取物理内存的主要步骤为：打开内核对象 &#8594; 映射虚拟地址空间 &#8594; 读取(复制)内存 &#8594; 取消地址空间映射。</p>
<pre class=bhw98><code class=bhw98><span class=key>typedef</span> LONG    NTSTATUS;
<span class=key>typedef</span> <span class=key>struct</span> _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
<span class=key>typedef</span> <span class=key>enum</span> _SECTION_INHERIT
{
ViewShare = <span class=num>1</span>,
ViewUnmap = <span class=num>2</span>
} SECTION_INHERIT, *PSECTION_INHERIT;
<span class=key>typedef</span> <span class=key>struct</span> _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
<span class=key>#define</span> InitializeObjectAttributes( p, n, a, r, s ) { \
(p)-&gt;Length = <span class=key>sizeof</span>( OBJECT_ATTRIBUTES ); \
(p)-&gt;RootDirectory = r; \
(p)-&gt;Attributes = a; \
(p)-&gt;ObjectName = n; \
(p)-&gt;SecurityDescriptor = s; \
(p)-&gt;SecurityQualityOfService = NULL; \
}
<span class=rem>// Interesting functions in NTDLL</span>
<span class=key>typedef</span> NTSTATUS (WINAPI *ZwOpenSectionProc)
(
PHANDLE SectionHandle,
DWORD DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes
);
<span class=key>typedef</span> NTSTATUS (WINAPI *ZwMapViewOfSectionProc)
(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG ZeroBits,
ULONG CommitSize,
PLARGE_INTEGER SectionOffset,
PULONG ViewSize,
SECTION_INHERIT InheritDisposition,
ULONG AllocationType,
ULONG Protect
);
<span class=key>typedef</span> NTSTATUS (WINAPI *ZwUnmapViewOfSectionProc)
(
HANDLE ProcessHandle,
PVOID BaseAddress
);
<span class=key>typedef</span> VOID (WINAPI *RtlInitUnicodeStringProc)
(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);
<span class=rem>// Global variables</span>
<span class=key>static</span> HMODULE hModule = NULL;
<span class=key>static</span> HANDLE hPhysicalMemory = NULL;
<span class=key>static</span> ZwOpenSectionProc ZwOpenSection;
<span class=key>static</span> ZwMapViewOfSectionProc ZwMapViewOfSection;
<span class=key>static</span> ZwUnmapViewOfSectionProc ZwUnmapViewOfSection;
<span class=key>static</span> RtlInitUnicodeStringProc RtlInitUnicodeString;
<span class=rem>// initialize</span>
BOOL InitPhysicalMemory()
{
<span class=key>if</span> (!(hModule = LoadLibrary(<span class=str>"ntdll.dll"</span>)))
{
<span class=key>return</span> FALSE;
}
<span class=rem>// 以下从NTDLL获取我们需要的几个函数指针</span>
<span class=key>if</span> (!(ZwOpenSection = (ZwOpenSectionProc)GetProcAddress(hModule, <span class=str>"ZwOpenSection"</span>)))
{
<span class=key>return</span> FALSE;
}
<span class=key>if</span> (!(ZwMapViewOfSection = (ZwMapViewOfSectionProc)GetProcAddress(hModule, <span class=str>"ZwMapViewOfSection"</span>)))
{
<span class=key>return</span> FALSE;
}
<span class=key>if</span> (!(ZwUnmapViewOfSection = (ZwUnmapViewOfSectionProc)GetProcAddress(hModule, <span class=str>"ZwUnmapViewOfSection"</span>)))
{
<span class=key>return</span> FALSE;
}
<span class=key>if</span> (!(RtlInitUnicodeString = (RtlInitUnicodeStringProc)GetProcAddress(hModule, <span class=str>"RtlInitUnicodeString"</span>)))
{
<span class=key>return</span> FALSE;
}
<span class=rem>// 以下打开内核对象</span>
WCHAR PhysicalMemoryName[] = L<span class=str>"\\Device\\PhysicalMemory"</span>;
UNICODE_STRING PhysicalMemoryString;
OBJECT_ATTRIBUTES attributes;
RtlInitUnicodeString(&amp;PhysicalMemoryString, PhysicalMemoryName);
InitializeObjectAttributes(&amp;attributes, &amp;PhysicalMemoryString, <span class=num>0</span>, NULL, NULL);
NTSTATUS status = ZwOpenSection(&amp;hPhysicalMemory, SECTION_MAP_READ, &amp;attributes );
<span class=key>return</span> (status &gt;= <span class=num>0</span>);
}
<span class=rem>// terminate -- free handles</span>
<span class=key>void</span> ExitPhysicalMemory()
{
<span class=key>if</span> (hPhysicalMemory != NULL)
{
CloseHandle(hPhysicalMemory);
}
<span class=key>if</span> (hModule != NULL)
{
FreeLibrary(hModule);
}
}
BOOL ReadPhysicalMemory(PVOID buffer, DWORD address, DWORD length)
{
DWORD outlen;            <span class=rem>// 输出长度，根据内存分页大小可能大于要求的长度</span>
PVOID vaddress;          <span class=rem>// 映射的虚地址</span>
NTSTATUS status;         <span class=rem>// NTDLL函数返回的状态</span>
LARGE_INTEGER base;      <span class=rem>// 物理内存地址</span>
vaddress = <span class=num>0</span>;
outlen = length;
base.QuadPart = (ULONGLONG)(address);
<span class=rem>// 映射物理内存地址到当前进程的虚地址空间</span>
status = ZwMapViewOfSection(hPhysicalMemory,
(HANDLE) <span class=num>-1</span>,
(PVOID *)&amp;vaddress,
<span class=num>0</span>,
length,
&amp;base,
&amp;outlen,
ViewShare,
<span class=num>0</span>,
PAGE_READONLY);
<span class=key>if</span> (status &lt; <span class=num>0</span>)
{
<span class=key>return</span> FALSE;
}
<span class=rem>// 当前进程的虚地址空间中，复制数据到输出缓冲区</span>
memmove(buffer, vaddress, length);
<span class=rem>// 完成访问，取消地址映射</span>
status = ZwUnmapViewOfSection((HANDLE)<span class=num>-1</span>, (PVOID)vaddress);
<span class=key>return</span> (status &gt;= <span class=num>0</span>);
}
<span class=rem>// 一个测试函数，从物理地址0xfe000开始，读取4096个字节</span>
<span class=rem>// 对于Award BIOS，可以从这段数据找到序列号等信息</span>
BOOL test()
{
UCHAR buf[<span class=num>4096</span>];
<span class=key>if</span> (!InitPhysicalMemory())
{
<span class=key>return</span> FALSE;
}
<span class=key>if</span> (!ReadPhysicalMemory(buf, <span class=num>0xfe000</span>, <span class=num>4096</span>))
{
<span class=rem>// ... 成功读取了指定数据</span>
ExitPhysicalMemory();
<span class=key>return</span> FALSE;
}
ExitPhysicalMemory();
<span class=key>return</span> TRUE;
}
</code></pre>
<p class=bhw98>补充说明一点，由于Windows虚拟内存页面大小默认是4KB，NtMapViewOfSection()返回的虚拟空间基址是按照4KB对齐的，返回的长度也是4KB的整数倍。在上面的ReadPhysicalMemory()中，认为输入的物理地址也是4KB对齐的，如果不是，需要更加全面地考虑。</p>
<p class=bhw98>&nbsp;
<h2 class=bhw98>[相关资源]</h2>
<li class=bhw98>本文Demo源码：<a href="http://www.kernelstudio.com/"><font color=#336699>Kernel Studio</font></a>
<li class=bhw98>bhw98的专栏：<a href="http://www.csdn.net/develop/author/netauthor/bhw98/"><font color=#336699>http://www.csdn.net/develop/author/netauthor/bhw98/</font></a> </li>
<img src ="http://www.cppblog.com/iniwf/aggbug/79827.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-13 21:17 <a href="http://www.cppblog.com/iniwf/archive/2009/04/13/79827.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战DeviceIoControl 之六：访问物理端口</title><link>http://www.cppblog.com/iniwf/archive/2009/04/13/79826.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 13 Apr 2009 13:13:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/13/79826.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79826.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/13/79826.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79826.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79826.html</trackback:ping><description><![CDATA[转自<a href="http://blog.csdn.net/bhw98/archive/2003/05/26/19665.aspx">http://blog.csdn.net/bhw98/archive/2003/05/26/19665.aspx</a><br><br>
<p class=bhw98><strong class=bhw98>Q</strong> 在NT/2000/XP中，如何读取CMOS数据？
<p class=bhw98><strong class=bhw98>Q</strong> 在NT/2000/XP中，如何控制speaker发声？
<p class=bhw98><strong class=bhw98>Q</strong> 在NT/2000/XP中，如何直接访问物理端口？
<p class=bhw98><strong class=bhw98>A</strong> 看似小小问题，难倒多少好汉！
<p class=bhw98>NT/2000/XP从安全性、可靠性、稳定性上考虑，应用程序和操作系统是分开的，操作系统代码运行在核心态，有权访问系统数据和硬件，能执行特权指令；应用程序运行在用户态，能够使用的接口和访问系统数据的权限都受到严格限制。当用户程序调用系统服务时，处理器捕获该调用，然后把调用的线程切换到核心态。当系统服务完成后，操作系统将线程描述表切换回用户态，调用者继续运行。
<p class=bhw98>想在用户态应用程序中实现I/O读写，直接存取硬件，可以通过编写驱动程序，实现CreateFile、CloseHandle、 DeviceIOControl、ReadFile、WriteFile等功能。从Windows 2000开始，引入WDM核心态驱动程序的概念。
<p class=bhw98>下面是本人写的一个非常简单的驱动程序，可实现字节型端口I/O。
<pre class=bhw98><code class=bhw98><span class=key>#include</span> <span class=str>&lt;ntddk.h&gt;</span>
<span class=key>#include</span> <span class=str>"MyPort.h"</span>
<span class=rem>// 设备类型定义</span>
<span class=rem>// 0-32767被Microsoft占用，用户自定义可用32768-65535</span>
<span class=key>#define</span> FILE_DEVICE_MYPORT    <span class=num>0x0000f000</span>
<span class=rem>// I/O控制码定义</span>
<span class=rem>// 0-2047被Microsoft占用，用户自定义可用2048-4095 </span>
<span class=key>#define</span> MYPORT_IOCTL_BASE <span class=num>0xf00</span>
<span class=key>#define</span> IOCTL_MYPORT_READ_BYTE   CTL_CODE(FILE_DEVICE_MYPORT, MYPORT_IOCTL_BASE, METHOD_BUFFERED, FILE_ANY_ACCESS)
<span class=key>#define</span> IOCTL_MYPORT_WRITE_BYTE  CTL_CODE(FILE_DEVICE_MYPORT, MYPORT_IOCTL_BASE+1, METHOD_BUFFERED, FILE_ANY_ACCESS)
<span class=rem>// IOPM是65536个端口的位屏蔽矩阵，包含8192字节(8192 x 8 = 65536)</span>
<span class=rem>// 0 bit: 允许应用程序访问对应端口</span>
<span class=rem>// 1 bit: 禁止应用程序访问对应端口</span>
<span class=key>#define</span> IOPM_SIZE    <span class=num>8192</span>
<span class=key>typedef</span> UCHAR IOPM[IOPM_SIZE];
IOPM *pIOPM = NULL;
<span class=rem>// 设备名(要求以UNICODE表示)</span>
<span class=key>const</span> WCHAR NameBuffer[] = <span class=str>L"\\Device\\MyPort"</span>;
<span class=key>const</span> WCHAR DOSNameBuffer[] = <span class=str>L"\\DosDevices\\MyPort"</span>;
<span class=rem>// 这是两个在ntoskrnl.exe中的未见文档的服务例程</span>
<span class=rem>// 没有现成的已经说明它们原型的头文件，我们自己声明</span>
<span class=key>void</span> Ke386SetIoAccessMap(<span class=key>int</span>, IOPM *);
<span class=key>void</span> Ke386IoSetAccessProcess(PEPROCESS, <span class=key>int</span>);
<span class=rem>// 函数原型预先说明</span>
NTSTATUS MyPortDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
<span class=key>void</span> MyPortUnload(IN PDRIVER_OBJECT DriverObject);
<span class=rem>// 驱动程序入口，由系统自动调用，就像WIN32应用程序的WinMain</span>
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
UNICODE_STRING uniNameString, uniDOSString;
<span class=rem>// 为IOPM分配内存</span>
pIOPM = MmAllocateNonCachedMemory(<span class=key>sizeof</span>(IOPM));
<span class=key>if</span> (pIOPM == <span class=num>0</span>)
{
<span class=key>return</span> STATUS_INSUFFICIENT_RESOURCES;
}
<span class=rem>// IOPM全部初始化为0(允许访问所有端口)</span>
RtlZeroMemory(pIOPM, <span class=key>sizeof</span>(IOPM));
<span class=rem>// 将IOPM加载到当前进程</span>
Ke386IoSetAccessProcess(PsGetCurrentProcess(), <span class=num>1</span>);
Ke386SetIoAccessMap(<span class=num>1</span>, pIOPM);
<span class=rem>// 指定驱动名字</span>
RtlInitUnicodeString(&amp;uniNameString, NameBuffer);
RtlInitUnicodeString(&amp;uniDOSString, DOSNameBuffer);
<span class=rem>// 创建设备</span>
status = IoCreateDevice(DriverObject, <span class=num>0</span>,
&amp;uniNameString,
FILE_DEVICE_MYPORT,
<span class=num>0</span>, FALSE, &amp;deviceObject);
<span class=key>if</span> (!NT_SUCCESS(status))
{
<span class=key>return</span> status;
}
<span class=rem>// 创建WIN32应用程序需要的符号连接</span>
status = IoCreateSymbolicLink (&amp;uniDOSString, &amp;uniNameString);
<span class=key>if</span> (!NT_SUCCESS(status))
{
<span class=key>return</span> status;
}
<span class=rem>// 指定驱动程序有关操作的模块入口(函数指针)</span>
<span class=rem>// 涉及以下两个模块：MyPortDispatch和MyPortUnload</span>
DriverObject-&gt;MajorFunction[IRP_MJ_CREATE]         =
DriverObject-&gt;MajorFunction[IRP_MJ_CLOSE]          =
DriverObject-&gt;MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyPortDispatch;
DriverObject-&gt;DriverUnload = MyPortUnload;
<span class=key>return</span> STATUS_SUCCESS;
}
<span class=rem>// IRP处理模块</span>
NTSTATUS MyPortDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION IrpStack;
ULONG              dwInputBufferLength;
ULONG              dwOutputBufferLength;
ULONG              dwIoControlCode;
PULONG             pvIOBuffer;
NTSTATUS           ntStatus;
<span class=rem>// 填充几个默认值</span>
Irp-&gt;IoStatus.Status = STATUS_SUCCESS;    <span class=rem>// 返回状态</span>
Irp-&gt;IoStatus.Information = <span class=num>0</span>;            <span class=rem>// 输出长度</span>
IrpStack = IoGetCurrentIrpStackLocation(Irp);
<span class=rem>// Get the pointer to the input/output buffer and it's length</span>
<span class=rem>// 输入输出共用的缓冲区</span>
<span class=rem>// 因为我们在IOCTL中指定了METHOD_BUFFERED，</span>
pvIOBuffer = Irp-&gt;AssociatedIrp.SystemBuffer;
<span class=key>switch</span> (IrpStack-&gt;MajorFunction)
{
<span class=key>case</span> IRP_MJ_CREATE:        <span class=rem>// 与WIN32应用程序中的CreateFile对应</span>
<span class=key>break</span>;
<span class=key>case</span> IRP_MJ_CLOSE:        <span class=rem>// 与WIN32应用程序中的CloseHandle对应</span>
<span class=key>break</span>;
<span class=key>case</span> IRP_MJ_DEVICE_CONTROL:        <span class=rem>// 与WIN32应用程序中的DeviceIoControl对应</span>
dwIoControlCode = IrpStack-&gt;Parameters.DeviceIoControl.IoControlCode;
<span class=key>switch</span> (dwIoControlCode)
{
<span class=rem>// 我们约定，缓冲区共两个DWORD，第一个DWORD为端口，第二个DWORD为数据</span>
<span class=rem>// 一般做法是专门定义一个结构，此处简单化处理了</span>
<span class=key>case</span> IOCTL_MYPORT_READ_BYTE:        <span class=rem>// 从端口读字节</span>
pvIOBuffer[<span class=num>1</span>] = _inp(pvIOBuffer[<span class=num>0</span>]);
Irp-&gt;IoStatus.Information = <span class=num>8</span>;  <span class=rem>// 输出长度为8</span>
<span class=key>break</span>;
<span class=key>case</span> IOCTL_MYPORT_WRITE_BYTE:       <span class=rem>// 写字节到端口</span>
_outp(pvIOBuffer[<span class=num>0</span>], pvIOBuffer[<span class=num>1</span>]);
<span class=key>break</span>;
<span class=key>default</span>:        <span class=rem>// 不支持的IOCTL</span>
Irp-&gt;IoStatus.Status = STATUS_INVALID_PARAMETER;
}
}
ntStatus = Irp-&gt;IoStatus.Status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
<span class=key>return</span> ntStatus;
}
<span class=rem>// 删除驱动</span>
<span class=key>void</span> MyPortUnload(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING uniDOSString;
<span class=key>if</span>(pIOPM)
{
<span class=rem>// 释放IOPM占用的空间</span>
MmFreeNonCachedMemory(pIOPM, <span class=key>sizeof</span>(IOPM));
}
RtlInitUnicodeString(&amp;uniDOSString, DOSNameBuffer);
<span class=rem>// 删除符号连接和设备</span>
IoDeleteSymbolicLink (&amp;uniDOSString);
IoDeleteDevice(DriverObject-&gt;DeviceObject);
}
</code></pre>
<p class=bhw98>下面给出实现设备驱动程序的动态加载的源码。动态加载的好处是，你不用做任何添加新硬件的操作，也不用编辑注册表，更不用重新启动计算机。
<pre class=bhw98><code class=bhw98><span class=rem>// 安装驱动并启动服务</span>
<span class=rem>// lpszDriverPath:  驱动程序路径</span>
<span class=rem>// lpszServiceName: 服务名 </span>
BOOL StartDriver(LPCTSTR lpszDriverPath, LPCTSTR lpszServiceName)
{
SC_HANDLE hSCManager;        <span class=rem>// 服务控制管理器句柄</span>
SC_HANDLE hService;          <span class=rem>// 服务句柄</span>
DWORD dwLastError;           <span class=rem>// 错误码</span>
BOOL bResult = FALSE;        <span class=rem>// 返回值</span>
<span class=rem>// 打开服务控制管理器</span>
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
<span class=key>if</span> (hSCManager)
{
<span class=rem>// 创建服务</span>
hService = CreateService(hSCManager,
lpszServiceName,
lpszServiceName,
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
lpszDriverPath,
NULL,
NULL,
NULL,
NULL,
NULL);
<span class=key>if</span> (hService == NULL)
{
<span class=key>if</span> (::GetLastError() == ERROR_SERVICE_EXISTS)
{
hService = ::OpenService(hSCManager, lpszServiceName, SERVICE_ALL_ACCESS);
}
}
<span class=key>if</span> (hService)
{
<span class=rem>// 启动服务</span>
bResult = StartService(hService, <span class=num>0</span>, NULL);
<span class=rem>// 关闭服务句柄</span>
CloseServiceHandle(hService);
}
<span class=rem>// 关闭服务控制管理器句柄</span>
CloseServiceHandle(hSCManager);
}
<span class=key>return</span> bResult;
}
<span class=rem>// 停止服务并卸下驱动</span>
<span class=rem>// lpszServiceName: 服务名 </span>
BOOL StopDriver(LPCTSTR lpszServiceName)
{
SC_HANDLE hSCManager;        <span class=rem>// 服务控制管理器句柄</span>
SC_HANDLE hService;          <span class=rem>// 服务句柄</span>
BOOL bResult;                <span class=rem>// 返回值</span>
SERVICE_STATUS ServiceStatus;
bResult = FALSE;
<span class=rem>// 打开服务控制管理器</span>
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
<span class=key>if</span> (hSCManager)
{
<span class=rem>// 打开服务</span>
hService = OpenService(hSCManager, lpszServiceName, SERVICE_ALL_ACCESS);
<span class=key>if</span> (hService)
{
<span class=rem>// 停止服务</span>
bResult = ControlService(hService, SERVICE_CONTROL_STOP, &amp;ServiceStatus);
<span class=rem>// 删除服务</span>
bResult = bResult &amp;&amp; DeleteService(hService);
<span class=rem>// 关闭服务句柄</span>
CloseServiceHandle(hService);
}
<span class=rem>// 关闭服务控制管理器句柄</span>
CloseServiceHandle(hSCManager);
}
<span class=key>return</span> bResult;
}
</code></pre>
<p class=bhw98>应用程序实现端口I/O的接口如下：
<pre class=bhw98><code class=bhw98><span class=rem>// 全局的设备句柄</span>
HANDLE hMyPort;
<span class=rem>// 打开设备</span>
<span class=rem>// lpszDevicePath: 设备的路径</span>
HANDLE OpenDevice(LPCTSTR lpszDevicePath)
{
HANDLE hDevice;
<span class=rem>// 打开设备</span>
hDevice = ::CreateFile(lpszDevicePath,    <span class=rem>// 设备路径</span>
GENERIC_READ | GENERIC_WRITE,        <span class=rem>// 读写方式</span>
FILE_SHARE_READ | FILE_SHARE_WRITE,  <span class=rem>// 共享方式</span>
NULL,                    <span class=rem>// 默认的安全描述符</span>
OPEN_EXISTING,           <span class=rem>// 创建方式</span>
<span class=num>0</span>,                       <span class=rem>// 不需设置文件属性</span>
NULL);                   <span class=rem>// 不需参照模板文件</span>
<span class=key>return</span> hDevice;
}
<span class=rem>// 打开端口驱动</span>
BOOL OpenMyPort()
{
BOOL bResult;
<span class=rem>// 设备名为"MyPort"，驱动程序位于Windows的"system32\drivers"目录中</span>
bResult = StartDriver(<span class=str>"system32\\drivers\\MyPort.sys"</span>, <span class=str>"MyPort"</span>);
<span class=rem>// 设备路径为"\\.\MyPort"</span>
<span class=key>if</span> (bResult)
{
hMyPort = OpenDevice(<span class=str>"\\\\.\\MyPort"</span>);
}
<span class=key>return</span> (bResult &amp;&amp; (hMyPort != INVALID_HANDLE_VALUE));
}
<span class=rem>// 关闭端口驱动</span>
BOOL CloseMyPort()
{
<span class=key>return</span> (CloseHandle(hMyPort) &amp;&amp; StopDriver(<span class=str>"MyPort"</span>));
}
<span class=rem>// 从指定端口读一个字节</span>
<span class=rem>// port: 端口</span>
BYTE ReadPortByte(WORD port)
{
DWORD buf[<span class=num>2</span>];            <span class=rem>// 输入输出缓冲区            </span>
DWORD dwOutBytes;        <span class=rem>// IOCTL输出数据长度</span>
buf[<span class=num>0</span>] = port;           <span class=rem>// 第一个DWORD是端口</span>
<span class=rem>//  buf[1] = 0;              // 第二个DWORD是数据</span>
<span class=rem>// 用IOCTL_MYPORT_READ_BYTE读端口</span>
::DeviceIoControl(hMyPort,   <span class=rem>// 设备句柄</span>
IOCTL_MYPORT_READ_BYTE,  <span class=rem>// 取设备属性信息</span>
buf, <span class=key>sizeof</span>(buf),        <span class=rem>// 输入数据缓冲区</span>
buf, <span class=key>sizeof</span>(buf),        <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,             <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);     <span class=rem>// 用同步I/O    </span>
<span class=key>return</span> (BYTE)buf[<span class=num>1</span>];
}
<span class=rem>// 将一个字节写到指定端口</span>
<span class=rem>// port: 端口</span>
<span class=rem>// data: 字节数据</span>
<span class=key>void</span> WritePortByte(WORD port, BYTE data)
{
DWORD buf[<span class=num>2</span>];            <span class=rem>// 输入输出缓冲区            </span>
DWORD dwOutBytes;        <span class=rem>// IOCTL输出数据长度</span>
buf[<span class=num>0</span>] = port;           <span class=rem>// 第一个DWORD是端口</span>
buf[<span class=num>1</span>] = data;           <span class=rem>// 第二个DWORD是数据</span>
<span class=rem>// 用IOCTL_MYPORT_WRITE_BYTE写端口</span>
::DeviceIoControl(hMyPort,   <span class=rem>// 设备句柄</span>
IOCTL_MYPORT_WRITE_BYTE, <span class=rem>// 取设备属性信息</span>
buf, <span class=key>sizeof</span>(buf),        <span class=rem>// 输入数据缓冲区</span>
buf, <span class=key>sizeof</span>(buf),        <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,             <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);     <span class=rem>// 用同步I/O</span>
}
</code></pre>
<p class=bhw98>有了ReadPortByte和WritePortByte这两个函数，我们就能很容易地操纵CMOS和speaker了(关于CMOS值的含义以及定时器寄存器定义，请参考相应的硬件资料)：
<pre class=bhw98><code class=bhw98><span class=rem>// 0x70是CMOS索引端口(只写)</span>
<span class=rem>// 0x71是CMOS数据端口</span>
BYTE ReadCmos(BYTE index)
{
BYTE data;
::WritePortByte(<span class=num>0x70</span>, index);
data = ::ReadPortByte(<span class=num>0x71</span>);
<span class=key>return</span> data;
}
<span class=rem>// 0x61是speaker控制端口</span>
<span class=rem>// 0x43是8253/8254定时器控制端口</span>
<span class=rem>// 0x42是8253/8254定时器通道2的端口</span>
<span class=key>void</span> Sound(DWORD freq)
{
BYTE data;
<span class=key>if</span> ((freq &gt;= <span class=num>20</span>) &amp;&amp; (freq &lt;= <span class=num>20000</span>))
{
freq = <span class=num>1193181</span> / freq;
data = ::ReadPortByte(<span class=num>0x61</span>);
<span class=key>if</span> ((data &amp; <span class=num>3</span>) == <span class=num>0</span>)
{
::WritePortByte(<span class=num>0x61</span>, data | <span class=num>3</span>);
::WritePortByte(<span class=num>0x43</span>, <span class=num>0xb6</span>);
}
::WritePortByte(<span class=num>0x42</span>, (BYTE)(freq % <span class=num>256</span>));
::WritePortByte(<span class=num>0x42</span>, (BYTE)(freq / <span class=num>256</span>));
}
}
<span class=key>void</span> NoSound(<span class=key>void</span>)
{
BYTE data;
data = ::ReadPortByte(<span class=num>0x61</span>);
::WritePortByte(<span class=num>0x61</span>, data &amp; <span class=num>0xfc</span>);
}
</code></pre>
<pre class=bhw98><code class=bhw98>
<span class=rem>// 以下读出CMOS 128个字节</span>
<span class=key>for</span> (<span class=key>int</span> i = <span class=num>0</span>; i &lt; <span class=num>128</span>; i++)
{
BYTE data = ::ReadCmos(i);
... ...
}
<span class=rem>// 以下用C调演奏&#8220;多-来-米&#8221;</span>
<span class=rem>// 1 = 262 Hz</span>
::Sound(<span class=num>262</span>);
::Sleep(<span class=num>200</span>);
::NoSound();
<span class=rem>// 2 = 288 Hz</span>
::Sound(<span class=num>288</span>);
::Sleep(<span class=num>200</span>);
::NoSound();
<span class=rem>// 3 = 320 Hz</span>
::Sound(<span class=num>320</span>);
::Sleep(<span class=num>200</span>);
::NoSound();
</code></pre>
<p class=bhw98><strong class=bhw98>Q</strong> 就是个简单的端口I/O，这么麻烦才能实现，搞得俺头脑稀昏，有没有简洁明了的办法啊？
<p class=bhw98><strong class=bhw98>A</strong> 上面的例子，之所以从编写驱动程序，到安装驱动，到启动服务，到打开设备，到访问设备，一直到读写端口，这样一路下来，是为了揭示在NT/2000/XP中硬件访问技术的本质。假如将所有过程封装起来，只提供OpenMyPort, CloseMyPort, ReadPortByte, WritePortByte甚至更高层的ReadCmos、WriteCmos、Sound、NoSound给你调用，是不是会感觉清爽许多？
<p class=bhw98>实际上，我们平常做的基于一定硬件的二次开发，一般会先安装驱动程序(DRV)和用户接口的运行库(DLL)，然后在此基础上开发出我们的应用程序(APP)。DRV、DLL、APP三者分别运行在核心态、核心态/用户态联络带、用户态。比如买了一块图象采集卡，要先安装核心驱动，它的&#8220;Development Tool Kit&#8221;，提供类似于PCV_Initialize, PCV_Capture等的API，就是扮演核心态和用户态联络员的角色。我们根本不需要CreateFile、CloseHandle、 DeviceIOControl、ReadFile、WriteFile等较低层次的直接调用。
<p class=bhw98>Yariv Kaplan写过一个WinIO的例子，能实现对物理端口和内存的访问，提供了DRV、DLL、APP三方面的源码，有兴趣的话可以深入研究一下。
<h2 class=bhw98>[相关资源]</h2>
<li class=bhw98>本文驱动程序源码：<a href="http://www.csdn.net/develop/author/bhw98/MyPort.zip"><font color=#336699>MyPort.zip</font></a> (3KB, 编译环境: VC6+2000DDK)
<li class=bhw98>本文应用程序源码：<a href="http://www.csdn.net/develop/author/bhw98/MyPortIo.zip"><font color=#336699>MyPortIo.zip</font></a> (22KB, 文件MyPort.sys需复制到windows的system32\drivers目录中)
<li class=bhw98>Yariv Kaplan的主页：<a href="http://www.internals.com/"><font color=#336699>http://www.internals.com</font></a>
<li class=bhw98>bhw98的专栏：<a href="http://www.csdn.net/develop/author/netauthor/bhw98/"><font color=#336699>http://www.csdn.net/develop/author/netauthor/bhw98/</font></a> </li>
<img src ="http://www.cppblog.com/iniwf/aggbug/79826.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-13 21:13 <a href="http://www.cppblog.com/iniwf/archive/2009/04/13/79826.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战DeviceIoControl 之五：列举已安装的存储设备</title><link>http://www.cppblog.com/iniwf/archive/2009/04/13/79825.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 13 Apr 2009 13:09:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/13/79825.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79825.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/13/79825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79825.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79825.html</trackback:ping><description><![CDATA[转自<a href="http://www.cnblogs.com/henryzc/archive/2005/11/08/271912.html">http://www.cnblogs.com/henryzc/archive/2005/11/08/271912.html</a><br><br>
<div class=postText>
<p class=bhw98><strong class=bhw98>Q</strong> 前几次我们讨论的都是设备名比较清楚的情况，有了设备名(路径)，就可以直接调用CreateFile打开设备，进行它所支持的I/O操作了。如果事先并不能确切知道设备名，如何去访问设备呢？
<p class=bhw98><strong class=bhw98>A</strong> 访问设备必须用设备句柄，而得到设备句柄必须知道设备路径，这个套路以你我之力是改变不了的。每个设备都有它所属类型的GUID，我们顺着这个GUID就能获得设备路径。
<p class=bhw98>GUID是同类或同种设备的全球唯一识别码，它是一个128 bit(16字节)的整形数，真实面目为
<pre class=bhw98><code class=bhw98><span class=key>typedef</span> <span class=key>struct</span> _GUID
{
<span class=key>unsigned</span> <span class=key>long</span>  Data1;
<span class=key>unsigned</span> <span class=key>short</span> Data2;
<span class=key>unsigned</span> <span class=key>short</span> Data3;
<span class=key>unsigned</span> <span class=key>char</span>  Data4[<span class=num>8</span>];
} GUID, *PGUID;
</code></pre>
<p class=bhw98>例如，Disk类的GUID为&#8220;53f56307-b6bf-11d0-94f2-00a0c91efb8b&#8221;，在我们的程序里可以定义为
<pre class=bhw98><code class=bhw98><span class=key>const</span> GUID DiskClassGuid = {0x53f56307L, <span class=num>0xb6bf</span>, <span class=num>0x11d0</span>, {<span class=num>0x94</span>, <span class=num>0xf2</span>, <span class=num>0x00</span>, <span class=num>0xa0</span>, <span class=num>0xc9</span>, <span class=num>0x1e</span>, <span class=num>0xfb</span>, <span class=num>0x8b</span>)};
</code></pre>
<p class=bhw98>或者用一个宏来定义
<pre class=bhw98><code class=bhw98>DEFINE_GUID(DiskClassGuid, 0x53f56307L, <span class=num>0xb6bf</span>, <span class=num>0x11d0</span>, <span class=num>0x94</span>, <span class=num>0xf2</span>, <span class=num>0x00</span>, <span class=num>0xa0</span>, <span class=num>0xc9</span>, <span class=num>0x1e</span>, <span class=num>0xfb</span>, <span class=num>0x8b</span>);
</code></pre>
<p class=bhw98>通过GUID找出设备路径，需要用到一组设备管理的API函数
<p class=bhw98>SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetInterfaceDeviceDetail, SetupDiDestroyDeviceInfoList,
<p class=bhw98>以及结构SP_DEVICE_INTERFACE_DATA, SP_DEVICE_INTERFACE_DETAIL_DATA。
<p class=bhw98>有关信息请查阅MSDN，这里就不详细介绍了。
<p class=bhw98>实现GUID到设备路径的代码如下：
<pre class=bhw98><code class=bhw98><span class=rem>// SetupDiGetInterfaceDeviceDetail所需要的输出长度，定义足够大</span>
<span class=key>#define</span> INTERFACE_DETAIL_SIZE    (<span class=num>1024</span>)
<span class=rem>// 根据GUID获得设备路径</span>
<span class=rem>// lpGuid: GUID指针</span>
<span class=rem>// pszDevicePath: 设备路径指针的指针</span>
<span class=rem>// 返回: 成功得到的设备路径个数，可能不止1个</span>
<span class=key>int</span> GetDevicePath(LPGUID lpGuid, LPTSTR* pszDevicePath)
{
HDEVINFO hDevInfoSet;
SP_DEVICE_INTERFACE_DATA ifdata;
PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;
<span class=key>int</span> nCount;
BOOL bResult;
<span class=rem>// 取得一个该GUID相关的设备信息集句柄</span>
hDevInfoSet = ::SetupDiGetClassDevs(lpGuid,     <span class=rem>// class GUID </span>
NULL,                    <span class=rem>// 无关键字 </span>
NULL,                    <span class=rem>// 不指定父窗口句柄 </span>
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);    <span class=rem>// 目前存在的设备</span>
<span class=rem>// 失败...</span>
<span class=key>if</span> (hDevInfoSet == INVALID_HANDLE_VALUE)
{
<span class=key>return</span> <span class=num>0</span>;
}
<span class=rem>// 申请设备接口数据空间</span>
pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
pDetail-&gt;cbSize = <span class=key>sizeof</span>(SP_DEVICE_INTERFACE_DETAIL_DATA);
nCount = <span class=num>0</span>;
bResult = TRUE;
<span class=rem>// 设备序号=0,1,2... 逐一测试设备接口，到失败为止</span>
<span class=key>while</span> (bResult)
{
ifdata.cbSize = <span class=key>sizeof</span>(ifdata);
<span class=rem>// 枚举符合该GUID的设备接口</span>
bResult = ::SetupDiEnumDeviceInterfaces(
hDevInfoSet,     <span class=rem>// 设备信息集句柄</span>
NULL,            <span class=rem>// 不需额外的设备描述</span>
lpGuid,          <span class=rem>// GUID</span>
(ULONG)nCount,   <span class=rem>// 设备信息集里的设备序号</span>
&amp;ifdata);        <span class=rem>// 设备接口信息</span>
<span class=key>if</span> (bResult)
{
<span class=rem>// 取得该设备接口的细节(设备路径)</span>
bResult = SetupDiGetInterfaceDeviceDetail(
hDevInfoSet,    <span class=rem>// 设备信息集句柄</span>
&amp;ifdata,        <span class=rem>// 设备接口信息</span>
pDetail,        <span class=rem>// 设备接口细节(设备路径)</span>
INTERFACE_DETAIL_SIZE,    <span class=rem>// 输出缓冲区大小</span>
NULL,           <span class=rem>// 不需计算输出缓冲区大小(直接用设定值)</span>
NULL);          <span class=rem>// 不需额外的设备描述</span>
<span class=key>if</span> (bResult)
{
<span class=rem>// 复制设备路径到输出缓冲区</span>
::strcpy(pszDevicePath[nCount], pDetail-&gt;DevicePath);
<span class=rem>// 调整计数值</span>
nCount++;
}
}
}
<span class=rem>// 释放设备接口数据空间</span>
::GlobalFree(pDetail);
<span class=rem>// 关闭设备信息集句柄</span>
::SetupDiDestroyDeviceInfoList(hDevInfoSet);
<span class=key>return</span> nCount;
}
</code></pre>
<p class=bhw98>调用GetDevicePath函数时要注意，pszDevicePath是个指向字符串指针的指针，例如可以这样
<pre class=bhw98><code class=bhw98>    <span class=key>int</span> i;
<span class=key>char</span>* szDevicePath[MAX_DEVICE];        <span class=rem>// 设备路径</span>
<span class=rem>// 分配需要的空间</span>
<span class=key>for</span> (i = <span class=num>0</span>; i &lt; MAX_DEVICE; i++)
{
szDevicePath[i] = <span class=key>new</span> <span class=key>char</span>[<span class=num>256</span>];
}
<span class=rem>// 取设备路径</span>
nDevice = ::GetDevicePath((LPGUID)&amp;DiskClassGuid, szDevicePath);
<span class=rem>// 逐一获取设备信息</span>
<span class=key>for</span> (i = <span class=num>0</span>; i &lt; nDevice; i++)
{
<span class=rem>// 打开设备</span>
hDevice = ::OpenDevice(szDevicePath[i]);
<span class=key>if</span> (hDevice != INVALID_HANDLE_VALUE)
{
... ...        <span class=rem>// I/O操作</span>
::CloseHandle(hDevice);
}
}
<span class=rem>// 释放空间</span>
<span class=key>for</span> (i = <span class=num>0</span>; i &amp; lt; MAX_DEVICE; i++)
{
<span class=key>delete</span> []szDevicePath[i];
}
</code></pre>
<p class=bhw98>本例的Project中除了要包含winioctl.h外，还要包含initguid.h，setupapi.h，以及连接setupapi.lib。
<p class=bhw98><strong class=bhw98>Q</strong> 得到设备路径后，就可以到下一步，用CreateFile打开设备，然后用DeviceIoControl进行读写了吧？
<p class=bhw98><strong class=bhw98>A</strong> 是的。尽管该设备路径与以前我们接触的那些不太一样。本是&#8220;\\.\PhysicalDrive0&#8221;，现在鸟枪换炮，变成了类似这样的一副尊容：
<p class=bhw98>&#8220;\\?\ide#diskmaxtor_2f040j0__________________________vam51jj0#3146563447534558202020202020202020202020#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}&#8221;。
<p class=bhw98>其实这个设备名在注册表的某处可以找到，例如在Win2000中这个名字可以位于
<p class=bhw98>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Disk\Enum\0，
<p class=bhw98>只不过&#8220;#&#8221;换成了&#8220;\&#8221;。分析一下这样的设备路径，你会发现很有趣的东西，它们是由接口类型、产品型号、固件版本、序列号、计算机名、GUID等信息组合而成的。当然，它是没有规范的，不能指望从这里面得到你希望知道的东西。
<p class=bhw98>用CreateFile打开设备后，对于存储设备，IOCTL_DISK_GET_DRIVE_GEOMETRY，IOCTL_STORAGE_GET_MEDIA_TYPES_EX等I/O控制码照常使用。
<p class=bhw98>今天我们讨论一个新的控制码：IOCTL_STORAGE_QUERY_PROPERTY，获取设备属性信息，希望得到系统中所安装的各种固定的和可移动的硬盘、优盘和CD/DVD-ROM/R/W的接口类型、序列号、产品ID等信息。
<pre class=bhw98><code class=bhw98><span class=rem>// IOCTL控制码</span>
<span class=key>#define</span> IOCTL_STORAGE_QUERY_PROPERTY   CTL_CODE(IOCTL_STORAGE_BASE, <span class=num>0x0500</span>, METHOD_BUFFERED, FILE_ANY_ACCESS)
<span class=rem>// 存储设备的总线类型</span>
<span class=key>typedef</span> <span class=key>enum</span> _STORAGE_BUS_TYPE {
BusTypeUnknown = <span class=num>0x00</span>,
BusTypeScsi,
BusTypeAtapi,
BusTypeAta,
BusType1394,
BusTypeSsa,
BusTypeFibre,
BusTypeUsb,
BusTypeRAID,
BusTypeMaxReserved = <span class=num>0x7F</span>
} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
<span class=rem>// 查询存储设备属性的类型</span>
<span class=key>typedef</span> <span class=key>enum</span> _STORAGE_QUERY_TYPE {
PropertyStandardQuery = <span class=num>0</span>,          <span class=rem>// 读取描述</span>
PropertyExistsQuery,                <span class=rem>// 测试是否支持</span>
PropertyMaskQuery,                  <span class=rem>// 读取指定的描述</span>
PropertyQueryMaxDefined             <span class=rem>// 验证数据</span>
} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
<span class=rem>// 查询存储设备还是适配器属性</span>
<span class=key>typedef</span> <span class=key>enum</span> _STORAGE_PROPERTY_ID {
StorageDeviceProperty = <span class=num>0</span>,          <span class=rem>// 查询设备属性</span>
StorageAdapterProperty              <span class=rem>// 查询适配器属性</span>
} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
<span class=rem>// 查询属性输入的数据结构</span>
<span class=key>typedef</span> <span class=key>struct</span> _STORAGE_PROPERTY_QUERY {
STORAGE_PROPERTY_ID PropertyId;     <span class=rem>// 设备/适配器</span>
STORAGE_QUERY_TYPE QueryType;       <span class=rem>// 查询类型 </span>
UCHAR AdditionalParameters[<span class=num>1</span>];      <span class=rem>// 额外的数据(仅定义了象征性的1个字节)</span>
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
<span class=rem>// 查询属性输出的数据结构</span>
<span class=key>typedef</span> <span class=key>struct</span> _STORAGE_DEVICE_DESCRIPTOR {
ULONG Version;                    <span class=rem>// 版本</span>
ULONG Size;                       <span class=rem>// 结构大小</span>
UCHAR DeviceType;                 <span class=rem>// 设备类型</span>
UCHAR DeviceTypeModifier;         <span class=rem>// SCSI-2额外的设备类型</span>
BOOLEAN RemovableMedia;           <span class=rem>// 是否可移动</span>
BOOLEAN CommandQueueing;          <span class=rem>// 是否支持命令队列</span>
ULONG VendorIdOffset;             <span class=rem>// 厂家设定值的偏移</span>
ULONG ProductIdOffset;            <span class=rem>// 产品ID的偏移</span>
ULONG ProductRevisionOffset;      <span class=rem>// 产品版本的偏移</span>
ULONG SerialNumberOffset;         <span class=rem>// 序列号的偏移</span>
STORAGE_BUS_TYPE BusType;         <span class=rem>// 总线类型</span>
ULONG RawPropertiesLength;        <span class=rem>// 额外的属性数据长度</span>
UCHAR RawDeviceProperties[<span class=num>1</span>];     <span class=rem>// 额外的属性数据(仅定义了象征性的1个字节)</span>
} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
<span class=rem>// 取设备属性信息</span>
<span class=rem>// hDevice -- 设备句柄</span>
<span class=rem>// pDevDesc -- 输出的设备描述和属性信息缓冲区指针(包含连接在一起的两部分)</span>
BOOL GetDriveProperty(HANDLE hDevice, PSTORAGE_DEVICE_DESCRIPTOR pDevDesc)
{
STORAGE_PROPERTY_QUERY Query;    <span class=rem>// 查询输入参数</span>
DWORD dwOutBytes;                <span class=rem>// IOCTL输出数据长度</span>
BOOL bResult;                    <span class=rem>// IOCTL返回值</span>
<span class=rem>// 指定查询方式</span>
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
<span class=rem>// 用IOCTL_STORAGE_QUERY_PROPERTY取设备属性信息</span>
bResult = ::DeviceIoControl(hDevice, <span class=rem>// 设备句柄</span>
IOCTL_STORAGE_QUERY_PROPERTY,    <span class=rem>// 取设备属性信息</span>
&amp;Query, <span class=key>sizeof</span>(STORAGE_PROPERTY_QUERY),    <span class=rem>// 输入数据缓冲区</span>
pDevDesc, pDevDesc-&gt;Size,        <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,                     <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);             <span class=rem>// 用同步I/O    </span>
<span class=key>return</span> bResult;
}
</code></pre>
<p class=bhw98><strong class=bhw98>Q</strong> 我用这个方法从IOCTL_STORAGE_QUERY_PROPERTY返回的数据中，没有得到CDROM和USB接口的外置硬盘的序列号、产品ID等信息。但从设备路径上看，明明是有这些信息的，为什么它没有填充到STORAGE_DEVICE_DESCRIPTOR中呢？再就是为什么硬盘序列号本是&#8220;D22P7KHE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8221;，为什么它填充的是&#8220;3146563447534558202020202020202020202020&#8221;这种形式呢？
<p class=bhw98><strong class=bhw98>A</strong> 对这两个问题我也是心存疑惑，但又不敢妄加猜测，正琢磨着向微软请教呢。 </p>
<h2 class=bhw98>[相关资源]</h2>
<li class=bhw98>本文Demo源码：<a href="http://www.csdn.net/develop/author/bhw98/StorageEnum.zip"><font color=#000080>StorageEnum.zip</font></a> (23KB) </li>
</div>
<img src ="http://www.cppblog.com/iniwf/aggbug/79825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-13 21:09 <a href="http://www.cppblog.com/iniwf/archive/2009/04/13/79825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战DeviceIoControl 之四：获取硬盘的详细信息</title><link>http://www.cppblog.com/iniwf/archive/2009/04/13/79824.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 13 Apr 2009 13:06:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/13/79824.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79824.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/13/79824.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79824.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79824.html</trackback:ping><description><![CDATA[转自<a href="http://www.cnblogs.com/henryzc/archive/2005/11/08/271911.html">http://www.cnblogs.com/henryzc/archive/2005/11/08/271911.html</a><br><br>
<div class=postText>
<p class=bhw98><strong class=bhw98>Q</strong> 用IOCTL_DISK_GET_DRIVE_GEOMETRY或IOCTL_STORAGE_GET_MEDIA_TYPES_EX只能得到很少的磁盘参数，我想获得包括硬盘序列号在内的更加详细的信息，有什么办法呀？
<p class=bhw98><strong class=bhw98>A</strong> 确实，用你所说的I/O控制码，只能得到最基本的磁盘参数。获取磁盘出厂信息的I/O控制码，微软在VC/MFC环境中没有开放，在DDK中可以发现一些线索。早先，Lynn McGuire写了一个很出名的获取IDE硬盘详细信息的程序DiskID32，下面的例子是在其基础上经过增删和改进而成的。
<p class=bhw98>本例中，我们要用到ATA/APAPI的IDENTIFY DEVICE指令。ATA/APAPI是国际组织T13起草和发布的IDE/EIDE/UDMA硬盘及其它可移动存储设备与主机接口的标准，至今已经到了ATA/APAPI-7版本。该接口标准规定了ATA/ATAPI设备的输入输出寄存器和指令集。欲了解更详细的ATA/ATAPI技术资料，可访问T13的站点。
<p class=bhw98>用到的常量及数据结构有以下一些：
<pre class=bhw98><code class=bhw98><span class=rem>// IOCTL控制码</span>
<span class=rem>// #define  DFP_SEND_DRIVE_COMMAND   0x0007c084</span>
<span class=key>#define</span>  DFP_SEND_DRIVE_COMMAND   CTL_CODE(IOCTL_DISK_BASE, <span class=num>0x0021</span>, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
<span class=rem>// #define  DFP_RECEIVE_DRIVE_DATA   0x0007c088</span>
<span class=key>#define</span>  DFP_RECEIVE_DRIVE_DATA   CTL_CODE(IOCTL_DISK_BASE, <span class=num>0x0022</span>, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
<span class=key>#define</span>  FILE_DEVICE_SCSI           <span class=num>0x0000001B</span>
<span class=key>#define</span>  IOCTL_SCSI_MINIPORT_IDENTIFY      ((FILE_DEVICE_SCSI &lt;&lt; <span class=num>16</span>) + <span class=num>0x0501</span>)
<span class=key>#define</span>  IOCTL_SCSI_MINIPORT        <span class=num>0x0004D008</span>          <span class=rem>//  see NTDDSCSI.H for definition</span>
<span class=rem>// ATA/ATAPI指令</span>
<span class=key>#define</span>  IDE_ATA_IDENTIFY           <span class=num>0xEC</span>     <span class=rem>// ATA的ID指令(IDENTIFY DEVICE)</span>
<span class=rem>// IDE命令寄存器</span>
<span class=key>typedef</span> <span class=key>struct</span> _IDEREGS
{
BYTE bFeaturesReg;       <span class=rem>// 特征寄存器(用于SMART命令)</span>
BYTE bSectorCountReg;    <span class=rem>// 扇区数目寄存器</span>
BYTE bSectorNumberReg;   <span class=rem>// 开始扇区寄存器</span>
BYTE bCylLowReg;         <span class=rem>// 开始柱面低字节寄存器</span>
BYTE bCylHighReg;        <span class=rem>// 开始柱面高字节寄存器</span>
BYTE bDriveHeadReg;      <span class=rem>// 驱动器/磁头寄存器</span>
BYTE bCommandReg;        <span class=rem>// 指令寄存器</span>
BYTE bReserved;          <span class=rem>// 保留</span>
} IDEREGS, *PIDEREGS, *LPIDEREGS;
<span class=rem>// 从驱动程序返回的状态</span>
<span class=key>typedef</span> <span class=key>struct</span> _DRIVERSTATUS
{
BYTE bDriverError;      <span class=rem>// 错误码</span>
BYTE bIDEStatus;        <span class=rem>// IDE状态寄存器</span>
BYTE bReserved[<span class=num>2</span>];      <span class=rem>// 保留</span>
DWORD dwReserved[<span class=num>2</span>];    <span class=rem>// 保留</span>
} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
<span class=rem>// IDE设备IOCTL输入数据结构</span>
<span class=key>typedef</span> <span class=key>struct</span> _SENDCMDINPARAMS
{
DWORD cBufferSize;      <span class=rem>// 缓冲区字节数</span>
IDEREGS irDriveRegs;    <span class=rem>// IDE寄存器组</span>
BYTE bDriveNumber;      <span class=rem>// 驱动器号</span>
BYTE bReserved[<span class=num>3</span>];      <span class=rem>// 保留</span>
DWORD dwReserved[<span class=num>4</span>];    <span class=rem>// 保留</span>
BYTE bBuffer[<span class=num>1</span>];        <span class=rem>// 输入缓冲区(此处象征性地包含1字节)</span>
} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
<span class=rem>// IDE设备IOCTL输出数据结构</span>
<span class=key>typedef</span> <span class=key>struct</span> _SENDCMDOUTPARAMS
{
DWORD cBufferSize;          <span class=rem>// 缓冲区字节数</span>
DRIVERSTATUS DriverStatus;  <span class=rem>// 驱动程序返回状态</span>
BYTE bBuffer[<span class=num>1</span>];            <span class=rem>// 输入缓冲区(此处象征性地包含1字节)</span>
} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
<span class=rem>// IDE的ID命令返回的数据</span>
<span class=rem>// 共512字节(256个WORD)，这里仅定义了一些感兴趣的项(基本上依据ATA/ATAPI-4)</span>
<span class=key>typedef</span> <span class=key>struct</span> _IDINFO
{
USHORT  wGenConfig;                 <span class=rem>// WORD 0: 基本信息字</span>
USHORT  wNumCyls;                   <span class=rem>// WORD 1: 柱面数</span>
USHORT  wReserved2;                 <span class=rem>// WORD 2: 保留</span>
USHORT  wNumHeads;                  <span class=rem>// WORD 3: 磁头数</span>
USHORT  wReserved4;                 <span class=rem>// WORD 4: 保留</span>
USHORT  wReserved5;                 <span class=rem>// WORD 5: 保留</span>
USHORT  wNumSectorsPerTrack;        <span class=rem>// WORD 6: 每磁道扇区数</span>
USHORT  wVendorUnique[<span class=num>3</span>];           <span class=rem>// WORD 7-9: 厂家设定值</span>
CHAR    sSerialNumber[<span class=num>20</span>];          <span class=rem>// WORD 10-19:序列号</span>
USHORT  wBufferType;                <span class=rem>// WORD 20: 缓冲类型</span>
USHORT  wBufferSize;                <span class=rem>// WORD 21: 缓冲大小</span>
USHORT  wECCSize;                   <span class=rem>// WORD 22: ECC校验大小</span>
CHAR    sFirmwareRev[<span class=num>8</span>];            <span class=rem>// WORD 23-26: 固件版本</span>
CHAR    sModelNumber[<span class=num>40</span>];           <span class=rem>// WORD 27-46: 内部型号</span>
USHORT  wMoreVendorUnique;          <span class=rem>// WORD 47: 厂家设定值</span>
USHORT  wReserved48;                <span class=rem>// WORD 48: 保留</span>
<span class=key>struct</span> {
USHORT  reserved1:<span class=num>8</span>;
USHORT  DMA:<span class=num>1</span>;                  <span class=rem>// 1=支持DMA</span>
USHORT  LBA:<span class=num>1</span>;                  <span class=rem>// 1=支持LBA</span>
USHORT  DisIORDY:<span class=num>1</span>;             <span class=rem>// 1=可不使用IORDY</span>
USHORT  IORDY:<span class=num>1</span>;                <span class=rem>// 1=支持IORDY</span>
USHORT  SoftReset:<span class=num>1</span>;            <span class=rem>// 1=需要ATA软启动</span>
USHORT  Overlap:<span class=num>1</span>;              <span class=rem>// 1=支持重叠操作</span>
USHORT  Queue:<span class=num>1</span>;                <span class=rem>// 1=支持命令队列</span>
USHORT  InlDMA:<span class=num>1</span>;               <span class=rem>// 1=支持交叉存取DMA</span>
} wCapabilities;                    <span class=rem>// WORD 49: 一般能力</span>
USHORT  wReserved1;                 <span class=rem>// WORD 50: 保留</span>
USHORT  wPIOTiming;                 <span class=rem>// WORD 51: PIO时序</span>
USHORT  wDMATiming;                 <span class=rem>// WORD 52: DMA时序</span>
<span class=key>struct</span> {
USHORT  CHSNumber:<span class=num>1</span>;            <span class=rem>// 1=WORD 54-58有效</span>
USHORT  CycleNumber:<span class=num>1</span>;          <span class=rem>// 1=WORD 64-70有效</span>
USHORT  UnltraDMA:<span class=num>1</span>;            <span class=rem>// 1=WORD 88有效</span>
USHORT  reserved:<span class=num>13</span>;
} wFieldValidity;                   <span class=rem>// WORD 53: 后续字段有效性标志</span>
USHORT  wNumCurCyls;                <span class=rem>// WORD 54: CHS可寻址的柱面数</span>
USHORT  wNumCurHeads;               <span class=rem>// WORD 55: CHS可寻址的磁头数</span>
USHORT  wNumCurSectorsPerTrack;     <span class=rem>// WORD 56: CHS可寻址每磁道扇区数</span>
USHORT  wCurSectorsLow;             <span class=rem>// WORD 57: CHS可寻址的扇区数低位字</span>
USHORT  wCurSectorsHigh;            <span class=rem>// WORD 58: CHS可寻址的扇区数高位字</span>
<span class=key>struct</span> {
USHORT  CurNumber:<span class=num>8</span>;            <span class=rem>// 当前一次性可读写扇区数</span>
USHORT  Multi:<span class=num>1</span>;                <span class=rem>// 1=已选择多扇区读写</span>
USHORT  reserved1:<span class=num>7</span>;
} wMultSectorStuff;                 <span class=rem>// WORD 59: 多扇区读写设定</span>
ULONG  dwTotalSectors;              <span class=rem>// WORD 60-61: LBA可寻址的扇区数</span>
USHORT  wSingleWordDMA;             <span class=rem>// WORD 62: 单字节DMA支持能力</span>
<span class=key>struct</span> {
USHORT  Mode0:<span class=num>1</span>;                <span class=rem>// 1=支持模式0 (4.17Mb/s)</span>
USHORT  Mode1:<span class=num>1</span>;                <span class=rem>// 1=支持模式1 (13.3Mb/s)</span>
USHORT  Mode2:<span class=num>1</span>;                <span class=rem>// 1=支持模式2 (16.7Mb/s)</span>
USHORT  Reserved1:<span class=num>5</span>;
USHORT  Mode0Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式0</span>
USHORT  Mode1Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式1</span>
USHORT  Mode2Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式2</span>
USHORT  Reserved2:<span class=num>5</span>;
} wMultiWordDMA;                    <span class=rem>// WORD 63: 多字节DMA支持能力</span>
<span class=key>struct</span> {
USHORT  AdvPOIModes:<span class=num>8</span>;          <span class=rem>// 支持高级POI模式数</span>
USHORT  reserved:<span class=num>8</span>;
} wPIOCapacity;                     <span class=rem>// WORD 64: 高级PIO支持能力</span>
USHORT  wMinMultiWordDMACycle;      <span class=rem>// WORD 65: 多字节DMA传输周期的最小值</span>
USHORT  wRecMultiWordDMACycle;      <span class=rem>// WORD 66: 多字节DMA传输周期的建议值</span>
USHORT  wMinPIONoFlowCycle;         <span class=rem>// WORD 67: 无流控制时PIO传输周期的最小值</span>
USHORT  wMinPOIFlowCycle;           <span class=rem>// WORD 68: 有流控制时PIO传输周期的最小值</span>
USHORT  wReserved69[<span class=num>11</span>];            <span class=rem>// WORD 69-79: 保留</span>
<span class=key>struct</span> {
USHORT  Reserved1:<span class=num>1</span>;
USHORT  ATA1:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA-1</span>
USHORT  ATA2:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA-2</span>
USHORT  ATA3:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA-3</span>
USHORT  ATA4:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA/ATAPI-4</span>
USHORT  ATA5:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA/ATAPI-5</span>
USHORT  ATA6:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA/ATAPI-6</span>
USHORT  ATA7:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA/ATAPI-7</span>
USHORT  ATA8:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA/ATAPI-8</span>
USHORT  ATA9:<span class=num>1</span>;                 <span class=rem>// 1=支持ATA/ATAPI-9</span>
USHORT  ATA10:<span class=num>1</span>;                <span class=rem>// 1=支持ATA/ATAPI-10</span>
USHORT  ATA11:<span class=num>1</span>;                <span class=rem>// 1=支持ATA/ATAPI-11</span>
USHORT  ATA12:<span class=num>1</span>;                <span class=rem>// 1=支持ATA/ATAPI-12</span>
USHORT  ATA13:<span class=num>1</span>;                <span class=rem>// 1=支持ATA/ATAPI-13</span>
USHORT  ATA14:<span class=num>1</span>;                <span class=rem>// 1=支持ATA/ATAPI-14</span>
USHORT  Reserved2:<span class=num>1</span>;
} wMajorVersion;                    <span class=rem>// WORD 80: 主版本</span>
USHORT  wMinorVersion;              <span class=rem>// WORD 81: 副版本</span>
USHORT  wReserved82[<span class=num>6</span>];             <span class=rem>// WORD 82-87: 保留</span>
<span class=key>struct</span> {
USHORT  Mode0:<span class=num>1</span>;                <span class=rem>// 1=支持模式0 (16.7Mb/s)</span>
USHORT  Mode1:<span class=num>1</span>;                <span class=rem>// 1=支持模式1 (25Mb/s)</span>
USHORT  Mode2:<span class=num>1</span>;                <span class=rem>// 1=支持模式2 (33Mb/s)</span>
USHORT  Mode3:<span class=num>1</span>;                <span class=rem>// 1=支持模式3 (44Mb/s)</span>
USHORT  Mode4:<span class=num>1</span>;                <span class=rem>// 1=支持模式4 (66Mb/s)</span>
USHORT  Mode5:<span class=num>1</span>;                <span class=rem>// 1=支持模式5 (100Mb/s)</span>
USHORT  Mode6:<span class=num>1</span>;                <span class=rem>// 1=支持模式6 (133Mb/s)</span>
USHORT  Mode7:<span class=num>1</span>;                <span class=rem>// 1=支持模式7 (166Mb/s) ???</span>
USHORT  Mode0Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式0</span>
USHORT  Mode1Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式1</span>
USHORT  Mode2Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式2</span>
USHORT  Mode3Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式3</span>
USHORT  Mode4Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式4</span>
USHORT  Mode5Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式5</span>
USHORT  Mode6Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式6</span>
USHORT  Mode7Sel:<span class=num>1</span>;             <span class=rem>// 1=已选择模式7</span>
} wUltraDMA;                        <span class=rem>// WORD 88:  Ultra DMA支持能力</span>
USHORT    wReserved89[<span class=num>167</span>];         <span class=rem>// WORD 89-255</span>
} IDINFO, *PIDINFO;
<span class=rem>// SCSI驱动所需的输入输出共用的结构</span>
<span class=key>typedef</span> <span class=key>struct</span> _SRB_IO_CONTROL
{
ULONG HeaderLength;        <span class=rem>// 头长度</span>
UCHAR Signature[<span class=num>8</span>];        <span class=rem>// 特征名称</span>
ULONG Timeout;             <span class=rem>// 超时时间</span>
ULONG ControlCode;         <span class=rem>// 控制码</span>
ULONG ReturnCode;          <span class=rem>// 返回码</span>
ULONG Length;              <span class=rem>// 缓冲区长度</span>
} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
</code></pre>
<p class=bhw98>需要引起注意的是IDINFO第57-58 WORD (CHS可寻址的扇区数)，因为不满足32位对齐的要求，不可定义为一个ULONG字段。Lynn McGuire的程序里正是由于定义为一个ULONG字段，导致该结构不可用。
<p class=bhw98>以下是核心代码：
<pre class=bhw98><code class=bhw98><span class=rem>// 打开设备</span>
<span class=rem>// filename: 设备的&#8220;文件名&#8221;(设备路径)</span>
HANDLE OpenDevice(LPCTSTR filename)
{
HANDLE hDevice;
<span class=rem>// 打开设备</span>
hDevice = ::CreateFile(filename,            <span class=rem>// 文件名</span>
GENERIC_READ | GENERIC_WRITE,          <span class=rem>// 读写方式</span>
FILE_SHARE_READ | FILE_SHARE_WRITE,    <span class=rem>// 共享方式</span>
NULL,                    <span class=rem>// 默认的安全描述符</span>
OPEN_EXISTING,           <span class=rem>// 创建方式</span>
<span class=num>0</span>,                       <span class=rem>// 不需设置文件属性</span>
NULL);                   <span class=rem>// 不需参照模板文件</span>
<span class=key>return</span> hDevice;
}
<span class=rem>// 向驱动发&#8220;IDENTIFY DEVICE&#8221;命令，获得设备信息</span>
<span class=rem>// hDevice: 设备句柄</span>
<span class=rem>// pIdInfo:  设备信息结构指针</span>
BOOL IdentifyDevice(HANDLE hDevice, PIDINFO pIdInfo)
{
PSENDCMDINPARAMS pSCIP;      <span class=rem>// 输入数据结构指针</span>
PSENDCMDOUTPARAMS pSCOP;     <span class=rem>// 输出数据结构指针</span>
DWORD dwOutBytes;            <span class=rem>// IOCTL输出数据长度</span>
BOOL bResult;                <span class=rem>// IOCTL返回值</span>
<span class=rem>// 申请输入/输出数据结构空间</span>
pSCIP = (PSENDCMDINPARAMS)::GlobalAlloc(LMEM_ZEROINIT, <span class=key>sizeof</span>(SENDCMDINPARAMS) - <span class=num>1</span>);
pSCOP = (PSENDCMDOUTPARAMS)::GlobalAlloc(LMEM_ZEROINIT, <span class=key>sizeof</span>(SENDCMDOUTPARAMS) + <span class=key>sizeof</span>(IDINFO) - <span class=num>1</span>);
<span class=rem>// 指定ATA/ATAPI命令的寄存器值</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bFeaturesReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bSectorCountReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bSectorNumberReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bCylLowReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bCylHighReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bDriveHeadReg = 0;</span>
pSCIP-&gt;irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;
<span class=rem>// 指定输入/输出数据缓冲区大小</span>
pSCIP-&gt;cBufferSize = <span class=num>0</span>;
pSCOP-&gt;cBufferSize = <span class=key>sizeof</span>(IDINFO);
<span class=rem>// IDENTIFY DEVICE</span>
bResult = ::DeviceIoControl(hDevice,        <span class=rem>// 设备句柄</span>
DFP_RECEIVE_DRIVE_DATA,                 <span class=rem>// 指定IOCTL</span>
pSCIP, <span class=key>sizeof</span>(SENDCMDINPARAMS) - <span class=num>1</span>,     <span class=rem>// 输入数据缓冲区</span>
pSCOP, <span class=key>sizeof</span>(SENDCMDOUTPARAMS) + <span class=key>sizeof</span>(IDINFO) - <span class=num>1</span>,    <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,                <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);        <span class=rem>// 用同步I/O</span>
<span class=rem>// 复制设备参数结构</span>
::memcpy(pIdInfo, pSCOP-&gt;bBuffer, <span class=key>sizeof</span>(IDINFO));
<span class=rem>// 释放输入/输出数据空间</span>
::GlobalFree(pSCOP);
::GlobalFree(pSCIP);
<span class=key>return</span> bResult;
}
<span class=rem>// 向SCSI MINI-PORT驱动发&#8220;IDENTIFY DEVICE&#8221;命令，获得设备信息</span>
<span class=rem>// hDevice: 设备句柄</span>
<span class=rem>// pIdInfo:  设备信息结构指针</span>
BOOL IdentifyDeviceAsScsi(HANDLE hDevice, <span class=key>int</span> nDrive, PIDINFO pIdInfo)
{
PSENDCMDINPARAMS pSCIP;     <span class=rem>// 输入数据结构指针</span>
PSENDCMDOUTPARAMS pSCOP;    <span class=rem>// 输出数据结构指针</span>
PSRB_IO_CONTROL pSRBIO;     <span class=rem>// SCSI输入输出数据结构指针</span>
DWORD dwOutBytes;           <span class=rem>// IOCTL输出数据长度</span>
BOOL bResult;               <span class=rem>// IOCTL返回值</span>
<span class=rem>// 申请输入/输出数据结构空间</span>
pSRBIO = (PSRB_IO_CONTROL)::GlobalAlloc(LMEM_ZEROINIT,
<span class=key>sizeof</span>(SRB_IO_CONTROL) + <span class=key>sizeof</span>(SENDCMDOUTPARAMS) + <span class=key>sizeof</span>(IDINFO) - <span class=num>1</span>);
pSCIP = (PSENDCMDINPARAMS)((<span class=key>char</span> *)pSRBIO + <span class=key>sizeof</span>(SRB_IO_CONTROL));
pSCOP = (PSENDCMDOUTPARAMS)((<span class=key>char</span> *)pSRBIO + <span class=key>sizeof</span>(SRB_IO_CONTROL));
<span class=rem>// 填充输入/输出数据</span>
pSRBIO-&gt;HeaderLength = <span class=key>sizeof</span>(SRB_IO_CONTROL);
pSRBIO-&gt;Timeout = <span class=num>10000</span>;
pSRBIO-&gt;Length = <span class=key>sizeof</span>(SENDCMDOUTPARAMS) + <span class=key>sizeof</span>(IDINFO) - <span class=num>1</span>;
pSRBIO-&gt;ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
::strncpy ((<span class=key>char</span> *)pSRBIO-&gt;Signature, <span class=str>"SCSIDISK"</span>, <span class=num>8</span>);
<span class=rem>// 指定ATA/ATAPI命令的寄存器值</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bFeaturesReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bSectorCountReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bSectorNumberReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bCylLowReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bCylHighReg = 0;</span>
<span class=rem>//    pSCIP-&gt;irDriveRegs.bDriveHeadReg = 0;</span>
pSCIP-&gt;irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;
pSCIP-&gt;bDriveNumber = nDrive;
<span class=rem>// IDENTIFY DEVICE</span>
bResult = ::DeviceIoControl(hDevice,    <span class=rem>// 设备句柄</span>
IOCTL_SCSI_MINIPORT,                <span class=rem>// 指定IOCTL</span>
pSRBIO, <span class=key>sizeof</span>(SRB_IO_CONTROL) + <span class=key>sizeof</span>(SENDCMDINPARAMS) - <span class=num>1</span>,    <span class=rem>// 输入数据缓冲区</span>
pSRBIO, <span class=key>sizeof</span>(SRB_IO_CONTROL) + <span class=key>sizeof</span>(SENDCMDOUTPARAMS) + <span class=key>sizeof</span>(IDINFO) - <span class=num>1</span>,    <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,            <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);    <span class=rem>// 用同步I/O</span>
<span class=rem>// 复制设备参数结构</span>
::memcpy(pIdInfo, pSCOP-&gt;bBuffer, <span class=key>sizeof</span>(IDINFO));
<span class=rem>// 释放输入/输出数据空间</span>
::GlobalFree(pSRBIO);
<span class=key>return</span> bResult;
}
<span class=rem>// 将串中的字符两两颠倒</span>
<span class=rem>// 原因是ATA/ATAPI中的WORD，与Windows采用的字节顺序相反</span>
<span class=rem>// 驱动程序中已经将收到的数据全部反过来，我们来个负负得正</span>
<span class=key>void</span> AdjustString(<span class=key>char</span>* str, <span class=key>int</span> len)
{
<span class=key>char</span> ch;
<span class=key>int</span> i;
<span class=rem>// 两两颠倒</span>
<span class=key>for</span> (i = <span class=num>0</span>; i &lt; len; i += <span class=num>2</span>)
{
ch = str[i];
str[i] = str[i + <span class=num>1</span>];
str[i + <span class=num>1</span>] = ch;
}
<span class=rem>// 若是右对齐的，调整为左对齐 (去掉左边的空格)</span>
i = <span class=num>0</span>;
<span class=key>while</span> ((i &lt; len) &amp;&amp; (str[i] == <span class=str>' '</span>)) i++;
::memmove(str, &amp;str[i], len - i);
<span class=rem>// 去掉右边的空格</span>
i = len - <span class=num>1</span>;
<span class=key>while</span> ((i &gt;= <span class=num>0</span>) &amp;&amp; (str[i] == <span class=str>' '</span>))
{
str[i] = <span class=str>'\0'</span>;
i--;
}
}
<span class=rem>// 读取IDE硬盘的设备信息，必须有足够权限</span>
<span class=rem>// nDrive: 驱动器号(0=第一个硬盘，1=0=第二个硬盘，......)</span>
<span class=rem>// pIdInfo: 设备信息结构指针</span>
BOOL GetPhysicalDriveInfoInNT(<span class=key>int</span> nDrive, PIDINFO pIdInfo)
{
HANDLE hDevice;         <span class=rem>// 设备句柄</span>
BOOL bResult;           <span class=rem>// 返回结果</span>
<span class=key>char</span> szFileName[<span class=num>20</span>];    <span class=rem>// 文件名</span>
::sprintf(szFileName,<span class=str>"\\\\.\\PhysicalDrive%d"</span>, nDrive);
hDevice = ::OpenDevice(szFileName);
<span class=key>if</span> (hDevice == INVALID_HANDLE_VALUE)
{
<span class=key>return</span> FALSE;
}
<span class=rem>// IDENTIFY DEVICE</span>
bResult = ::IdentifyDevice(hDevice, pIdInfo);
<span class=key>if</span> (bResult)
{
<span class=rem>// 调整字符串</span>
::AdjustString(pIdInfo-&gt;sSerialNumber, <span class=num>20</span>);
::AdjustString(pIdInfo-&gt;sModelNumber, <span class=num>40</span>);
::AdjustString(pIdInfo-&gt;sFirmwareRev, <span class=num>8</span>);
}
::CloseHandle (hDevice);
<span class=key>return</span> bResult;
}
<span class=rem>// 用SCSI驱动读取IDE硬盘的设备信息，不受权限制约</span>
<span class=rem>// nDrive: 驱动器号(0=Primary Master, 1=Promary Slave, 2=Secondary master, 3=Secondary slave)</span>
<span class=rem>// pIdInfo: 设备信息结构指针</span>
BOOL GetIdeDriveAsScsiInfoInNT(<span class=key>int</span> nDrive, PIDINFO pIdInfo)
{
HANDLE hDevice;         <span class=rem>// 设备句柄</span>
BOOL bResult;           <span class=rem>// 返回结果</span>
<span class=key>char</span> szFileName[<span class=num>20</span>];    <span class=rem>// 文件名</span>
::sprintf(szFileName,<span class=str>"\\\\.\\Scsi%d:"</span>, nDrive/<span class=num>2</span>);
hDevice = ::OpenDevice(szFileName);
<span class=key>if</span> (hDevice == INVALID_HANDLE_VALUE)
{
<span class=key>return</span> FALSE;
}
<span class=rem>// IDENTIFY DEVICE</span>
bResult = ::IdentifyDeviceAsScsi(hDevice, nDrive%<span class=num>2</span>, pIdInfo);
<span class=rem>// 检查是不是空串</span>
<span class=key>if</span> (pIdInfo-&gt;sModelNumber[<span class=num>0</span>] == <span class=str>'\0'</span>)
{
bResult = FALSE;
}
<span class=key>if</span> (bResult)
{
<span class=rem>// 调整字符串</span>
::AdjustString(pIdInfo-&gt;sSerialNumber, <span class=num>20</span>);
::AdjustString(pIdInfo-&gt;sModelNumber, <span class=num>40</span>);
::AdjustString(pIdInfo-&gt;sFirmwareRev, <span class=num>8</span>);
}
<span class=key>return</span> bResult;
}
</code></pre>
<p class=bhw98><strong class=bhw98>Q</strong> 我注意到ATA/ATAPI里，以及DiskID32里，有一个&#8220;IDENTIFY PACKET DEVICE&#8221;指令，与&#8220;IDENTIFY DEVICE&#8221;有什么区别？
<p class=bhw98><strong class=bhw98>A</strong> IDENTIFY DEVICE专门用于固定硬盘，而IDENTIFY PACKET DEVICE用于可移动存储设备如CDROM、CF、MO、ZIP、TAPE等。因为驱动程序的原因，实际上用本例的方法，不管是IDENTIFY DEVICE也好，IDENTIFY PACKET DEVICE也好，获取可移动存储设备的详细信息，一般是做不到的。而且除了IDE硬盘，对SCSI、USB等接口的硬盘也不起作用。除非厂商提供的驱动支持这样的功能。
<p class=bhw98><strong class=bhw98>Q</strong> ATA/ATAPI有很多指令，如READ SECTORS, WRITE SECTORS, SECURITY, SLEEP, STANDBY等，利用上述方法，是否可进行相应操作？
<p class=bhw98><strong class=bhw98>A</strong> 应该没问题。但切记，要慎重慎重再慎重！
<p class=bhw98><strong class=bhw98>Q</strong> 关于权限问题，请解释一下好吗？
<p class=bhw98><strong class=bhw98>A</strong> 在NT/2000/XP下，administrator可以管理设备，上述两种访问驱动的方法都行。但在user身份下，或者登录到域后，用户无法访问PhysicalDrive驱动的核心层，但SCSI MINI-PORT驱动却可以。目前是可以，不知道Windows以后的版本是否支持。因为这肯定是一个安全隐患。
<p class=bhw98>另外，我们着重讨论NT/2000/XP中DeviceIoControl的应用，如果需要在98/ME中得到包括硬盘序列号在内的更加详细的信息，请参考DiskID32。 </p>
<h2 class=bhw98>[相关资源]</h2>
<li class=bhw98>本文Demo源码：<a href="http://www.csdn.net/develop/author/bhw98/IdeDiskInfo.zip"><font color=#000080>IdeDiskInfo.zip</font></a> (25KB)
<li class=bhw98>Lynn McGuire的 <a href="http://www.codeguru.com/system/DiskId32.zip"><font color=#000080>DiskID32.zip</font></a> (30KB)
<li class=bhw98>T13官方网站：<a href="http://www.t13.org/"><font color=#000080>http://www.t13.org</font></a> </li>
</div>
<img src ="http://www.cppblog.com/iniwf/aggbug/79824.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-13 21:06 <a href="http://www.cppblog.com/iniwf/archive/2009/04/13/79824.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战DeviceIoControl 之三：制作磁盘镜像文件</title><link>http://www.cppblog.com/iniwf/archive/2009/04/13/79823.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 13 Apr 2009 13:04:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/13/79823.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79823.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/13/79823.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79823.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79823.html</trackback:ping><description><![CDATA[转自<a href="http://www.cnblogs.com/henryzc/archive/2005/11/08/271910.html">http://www.cnblogs.com/henryzc/archive/2005/11/08/271910.html</a><br><br>
<div class=postText>
<p class=bhw98><strong class=bhw98>Q</strong> DOS命令DISKCOPY给我很深的印象，现在也有许多&#8220;克隆&#8221;软件，可以对磁盘进行全盘复制。我想，要制作磁盘镜像文件，DeviceIoControl应该很有用武之地吧？
<p class=bhw98><strong class=bhw98>A</strong> 是的。这里举一个制作软盘镜像文件，功能类似于&#8220;DISKCOPY&#8221;的例子。
<p class=bhw98>本例实现其功能的核心代码如下：
<pre class=bhw98><code class=bhw98><span class=rem>// 打开磁盘</span>
HANDLE OpenDisk(LPCTSTR filename)
{
HANDLE hDisk;
<span class=rem>// 打开设备</span>
hDisk = ::CreateFile(filename,           <span class=rem>// 文件名</span>
GENERIC_READ | GENERIC_WRITE,        <span class=rem>// 读写方式</span>
FILE_SHARE_READ | FILE_SHARE_WRITE,  <span class=rem>// 共享方式</span>
NULL,                                <span class=rem>// 默认的安全描述符</span>
OPEN_EXISTING,                       <span class=rem>// 创建方式</span>
<span class=num>0</span>,                                   <span class=rem>// 不需设置文件属性</span>
NULL);                               <span class=rem>// 不需参照模板文件</span>
<span class=key>return</span> hDisk;
}
<span class=rem>// 获取磁盘参数</span>
BOOL GetDiskGeometry(HANDLE hDisk, PDISK_GEOMETRY lpGeometry)
{
DWORD dwOutBytes;
BOOL bResult;
<span class=rem>// 用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盘参数</span>
bResult = ::DeviceIoControl(hDisk,        <span class=rem>// 设备句柄</span>
IOCTL_DISK_GET_DRIVE_GEOMETRY,        <span class=rem>// 取磁盘参数</span>
NULL, <span class=num>0</span>,                              <span class=rem>// 不需要输入数据</span>
lpGeometry, <span class=key>sizeof</span>(DISK_GEOMETRY),    <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,                          <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);                  <span class=rem>// 用同步I/O</span>
<span class=key>return</span> bResult;
}
<span class=rem>// 从指定磁道开始读磁盘</span>
BOOL ReadTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD dwCylinderNumber)
{
DWORD VirtBufSize;
DWORD BytesRead;
<span class=rem>// 大小</span>
VirtBufSize =  lpGeometry-&gt;TracksPerCylinder * lpGeometry-&gt;SectorsPerTrack * lpGeometry-&gt;BytesPerSector;
<span class=rem>// 偏移</span>
::SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN);
<span class=key>return</span> ::ReadFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &amp;BytesRead, NULL);
}
<span class=rem>// 从指定磁道开始写磁盘</span>
BOOL WriteTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD dwCylinderNumber)
{
DWORD VirtBufSize;
DWORD BytesWritten;
<span class=rem>// 大小</span>
VirtBufSize =  lpGeometry-&gt;TracksPerCylinder * lpGeometry-&gt;SectorsPerTrack * lpGeometry-&gt;BytesPerSector;
<span class=rem>// 偏移</span>
::SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN);
<span class=key>return</span> ::WriteFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &amp;BytesWritten, NULL);
}
<span class=rem>// 从指定磁道开始格式化磁盘</span>
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-&gt;MediaType;
FormatParameters.StartCylinderNumber = dwStartCylinder;
FormatParameters.EndCylinderNumber = dwStartCylinder + dwCylinderNumber - <span class=num>1</span>;
FormatParameters.StartHeadNumber = <span class=num>0</span>;
FormatParameters.EndHeadNumber = lpGeometry-&gt;TracksPerCylinder - <span class=num>1</span>;
dwBufSize = lpGeometry-&gt;TracksPerCylinder * <span class=key>sizeof</span>(BAD_TRACK_NUMBER);
lpBadTrack = (PBAD_TRACK_NUMBER) new BYTE[dwBufSize];
<span class=rem>// 用IOCTL_DISK_FORMAT_TRACKS对连续磁道进行低级格式化</span>
bResult = ::DeviceIoControl(hDisk,               <span class=rem>// 设备句柄</span>
IOCTL_DISK_FORMAT_TRACKS,                    <span class=rem>// 低级格式化</span>
&amp;FormatParameters, <span class=key>sizeof</span>(FormatParameters), <span class=rem>// 输入数据缓冲区</span>
lpBadTrack, dwBufSize,                       <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,                                 <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);                         <span class=rem>// 用同步I/O</span>
delete lpBadTrack;
<span class=key>return</span> bResult;
}
<span class=rem>// 将卷锁定</span>
BOOL LockVolume(HANDLE hDisk)
{
DWORD dwOutBytes;
BOOL bResult;
<span class=rem>// 用FSCTL_LOCK_VOLUME锁卷</span>
bResult = ::DeviceIoControl(hDisk,        <span class=rem>// 设备句柄</span>
FSCTL_LOCK_VOLUME,                    <span class=rem>// 锁卷</span>
NULL, <span class=num>0</span>,                              <span class=rem>// 不需要输入数据</span>
NULL, <span class=num>0</span>,                              <span class=rem>// 不需要输出数据</span>
&amp;dwOutBytes,                          <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);                  <span class=rem>// 用同步I/O</span>
<span class=key>return</span> bResult;
}
<span class=rem>// 将卷解锁</span>
BOOL UnlockVolume(HANDLE hDisk)
{
DWORD dwOutBytes;
BOOL bResult;
<span class=rem>// 用FSCTL_UNLOCK_VOLUME开卷锁</span>
bResult = ::DeviceIoControl(hDisk,        <span class=rem>// 设备句柄</span>
FSCTL_UNLOCK_VOLUME,                  <span class=rem>// 开卷锁</span>
NULL, <span class=num>0</span>,                              <span class=rem>// 不需要输入数据</span>
NULL, <span class=num>0</span>,                              <span class=rem>// 不需要输出数据</span>
&amp;dwOutBytes,                          <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);                  <span class=rem>// 用同步I/O</span>
<span class=key>return</span> bResult;
}
<span class=rem>// 将卷卸下</span>
<span class=rem>// 该操作使系统重新辨识磁盘，等效于重新插盘</span>
BOOL DismountVolume(HANDLE hDisk)
{
DWORD dwOutBytes;
BOOL bResult;
<span class=rem>// 用FSCTL_DISMOUNT_VOLUME卸卷</span>
bResult = ::DeviceIoControl(hDisk,        <span class=rem>// 设备句柄</span>
FSCTL_DISMOUNT_VOLUME,                <span class=rem>// 卸卷</span>
NULL, <span class=num>0</span>,                              <span class=rem>// 不需要输入数据</span>
NULL, <span class=num>0</span>,                              <span class=rem>// 不需要输出数据</span>
&amp;dwOutBytes,                          <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);                  <span class=rem>// 用同步I/O</span>
<span class=key>return</span> bResult;
}
</code></pre>
<p class=bhw98>将软盘保存成镜像文件的步骤简单描述为：<br>1、创建空的镜像文件。<br>2、调用OpenDisk打开软盘。成功转3，失败转8。<br>3、调用LockVolume将卷锁定。成功转4，失败转7。<br>4、调用GetDiskGeometry获取参数。成功转5，失败转6。<br>5、将磁盘参数写入镜像文件作为文件头。调用ReadTracks按柱面读出数据，保存在镜像文件中。循环次数等于柱面数。<br>6、调用UnlockVolume将卷解锁。<br>7、调用CloseDisk关闭软盘。<br>8、关闭镜像文件。</p>
<p class=bhw98>将镜像文件载入软盘的步骤简单描述为：<br>1、打开镜像文件。<br>2、调用OpenDisk打开软盘。成功转3，失败转11。<br>3、调用LockVolume将卷锁定。成功转4，失败转10。<br>4、调用GetDiskGeometry获取参数。成功转5，失败转9。<br>5、从镜像文件中读出文件头，判断两个磁盘参数是否一致。不一致转6，否则转7。<br>6、调用LowLevelFormatTracks按柱面格式化软盘。循环次数等于柱面数。成功转7，失败转8。<br>7、从镜像文件中读出数据，并调用WriteTracks按柱面写入磁盘。循环次数等于柱面数。<br>8、调用DismountVolume将卷卸下。<br>9、调用UnlockVolume将卷解锁。<br>10、调用CloseDisk关闭软盘。<br>11、关闭镜像文件。</p>
<p class=bhw98><strong class=bhw98>Q</strong> 我注意到，磁盘读写和格式化是按柱面进行的，有什么道理吗？
<p class=bhw98><strong class=bhw98>A</strong> 没有特别的原因，只是因为在这个例子中可以方便地显示处理进度。
<p class=bhw98>有一点需要特别提及，按绝对地址读写磁盘数据时，&#8220;最小单位&#8221;是扇区，地址一定要与扇区对齐，长度也要等于扇区长度的整数倍。比如，每扇区512字节，那么起始地址和数据长度都应能被512整除才行。
<p class=bhw98><strong class=bhw98>Q</strong> 我忽然产生了一个伟大的想法，用绝对地址读写的方式使用磁盘，包括U盘啦，MO啦，而不是用现成的文件系统，那不是可以将数据保密了吗？
<p class=bhw98><strong class=bhw98>A</strong> 当然，只要你喜欢。可千万别在你的系统盘上做试验，否则......可别怪bhw98没有提醒过你喔！
<p class=bhw98><strong class=bhw98>Q</strong> 我知道怎么测试光驱的传输速度了，就用上面的方法，读出一定长度数据，除以所需时间，应该可以吧？
<p class=bhw98><strong class=bhw98>A</strong> 可以。但取光盘参数时要用IOCTL_STORAGE_GET_MEDIA_TYPES_EX，我们已经探讨过的。 </p>
<h2 class=bhw98>[相关资源]</h2>
<li class=bhw98>本文Demo源码：<a href="http://www.csdn.net/develop/author/bhw98/FloppyImage.zip"><font color=#000080>FloppyImage.zip</font></a> (16KB)
<li class=bhw98>Microsoft的例子：<a href="http://download.microsoft.com/download/vstudio60pro/Utility/6.0/W98NT42KMeXP/EN-US/vs6samples.exe"><font color=#000080>vs6samples.exe</font></a> (134,518KB) </li>
</div>
<img src ="http://www.cppblog.com/iniwf/aggbug/79823.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-13 21:04 <a href="http://www.cppblog.com/iniwf/archive/2009/04/13/79823.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战DeviceIoControl 之二：获取软盘/硬盘/光盘的参数</title><link>http://www.cppblog.com/iniwf/archive/2009/04/13/79822.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 13 Apr 2009 13:03:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/13/79822.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79822.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/13/79822.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79822.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79822.html</trackback:ping><description><![CDATA[转自<a href="http://www.cnblogs.com/henryzc/archive/2005/11/08/271909.html">http://www.cnblogs.com/henryzc/archive/2005/11/08/271909.html</a><br><br>
<div class=postText>
<p class=bhw98><strong class=bhw98>Q</strong> 在MSDN的那个demo中，将设备名换成&#8220;A:&#8221;取A盘参数，先用资源管理器读一下盘，再运行这个程序可以成功，但换一张盘后就失败；换成&#8220;CDROM0&#8221;取CDROM参数，无论如何都不行。这个问题如何解决呢？
<p class=bhw98><strong class=bhw98>A</strong> 取软盘参数是从软盘上读取格式化后的信息，也就是必须执行读操作，这一点与硬盘不同。将CreateFile中的访问方式改为GENERIC_READ就行了。
<p class=bhw98>IOCTL_DISK_GET_DRIVE_GEOMETRY这个I/O控制码，对软盘和硬盘有效，但对一些可移动媒介如CD/DVD-ROM、TAPE等就不管用了。要取CDROM参数，还得另辟蹊径。IOCTL_STORAGE_GET_MEDIA_TYPES_EX能够帮我们解决问题。
<p class=bhw98><strong class=bhw98>Q</strong> 使用这些I/O控制码，需要什么样的输入输出数据格式呢？
<p class=bhw98><strong class=bhw98>A</strong> DeviceIoControl使用这两个控制码时，都不需要输入数据。
<p class=bhw98>IOCTL_DISK_GET_DRIVE_GEOMETRY直接输出一个DISK_GEOMETRY结构：
<pre class=bhw98><code class=bhw98><span class=key>typedef struct</span> _DISK_GEOMETRY {
LARGE_INTEGER Cylinders;   <span class=rem>// 柱面数</span>
MEDIA_TYPE MediaType;      <span class=rem>// 介质类型</span>
DWORD TracksPerCylinder;   <span class=rem>// 每柱面的磁道数</span>
DWORD SectorsPerTrack;     <span class=rem>// 每磁道的扇区数</span>
DWORD BytesPerSector;      <span class=rem>// 每扇区的字节数</span>
} DISK_GEOMETRY;
</code></pre>
<p class=bhw98>IOCTL_STORAGE_GET_MEDIA_TYPES_EX输出一个GET_MEDIA_TYPES结构：
<pre class=bhw98><code class=bhw98><span class=key>typedef struct</span> _GET_MEDIA_TYPES {
DWORD DeviceType;               <span class=rem>// 设备类型</span>
DWORD MediaInfoCount;           <span class=rem>// 介质信息条数</span>
DEVICE_MEDIA_INFO MediaInfo[<span class=num>1</span>]; <span class=rem>// 介质信息</span>
} GET_MEDIA_TYPES;
</code></pre>
<p class=bhw98>让我们来看一下DEVICE_MEDIA_INFO结构的定义：
<pre class=bhw98><code class=bhw98><span class=key>typedef struct</span> _DEVICE_MEDIA_INFO {
<span class=key>union</span> {
<span class=key>struct</span> {
LARGE_INTEGER Cylinders;       <span class=rem>// 柱面数</span>
STORAGE_MEDIA_TYPE MediaType;  <span class=rem>// 介质类型</span>
DWORD TracksPerCylinder;       <span class=rem>// 每柱面的磁道数</span>
DWORD SectorsPerTrack;         <span class=rem>// 每磁道的扇区数</span>
DWORD BytesPerSector;          <span class=rem>// 每扇区的字节数</span>
DWORD NumberMediaSides;        <span class=rem>// 介质面数</span>
DWORD MediaCharacteristics;    <span class=rem>// 介质特性</span>
} DiskInfo;            <span class=rem>// 硬盘信息</span>
<span class=key>struct</span> {
LARGE_INTEGER Cylinders;       <span class=rem>// 柱面数</span>
STORAGE_MEDIA_TYPE MediaType;  <span class=rem>// 介质类型</span>
DWORD TracksPerCylinder;       <span class=rem>// 每柱面的磁道数</span>
DWORD SectorsPerTrack;         <span class=rem>// 每磁道的扇区数</span>
DWORD BytesPerSector;          <span class=rem>// 每扇区的字节数</span>
DWORD NumberMediaSides;        <span class=rem>// 介质面数</span>
DWORD MediaCharacteristics;    <span class=rem>// 介质特性</span>
} RemovableDiskInfo;   <span class=rem>// &#8220;可移动盘&#8221;信息</span>
<span class=key>struct</span> {
STORAGE_MEDIA_TYPE MediaType;  <span class=rem>// 介质类型</span>
DWORD   MediaCharacteristics;  <span class=rem>// 介质特性</span>
DWORD   CurrentBlockSize;      <span class=rem>// 块的大小</span>
} TapeInfo;           <span class=rem>// 磁带信息</span>
} DeviceSpecific;
} DEVICE_MEDIA_INFO;
</code></pre>
<p class=bhw98>其中CD-ROM属于&#8220;可移动盘&#8221;的范围。请注意，GET_MEDIA_TYPES结构本身只定义了一条DEVICE_MEDIA_INFO，额外的DEVICE_MEDIA_INFO需要紧接此结构的另外的空间。
<p class=bhw98><strong class=bhw98>Q</strong> 调用方法我了解了，请用VC举个例子来实现我所期待已久的功能吧？
<p class=bhw98><strong class=bhw98>A</strong> 好，现在就演示一下如何取软盘/硬盘/光盘的参数。测试时，记得要有软盘/光盘插在驱动器里喔！
<p class=bhw98>首先，用MFC AppWizard生成一个单文档的应用程序，取名为DiskGeometry，让它的View基于CEditView。
<p class=bhw98>然后，添加以下的.h和.cpp文件。
<pre class=bhw98><code class=bhw98><span class=rem>//////////////////////////////////////////////////////////////////////////////
// GetDiskGeometry.h
//////////////////////////////////////////////////////////////////////////////</span>
<span class=key>#if</span> !<span class=key>defined</span>(GET_DISK_GEOMETRY_H__)
<span class=key>#define</span> GET_DISK_GEOMETRY_H__
<span class=key>#if</span> _MSC_VER &gt; <span class=num>1000</span>
<span class=key>#pragma once</span>
<span class=key>#endif</span> <span class=rem>// _MSC_VER &gt; 1000</span>
<span class=key>#include</span> <span class=str>&lt;winioctl.h&gt;</span>
BOOL GetDriveGeometry(<span class=key>const char</span>* filename, DISK_GEOMETRY *pdg);
<span class=key>#endif</span> <span class=rem>// !defined(GET_DISK_GEOMETRY_H__)</span>
<span class=rem>//////////////////////////////////////////////////////////////////////////////
// GetDiskGeometry.cpp
//////////////////////////////////////////////////////////////////////////////</span>
<span class=key>#include</span> <span class=str>"stdafx.h"</span>
<span class=key>#include</span> <span class=str>"GetDiskGeometry.h"</span>
<span class=rem>// IOCTL_STORAGE_GET_MEDIA_TYPES_EX可能返回不止一条DEVICE_MEDIA_INFO，故定义足够的空间</span>
<span class=key>#define</span> MEDIA_INFO_SIZE    <span class=key>sizeof</span>(GET_MEDIA_TYPES)+<span class=num>15</span>*<span class=key>sizeof</span>(DEVICE_MEDIA_INFO)
<span class=rem>// filename -- 用于设备的文件名
// pdg -- 参数缓冲区指针</span>
BOOL GetDriveGeometry(<span class=key>const char</span>* filename, DISK_GEOMETRY *pdg)
{
HANDLE hDevice;         <span class=rem>// 设备句柄</span>
BOOL bResult;           <span class=rem>// DeviceIoControl的返回结果</span>
GET_MEDIA_TYPES *pmt;   <span class=rem>// 内部用的输出缓冲区</span>
DWORD dwOutBytes;       <span class=rem>// 输出数据长度</span>
<span class=rem>// 打开设备</span>
hDevice = ::CreateFile(filename,           <span class=rem>// 文件名</span>
GENERIC_READ,                          <span class=rem>// 软驱需要读盘</span>
FILE_SHARE_READ | FILE_SHARE_WRITE,    <span class=rem>// 共享方式</span>
NULL,                                  <span class=rem>// 默认的安全描述符</span>
OPEN_EXISTING,                         <span class=rem>// 创建方式</span>
<span class=num>0</span>,                                     <span class=rem>// 不需设置文件属性</span>
NULL);                                 <span class=rem>// 不需参照模板文件</span>
<span class=key>if</span> (hDevice == INVALID_HANDLE_VALUE)
{
<span class=rem>// 设备无法打开...</span>
<span class=key>return</span> FALSE;
}
<span class=rem>// 用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盘参数</span>
bResult = ::DeviceIoControl(hDevice,       <span class=rem>// 设备句柄</span>
IOCTL_DISK_GET_DRIVE_GEOMETRY,         <span class=rem>// 取磁盘参数</span>
NULL, <span class=num>0</span>,                               <span class=rem>// 不需要输入数据</span>
pdg, sizeof(DISK_GEOMETRY),            <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,                           <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);                   <span class=rem>// 用同步I/O</span>
<span class=rem>// 如果失败，再用IOCTL_STORAGE_GET_MEDIA_TYPES_EX取介质类型参数</span>
<span class=key>if</span> (!bResult)
{
pmt = (GET_MEDIA_TYPES *)<span class=key>new</span> BYTE[MEDIA_INFO_SIZE];
bResult = ::DeviceIoControl(hDevice,    <span class=rem>// 设备句柄</span>
IOCTL_STORAGE_GET_MEDIA_TYPES_EX,   <span class=rem>// 取介质类型参数</span>
NULL, <span class=num>0</span>,                            <span class=rem>// 不需要输入数据</span>
pmt, MEDIA_INFO_SIZE,               <span class=rem>// 输出数据缓冲区</span>
&amp;dwOutBytes,                        <span class=rem>// 输出数据长度</span>
(LPOVERLAPPED)NULL);                <span class=rem>// 用同步I/O</span>
<span class=key>if</span> (bResult)
{
<span class=rem>// 注意到结构DEVICE_MEDIA_INFO是在结构DISK_GEOMETRY的基础上扩充的
// 为简化程序，用memcpy代替如下多条赋值语句：
// pdg-&gt;MediaType = (MEDIA_TYPE)pmt-&gt;MediaInfo[0].DeviceSpecific.DiskInfo.MediaType;
// pdg-&gt;Cylinders = pmt-&gt;MediaInfo[0].DeviceSpecific.DiskInfo.Cylinders;
// pdg-&gt;TracksPerCylinder = pmt-&gt;MediaInfo[0].DeviceSpecific.DiskInfo.TracksPerCylinder;
// ... ...</span>
::memcpy(pdg, pmt-&gt;MediaInfo, <span class=key>sizeof</span>(DISK_GEOMETRY));
}
<span class=key>delete</span> pmt;
}
<span class=rem>// 关闭设备句柄</span>
::CloseHandle(hDevice);
<span class=key>return</span> (bResult);
}
</code></pre>
<p class=bhw98>然后，在Toolbar的IDR_MAINFRAME上添加一个按钮，ID为ID_GET_DISK_GEOMETRY。打开ClassWizard，在DiskGeometryView中
<p class=bhw98>添加ID_GET_DISK_GEOMETRY的映射函数OnGetDiskGeometry。打开DiskGeometryView.cpp，包含头文件GetDiskGeometry.h。
<p class=bhw98>在OnGetDiskGeometry中，添加以下代码
<pre class=bhw98><code class=bhw98>    <span class=key>const char</span> *szDevName[]=
{
<span class=str>"\\\\.\\A:"</span>,
<span class=str>"\\\\.\\B:"</span>,
<span class=str>"\\\\.\\PhysicalDrive0"</span>,
<span class=str>"\\\\.\\PhysicalDrive1"</span>,
<span class=str>"\\\\.\\PhysicalDrive2"</span>,
<span class=str>"\\\\.\\PhysicalDrive3"</span>,
<span class=str>"\\\\.\\Cdrom0"</span>,
<span class=str>"\\\\.\\Cdrom1"</span>,
};
DISK_GEOMETRY dg;
ULONGLONG DiskSize;
BOOL bResult;
CString strMsg;
CString strTmp;
<span class=key>for</span> (<span class=key>int</span> i = <span class=num>0</span>; i &lt; <span class=key>sizeof</span>(szDevName)/<span class=key>sizeof</span>(<span class=key>char</span>*); i++)
{
bResult = GetDriveGeometry(szDevName[i], &amp;dg);
strTmp.Format(<span class=str>"\r\n%s  result = %s\r\n"</span>, szDevName[i], bResult ? <span class=str>"success"</span> : <span class=str>"failure"</span>);
strMsg+=strTmp;
<span class=key>if</span> (!bResult) <span class=key>continue</span>;
strTmp.Format(<span class=str>"    Media Type = %d\r\n"</span>, dg.MediaType);
strMsg+=strTmp;
strTmp.Format(<span class=str>"    Cylinders = %I64d\r\n"</span>, dg.Cylinders);
strMsg+=strTmp;
strTmp.Format(<span class=str>"    Tracks per cylinder = %ld\r\n"</span>, (ULONG) dg.TracksPerCylinder);
strMsg+=strTmp;
strTmp.Format(<span class=str>"    Sectors per track = %ld\r\n"</span>, (ULONG) dg.SectorsPerTrack);
strMsg+=strTmp;
strTmp.Format(<span class=str>"    Bytes per sector = %ld\r\n"</span>, (ULONG) dg.BytesPerSector);
strMsg+=strTmp;
DiskSize = dg.Cylinders.QuadPart * (ULONG)dg.TracksPerCylinder *
(ULONG)dg.SectorsPerTrack * (ULONG)dg.BytesPerSector;
strTmp.Format(<span class=str>"    Disk size = %I64d (Bytes) = %I64d (Mb)\r\n"</span>, DiskSize, DiskSize / (<span class=num>1024</span> * <span class=num>1024</span>));
strMsg+=strTmp;
}
CEdit&amp; Edit = GetEditCtrl();
Edit.SetWindowText(strMsg);
</code></pre>
<p class=bhw98>最后，最后干什么呢？编译，运行...... </p>
<h2 class=bhw98>[相关资源]</h2>
<li class=bhw98>本文Demo源码：<a href="http://www.csdn.net/develop/author/bhw98/DiskGeometry.zip" s_oidt="0" s_oid="http://www.csdn.net/develop/author/bhw98/DiskGeometry.zip"><font color=#000080>DiskGeometry.zip</font></a> (21KB) </li>
</div>
<img src ="http://www.cppblog.com/iniwf/aggbug/79822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-13 21:03 <a href="http://www.cppblog.com/iniwf/archive/2009/04/13/79822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战DeviceIoControl 之一：通过API访问设备驱动程序</title><link>http://www.cppblog.com/iniwf/archive/2009/04/13/79820.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 13 Apr 2009 13:01:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/13/79820.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79820.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/13/79820.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79820.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79820.html</trackback:ping><description><![CDATA[转自<a href="http://www.cnblogs.com/henryzc/archive/2005/11/08/271905.html">http://www.cnblogs.com/henryzc/archive/2005/11/08/271905.html</a><br><br>
<div class=postText>
<p class=bhw98><strong class=bhw98>Q</strong> 在NT/2000/XP中，我想用VC编写应用程序访问硬件设备，如获取磁盘参数、读写绝对扇区数据、测试光驱实际速度等，该从哪里入手呢？
<p class=bhw98><strong class=bhw98>A</strong> 在NT/2000/XP中，应用程序可以通过API函数DeviceIoControl来实现对设备的访问—获取信息，发送命令，交换数据等。利用该接口函数向指定的设备驱动发送正确的控制码及数据，然后分析它的响应，就可以达到我们的目的。
<p class=bhw98>DeviceIoControl的函数原型为
<pre class=bhw98><code class=bhw98>BOOL DeviceIoControl(
HANDLE hDevice,              <span class=rem>// 设备句柄</span>
DWORD dwIoControlCode,       <span class=rem>// 控制码</span>
LPVOID lpInBuffer,           <span class=rem>// 输入数据缓冲区指针</span>
DWORD nInBufferSize,         <span class=rem>// 输入数据缓冲区长度</span>
LPVOID lpOutBuffer,          <span class=rem>// 输出数据缓冲区指针</span>
DWORD nOutBufferSize,        <span class=rem>// 输出数据缓冲区长度</span>
LPDWORD lpBytesReturned,     <span class=rem>// 输出数据实际长度单元长度</span>
LPOVERLAPPED lpOverlapped    <span class=rem>// 重叠操作结构指针</span>
);
</code></pre>
<p class=bhw98>设备句柄用来标识你所访问的设备。
<p class=bhw98>发送不同的控制码，可以调用设备驱动程序的不同类型的功能。在头文件winioctl.h中，预定义的标准设备控制码，都以IOCTL或FSCTL开头。例如，IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数（介质类型、柱面数、每柱面磁道数、每磁道扇区数等）的控制码，FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。
<p class=bhw98>输入输出数据缓冲区是否需要，是何种结构，以及占多少字节空间，完全由不同设备的不同操作类型决定。在头文件winioctl.h中，已经为标准设备预定义了一些输入输出数据结构。重叠操作结构指针设置为NULL，DeviceIoControl将进行阻塞调用；否则，应在编程时按异步操作设计。
<p class=bhw98><strong class=bhw98>Q</strong> 设备句柄是从哪里获得的？
<p class=bhw98><strong class=bhw98>A</strong> 设备句柄可以用API函数CreateFile获得。它的原型为
<pre class=bhw98><code class=bhw98>HANDLE CreateFile(
LPCTSTR lpFileName,                         <span class=rem>// 文件名/设备路径</span>
DWORD dwDesiredAccess,                      <span class=rem>// 访问方式</span>
DWORD dwShareMode,                          <span class=rem>// 共享方式</span>
LPSECURITY_ATTRIBUTES lpSecurityAttributes, <span class=rem>// 安全描述符指针</span>
DWORD dwCreationDisposition,                <span class=rem>// 创建方式</span>
DWORD dwFlagsAndAttributes,                 <span class=rem>// 文件属性及标志</span>
HANDLE hTemplateFile                        <span class=rem>// 模板文件的句柄</span>
);
</code></pre>
<p class=bhw98>CreateFile这个函数用处很多，这里我们用它&#8220;打开&#8221;设备驱动程序，得到设备的句柄。操作完成后用CloseHandle关闭设备句柄。
<p class=bhw98>与普通文件名有所不同，设备驱动的&#8220;文件名&#8221;(常称为&#8220;设备路径&#8221;)形式固定为&#8220;\\.\DeviceName&#8221;(注意在C程序中该字符串写法为&#8220;\\\\.\\DeviceName&#8221;)，DeviceName必须与设备驱动程序内定义的设备名称一致。
<p class=bhw98>一般地，调用CreateFile获得设备句柄时，访问方式参数设置为0或GENERIC_READ|GENERIC_WRITE，共享方式参数设置为FILE_SHARE_READ|FILE_SHARE_WRITE，创建方式参数设置为OPEN_EXISTING，其它参数设置为0或NULL。
<p class=bhw98><strong class=bhw98>Q</strong> 可是，我怎么知道设备名称是什么呢？
<p class=bhw98><strong class=bhw98>A</strong> 一些存储设备的名称是微软定义好的，不可能有什么变化。大体列出如下
<table class=bhw98 align=center>
    <tbody>
        <tr>
            <td class=bhw98>软盘驱动器
            <td class=bhw98>A:, B:
            <tr>
                <td class=bhw98>硬盘逻辑分区
                <td class=bhw98>C:, D:, E:, ...
                <tr>
                    <td class=bhw98>物理驱动器
                    <td class=bhw98>PHYSICALDRIVEx
                    <tr>
                        <td class=bhw98>CD-ROM, DVD/ROM
                        <td class=bhw98>CDROMx
                        <tr>
                            <td class=bhw98>磁带机
                            <td class=bhw98>TAPEx </td>
                        </tr>
                    </tbody>
                </table>
                <p class=bhw98>其中，物理驱动器不包括软驱和光驱。逻辑驱动器可以是IDE/SCSI/PCMCIA/USB接口的硬盘分区（卷）、光驱、MO、CF卡等，甚至是虚拟盘。x=0，1，2 &#8230;&#8230;
                <p class=bhw98>其它的设备名称需通过驱动接口的GUID调用设备管理函数族取得，这里暂不讨论。
                <p class=bhw98><strong class=bhw98>Q</strong> 请举一个简单的例子说明如何通过DeviceIoControl访问设备驱动程序。
                <p class=bhw98><strong class=bhw98>A</strong> 这里有一个从MSDN上摘抄来的demo程序，演示在NT/2000/XP中如何通过DeviceIoControl获取硬盘的基本参数。
                <pre class=bhw98><code class=bhw98><span class=rem>/* The code of interest is in the subroutine GetDriveGeometry. The
                code in main shows how to interpret the results of the IOCTL call. */</span>
                <span class=key>#include</span> <span class=str>&lt;windows.h&gt;</span>
                <span class=key>#include</span> <span class=str>&lt;winioctl.h&gt;</span>
                BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)
                {
                HANDLE hDevice;               <span class=rem>// handle to the drive to be examined</span>
                BOOL bResult;                 <span class=rem>// results flag</span>
                DWORD junk;                   <span class=rem>// discard results</span>
                hDevice = CreateFile(<span class=str>"\\\\.\\PhysicalDrive0"</span>,  <span class=rem>// drive to open</span>
                <span class=num>0</span>,                <span class=rem>// no access to the drive</span>
                FILE_SHARE_READ | <span class=rem>// share mode</span>
                FILE_SHARE_WRITE,
                NULL,             <span class=rem>// default security attributes</span>
                OPEN_EXISTING,    <span class=rem>// disposition</span>
                <span class=num>0</span>,                <span class=rem>// file attributes</span>
                NULL);            <span class=rem>// do not copy file attributes</span>
                <span class=key>if</span> (hDevice == INVALID_HANDLE_VALUE) <span class=rem>// cannot open the drive</span>
                {
                <span class=key>return</span> (FALSE);
                }
                bResult = DeviceIoControl(hDevice,     <span class=rem>// device to be queried</span>
                IOCTL_DISK_GET_DRIVE_GEOMETRY,     <span class=rem>// operation to perform</span>
                NULL, <span class=num>0</span>,               <span class=rem>// no input buffer</span>
                pdg, <span class=key>sizeof</span>(*pdg),     <span class=rem>// output buffer</span>
                &amp;junk,                 <span class=rem>// # bytes returned</span>
                (LPOVERLAPPED) NULL);  <span class=rem>// synchronous I/O</span>
                CloseHandle(hDevice);
                <span class=key>return</span> (bResult);
                }
                <span class=key>int</span> main(<span class=key>int</span> argc, <span class=key>char</span> *argv[])
                {
                DISK_GEOMETRY pdg;            <span class=rem>// disk drive geometry structure</span>
                BOOL bResult;                 <span class=rem>// generic results flag</span>
                ULONGLONG DiskSize;           <span class=rem>// size of the drive, in bytes</span>
                bResult = GetDriveGeometry (&amp;pdg);
                <span class=key>if</span> (bResult)
                {
                printf(<span class=str>"Cylinders = %I64d\n"</span>, pdg.Cylinders);
                printf(<span class=str>"Tracks per cylinder = %ld\n"</span>, (ULONG) pdg.TracksPerCylinder);
                printf(<span class=str>"Sectors per track = %ld\n"</span>, (ULONG) pdg.SectorsPerTrack);
                printf(<span class=str>"Bytes per sector = %ld\n"</span>, (ULONG) pdg.BytesPerSector);
                DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder *
                (ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector;
                printf(<span class=str>"Disk size = %I64d (Bytes) = %I64d (Mb)\n"</span>, DiskSize,
                DiskSize / (<span class=num>1024</span> * <span class=num>1024</span>));
                }
                <span class=key>else</span>
                {
                printf(<span class=str>"GetDriveGeometry failed. Error %ld.\n"</span>, GetLastError());
                }
                <span class=key>return</span> ((<span class=key>int</span>)bResult);
                }
                </code></pre>
                <p class=bhw98><strong class=bhw98>Q</strong> 如果将设备名换成&#8220;A:&#8221;就可以取A盘参数，换成&#8220;CDROM0&#8221;就可以取CDROM参数，是这样吗？
                <p class=bhw98><strong class=bhw98>A</strong> 这个问题暂不做回答。请动手试一下。
                <p class=bhw98>现在我们总结一下通过DeviceIoControl访问设备驱动程序的&#8220;三步曲&#8221;：首先用CreateFile取得设备句柄，然后用DeviceIoControl与设备进行I/O，最后别忘记用CloseHandle关闭设备句柄。 </p>
                </div>
<img src ="http://www.cppblog.com/iniwf/aggbug/79820.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-13 21:01 <a href="http://www.cppblog.com/iniwf/archive/2009/04/13/79820.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows系统编程</title><link>http://www.cppblog.com/iniwf/archive/2009/04/06/79085.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 06 Apr 2009 04:57:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/06/79085.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79085.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/06/79085.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79085.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79085.html</trackback:ping><description><![CDATA[转自<a href="http://bbs.pediy.com/showthread.php?t=26692">http://bbs.pediy.com/showthread.php?t=26692</a><br><br>
<table class=tb cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td>
            <div class=bword id=post_message_186530 style="LINE-HEIGHT: 18px">【本版定位】<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;给程序设计爱好者提供一个广泛的交流平台，以求共同进步。<br><br>【目前状况】<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;经过一段时间的发展，大家对于程序设计的兴趣也日渐增长，更有许多新人涌现.从发贴情况来看，很大一部分是关于Windows系统程序设计这个领域，但从知识点来看，感觉有些散乱。大家往往过重于某种或者某些&#8216;新奇&#8217;和&#8216;深奥&#8217;的技术，而忽略了基础。给个最简单的例子，OpenProcess,&nbsp;一般在写程序的时候都用PROCESS_ALL_ACCESS权限，这是可以的，其实并不是只有给完全权限才能打开，从这里可以看出，对于进程的打开就显得有些粗糙了。但这种粗糙可能会给以后的学习、研究中带来不必要的麻烦。和其他任何种类的学习一样，Windows系统程序设计也是需要深厚的基础。从我们论坛的总体氛围来说，对于学习Windows系统程序设计是很有利的。逆向工程的深厚积淀，使得我们对于程序的运行原理的有了相对清晰的了解。<br><br>【最近计划】<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;经过与小虾的商榷，利用本版的人才资源，对Windows系统程序设计做一个总结。在内容分块上力求广度，在专向编写方面力求深度.具体，分为以下几个步骤：<br>&nbsp;&nbsp;（1）把这一领域的内容分块,&nbsp;对于这个计划的具体细节进行讨论。<br>&nbsp;&nbsp;（2）采用自愿认领的方式（VC/Delphi/汇编/VB等皆可）<br>&nbsp;&nbsp;（3）某块内容完成后，需公开审核，对于其中的不足、错误、遗漏之处给予意见。<br>&nbsp;&nbsp;（4）等全部完成后的打包成CHM<br><br>【注意事项】<br>&nbsp;&nbsp;在进行编写某项专题时，需要介绍清楚这个方面的原理，并结合实际应用能够原创性的开发几个小软件对该原理进行实践验证。文章的侧重点在于实践，尽量多阐述在相应知识方面的软件制作过程中所遇到的问题，比如说参数设置、权限设置，以及整个软件开发后的心得。当然，对于在于知识点的理论方面需要做简要的概括，要求能够体现这个知识点的重点即可。（例子应该是我们原创性的，而不是照搬别人的东西，重在编写软件过程的设计思路、存在问题、难点，以及在开发过程中所遇到的困难）<br><br>【内容分块】<br>&nbsp;&nbsp;按照内容来分（参考《Windows核心编程》jeffrey&nbsp;Richter），如下<br>&nbsp;&nbsp;&nbsp;（1）Windows系统程序设计之进程管理&nbsp;<br>&nbsp;&nbsp;&nbsp;Windows进程/线程浅谈&nbsp;(&nbsp;EDD&nbsp;完成&nbsp;)<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=28100" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=28100</font></a><br>&nbsp;&nbsp;&nbsp;[已更新！][原创/探讨]Windows&nbsp;核心编程研究系列之一(改变进程&nbsp;PTE)&nbsp;--&nbsp;hopy<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;postid=239570#post239570" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?...570#post239570</font></a><br><br>&nbsp;&nbsp;&nbsp;（2）Windows系统程序设计之线程管理(包括同步机制)<br><br>&nbsp;&nbsp;&nbsp;（3）Windows系统程序设计之进程间通信&nbsp;（北极星2003&nbsp;已完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=26252" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=26252</font></a>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;（4）Windows系统程序设计之内存管理<br>&nbsp;&nbsp;&nbsp;读取指定物理内存地址中的内容&nbsp;(hopy)<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=36645" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=36645</font></a><br><br>&nbsp;&nbsp;&nbsp;（5）Windows系统程序设计之内存映射（北极星2003&nbsp;已完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;postid=214578#post214578" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?...578#post214578</font></a><br><br>&nbsp;&nbsp;&nbsp;（6）Windows系统程序设计之系统服务（北极星2003&nbsp;已完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=29187" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=29187</font></a><br><br>&nbsp;&nbsp;&nbsp;（7）Windows系统程序设计之异步I/O和完成端口&nbsp;（北极星2003&nbsp;已完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=28342" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=28342</font></a><br><br>&nbsp;&nbsp;&nbsp;（8）Windows系统程序设计之结构化异常处理<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=32222" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=32222</font></a><br><br>&nbsp;&nbsp;&nbsp;（9）Windows系统程序设计之DLL基础（包括加载）（condor已完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;postid=199940#post199940" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?...940#post199940</font></a><br><br>&nbsp;&nbsp;&nbsp;（10）Windows系统程序设计之插入DLL和挂接API&nbsp;（北极星2003&nbsp;已完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=27235" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=27235</font></a><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;汇编ring3下实现HOOK&nbsp;API&nbsp;&nbsp;&nbsp;（非安全&nbsp;已完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=28895" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=28895</font></a><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用户层下拦截系统api的原理与实现&nbsp;&nbsp;（&nbsp;默数悲伤&nbsp;已完成&nbsp;）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;postid=227086#post227086" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?...086#post227086</font></a>&nbsp;&nbsp;（&nbsp;默数悲伤&nbsp;已完成&nbsp;）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;拦截网络数据的小玩意<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=32277" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=32277</font></a><br>&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;（11）对DllMain编程的几点说明：what&nbsp;you&nbsp;can&nbsp;do,&nbsp;and&nbsp;what&nbsp;you&nbsp;CANNOT&nbsp;do&nbsp;（bookworm&nbsp;完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=30058" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=30058</font></a><br><br>&nbsp;&nbsp;&nbsp;（12）&nbsp;接着讲和DLL与代码插入有关的几点看法&nbsp;（bookworm&nbsp;完成）<br>&nbsp;&nbsp;&nbsp;<a href="http://bbs.pediy.com/showthread.php?s=&amp;threadid=30123" target=_blank><font color=#000000>http://bbs.pediy.com/showthread.php?s=&amp;threadid=30123</font></a><br><br>【文章格式（示例）】<br>－－－－－－－－－－－－－－－－－－－－－－－－<br>(标题)Windows系统程序设计之&#8230;&#8230;<br><br>【作者】******<br>【来源】看雪技术论坛（bbs.pediy.com）&nbsp;<br>【时间】*年*月*日<br><br>（正文）<br>实例部分的格式如下:<br>1.设计目标<br>2.设计思路<br>3.难点<br>4.详细设计<br>5.遇到的困难<br>6.总结(心得)<br><br>【参考文献】<br>[1].&#8230;&#8230;<br>[2].&#8230;&#8230;<br>【版权声明】必须注明原创于看雪技术论坛(bbs.pediy.com)&nbsp;及作者，并保持文章的完整性。<br>－－－－－－－－－－－－－－－－－－－－－－－－<br><br>【讨论主题】<br>&nbsp;&nbsp;欢迎大家对于以上计划的各方面给予讨论。<br>&nbsp;&nbsp;尤其是对于内容分块方面，不知是否有所遗漏<br>&nbsp;&nbsp;对于一些初学的朋友，也可以尽量提出自己的需要。<br>&nbsp;&nbsp;<br>【如果有朋友需要认领其中的某篇内容，直接跟贴即可】<br>【跟贴的时候,最好说明自己的语言,如VC/Delphi/ASM/VB,这样不同语言的朋友可以认领同一篇】<br></div>
            </td>
        </tr>
    </tbody>
</table>
<!-- / message -->
<p>&#160;</p>
<!-- edit note -->
<div class=smallfont><br><em>此帖于 2006-12-21 <span class=time><font color=#800800>10:49</font></span> 被 北极星2003 最后编辑. </em></div>
<!-- / edit note -->
<img src ="http://www.cppblog.com/iniwf/aggbug/79085.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-06 12:57 <a href="http://www.cppblog.com/iniwf/archive/2009/04/06/79085.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>获取操作系统版本和系统位数</title><link>http://www.cppblog.com/iniwf/archive/2009/03/19/77108.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Thu, 19 Mar 2009 01:09:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/03/19/77108.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/77108.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/03/19/77108.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/77108.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/77108.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: #include&nbsp;&lt;windows.h&gt;#include&nbsp;&lt;tchar.h&gt;#include&nbsp;&lt;stdio.h&gt;#define&nbsp;BUFSIZE&nbsp;80typedef&nbsp;void&nbsp;(WINAPI&nbsp;*PGNSI)(LPSYSTEM_INFO);int&nbsp;__cdecl&nbsp;_t...&nbsp;&nbsp;<a href='http://www.cppblog.com/iniwf/archive/2009/03/19/77108.html'>阅读全文</a><img src ="http://www.cppblog.com/iniwf/aggbug/77108.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-03-19 09:09 <a href="http://www.cppblog.com/iniwf/archive/2009/03/19/77108.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>