﻿<?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</title><link>http://www.cppblog.com/iniwf/</link><description>风是温柔的，雨是伤心的，云是快乐的，月是多情的，爱是迷失的，恋是醉人的，情是难忘的，天是长久的，地是永恒的</description><language>zh-cn</language><lastBuildDate>Sun, 19 Apr 2026 11:01:13 GMT</lastBuildDate><pubDate>Sun, 19 Apr 2026 11:01:13 GMT</pubDate><ttl>60</ttl><item><title>新blog已转到百度hi</title><link>http://www.cppblog.com/iniwf/archive/2010/06/09/117471.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Wed, 09 Jun 2010 06:28:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/06/09/117471.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/117471.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/06/09/117471.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/117471.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/117471.html</trackback:ping><description><![CDATA[<a href="http://hi.baidu.com/iniwf/blog">http://hi.baidu.com/iniwf/blog</a> 
<img src ="http://www.cppblog.com/iniwf/aggbug/117471.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> 2010-06-09 14:28 <a href="http://www.cppblog.com/iniwf/archive/2010/06/09/117471.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内核反编译学习笔记6 passthru静态分析</title><link>http://www.cppblog.com/iniwf/archive/2010/04/18/112927.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 18 Apr 2010 11:26:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/18/112927.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112927.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/18/112927.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112927.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112927.html</trackback:ping><description><![CDATA[内核反编译学习笔记6
<p>passthru静态分析</p>
<p>来源：passthru.sys反汇编和源代码</p>
<p>一，导入的模块<br>二，模块要使用的函数<br>三，函数原型<br>四，文件中函数列表</p>
<p>有源代码，反汇编比源代码更简洁，特别是总揽方面，有优势。<br>有兴趣的话，可以把汇编和代码对应。我已经把函数内调用函数都罗列了。</p>
<p>////////////////////////////////////////////////</p>
<p>一，导入三个模块：<br>import Module:ntoskrnl.exe<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> HAL.dll<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NDIS.SYS</p>
<p>//////////////////////////////////////////////</p>
<p>二，每个模块导出函数：<br>我们有函数名，就可以bp 模块！函数&nbsp;<wbr> 下断了。<br>有的函数是被宏调用的，具体可以查看ndis.h中宏的定义。</p>
<p>ntoskrnl.exe:<br>KeBugCheckEx<br>KeTickCount<br>IoGetDeviceProperty<br>RtlCopyUnicodeString<br>RtlAppendUnicodeToString<wbr><br>IoCreateDevice<br>_vsnprint f<br>MmMapLockedPagesSpecifyC<wbr>ache<br>IoDeleteDevice<br>memcpy<br>IofCompleteRequest<br>memset<br>RtlInitUnicodeString<br>DbgPring<br>RtlAssert<br>RtlUnwind</p>
<p>HAL.dll:<br>KfReleaseSpinLock<br>KfAcquireSpinLock</p>
<p>接下来是重点了，ndis专用函数<br>NDIS.SYS:</p>
<p><br>NdisIMNotifyPnPEvent<br>NdisGetReceivedPacket<br>NdisDprAllocatePacket<br>NdisDprFreePacket<br>NdisDeregisterProtocol<br>NdisIMCancelInitializeDe<wbr>viceInstance<br>NdisReEnumerateProtocolB<wbr>indings<br>NdisFreeMemory<br>NdisOpenProtocolConfigur<wbr>ation<br>NdisReadConfiguration<br>NdisAllocateMemoryWithTa<wbr>g<br>NdisInitializeEvent<br>NdisAllocatePacketPoolEx<wbr><br>NdisPacketPoolUsage<br>NdisIMDeInitializeDevice<wbr>Instance<br>NdisCloseAdapter<br>NdisSetEvent<br>NdisMSetAttributesEx<br>NdisIMGetDeviceContext<br>NdisFreePacket<br>NdisIMCopySendCompletePe<wbr>rPacketInfo<br>NdisIMCopySendPerPacketI<wbr>nfo<br>NdisAllocatePacket<br>NdisIMGetCurrentPacketSt<wbr>ack<br>NdisRequest<br>NdisMIndicateStatusCompl<wbr>ete<br>NdisMIndicateStatus<br>NdisReturnPackets<br>NdisGetPoolFromPacket<br>NdisWaitEvent<br>NdisResetEvent<br>NdisCancelSendPackets<br>NdisFreePacketPool<br>NdisTerminateWrapper<br>NdisIMAssociateMiniport<br>NdisIMDeregisterLayeredM<wbr>iniport<br>NdisRegisterProtocol<br>NdisMRegisterUnloadHandl<wbr>er<br>NdisIMRegisterLayeredMin<wbr>iport<br>NdisInitializeWrapper<br>NdisMRegisterDevice<br>NdisMSleep<br>NdisMDeregisterDevice<br>NdisCloseConfiguration<br>NdisIMInitializeDeviceIn<wbr>stanceEx<br>NdisOpenAdapter</p>
<p>/////////////////////////////////////<br>三，函数原型：呵呵</p>
<p>NDIS_STATUS&nbsp;<wbr> NdisIMNotifyPnPEvent(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN&nbsp;<wbr> NDIS_HANDLE&nbsp;<wbr> MiniportHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN&nbsp;<wbr> PNET_PNP_EVENT&nbsp;<wbr> NetPnPEvent&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>PNDIS_PACKET&nbsp;<wbr>&nbsp;<wbr> NdisGetReceivedPacket(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_HANDLE&nbsp;<wbr> NdisBindingHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_HANDLE&nbsp;<wbr> MacContext&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisDprAllocatePacket(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_PACKET&nbsp;<wbr> *Packet,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> PoolHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisDprFreePacket(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> Packet&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_STATUS&nbsp;<wbr>&nbsp;<wbr> NdisIMCancelInitializeDe<wbr>viceInstance(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> DriverHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_STRING&nbsp;<wbr> DeviceInstance&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisReEnumerateProtocolB<wbr>indings(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisProtocolHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisFreeMemory(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PVOID&nbsp;<wbr> VirtualAddress,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> Length,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> MemoryFlags&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisOpenProtocolConfigur<wbr>ation(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_HANDLE&nbsp;<wbr> ConfigurationHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_STRING&nbsp;<wbr> ProtocolSection&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisReadConfiguration(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_CONFIGURATION_PARAMETER&nbsp;<wbr> *ParameterValue,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> ConfigurationHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_STRING&nbsp;<wbr> Keyword,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_PARAMETER_TYPE&nbsp;<wbr> ParameterType&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_STATUS&nbsp;<wbr> NdisAllocateMemoryWithTa<wbr>g(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PVOID&nbsp;<wbr> *VirtualAddress,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> Length,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN ULONG&nbsp;<wbr> Tag&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisInitializeEvent(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_EVENT&nbsp;<wbr> Event&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisAllocatePacketPoolEx<wbr>(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_HANDLE&nbsp;<wbr> PoolHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> NumberOfDescriptors,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> NumberOfOverflowDescript<wbr>ors,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> ProtocolReservedLength&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>UINT&nbsp;<wbr> NdisPacketPoolUsage(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> PoolHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_STATUS&nbsp;<wbr> NdisIMDeInitializeDevice<wbr>Instance(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisMiniportHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisCloseAdapter(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisBindingHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisSetEvent(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_EVENT&nbsp;<wbr> Event&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr>&nbsp;<wbr> NdisMSetAttributesEx(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE MiniportAdapterHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE MiniportAdapterContext,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> CheckForHangTimeInSecond<wbr>s&nbsp;<wbr> OPTIONAL,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN ULONG&nbsp;<wbr> AttributeFlags,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_INTERFACE_TYPE AdapterType&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_HANDLE&nbsp;<wbr> NdisIMGetDeviceContext(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> MiniportAdapterHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisFreePacket(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> Packet&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisIMCopySendCompletePe<wbr>rPacketInfo(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> DstPacket,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> SrcPacket&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisIMCopySendPerPacketI<wbr>nfo(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> DstPacket,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> SrcPacket&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisAllocatePacket(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_PACKET&nbsp;<wbr> *Packet,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> PoolHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>PNDIS_PACKET_STACK&nbsp;<wbr> NdisIMGetCurrentPacketSt<wbr>ack(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> Packet&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT BOOLEAN&nbsp;<wbr> *StacksRemaining&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisRequest(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisBindingHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_REQUEST&nbsp;<wbr> NdisRequest&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateStatusCompl<wbr>ete(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> MiniportAdapterHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateStatus(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> MiniportAdapterHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_STATUS&nbsp;<wbr> GeneralStatus,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PVOID&nbsp;<wbr> StatusBuffer,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> StatusBufferSize&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisReturnPackets(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> *PacketsToReturn,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> NumberOfPackets&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_Handle&nbsp;<wbr> NdisGetPoolFromPacket(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PACKET&nbsp;<wbr> Packet&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>BOOLEAN&nbsp;<wbr> NdisWaitEvent(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_EVENT&nbsp;<wbr> Event,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> MsToWait&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisResetEvent(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_EVENT&nbsp;<wbr> Event&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisCancelSendPackets(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisBindingHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PVOID&nbsp;<wbr> CancelId&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisFreePacketPool(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> PoolHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisTerminateWrapper(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisWrapperHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PVOID&nbsp;<wbr> SystemSpecific&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisIMAssociateMiniport(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> DriverHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> ProtocolHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr>&nbsp;<wbr> NdisIMDeregisterLayeredM<wbr>iniport(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> DriverHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisRegisterProtocol(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_HANDLE&nbsp;<wbr> NdisProtocolHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_PROTOCOL_CHARACTERISTICS&nbsp;<wbr> ProtocolCharacteristics,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> CharacteristicsLength&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisMRegisterUnloadHandl<wbr>er(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisWrapperHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PDRIVER_UNLOAD&nbsp;<wbr> UnloadHandler&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_STATUS&nbsp;<wbr> NdisIMRegisterLayeredMin<wbr>iport(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisWrapperHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_MINIPORT_CHARACTERISTICS&nbsp;<wbr> MiniportCharacteristics,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> CharacteristicsLength,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_HANDLE&nbsp;<wbr> DriverHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>&nbsp;<wbr></p>
<p>NDIS_STATUS&nbsp;<wbr> NdisMRegisterDevice(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisWrapperHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_STRING&nbsp;<wbr> DeviceName,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_STRING&nbsp;<wbr> SymbolicName,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PDRIVER_DISPATCH&nbsp;<wbr> MajorFunctions[],&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PDEVICE_OBJECT&nbsp;<wbr> *pDeviceObject,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT NDIS_HANDLE&nbsp;<wbr> *NdisDeviceHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisMSleep(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN ULONG&nbsp;<wbr> MicrosecondsToSleep&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_STATUS&nbsp;<wbr> NdisMDeregisterDevice(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisDeviceHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisCloseConfiguration(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> ConfigurationHandle&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>NDIS_STATUS&nbsp;<wbr> NdisIMInitializeDeviceIn<wbr>stanceEx(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> DriverHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_STRING&nbsp;<wbr> DriverInstance,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> DeviceContext&nbsp;<wbr> OPTIONAL&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>VOID&nbsp;<wbr> NdisOpenAdapter(&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> Status,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_STATUS&nbsp;<wbr> OpenErrorStatus,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PNDIS_HANDLE&nbsp;<wbr> NdisBindingHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> OUT PUINT&nbsp;<wbr> SelectedMediumIndex,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_MEDIUM&nbsp;<wbr> MediumArray,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> MediumArraySize,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> NdisProtocolHandle,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN NDIS_HANDLE&nbsp;<wbr> ProtocolBindingContext,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PNDIS_STRING&nbsp;<wbr> AdapterName,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN UINT&nbsp;<wbr> OpenOptions,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IN PSTRING&nbsp;<wbr> AddressingInformation&nbsp;<wbr> OPTIONAL,&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> );</p>
<p>&nbsp;<wbr></p>
<p>///////////////////////////////////////</p>
<p>四，文件中函数列表<br>常用的就不在函数内罗列了<br>NdisZeroMemory<br>NdisMoveMemory<br>NdisFreeMemory<br>NdisMSleep<br>NdisInitUnicodeString<br>NdisAcquireSpinLock<br>NdisReleaseSpinLock<br>NdisFreeSpinLock<br>&nbsp;<wbr></p>
<p><br>1，passthru.c:<br>&nbsp;<wbr>&nbsp;<wbr> DriverEntry<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 其中大概用了下面这些：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisAllocateSpinLock<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMInitializeWrapper<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMRegisterLayeredMin<wbr>iport<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisRegisterProtocol<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMAssociateMiniport<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></p>
<p>&nbsp;<wbr>&nbsp;<wbr> PtRegisterDevice</p>
<p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMRegisterDevice<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtDispatch<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IoGetCurrentIrpStackLoca<wbr>tion<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IoCompleteRequest<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtDeregisterDevice</p>
<p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtUnload<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtUnloadProtocol<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMDeregisterLayeredM<wbr>iniport</p>
<p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>2，miniport.c<br>&nbsp;<wbr>&nbsp;<wbr> MPInitialize<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMSetAttributesEx<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtRegisterDevice<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisSetEvent</p>
<p>&nbsp;<wbr>&nbsp;<wbr> MPSend<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMGetCurrentPacketSt<wbr>ack<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisSend<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisAllocatePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisFreePacket<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPSendPackets<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMSendComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMGetCurrentPacketSt<wbr>ack<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisSend<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisAllocatePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisGetPacketFlags<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMCopySendPerPacketI<wbr>nfo<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPQueryInformation<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisRequest<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtRequestComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPQueryPNPCapabilities<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPSetInformation<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> MPProcessSetPowerOid<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPProcessSetPowerOid<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateStatus<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateStatusCompl<wbr>ete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPReturnPacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisGetPoolFromPacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisReturnPackets<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisFreePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPTransferData<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IsIMDeviceStateOn<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisTransferData<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtDeregisterDevice<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisResetEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtDereferenceAdapt<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPCancelSendPackets<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisCancelSendPackets<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPDevicePnPEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPAdapterShutdown<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> MPFreeAllPacketPools<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisFreePacketPool<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>3，protocol.c<br>&nbsp;<wbr>&nbsp;<wbr> PtBindAdapter<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisOpenProtocolConfigur<wbr>ation<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisReadConfiguration<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisAllocateMemoryWithTa<wbr>g<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisInitializeEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisAllocatePacketPoolEx<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisOpenAdapter<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisWaitEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtReferenceAdapt<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisInitializeEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMInitializeDeviceIn<wbr>stanceEx<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtDereferenceAdapt<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisCloseConfiguration<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisCloseAdapter<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtOpenAdapterComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisSetEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtUnbindAdapter<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtRequestComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMCancelInitializeDe<wbr>viceInstance<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisWaitEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMDeInitializeDevice<wbr>Instance<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisResetEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisCloseAdapter<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisWaitEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> MPFreeAllPacketPools&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtUnloadProtocol<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisDeregisterProtocol<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> IoDeleteDevice<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtCloseAdapterComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisSetEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtResetComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtRequestComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMQueryInformationCom<wbr>plete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMSetInformationCompl<wbr>ete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtStatus<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateStatus<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtStatusComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateStatusCompl<wbr>ete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtSendComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisGetPoolFromPacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMSendComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisDprFreePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtTransferDataComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMTransferDataComplet<wbr>e<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtReceive<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisGetReceivedPacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisDprAllocatePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateReceivePack<wbr>et<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisDprFreePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMEthIndicateReceive<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMTrIndicateReceive<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMFddiIndicateReceive<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtReceiveComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> KeGetCurrentProcessorNum<wbr>ber<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMTrIndicateReceiveCo<wbr>mplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMFddiIndicateReceive<wbr>Complete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtReceivePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMGetCurrentPacketSt<wbr>ack<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisMIndicateReceivePack<wbr>et<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisDprFreePacket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtPNPHandler<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtPnPNetEventSetPower<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtPnPNetEventReconfigure<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMNotifyPnPEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtPnPNetEventReconfigure<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisReEnumerateProtocolB<wbr>indings<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMNotifyPnPEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtPnPNetEventSetPower<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisIMNotifyPnPEvent<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtRequestComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisPacketPoolUsage<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> NdisRequest<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> PtRequestComplete<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> PtReferenceAdapt<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> MPFreeAllPacketPools<br><wbr><wbr><wbr><wbr><br><wbr><wbr><wbr><wbr><br><wbr><br></p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112927.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> 2010-04-18 19:26 <a href="http://www.cppblog.com/iniwf/archive/2010/04/18/112927.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内核反编译学习笔记5</title><link>http://www.cppblog.com/iniwf/archive/2010/04/18/112926.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 18 Apr 2010 11:25:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/18/112926.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112926.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/18/112926.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112926.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112926.html</trackback:ping><description><![CDATA[<p>其实这是以前内容的复习，另外再通过dt 数据来获取结构和偏移量，手工在windbg中查看信息</p>
<p>程序：bz6</p>
<p>以前的驱动程序简直毫无驱动的样子，现在用个稍微健全的例子：</p>
<p>NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)<br>{<br>&nbsp;<wbr>NTSTATUS status;<br>&nbsp;<wbr><br>#if DBG<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> _asm int 3<br>#endif</p>
<p>&nbsp;<wbr>driver-&gt;DriverUnload = DriverUnload;<br>&nbsp;<wbr><br>&nbsp;<wbr>status = CreateDevice(driver);<br>&nbsp;<wbr><br>&nbsp;<wbr>Dump(driver);<br>&nbsp;<wbr><br>&nbsp;<wbr>return status;<br>}</p>
<p>CreateDevice(driver)是自定义函数，用来真正建立一个驱动设备。返回一个status，猜猜看，我们能不能在eax中读取返回值。</p>
<p>反汇编先：<br>kd&gt; uf bz6!driverentry<br>bz6!DriverEntry [d:\mydriver\bz6\bz6.c @ 110]:<br>&nbsp;<wbr> 110 f8428680 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr> 110 f8428682 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr> 110 f8428683 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr> 110 f8428685 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr> 114 f8428686 cc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> int&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 3<br>&nbsp;<wbr> 121 f8428687 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr> 121 f842868a c74034d08442f8&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [eax+34h],offset bz6!DriverUnload (f84284d0)<br>&nbsp;<wbr> 123 f8428691 8b4d08&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp+8]<br>&nbsp;<wbr> 123 f8428694 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr> 123 f8428695 e876fdffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!CreateDevice (f8428410)<br>&nbsp;<wbr> 123 f842869a 8945fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],eax<br>&nbsp;<wbr> 125 f842869d 8b5508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp+8]<br>&nbsp;<wbr> 125 f84286a0 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr> 125 f84286a1 e8aafeffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!Dump (f8428550)<br>&nbsp;<wbr> 127 f84286a6 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr> 128 f84286a9 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr> 128 f84286ab 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr> 128 f84286ac c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8</p>
<p>我们在源程序或Disassembly窗口在调用处F9下断，按g运行：</p>
<p>kd&gt; g<br>Breakpoint 0 hit<br>bz6!DriverEntry+0x15:<br>f8428695 e876fdffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!CreateDevice (f8428410)<br>kd&gt; d eax<br>81e64f38&nbsp;<wbr> 04 00 a8 00 00 00 00 00-02 00 00 00 00 80 42 f8&nbsp;<wbr> ..............B.<br>81e64f48&nbsp;<wbr> 00 0c 00 00 b8 f3 d5 81-e0 4f e6 81 16 00 16 00</p>
<p>没错，返回值是04。</p>
<p>Dump(driver)是显示driver和device的一个函数，里面有个循环。<br>我们看看非玩具状态（呵呵，以前的函数毫无实用价值）下反汇编以后是什么样子。</p>
<p>void Dump(IN PDRIVER_OBJECT&nbsp;<wbr>pDriverObject)<br>{<br>&nbsp;<wbr>ULONG i=1;<br>&nbsp;<wbr>PDEVICE_OBJECT pDevice = pDriverObject-&gt;DeviceObject;<br>&nbsp;<wbr>KdPrint(("----------------------------------------------\n"));<br>&nbsp;<wbr>KdPrint(("Begin Dump...\n"));<br>&nbsp;<wbr>KdPrint(("Driver Address:0X%08X\n",pDriverObject));<br>&nbsp;<wbr>KdPrint(("Driver name:%wZ\n",&amp;pDriverObject-&gt;DriverName));<br>&nbsp;<wbr>KdPrint(("Driver HardwareDatabase:%wZ\n",pDriverObject-&gt;HardwareDatabase));<br>&nbsp;<wbr>KdPrint(("Driver first device:0X%08X\n",pDriverObject-&gt;DeviceObject));<br>&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr>for (;pDevice!=NULL;pDevice = pDevice-&gt;NextDevice)<br>&nbsp;<wbr>{<br>&nbsp;<wbr>&nbsp;<wbr>KdPrint(("The %d device\n",i++));<br>&nbsp;<wbr>&nbsp;<wbr>KdPrint(("Device AttachedDevice:0X%08X\n",pDevice-&gt;AttachedDevice));<br>&nbsp;<wbr>&nbsp;<wbr>KdPrint(("Device NextDevice:0X%08X\n",pDevice-&gt;NextDevice));<br>&nbsp;<wbr>&nbsp;<wbr>KdPrint(("Device StackSize:%d\n",pDevice-&gt;StackSize));<br>&nbsp;<wbr>&nbsp;<wbr>KdPrint(("Device's DriverObject:0X%08X\n",pDevice-&gt;DriverObject));<br>&nbsp;<wbr>}<br>&nbsp;<wbr><br>&nbsp;<wbr>KdPrint(("Dump over!\n"));<br>&nbsp;<wbr>KdPrint(("----------------------------------------------\n"));<br>}</p>
<p>////////////////////////////////////<br>反汇编：<br>kd&gt; uf bz6!dump<br>bz6!Dump [d:\mydriver\bz6\bz6.c @ 82]:<br>&nbsp;<wbr>&nbsp;<wbr> 82 f8428550 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 82 f8428552 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 82 f8428553 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 82 f8428555 83ec0c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,0Ch<br>&nbsp;<wbr>&nbsp;<wbr> 83 f8428558 c745f801000000&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-8],1<br>&nbsp;<wbr>&nbsp;<wbr> 84 f842855f 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 84 f8428562 8b4804&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [eax+4]<br>&nbsp;<wbr>&nbsp;<wbr> 84 f8428565 894dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],ecx<br>&nbsp;<wbr>&nbsp;<wbr> 85 f8428568 68608842f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428860)<br>&nbsp;<wbr>&nbsp;<wbr> 85 f842856d e842010000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 85 f8428572 83c404&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,4<br>&nbsp;<wbr>&nbsp;<wbr> 86 f8428575 68508842f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428850)<br>&nbsp;<wbr>&nbsp;<wbr> 86 f842857a e835010000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 86 f842857f 83c404&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,4<br>&nbsp;<wbr>&nbsp;<wbr> 87 f8428582 8b5508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 87 f8428585 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 87 f8428586 68308842f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428830)<br>&nbsp;<wbr>&nbsp;<wbr> 87 f842858b e824010000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 87 f8428590 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 88 f8428593 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 88 f8428596 83c01c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,1Ch<br>&nbsp;<wbr>&nbsp;<wbr> 88 f8428599 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 88 f842859a 68108842f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428810)<br>&nbsp;<wbr>&nbsp;<wbr> 88 f842859f e810010000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 88 f84285a4 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 89 f84285a7 8b4d08&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 89 f84285aa 8b5124&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ecx+24h]<br>&nbsp;<wbr>&nbsp;<wbr> 89 f84285ad 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 89 f84285ae 68f08742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f84287f0)<br>&nbsp;<wbr>&nbsp;<wbr> 89 f84285b3 e8fc000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 89 f84285b8 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 90 f84285bb 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 90 f84285be 8b4804&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [eax+4]<br>&nbsp;<wbr>&nbsp;<wbr> 90 f84285c1 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 90 f84285c2 68d08742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f84287d0)<br>&nbsp;<wbr>&nbsp;<wbr> 90 f84285c7 e8e8000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 90 f84285cc 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 90 f84285cf eb09&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> jmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!Dump+0x8a (f84285da)</p>
<p>bz6!Dump+0x81 [d:\mydriver\bz6\bz6.c @ 94]:<br>&nbsp;<wbr>&nbsp;<wbr> 94 f84285d1 8b55fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 94 f84285d4 8b420c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [edx+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 94 f84285d7 8945fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],eax</p>
<p>bz6!Dump+0x8a [d:\mydriver\bz6\bz6.c @ 94]:<br>&nbsp;<wbr>&nbsp;<wbr> 94 f84285da 837dfc00&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],0<br>&nbsp;<wbr>&nbsp;<wbr> 94 f84285de 7476&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> je&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!Dump+0x106 (f8428656)</p>
<p>bz6!Dump+0x90 [d:\mydriver\bz6\bz6.c @ 96]:<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285e0 8b4df8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285e3 894df4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-0Ch],ecx<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285e6 8b55f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp-0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285e9 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285ea 68c08742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f84287c0)<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285ef e8c0000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285f4 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285f7 8b45f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285fa 83c001&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,1<br>&nbsp;<wbr>&nbsp;<wbr> 96 f84285fd 8945f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-8],eax<br>&nbsp;<wbr>&nbsp;<wbr> 97 f8428600 8b4dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 97 f8428603 8b5110&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ecx+10h]<br>&nbsp;<wbr>&nbsp;<wbr> 97 f8428606 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 97 f8428607 68a08742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f84287a0)<br>&nbsp;<wbr>&nbsp;<wbr> 97 f842860c e8a3000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 97 f8428611 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 98 f8428614 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 98 f8428617 8b480c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [eax+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 98 f842861a 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 98 f842861b 68808742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428780)<br>&nbsp;<wbr>&nbsp;<wbr> 98 f8428620 e88f000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 98 f8428625 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 99 f8428628 8b55fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 99 f842862b 0fbe4230&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> movsx&nbsp;<wbr>&nbsp;<wbr> eax,byte ptr [edx+30h]<br>&nbsp;<wbr>&nbsp;<wbr> 99 f842862f 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 99 f8428630 68608742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428760)<br>&nbsp;<wbr>&nbsp;<wbr> 99 f8428635 e87a000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr>&nbsp;<wbr> 99 f842863a 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr> 100 f842863d 8b4dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-4]<br>&nbsp;<wbr> 100 f8428640 8b5108&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ecx+8]<br>&nbsp;<wbr> 100 f8428643 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr> 100 f8428644 68408742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428740)<br>&nbsp;<wbr> 100 f8428649 e866000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr> 100 f842864e 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr> 101 f8428651 e97bffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> jmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!Dump+0x81 (f84285d1)</p>
<p>bz6!Dump+0x106 [d:\mydriver\bz6\bz6.c @ 103]:<br>&nbsp;<wbr> 103 f8428656 68308742f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428730)<br>&nbsp;<wbr> 103 f842865b e854000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr> 103 f8428660 83c404&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,4<br>&nbsp;<wbr> 104 f8428663 68608842f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz6! ?? ::FNODOBFM::`string' (f8428860)<br>&nbsp;<wbr> 104 f8428668 e847000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!DbgPrint (f84286b4)<br>&nbsp;<wbr> 104 f842866d 83c404&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,4<br>&nbsp;<wbr> 105 f8428670 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr> 105 f8428672 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr> 105 f8428673 c20400&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 4</p>
<p><br>/////////////<br>我们简单数下源程序中的KdPrint，再比较call，6个call以后开始循环：<br>&nbsp;<wbr>&nbsp;<wbr> 94 f84285da 837dfc00&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],0<br>&nbsp;<wbr>&nbsp;<wbr> 94 f84285de 7476&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> je&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz6!Dump+0x106 (f8428656)<br>&nbsp;<wbr>&nbsp;<wbr> ........<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>//////<br>&nbsp;<wbr>开始手工看数据啦~~~<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> 我们可以dt _driver_object来看数据结构<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> kd&gt; dt _driver_object<br>ntdll!_DRIVER_OBJECT<br>&nbsp;<wbr>&nbsp;<wbr> +0x000 Type&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Int2B<br>&nbsp;<wbr>&nbsp;<wbr> +0x002 Size&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Int2B<br>&nbsp;<wbr>&nbsp;<wbr> +0x004 DeviceObject&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _DEVICE_OBJECT<br>&nbsp;<wbr>&nbsp;<wbr> +0x008 Flags&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x00c DriverStart&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 Void<br>&nbsp;<wbr>&nbsp;<wbr> +0x010 DriverSize&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x014 DriverSection&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 Void<br>&nbsp;<wbr>&nbsp;<wbr> +0x018 DriverExtension&nbsp;<wbr> : Ptr32 _DRIVER_EXTENSION<br>&nbsp;<wbr>&nbsp;<wbr> +0x01c DriverName&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : _UNICODE_STRING<br>&nbsp;<wbr>&nbsp;<wbr> +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING<br>&nbsp;<wbr>&nbsp;<wbr> +0x028 FastIoDispatch&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _FAST_IO_DISPATCH<br>&nbsp;<wbr>&nbsp;<wbr> +0x02c DriverInit&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> long<br>&nbsp;<wbr>&nbsp;<wbr> +0x030 DriverStartIo&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> void<br>&nbsp;<wbr>&nbsp;<wbr> +0x034 DriverUnload&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> void<br>&nbsp;<wbr>&nbsp;<wbr> +0x038 MajorFunction&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : [28] Ptr32&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> long</p>
<p>显然是driver_object+10会得到DriverSize，那我们手工查看一下。</p>
<p>从反汇编窗口其实已经在代码后有提示了。<br>我们dd ebp+8可以获得driver_object的地址，在这个地址上+10（dd driver_object地址+10就可以了，当然也可以直接dd 算好的地址）：<br>kd&gt; dd 81e64f38+10<br>81e64f48&nbsp;<wbr> 00000c00 81d5f3b8 81e64fe0 00160016</p>
<p>我们还可以进循环，看device_object的具体情况，先看下结构：</p>
<p>kd&gt; dt _device_object<br>ntdll!_DEVICE_OBJECT<br>&nbsp;<wbr>&nbsp;<wbr> +0x000 Type&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Int2B<br>&nbsp;<wbr>&nbsp;<wbr> +0x002 Size&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint2B<br>&nbsp;<wbr>&nbsp;<wbr> +0x004 ReferenceCount&nbsp;<wbr>&nbsp;<wbr> : Int4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x008 DriverObject&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _DRIVER_OBJECT<br>&nbsp;<wbr>&nbsp;<wbr> +0x00c NextDevice&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _DEVICE_OBJECT<br>&nbsp;<wbr>&nbsp;<wbr> +0x010 AttachedDevice&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _DEVICE_OBJECT<br>&nbsp;<wbr>&nbsp;<wbr> +0x014 CurrentIrp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _IRP<br>&nbsp;<wbr>&nbsp;<wbr> +0x018 Timer&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _IO_TIMER<br>&nbsp;<wbr>&nbsp;<wbr> +0x01c Flags&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x020 Characteristics&nbsp;<wbr> : Uint4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x024 Vpb&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 _VPB<br>&nbsp;<wbr>&nbsp;<wbr> +0x028 DeviceExtension&nbsp;<wbr> : Ptr32 Void<br>&nbsp;<wbr>&nbsp;<wbr> +0x02c DeviceType&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x030 StackSize&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Char<br>&nbsp;<wbr>&nbsp;<wbr> +0x034 Queue&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : __unnamed<br>&nbsp;<wbr>&nbsp;<wbr> +0x05c AlignmentRequirement : Uint4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x060 DeviceQueue&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : _KDEVICE_QUEUE<br>&nbsp;<wbr>&nbsp;<wbr> +0x074 Dpc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : _KDPC<br>&nbsp;<wbr>&nbsp;<wbr> +0x094 ActiveThreadCount : Uint4B<br>&nbsp;<wbr>&nbsp;<wbr> +0x098 SecurityDescriptor : Ptr32 Void<br>&nbsp;<wbr>&nbsp;<wbr> +0x09c DeviceLock&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : _KEVENT<br>&nbsp;<wbr>&nbsp;<wbr> +0x0ac SectorSize&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint2B<br>&nbsp;<wbr>&nbsp;<wbr> +0x0ae Spare1&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint2B<br>&nbsp;<wbr>&nbsp;<wbr> +0x0b0 DeviceObjectExtension : Ptr32 _DEVOBJ_EXTENSION<br>&nbsp;<wbr>&nbsp;<wbr> +0x0b4 Reserved&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Ptr32 Void</p>
<p>在循环中下断，进去看数据：<br>kd&gt; dd 81d23b28<br>81d23b28&nbsp;<wbr> 00cc0003 00000000 81e64f38 00000000</p>
<p>对照看看：<br>+0x000 Type&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Int2B&nbsp;<wbr>&nbsp;<wbr> 00cc0003中的03<br>+0x002 Size&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> : Uint2B&nbsp;<wbr> 00cc0003中的cc</p>
<p>一般来说，如果内存中涉及堆栈，那么堆中放数据，栈中放数据指针......反正就这么慢慢看吧。</p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112926.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> 2010-04-18 19:25 <a href="http://www.cppblog.com/iniwf/archive/2010/04/18/112926.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内核反编译学习笔记4</title><link>http://www.cppblog.com/iniwf/archive/2010/04/18/112925.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 18 Apr 2010 11:24:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/18/112925.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112925.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/18/112925.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112925.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112925.html</trackback:ping><description><![CDATA[<p>内核驱动反编译笔记4<br>if else啦</p>
<p>需要掌握：<br>if else在程序反汇编中的状态，用16进制编辑器修改程序</p>
<p>cmp&nbsp;<wbr> 比较<br>jbe&nbsp;<wbr> 小于等于<br>jmp&nbsp;<wbr> 无条件转移<br>shl&nbsp;<wbr> 数值左移：左移一位（shl&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,1）等于乘2，左移2位等于乘4<br>add&nbsp;<wbr> 加</p>
<p><br>需要了解：<br>jg,jl,jgl 大于，小于，大于等于<br>PUSH 入栈同时esp-4<br>POP&nbsp;<wbr> 出栈同时esp+4<br>用16进制编辑器修改程序</p>
<p>&nbsp;<wbr></p>
<p>命令(16进制代码)<br>JA/JNBE (77)　<br>JAE/JNB (73)&nbsp;<wbr><br>JB/JNAE (72)&nbsp;<wbr><br>JBE/JNA (76)&nbsp;<wbr><br>JG/JNLE (7F)&nbsp;<wbr><br>JGE/JNL (7D)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>JL/JNGE (7C)&nbsp;<wbr><br>JLE/JNG (7E)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></p>
<p>&nbsp;<wbr></p>
<p>无需了解<br>call与ret对esp的影响<br>右移shr</p>
<p>正确的来说左移和右移操作是操作数乘于（或除于）2的平方（（SHL）n * 2 ^ 2、（SHR）n / 2 ^ 2)。<br>即操作数每向左或右移动一次都乘于或除于2一次。</p>
<p>文件校验和</p>
<p>/////////////</p>
<p><br>ULONG MyAdd1(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>ULONG u3;<br>&nbsp;<wbr>if (u1&gt;u2)<br>&nbsp;<wbr>&nbsp;<wbr>u3=u1*2;<br>&nbsp;<wbr>else<br>&nbsp;<wbr>&nbsp;<wbr>u3=u2*4;<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>return u3+100;&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr>}<br>&nbsp;<wbr><br>&nbsp;<wbr>使用：<br>MyAdd1(5,8)<br>////////////</p>
<p>&nbsp;<wbr></p>
<p><br>kd&gt; uf bz5!myadd1<br>bz5!MyAdd1 [d:\mydriver\bz5\bz5.c @ 7]:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 7 f8513490 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 7 f8513492 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 7 f8513493 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 7 f8513495 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> //这里开始<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f8513496 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;5<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f8513499 3b450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;将5与8比较<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f851349c 760a&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> jbe&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz5!MyAdd1+0x18 (f85134a8)&nbsp;<wbr> ;小于等于就跳转到f85134a8运行</p>
<p>bz5!MyAdd1+0xe [d:\mydriver\bz5\bz5.c @ 10]:<br>&nbsp;<wbr>&nbsp;<wbr> 10 f851349e 8b4d08&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 10 f85134a1 d1e1&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> shl&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,1<br>&nbsp;<wbr>&nbsp;<wbr> 10 f85134a3 894dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],ecx<br>&nbsp;<wbr>&nbsp;<wbr> 11 f85134a6 eb09&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> jmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz5!MyAdd1+0x21 (f85134b1)</p>
<p>bz5!MyAdd1+0x18 [d:\mydriver\bz5\bz5.c @ 12]:<br>&nbsp;<wbr>&nbsp;<wbr> 12 f85134a8 8b550c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp+0Ch]&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;8<br>&nbsp;<wbr>&nbsp;<wbr> 12 f85134ab c1e202&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> shl&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,2&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;8*4<br>&nbsp;<wbr>&nbsp;<wbr> 12 f85134ae 8955fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],edx&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;32</p>
<p>bz5!MyAdd1+0x21 [d:\mydriver\bz5\bz5.c @ 14]:<br>&nbsp;<wbr>&nbsp;<wbr> 14 f85134b1 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;32<br>&nbsp;<wbr>&nbsp;<wbr> 14 f85134b4 83c064&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,64h&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;32+100&nbsp;<wbr> 准备返回<br>&nbsp;<wbr>&nbsp;<wbr> //赋值给eax作为返回值结束<br>&nbsp;<wbr>&nbsp;<wbr> 16 f85134b7 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr>&nbsp;<wbr> 16 f85134b9 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 16 f85134ba c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8</p>
<p>最后运行结果：<br>kd&gt; g<br>x3 Result:132<br>!<br>当我把<br>&nbsp;<wbr> if (u1&gt;u2)<br>改为<br>&nbsp;<wbr> if (u1&lt;u2)&nbsp;<wbr><br>&nbsp;<wbr> &nbsp;<wbr><br>反汇编以后：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f8567499 3b450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cmp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f856749c 730a&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> jae&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz5!MyAdd1+0x18 (f85674a8)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>运行结果:<br>kd&gt; g<br>x3 Result:110<br>!</p>
<p>内核反编译学习笔记4(下)附windbg常用命令</p>
<p>那么我们能否把原来的730a改为7f0a这样的手段，修改驱动的判断呢？</p>
<p>&nbsp;<wbr></p>
<p>当然可以，我们可以用ultraedit等编辑器，打开bz5.sys，搜索"3b450c"，下面的73改为自己需要的：<br>命令(16进制代码)<br>JA/JNBE (77)　<br>JAE/JNB (73)&nbsp;<wbr><br>JB/JNAE (72)&nbsp;<wbr><br>JBE/JNA (76)&nbsp;<wbr><br>JG/JNLE (7F)&nbsp;<wbr><br>JGE/JNL (7D)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>JL/JNGE (7C)&nbsp;<wbr><br>JLE/JNG (7E)&nbsp;<wbr></p>
<p>比如7F，保存，然后注册运行。没错，你会发现不能运行。</p>
<p>////////////////////////<br>解决方法：重新计算校验和并保存。<br>用loadPe就可以了，里面有校验和，旁边有个问号，重新计算以后别忘了保存，再确定，然后，阿弥陀佛。</p>
<p><br>1.基本调试控制<br>运行程序(Run): 快捷键:F5 命令:g<br>单步步入(Step In)： 快捷键:F8 命令:p<br>单步步过(Step Over): 快捷键:F10<br>运行到光标所在行： 快捷键:F7<br>执行到返回：gu<br>执行到指定地址：g [Address]<br>重新运行调试程序: 快捷键:Ctrl+Shift+F5(这个对驱动一般用不到)</p>
<p>2.断点<br>断点之于调试当然是非常重要的<br>常用命令：<br>bp [Address]or[Symbol] 在指定地址下断<br>可以使用地址或符号，如<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bp 80561259(Windbg默认使用16进制)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bp MyDriver!GetKernelPath<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bp MyDriver!GetKernelPath+0x12<br>bp [Address] /p eprocess 仅当当前进程为eprocess时才中断<br>这个很常用，比如你bp nt!NtTerminateProcess,但是只想在某一进程触发此断点时才断下来，那就加上这个参数吧，因为内核中的代码是各个进程共用的，所以此命令很实用<br>bp [Address] /t ethread 仅当当前线程为ethread时才中断，用法跟/p参数类似<br>bu [Address]or[Symbol] 下一个未解析的断点(就是说这个断点需要延迟解析)<br>这个也很常用，比如我们的驱动名为MyDriver.sys,那么在驱动加载之前下断bu MyDriver!DriverEntry，<br>然后加载这个驱动时就可以断在驱动入口，并且这个是不需要调试符号支持的<br>bl 列出所有断点,L=List<br>bc[id] 清除断点，c=Clear,id是bl查看时的断点编号<br>bd[id] 禁用断点，d=Disable,id即断点编号<br>be[id] 启用断点，e=Enable,id为断点编号</p>
<p>3.查看和修改数据<br>调试中不可避免的要查看和修改数据<br>查看内存：<br>db/dw/dd/dq [Address]&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 字节/字/双字/四字方式查看数据<br>da/du [Address]&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ASCII字符串/Unicode字符串方式查看指定地址<br>其它常用的如查看结构<br>dt nt!_EPROCESS<br>dt nt!_EPROCESS 89330da0 (把0x89330da0作为对象指针)<br>修改内存：<br>eb/ew/ed/eq/ef/ep Address [Values]<br>字节/字/双字/四字/浮点数/指针/<br>ea/eu/eza/ezu Address [Values]<br>ASCII字符串/Unicode字符串/以NULL结尾的ASCII字符串/以NULL结尾的Unicode字符串<br>搜索内存：<br>s -[b/w/d/q/a/u] Range Target<br>搜索字节/字/双字/四字/ASCII字符串/Unicode字符串</p>
<p>4.寄存器<br>在用Windbg调试时可以Alt+4直接调出寄存器窗口，然后拖放到合适的位置就可以。<br>要修改呢就直接双击相应的项就可以了。<br>把命令的方式也说一下，比较简单：<br>r 显示所有寄存器的值<br>r eax 显示eax的值<br>r eax=1 修改eax的值为1</p>
<p>5.辅助命令<br>!process 显示当前进程信息<br>!process 0 0 显示当前所有进程(会有僵尸进程)<br>!process 1f4 显示pid为1f4的进程信息，后面也可以跟eprocess的值<br>!thread 显示当前线程信息<br>!thread<br>!process 1f4 显示tid为768的线程信息，后面也可以跟ethread的值<br>栈相关:<br>k 显示调用栈<br>kb 显示ebp和前3个参数<br>kp 以函数调用形式显示栈</p>
<p>&nbsp;<wbr></p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112925.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> 2010-04-18 19:24 <a href="http://www.cppblog.com/iniwf/archive/2010/04/18/112925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内核反编译学习笔记3</title><link>http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 18 Apr 2010 11:22:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112924.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112924.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112924.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<p>本节主要看全局变量和局部变量，程序越来越长，可以跳开查看。</p>
<p>全局变量在程序开始定义赋值的话，存放在Data块，Data块可以通过静态反汇编获得。<br>局部变量定义在函数内部，使用的时候需要类似sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,10h，开辟空间存放</p>
<p>需要掌握：<br>静态反汇编工具<br>变量存放地点<br>sub</p>
<p>简单了解：<br>读取全局变量的方法：1，获取全局变量存放地址。2，偏移量与实际内存地址关系。</p>
<p><br>不需了解：<br>w32asm反汇编以后，需要复制其中内容的，先保存为alf文件，再用文本读取程序打开。</p>
<p><br>所用程序：bz4</p>
<p><br>#include &lt;ntddk.h&gt;</p>
<p>ULONG au1,au2;<br>ULONG au3 = 7;<br>ULONG au4 = 9;</p>
<p>ULONG MyAdd1(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>&nbsp;<wbr> return u1+u2;</p>
<p>}<br>&nbsp;<wbr><br>ULONG MyAdd2(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>&nbsp;<wbr> ULONG u3;<br>&nbsp;<wbr>&nbsp;<wbr> u3 = u1+u2;<br>&nbsp;<wbr>&nbsp;<wbr> return u3;&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr></p>
<p>}</p>
<p>ULONG MyAdd3(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>&nbsp;<wbr> ULONG u3,u4,u5,u6;<br>&nbsp;<wbr>&nbsp;<wbr> u3 = u1+u2;<br>&nbsp;<wbr>&nbsp;<wbr> u4 = u3+u1;<br>&nbsp;<wbr>&nbsp;<wbr> u5 = u1;<br>&nbsp;<wbr>&nbsp;<wbr> u6 = u1+u3;<br>&nbsp;<wbr>&nbsp;<wbr> return u3+u4+u5+u6;&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr></p>
<p>}</p>
<p><br>VOID DriverUnload(PDRIVER_OBJECT driver)<br>{<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("unload&#8230;\r\n");<br>}</p>
<p><br>NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)<br>{<br>&nbsp;<wbr>ULONG x1 = 5;<br>&nbsp;<wbr>ULONG x2 = 8;<br>&nbsp;<wbr>ULONG x3 ;<br>&nbsp;<wbr><br>&nbsp;<wbr><br>#if DBG<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> _asm int 3<br>#endif<br>&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr>au1 = MyAdd1(x1,x2);&nbsp;<wbr> //使用自定义函数，反汇编看看结果<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("au1 Result:%d\n!",au1);<br>&nbsp;<wbr><br>&nbsp;<wbr>au2 = MyAdd2(x1,x2);&nbsp;<wbr> //使用自定义函数，反汇编看看结果<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("au2 Result:%d\n!",au2);<br>&nbsp;<wbr><br>&nbsp;<wbr>x3 = MyAdd3(x1,x2);&nbsp;<wbr> //使用自定义函数，反汇编看看结果<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("Result:%d\n!",x3);<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("au3 Result:%d\n!",au3);<br>&nbsp;<wbr>DbgPrint("au4 Result:%d\n!",au4);<br>&nbsp;<wbr><br>&nbsp;<wbr>driver-&gt;DriverUnload = DriverUnload;<br>&nbsp;<wbr>return STATUS_SUCCESS;<br>}</p>
<p>接下来要用w32asm和windbg反汇编，看其中对应关系，都是从55F到5B1，显然一一对应，只是前面地址有偏移。<br>我们知道，Data Offset是Data数据段，存放的是全局变量。本程序全局变量是：<br>ULONG au3 = 7;<br>ULONG au4 = 9;<br>显然我们dd 00000700是看不到数据的，要dd f84f7700，等我们运行起来的时候，看看是不是</p>
<p><br>w32asm反汇编：</p>
<p><br>Code Offset = 00000480, Code Size = 00000200<br>Data Offset = 00000700, Data Size = 00000080<br>...............</p>
<p>:0001055F 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push edx</p>
<p>* Possible StringData Ref from Code Obj -&gt;"au1 Result:%d<br>!"<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:00010560 6850060100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push 00010650</p>
<p>* Reference To: ntoskrnl.DbgPrint, Ord:0030h<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:00010565 E888000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Call 000105F2<br>:0001056A 83C408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add esp, 00000008<br>:0001056D 8B45FC&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov eax, dword ptr [ebp-04]<br>:00010570 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push eax<br>:00010571 8B4DF8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov ecx, dword ptr [ebp-08]<br>:00010574 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push ecx<br>:00010575 E836FFFFFF&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call 000104B0<br>:0001057A A310070100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov dword ptr [00010710], eax<br>:0001057F 8B1510070100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov edx, dword ptr [00010710]<br>:00010585 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push edx<br>:00010586 6840060100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push 00010640<br></p>
<p>* Reference To: ntoskrnl.DbgPrint, Ord:0030h<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:0001058B E862000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Call 000105F2<br>:00010590 83C408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add esp, 00000008<br>:00010593 8B45FC&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov eax, dword ptr [ebp-04]<br>:00010596 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push eax<br>:00010597 8B4DF8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov ecx, dword ptr [ebp-08]<br>:0001059A 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push ecx<br>:0001059B E830FFFFFF&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call 000104D0<br>:000105A0 8945F4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov dword ptr [ebp-0C], eax<br>:000105A3 8B55F4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov edx, dword ptr [ebp-0C]<br>:000105A6 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push edx</p>
<p>* Possible StringData Ref from Code Obj -&gt;"Result:%d<br>!"<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:000105A7 6830060100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push 00010630</p>
<p>* Reference To: ntoskrnl.DbgPrint, Ord:0030h<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:000105AC E841000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Call 000105F2<br>:000105B1 83C408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add esp, 00000008</p>
<p>&nbsp;<wbr></p>
<p><br>主函数中主要反汇编代码，也就是调用几个自定义函数的部分：</p>
<p>&nbsp;<wbr>&nbsp;<wbr> 57 f84f755f 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 57 f84f7560 6850764ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz4! ?? ::FNODOBFM::`string' (f84f7650)<br>&nbsp;<wbr>&nbsp;<wbr> 57 f84f7565 e888000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!DbgPrint (f84f75f2)<br>&nbsp;<wbr>&nbsp;<wbr> 57 f84f756a 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f756d 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7570 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7571 8b4df8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7574 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7575 e836ffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!MyAdd2 (f84f74b0)<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f757a a310774ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [bz4!au2 (f84f7710)],eax<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f757f 8b1510774ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [bz4!au2 (f84f7710)]<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f7585 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f7586 6840764ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz4! ?? ::FNODOBFM::`string' (f84f7640)<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f758b e862000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!DbgPrint (f84f75f2)<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f7590 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f7593 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f7596 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f7597 8b4df8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f759a 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f759b e830ffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!MyAdd3 (f84f74d0)<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f75a0 8945f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-0Ch],eax<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75a3 8b55f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp-0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75a6 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75a7 6830764ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz4! ?? ::FNODOBFM::`string' (f84f7630)<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75ac e841000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!DbgPrint (f84f75f2)<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75b1 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8</p>
<p><br>三个自定义函数</p>
<p>kd&gt; uf bz4!myadd1<br>bz4!MyAdd1 [d:\mydriver\bz4\bz4.c @ 9]:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f84f7490 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f84f7492 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f84f7493 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 10 f84f7495 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 10 f84f7498 03450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 12 f84f749b 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 12 f84f749c c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8<br>kd&gt; uf bz4!myadd2<br>bz4!MyAdd2 [d:\mydriver\bz4\bz4.c @ 15]:<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b0 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b2 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b3 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b5 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84f74b6 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84f74b9 03450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84f74bc 8945fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],eax<br>&nbsp;<wbr>&nbsp;<wbr> 18 f84f74bf 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 21 f84f74c2 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr>&nbsp;<wbr> 21 f84f74c4 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 21 f84f74c5 c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8<br>kd&gt; uf bz4!myadd3<br>bz4!MyAdd3 [d:\mydriver\bz4\bz4.c @ 24]:<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d0 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d2 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d3 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d5 83ec10&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,10h<br>&nbsp;<wbr>&nbsp;<wbr> 26 f84f74d8 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 26 f84f74db 03450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 26 f84f74de 8945f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-8],eax<br>&nbsp;<wbr>&nbsp;<wbr> 27 f84f74e1 8b4df8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 27 f84f74e4 034d08&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 27 f84f74e7 894dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],ecx<br>&nbsp;<wbr>&nbsp;<wbr> 28 f84f74ea 8b5508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 28 f84f74ed 8955f0&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-10h],edx<br>&nbsp;<wbr>&nbsp;<wbr> 29 f84f74f0 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 29 f84f74f3 0345f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 29 f84f74f6 8945f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-0Ch],eax<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f74f9 8b45f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f74fc 0345fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f74ff 0345f0&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-10h]<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f7502 0345f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 33 f84f7505 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr>&nbsp;<wbr> 33 f84f7507 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 33 f84f7508 c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8</p>
<p>///////////////////////////////////////////</p>
<p>//终于等到分析了<br>先看下全局变量 ：<br>ULONG au3 = 7;<br>ULONG au4 = 9;</p>
<p>看程序中Data存放的:</p>
<p>kd&gt; dd f84f7700<br>f84f7700&nbsp;<wbr> 00000007 00000009 f84de439 07b21bc6</p>
<p>没错，以后要查看全局变量的值，先反汇编，获得Data的偏移量，dd 地址 就可以看见了。</p>
<p>/////////////////////</p>
<p>kd&gt; uf bz4!myadd3<br>bz4!MyAdd3 [d:\mydriver\bz4\bz4.c @ 24]:<br>.......<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d5 83ec10&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,10h<br>.......<br>&nbsp;<wbr>&nbsp;<wbr> 局部变量开辟空间。<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr>好了，简单赋值，调用就玩到这里，接下来是.....&nbsp;<wbr></p>
<p><br></p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112924.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> 2010-04-18 19:22 <a href="http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内核反编译学习笔记2</title><link>http://www.cppblog.com/iniwf/archive/2010/04/18/112922.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 18 Apr 2010 11:02:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/18/112922.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112922.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/18/112922.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112922.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112922.html</trackback:ping><description><![CDATA[<p>内核反编译学习笔记2（上）</p>
<p>&nbsp;<wbr></p>
<p>本节任务：通过具有不同数量参数的函数调用，看其中区别<br>所用程序bz3<br>需要掌握：<br>windbg命令：<br>uf 反汇编<br>dd 查看内容</p>
<p>调用的时候：<br>mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 给参数赋值<br>push&nbsp;<wbr>&nbsp;<wbr> eax 供函数使用的参数<br>被调用函数内部<br>push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 保存ebp，用来保存调用前运行的代码地址<br>mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 将esp赋值给ebp，现在有新的ebp指针了,指向栈顶</p>
<p>pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 恢复ebp，取得地址，准备跳到那地址的代码，继续运行程序</p>
<p><br>需要了解<br>JMP&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 无条件跳转，去运行跳转后地址的代码<br>ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 返回，注意,调用前每push一个参数，esp就-4，两个参数就-8，ret的时候，需要+8返回</p>
<p>无需了解：<br>call=push+JMP<br>ret = pop+JMP&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 将在后面继续说明</p>
<p><br>//先贴代码，仔细看其实很简单</p>
<p><br>VOID MyP0()<br>{<br>&nbsp;<wbr>DbgPrint("no arg...\r\n");<br>}</p>
<p>VOID MyP1(ULONG u1)<br>{<br>&nbsp;<wbr>DbgPrint("One arg:%d\n!",u1);<br>}</p>
<p>VOID MyP2(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>&nbsp;<wbr> ULONG u3;<br>&nbsp;<wbr>&nbsp;<wbr> u3 = u1+u2;<br>&nbsp;<wbr>&nbsp;<wbr> DbgPrint("two arg:%d\n!",u3);</p>
<p>}<br>&nbsp;<wbr></p>
<p>VOID DriverUnload(PDRIVER_OBJECT driver)<br>{<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("unload&#8230;\r\n");<br>}</p>
<p><br>NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)<br>{<br>&nbsp;<wbr>ULONG x1 = 5;<br>&nbsp;<wbr>ULONG x2 = 8;<br>&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr><br>#if DBG<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> _asm int 3<br>#endif<br>&nbsp;<wbr><br>&nbsp;<wbr>MyP0();<br>&nbsp;<wbr>MyP1(x1);<br>&nbsp;<wbr>MyP2(x1,x2);<br>&nbsp;<wbr></p>
<p>&nbsp;<wbr><br>&nbsp;<wbr>driver-&gt;DriverUnload = DriverUnload;<br>&nbsp;<wbr>return STATUS_SUCCESS;<br>}</p>
<p>//////代码结束</p>
<p><br>以下分别为Entry主函数和3个自定义函数的反汇编。<br>汇编完了先放那，具体解析在下面。</p>
<p><br>kd&gt; uf bz3!driverentry<br>bz3!DriverEntry [d:\mydriver\bz3\bz3.c @ 31]:<br>&nbsp;<wbr>&nbsp;<wbr> 31 f8451520 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 31 f8451522 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 31 f8451523 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 31 f8451525 83ec08&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 32 f8451528 c745f805000000&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-8],5<br>&nbsp;<wbr>&nbsp;<wbr> 33 f845152f c745fc08000000&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],8<br>&nbsp;<wbr>&nbsp;<wbr> 38 f8451536 cc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> int&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 3<br>&nbsp;<wbr>&nbsp;<wbr> 41 f8451537 e854ffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP0 (f8451490)<br>&nbsp;<wbr>&nbsp;<wbr> 42 f845153c 8b45f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 42 f845153f 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 42 f8451540 e86bffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP1 (f84514b0)<br>&nbsp;<wbr>&nbsp;<wbr> 43 f8451545 8b4dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 43 f8451548 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 43 f8451549 8b55f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 43 f845154c 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 43 f845154d e87effffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP2 (f84514d0)<br>&nbsp;<wbr>&nbsp;<wbr> 47 f8451552 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 47 f8451555 c74034001545f8&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [eax+34h],offset bz3!DriverUnload (f8451500)<br>&nbsp;<wbr>&nbsp;<wbr> 48 f845155c 33c0&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> xor&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,eax<br>&nbsp;<wbr>&nbsp;<wbr> 49 f845155e 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr>&nbsp;<wbr> 49 f8451560 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 49 f8451561 c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8<br>&nbsp;<wbr>&nbsp;<wbr></p>
<p>内核反编译学习笔记2（下）</p>
<p>&nbsp;<wbr></p>
<p>三个自定义：<br>kd&gt; uf bz3!myp0<br>bz3!MyP0 [d:\mydriver\bz3\bz3.c @ 5]:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 5 f8451490 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 5 f8451492 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 5 f8451493 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 6 f8451495 68701545f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz3! ?? ::FNODOBFM::`string' (f8451570)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 6 f845149a e8cb000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!DbgPrint (f845156a)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 6 f845149f 83c404&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,4<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 7 f84514a2 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 7 f84514a3 c3&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret<br>kd&gt; uf bz3!myp1<br>bz3!MyP1 [d:\mydriver\bz3\bz3.c @ 10]:<br>&nbsp;<wbr>&nbsp;<wbr> 10 f84514b0 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 10 f84514b2 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 10 f84514b3 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 11 f84514b5 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 11 f84514b8 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 11 f84514b9 68801545f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz3! ?? ::FNODOBFM::`string' (f8451580)<br>&nbsp;<wbr>&nbsp;<wbr> 11 f84514be e8a7000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!DbgPrint (f845156a)<br>&nbsp;<wbr>&nbsp;<wbr> 11 f84514c3 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 12 f84514c6 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 12 f84514c7 c20400&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 4<br>kd&gt; uf bz3!myp2<br>bz3!MyP2 [d:\mydriver\bz3\bz3.c @ 15]:<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84514d0 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84514d2 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84514d3 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84514d5 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84514d6 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84514d9 03450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84514dc 8945fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],eax<br>&nbsp;<wbr>&nbsp;<wbr> 18 f84514df 8b4dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 18 f84514e2 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 18 f84514e3 68901545f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz3! ?? ::FNODOBFM::`string' (f8451590)<br>&nbsp;<wbr>&nbsp;<wbr> 18 f84514e8 e87d000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!DbgPrint (f845156a)<br>&nbsp;<wbr>&nbsp;<wbr> 18 f84514ed 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 20 f84514f0 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr>&nbsp;<wbr> 20 f84514f2 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 20 f84514f3 c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> 正式开始我们的任务<br>&nbsp;<wbr>&nbsp;<wbr> ////////////<br>&nbsp;<wbr>&nbsp;<wbr> 以下为Entry中调用函数的反汇编代码<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP0 (f8451490)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ;无参数<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]&nbsp;<wbr>&nbsp;<wbr> ;1个参数<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP1 (f84514b0)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-4]&nbsp;<wbr>&nbsp;<wbr> ;2个参数<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP2 (f84514d0)&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></p>
<p>&nbsp;<wbr>&nbsp;<wbr> 显然我们可以得出结论：<br>&nbsp;<wbr>&nbsp;<wbr> 在调用函数的时候，无参数，直接call，<br>&nbsp;<wbr>&nbsp;<wbr> 有参数，有几个要push几个，把eax等寄存器内的数据入栈:push exx<br>&nbsp;<wbr>&nbsp;<wbr> eax,edx等寄存器内的数据哪里来的呢？通过mov，从存放地点移动过来赋值。<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr> //接下来我们进入函数内部，要动态追踪下面代码。以后每次看见这样代码，明白道理，就直接忽略：<br>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 保存ebp<br>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 保存esp</p>
<p>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 获得ebp<br>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 返回，注意，后面为什么是8<br>&nbsp;<wbr><br>&nbsp;<wbr> 我们先看一下Entry调用MyP0处的地址：<br>&nbsp;<wbr>&nbsp;<wbr> 38 f8451536 cc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> int&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 3<br>&nbsp;<wbr>&nbsp;<wbr> 41 f8451537 e854ffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP0 (f8451490)<br>&nbsp;<wbr>&nbsp;<wbr> 42 f845153c 8b45f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> 显然调用MyP0后，要运行f845153c的代码。然后我们到函数内部看<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 中ebp的内容，是不是f845153c<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr> 进入windbg:<br>//////////////////////////<br>&nbsp;<wbr> kd&gt; t<br>bz3!DriverEntry+0x17:<br>f8451537 e854ffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz3!MyP0 (f8451490)<br>kd&gt; t<br>bz3!MyP0:<br>f8451490 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>kd&gt; p<br>bz3!MyP0+0x5:<br>f8451495 68701545f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz3! ?? ::FNODOBFM::`string' (f8451570)<br>kd&gt; dd ebp<br>f8282c6c&nbsp;<wbr> f8282c7c f845153c 00000005 00000008<br>//////////////////<br>呵呵，没错，f845153c,5,8,都是要用的。<br>下面两个地址也应该出现在下面两个函数的ebp中,不然程序返回后不知道继续运行哪里的代码。<br>&nbsp;<wbr>f8451545 f8451552<br>&nbsp;<wbr><br>&nbsp;<wbr>顺便看下类似下面命令是什么意思<br>&nbsp;<wbr>mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr><br>我们要知道 ebp+8，要先看下ebp:<br>&nbsp;<wbr><br>kd&gt; dd ebp<br>f8282c64：&nbsp;<wbr> f8282c7c f8451552 00000005 00000008</p>
<p>可以看到，ebp+4就是f8451552，返回后继续运行的地址，ebp+8当然是5了,ebp+12(哦，要16进制:ebp+c)是8。<br>那我们直接看下dd ebp+8：<br>kd&gt; dd ebp+8<br>f8282c6c:&nbsp;<wbr>&nbsp;<wbr> 00000005 00000008 00000005 00000008<br>kd&gt; dd ebp+c<br>f8282c70：&nbsp;<wbr> 00000008 00000005 00000008 f8282d4c</p>
<p>应该没疑问了。<br>&nbsp;<wbr><br>//////////////<br>以后看汇编代码的时候，可以忽略：<br>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 保存返回地址<br>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 设置新的ebp指针，指向栈顶<br>中间是具体实现<br>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 获得返回地址<br>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 返回</p>
<p>只看这些代码中间的代码就可以了，以后直接贴这些代码中间的实现。</p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112922.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> 2010-04-18 19:02 <a href="http://www.cppblog.com/iniwf/archive/2010/04/18/112922.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内核驱动反编译笔记1</title><link>http://www.cppblog.com/iniwf/archive/2010/04/18/112921.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 18 Apr 2010 10:53:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/18/112921.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112921.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/18/112921.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112921.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112921.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自：http://blog.sina.com.cn/s/blog_541329b40100eyrt.html内核驱动反编译笔记1&nbsp;所用程序:bz1,bz2内核驱动反编译后，看看是什么样子，函数调用，全局局部变量，各种循环，数组，数据结构，各种算法都什么样子。没什么捷径，一个个编写了反编译比对吧。比对么，先要有个最简单的程序来做标本，所以以下有一个最原始最简单的程序，和一个添加了自定...&nbsp;&nbsp;<a href='http://www.cppblog.com/iniwf/archive/2010/04/18/112921.html'>阅读全文</a><img src ="http://www.cppblog.com/iniwf/aggbug/112921.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> 2010-04-18 18:53 <a href="http://www.cppblog.com/iniwf/archive/2010/04/18/112921.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TightVNC分析文档 </title><link>http://www.cppblog.com/iniwf/archive/2010/04/17/112874.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 17 Apr 2010 14:45:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/17/112874.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112874.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/17/112874.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112874.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112874.html</trackback:ping><description><![CDATA[<p>&nbsp; TightVNC分析文档 <br><a href="http://www.cnblogs.com/piccolo/articles/251356.html">http://www.cnblogs.com/piccolo/articles/251356.html</a></p>
<p>Content List:</p>
<p>System Shell <br>IActiveDesktop <br>SetProcessShutdownParameters <br>One Instance Running by Mutex <br>sscanf <br>Kill ScreenSaver <br>Diable Nagle Algorithm<br>Screen Capture <br>Poll Schema <br>TightVNC<br>System Shell ：<br>1.1 IActiveDesktop<br>&nbsp;&nbsp;&nbsp; Allows a client program to manage the desktop items and wallpaper on a local computer.</p>
<p>#include </p>
<p>IActiveDesktop* active_desktop = 0;<br>CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,<br>&nbsp;&nbsp;IID_IActiveDesktop, (void**)&amp;active_desktop);</p>
<p>1.2 SetProcessShutdownParameters<br>&nbsp;&nbsp;&nbsp; The SetProcessShutdownParameters function sets shutdown parameters for the currently calling process. This function sets a shutdown order for a process relative to the other processes in the system.</p>
<p>// Set this process to be the last application to be shut down.<br>SetProcessShutdownParameters(0x100, 0);</p>
<p>1.3 One Instance Running by Mutex<br>&nbsp;&nbsp;&nbsp; Use Win32 Mutex object to insure that only one instance is currently running in our OS.</p>
<p>const char mutexname [] = "WinVNC_Win32_Instance_Mutex";</p>
<p>BOOL vncInstHandler::Init()<br>{<br>&nbsp;// Create the named mutex<br>&nbsp;HANDLE mutex = CreateMutex(NULL, FALSE, mutexname);<br>&nbsp;if (mutex == NULL)<br>&nbsp;&nbsp;return FALSE;</p>
<p>&nbsp;// Check that the mutex didn't already exist<br>&nbsp;if (GetLastError() == ERROR_ALREADY_EXISTS)<br>&nbsp;&nbsp;return FALSE;</p>
<p>&nbsp;return TRUE;<br>}</p>
<p>1.4 sscanf<br>&nbsp;&nbsp;&nbsp; Read formatted data from a string.</p>
<p>// Check the protocol version<br>int major, minor;<br>sscanf((char *)&amp;protocol_ver, "RFB %03d.%03d\n", &amp;major, &amp;minor);</p>
<p>1.5 Kill Screen Saver</p>
<p><br>// How to kill the screen saver depends on the OS<br>switch (osversioninfo.dwPlatformId)<br>case VER_PLATFORM_WIN32_WINDOWS:<br>&nbsp;HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);<br>&nbsp;if (hsswnd != NULL)<br>&nbsp;&nbsp;PostMessage(hsswnd, WM_CLOSE, 0, 0); <br>&nbsp;break;<br>&nbsp;<br>case VER_PLATFORM_WIN32_NT:<br>&nbsp;HDESK hDesk = OpenDesktop(<br>&nbsp;&nbsp;&nbsp;&nbsp;"Screen-saver",<br>&nbsp;&nbsp;&nbsp;&nbsp;0,<br>&nbsp;&nbsp;&nbsp;&nbsp;FALSE,<br>&nbsp;&nbsp;&nbsp;&nbsp;DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS<br>&nbsp;&nbsp;&nbsp;&nbsp;);<br>&nbsp;if (hDesk != NULL)<br>&nbsp;{<br>&nbsp;&nbsp;EnumDesktopWindows(hDesk, (WNDENUMPROC) &amp;KillScreenSaverFunc, 0);<br>&nbsp;&nbsp;CloseDesktop(hDesk);<br>&nbsp;&nbsp;// Pause long enough for the screen-saver to close<br>&nbsp;&nbsp;//Sleep(2000);<br>&nbsp;&nbsp;// Reset the screen saver so it can run again<br>&nbsp;&nbsp;SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE); <br>&nbsp;}<br>&nbsp;break;<br>}&nbsp;</p>
<p>1.6 Disable Nagle Algorithm<br>&nbsp;&nbsp;&nbsp; Nagle Algorithm主要是用于优化小数据包的发送，用于在IP栈中缓冲小数据包，积累一定数量的小数据包一起发送。这样可以减少系统发包的数量，Nagle Algorithm实现时内部也有一个Timeout机制，但这个Timeout时间长度不能在外部设置。而且对每个IP栈的实现，这个Timeout Span 都有一个实现相关的值。</p>
<p>// Disable Nagle's algorithm<br>BOOL nodelayval = TRUE;<br>setsockopt(m_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&amp;nodelayval, sizeof(BOOL));</p>
<p><br>Screen Capture ：<br>2.1 Poll Schema<br>&nbsp;&nbsp;&nbsp; Poll方式就是最简单的一种方式了，也是大家在考虑截屏时最先想到一个解决方案。为取得系统的屏幕，poll方案每33ms轮询一次系统屏幕，将变化的部分添加加到一个UpdateRegion中。为获取这些屏幕变化的Rect,系统还采取了一种优化Polling算法，将屏幕分为32*32 pixel的小矩形块，算法给出了一个各个矩形块轮询的Order:</p>
<p>const int pollingOrder[32] = {<br>&nbsp;&nbsp;0, 16,&nbsp; 8, 24,&nbsp; 4, 20, 12, 28,<br>&nbsp;&nbsp;10, 26, 18,&nbsp; 2, 22,&nbsp; 6, 30, 14,<br>&nbsp;&nbsp;1, 17,&nbsp; 9, 25,&nbsp; 7, 23, 15, 31,<br>&nbsp;&nbsp;19,&nbsp; 3, 27, 11, 29, 13,&nbsp; 5, 21<br>};<br>获取当前屏幕鼠标图像的思路：<br>第一步：获取屏幕鼠标的HCURSOR<br>&nbsp;&nbsp;&nbsp; GetCursorPos获取屏幕鼠标位置，然后WindowFromPoint获取鼠标的拥有者窗口，GetWindowThreadProcessId获取相关线程ID,比较该ID和GetCurrentThreadId返回的ID，如果相同，通过GetCursor直接获取鼠标的HCURSOR；否则，通过AttachThreadInput连接上目标线程的Input Mechanism，再调用GetCursor获取鼠标的HCURSOR。 <br>第二步：获取HCURSOR的Bitmap</p>
<p><br>int :GetCursorSendBuffer(BYTE *pBuffer, int nSize)<br>{<br>&nbsp;....<br>&nbsp;ICONINFO IconInfo;<br>&nbsp;GetIconInfo(hcursor, &amp;IconInfo);<br>&nbsp;<br>&nbsp;BITMAP bmMask;<br>&nbsp;GetObject(IconInfo.hbmMask, sizeof(BITMAP), (LPVOID)&amp;bmMask);&nbsp;<br>&nbsp;GetBitmapBits(IconInfo.hbmMask,bmMask.bmWidthBytes * bmMask.bmHeight, m_mBits);<br>&nbsp;....<br>}</p>
<p>现在有必要对HRGN操作相关API做一下介绍</p>
<p>GetRgnBox&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获取HRGN的边界矩形<br>GetRegionData&nbsp; 可以将一个HRGN分解为一个RECT数组，见RGNDATA结构体说明<br>CombineRgn&nbsp;&nbsp;&nbsp;&nbsp; 对HRGN操作RGN_AND，RGN_COPY，RGN_DIFF，RGN_OR或是RGN_XOR</p>
<p><br>2.2 TightVNC<br>&nbsp;&nbsp;&nbsp; TightVNC(Tight Virtual Network Computing)是一个远程桌面控制的开源软件,详情请参考<a href="http://www.tightvnc.com/">http://www.tightvnc.com</a>.下载了TightVNC的代码，分析了一下其Server部分的代码， WinVNC下的文件很多，但我们按照它们各自的功能做一下划分,其结构如下：<br>Kernel vncBuffer.cpp vncClient.cpp vncDesktop.cpp vncServer.cpp WinVNC.cpp <br>GUI vncAbout.cpp vncAcceptDialog.cpp vncAdvancedProperties.cpp vncConnDialog.cpp vncMenu.cpp vncProperties.cpp vncTimedMsgBox.cpp <br>Misc d3des.c Log.cpp MinMax.cpp RectList.cpp stdhdrs.cpp tableinitcmtemplate.cpp tableinittctemplate.cpp tabletranstemplate.cpp translate.cpp vncauth.c vncInstHandler.cpp vncKeymap.cpp vncRegion.cpp&lt; vncService.cpp <br>Network VSocket.cpp vncSockConnect.cpp vncHTTPConnect.cpp rfbproto.h <br>Encoding vncEncodeCoRRE.cpp vncEncodeHexT.cpp vncEncoder.cpp vncEncodeRRE.cpp vncEncodeTight.cpp vncEncodeZlib.cpp vncEncodeZlibHex.cpp </p>
<p>&nbsp;</p>
<p><br>其服务端的主要功能模块结构如下：<br>其核心框架就是四个类vncClient，vncServer，vncDesktop和vncBuffer.下面我就这四个类之间的联系和用途来作一下简单的分析：<br>vncServer：<br>&nbsp;&nbsp;&nbsp; vncServer主要是做如下的一些工作：容许vncClient动态的添加和删除；将本地vncDesktop对象内部状态的任何改变"传播"到各个客户端；传播客户端的鼠标和键盘事件到本地的vncDesktop对象。同时，其还创建了vncSockConnect，vncCORBAConnect和vncHTTPConnect来接受Socket,Corba和HTTP的连接。 vncServer为每个连接上来的客户端分配了一个ClientID（其实就是内部客户对象数组的Index），并且提供了对客户端管理的众多函数：</p>
<p>virtual void DisableClients(BOOL state);<br>virtual void KillClient(vncClientId client);<br>virtual void KillAuthClients();<br>virtual void KillUnauthClients();</p>
<p>virtual vncClient* GetClient(vncClientId clientid);<br>vncClientId AddClient(VSocket *socket, BOOL auth, BOOL shared);<br>virtual void RemoveClient(vncClientId client);<br>同时，vncServer还提供了对客户Teleport，Capability，KeyboardEnabled，PointerEnabled，Name，Authenticated属性的get/set方法。<br>下面我们来看一下vncServer对客户端连接上来和客户端认证成功这两个事件的处理流程：<br>vncServer::AddClient：<br>&nbsp;&nbsp;&nbsp; 首先vncServer在其内部的vncClient *m_clientmap[MAX_CLIENTS]数组中为新连接上的客户端分配一个空闲的slot，并将其作为此客户的 clientID. 然后，为此连接分配一个vncClient对象，根据传递过来的参数，设置vncClient对象的相关属性，然后调用vncClient::Init方法将vncServer的实例指针和 clientID传给vncClient实例。接着，m_clientmap[clientid] = client并将此用户加入vncServer的未认证用户链表。<br>vncServer::Authenticated(vncClientId clientid)：<br>&nbsp;&nbsp;&nbsp; 首先从未认证用户列表中根据clientid获取vncClient对象，并将其从unauth list 中删除。如果是vncServer的第一个用户，创建vncDesktop对象，并调用m_desktop-&gt;Init(this)来初始化该vncDesktop对象。接下来，为这个用户分配一个vncBuffer *buffer = new vncBuffer(m_desktop);并通过调用vncClient::SetBuffer为vncClient设置这个Buffer，最后将此用户添加到auth list中。<br>&nbsp;&nbsp;&nbsp; vncServer提供了一个用户列表的操作接口，这些接口通过将vncServer的方法调用映射到对auth list中各个客户的同样的方法的函数调用，这些方法有：</p>
<p>virtual void TriggerUpdate();<br>virtual void UpdateRect(RECT &amp;rect);<br>virtual void UpdateRegion(vncRegion &#174;ion);<br>virtual void CopyRect(RECT &amp;dest, POINT &amp;source);<br>virtual void UpdateMouse();<br>virtual void UpdateClipText(LPSTR text);<br>virtual void UpdatePalette();<br>vncDesktop:<br>&nbsp;&nbsp;&nbsp; vncDesktop是一个全局唯一的对象，根据注释，vncDesktop主要是处理从display buffer中获取数据；同时，它还利用RFBLib DLL为vncServer提供诸如鼠标移动和屏幕更新等信息。上面提到，vncServer在第一个用户连接上来时发现其m_desktop为空时就创建一个vncDesktip对象，并调用 vncDesktop::Init(this)对其初始化.在vcnDesktop::Init的实现中我们发现其创建了一个vncDesktopThread，vncDesktop的方法调用大部分都在这个vncDesktopThread里完成的.下面我们来分析一下这个线程都做了些什么:<br>vncDesktopThread::run_undetached(void *arg)：<br>&nbsp;&nbsp;&nbsp; 首先调用vncDesktop::Startup初始化，vncDesktop对象（见vncDesktop::Startup），然后就是处理桌面消息，调用 m_server-&gt;UpdateMouse()和m_server-&gt;UpdateRegion(rgncache) ，接下来调用vncServer::TriggerUpdate来发送屏幕更新到每个vncClient.然后就是处理RFB_SCREEN_UPDATE和RFB_MOUSE_UPDATE这两个注册消息。 </p>
<p>vncClient:<br>&nbsp;&nbsp;&nbsp; vncClient做了数据发送的工作，在vncClient::SendUpdate函数的实现中，我们可以看到vncClient调用SendRFBMsg首先发送 ,然后SendCursorShapeUpdate发送鼠标形状更新，SendCursorPosUpdate发送鼠标Pos更新，发送SendCopyRect，最后调用SendRectangles发送需要更新的矩形的相关数据。其实每个客户端vncClient在调用vncClient::Init初始化的时候都开了一个线程，客户端的行为基本上都是在vncClientThread::run里完成的。该线程在跟客户端交互完成了认证，Pixel格式，Encoding算法等信息的协商后，就进入一个loop循环开始接受和处理远程客户端发过来的rfbSetPixelFormat，rfbSetEncodings，rfbFramebufferUpdateRequest，rfbKeyEvent，rfbPointerEvent，rfbClientCutText消息。 </p>
<p>vncBuffer:<br>&nbsp;&nbsp;&nbsp; vncBuffer主要处理发送数据的Encoding工作，其提供了远程客户的本地视图，其主要是利用内部的vncDesktop指针来获取相关的数据</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/chief1985/archive/2008/10/24/3138375.aspx">http://blog.csdn.net/chief1985/archive/2008/10/24/3138375.aspx</a></p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112874.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> 2010-04-17 22:45 <a href="http://www.cppblog.com/iniwf/archive/2010/04/17/112874.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VNC的Hextile编码原理及实现</title><link>http://www.cppblog.com/iniwf/archive/2010/04/17/112873.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 17 Apr 2010 14:40:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/17/112873.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112873.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/17/112873.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112873.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112873.html</trackback:ping><description><![CDATA[<p>转自：<a href="http://blog.csdn.net/chief1985/archive/2009/01/02/3687243.aspx">http://blog.csdn.net/chief1985/archive/2009/01/02/3687243.aspx</a></p>
<p><font color=#002c99>VNC</font></p>
<p><font color=#002c99>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 功能<br>在本机显示和控制别一台计算机的桌面，就像直接用那台计算机一样</font></p>
<p><font color=#002c99>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VNC的编码方式主要有</font></p>
<p><font color=#002c99>a)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Raw（0）：不进行编码，直接传送数据，是最慢的一种</font></p>
<p><font color=#002c99>b)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Copyrect（1）：对于客户端，在已经有了相同象素数据的时候比较有效，比如移动或窗口内容滚动时</font></p>
<p><font color=#002c99>c)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RRE（2）：将象素颜色相同的某一个矩形区域作为一个整体传输</font></p>
<p><font color=#002c99>d)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hextile（5）：是RRE编码的变种，把屏幕分成16x16象素的小块，每块用Raw或RRE方式转送</font></p>
<p><font color=#002c99>e)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ZRLE（16）：它结合了zlib压缩</font></p>
<p><font color=#002c99>二、Hextile原理<br>通过解释Hextile算法，说明了简单而常用的屏幕传送和压缩算法，希望对屏幕监测、传送相关的工作有所启发</font></p>
<p><font color=#002c99>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 分割</font></p>
<p><font color=#002c99>a)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 传送的图像区域被分为若干个大小为16&#215;16象素的块，如果整个矩形不是16的倍数，则最后1行（或列）的块宽度（或高度）变小</font></p>
<p><font color=#002c99>b)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这些块按从左到右，从上到下的顺序排列，屏幕中变化的块被传送，不变的不被传送</font></p>
<p><font color=#002c99>c)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于块大小是16x16，所以块内坐标XY可用一字节表示，WH可用一个字节表示</font></p>
<p><font color=#002c99>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 标志位：<br>传送每块数据的之前，先要传送一个字节的标志位，标记出该块的传送的格式，它的每一位意义如下</font></p>
<p><font color=#002c99>a)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 右一：Raw数据：不压缩，直接传送，一般此位置1时其它位都置0</font></p>
<p><font color=#002c99>b)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 右二：包含背景色数据：标志位之后需要接收背景色数据</font></p>
<p><font color=#002c99>c)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 右三：包含前景色数据：背景色之后需要接收前景色数据</font></p>
<p><font color=#002c99>d)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 右四：是否含有子块：只要该块中含有两种及两种以上颜色，则此位置1</font></p>
<p><font color=#002c99>e)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 右五：子块的颜色：如果含有两种颜色，此位置0，子块颜色用前景色；若该块中含有两种以上的颜色，此位置1，子块颜色需要单独指明</font></p>
<p><font color=#002c99>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 块内部的编码：</font></p>
<p><font color=#002c99>a)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计算块内部的颜色数：一种、两种、多种，并记录出现频率最多的颜色为背景色，如果仅有两种颜色，则另一颜色记为前景色</font></p>
<p><font color=#002c99>b)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 判断块内颜色数是否为一种，如果是，则先修改传送标志位，然后传送整块大小（0，0，w，h）和背景色，此块传送即完成</font></p>
<p><font color=#002c99>c)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果不是一种，则把块拆分成颜色不同的小矩形，方法如下：</font></p>
<p><font color=#002c99>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 先把块复制到一块内存区中，以免破坏原始数据，暂称tmpBuf</font></p>
<p><font color=#002c99>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ii.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从第一个象素开始判断，该点颜色是否与背景色相同</font></p>
<p><font color=#002c99>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iii.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果不同，则分别向右和向下求得与该点颜色连续的色块</font></p>
<p><font color=#002c99>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iv.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对比右色块和下色块，取出其中较大的一个，做为一个矩形色块</font></p>
<p><font color=#002c99>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在tmpBuf中把此矩形填成背景色，以避免重复判断</font></p>
<p><font color=#002c99>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vi.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 继续判断下一象素点&#8230;&#8230;</font></p>
<p><font color=#002c99>d)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 记录各个矩形色块的位置（x,y,w,h），如果块内含两种以上颜色，还要记录矩形色块的颜色值</font></p>
<p><font color=#002c99>e)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一边取得矩形色块，一边判断矩形色块描述数据的总长是否大于原始数据，如果大于原始数据，则放弃取色块，标志字节Raw位(右一)置1，以Raw方式直接传送原始数据，此块传送完成</font></p>
<p><font color=#002c99>f)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果块含两种以上颜色，则将标志位的子块位（右五）置1，否则置0</font></p>
<p><font color=#002c99>g)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 传送标志位，传送矩形色块个数，然后传送各矩形块数据，块中颜色数为2时不需要传送每个矩形块的颜色数据，只传位置即可</font></p>
<p><font color=#002c99>h)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意：如果背景色或前景色与前一块(16x16块)相同，则可以不传送背景色或前景色，客户端会默认延用前一块的</font></p>
<p><font color=#002c99>三、参考实例</font></p>
<p><font color=#002c99>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 源码：</font></p>
<p><font color=#002c99>a)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 源码包：tightvnc-1.2.9.orig.tar.gz</font></p>
<p><font color=#002c99>b)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编码程序：tightvnc-1.2.9.orig/Xvnc/programs/Xserver/hw/vnc/hextile.c</font></p>
<p><font color=#002c99>c)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解码程序：rightvnc-1.2.9.orig/vncviewer/hextile.c</font></p>
<p><font color=#002c99>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 参考文档</font></p>
<p><font color=#002c99>a)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VNC协议简单分析-beta+4.9.pdf&nbsp;&nbsp;&nbsp; (CSDN可下载)</font></p>
<p><font color=#002c99>b)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://blogimg.chinaunix.net/blog/upfile/070613090634.pdf">http://blogimg.chinaunix.net/blog/upfile/070613090634.pdf</a></font></p>
<p><font color=#002c99></font>&nbsp;</p>
<p><font color=#002c99>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/chief1985/archive/2009/01/02/3687243.aspx">http://blog.csdn.net/chief1985/archive/2009/01/02/3687243.aspx</a></font></p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112873.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> 2010-04-17 22:40 <a href="http://www.cppblog.com/iniwf/archive/2010/04/17/112873.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>钩子函数教程</title><link>http://www.cppblog.com/iniwf/archive/2010/04/14/112529.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Wed, 14 Apr 2010 02:11:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/14/112529.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112529.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/14/112529.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112529.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112529.html</trackback:ping><description><![CDATA[<p>转：<br></p>
<p>Windows系统是建立在事件驱动的机制上的，说穿了就是整个系统都是通过消息的传递来实现的。而钩子是Windows系统中非常重要的系统接 口，用它可以截获并处理送给其他应用程序的消息，来完成普通应用程序难以实现的功能。钩子可以监视系统或进程中的各种事件消息，截获发往目标窗口的消息并 进行处理。这样，我们就可以在系统中安装自定义的钩子，监视系统中特定事件的发生，完成特定的功能，比如截获键盘、鼠标的输入，屏幕取词，日志监视等等。 可见，利用钩子可以实现许多特殊而有用的功能。因此，对于高级编程人员来说，掌握钩子的编程方法是很有必要的。 </p>
<p>钩子的类型 <br>　　 一． 按事件分类，有如下的几种常用类型 <br>　　 （1） 键盘钩子和低级键盘钩子可以监视各种键盘消息。 <br>　　 （2） 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。 <br>　　 （3） 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。 <br>　　 （4） 日志钩子可以记录从系统消息队列中取出的各种事件消息。 <br>　　 （5） 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。 <br>　　 此外，还有一些特定事件的钩子提供给我们使用，不一一列举。 <br>下面描述常用的Hook类型： <br>1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks <br>WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC Hook子程，并且在窗口过程处理完消息之后调用WH_CALLWNDPRO <br>CRET Hook子程。WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构，再传递到Hook子程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值，同样也包括了与这个消息关联的消息参数。 </p>
<p>2、WH_CBT Hook <br>在以下事件之前，系统都会调用WH_CBT Hook子程，这些事件包括： <br>1. 激活，建立，销毁，最小化，最大化，移动，改变尺寸等窗口事件； <br>2. 完成系统指令； <br>3. 来自系统消息队列中的移动鼠标，键盘事件； <br>4. 设置输入焦点事件； <br>5. 同步系统消息队列事件。 <br>Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。 <br>3、WH_DEBUG Hook <br>在系统调用系统中与其他Hook关联的Hook子程之前，系统会调用WH_DEBUG Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。 <br>4、WH_FOREGROUNDIDLE Hook <br>当应用程序的前台线程处于空闲状态时，可以使用WH_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时，系统就会调用WH_FOREGROUNDIDLE Hook子程。 <br>5、WH_GETMESSAGE Hook <br>应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入，以及其他发送到消息队列中的消息。 <br>6、WH_JOURNALPLAYBACK Hook <br>WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装，正常的鼠标和键盘事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook，它不能象线程特定Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值，这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间（毫秒）。这就使Hook可以控制实时事件的回放。 WH_JOURNALPLAYBACK是system-wide local hooks，它們不會被注射到任何行程位址空間。（估计按键精灵是用这个hook做的） <br>7、WH_JOURNALRECORD Hook <br>WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的，可以使用这个Hook记录连续的鼠标和键盘事件，然后通过使用WH_JOURNALPLAYBACK Hook来回放。WH_JOURNALRECORD Hook是全局Hook，它不能象线程特定Hook一样使用。WH_JOURNALRECORD是system-wide local hooks，它們不會被注射到任何行程位址空間。 <br>8、WH_KEYBOARD Hook <br>在应用程序中，WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息，这些消息通过GetMessage or PeekMessage function返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。 <br>9、WH_KEYBOARD_LL Hook <br>WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。 <br>10、WH_MOUSE Hook <br>WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。 <br>11、WH_MOUSE_LL Hook <br>WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。 <br>12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks <br>WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单，滚动条，消息框，对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTER Hook只能监视传递到菜单，滚动条，消息框的消息，以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息，这等价于在主消息循环中过滤消息。通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数，应用程序能够在模式循环期间使用相同的代码去过滤消息，如同在主消息循环里一样。 <br>13、WH_SHELL Hook </p>
<p><br>外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时，系统调用WH_SHELL Hook子程。 <br>WH_SHELL 共有５钟情況： <br>1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁； <br>2. 当Taskbar需要重画某个按钮； <br>3. 当系统需要显示关于Taskbar的一个程序的最小化形式； <br>4. 当目前的键盘布局状态改变； <br>5. 当使用者按Ctrl+Esc去执行Task Manager（或相同级别的程序）。 <br>按照惯例，外壳应用程序都不接收WH_SHELL消息。所以，在应用程序能够接收WH_SHELL消息之前，应用程序必须调用SystemParametersInfo function注册它自己。 <br>以上是13种常用的hook类型！ <br>　　 二． 按使用范围分类，主要有线程钩子和系统钩子 <br>　　 （1） 线程钩子监视指定线程的事件消息。 <br>　　 （2） 系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序，所以钩子函数必须放在独立的动态链接库(DLL) <br>中。这是系统钩子和线程钩子很大的不同之处。 <br>　　 几点需要说明的地方： <br>　　 （1） 如果对于同一事件（如鼠标消息）既安装了线程钩子又安装了系统钩子，那么系统会自动先调用线程钩子，然后调用系统钩子。 <br>　　 （2） 对同一事件消息可安装多个钩子处理过程，这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始，而最早安装的钩子放在最后，也就是后加入的先获得控制权。 <br>　　 （3） 钩子特别是系统钩子会消耗消息处理时间，降低系统性能。只有在必要的时候才安装钩子，在使用完毕后要及时卸载。 <br>编写钩子程序 </p>
<p><br>编写钩子程序的步骤分为三步：定义钩子函数、安装钩子和卸载钩子。 <br>　　 1．定义钩子函数 <br>　　 钩子函数是一种特殊的回调函数。钩子监视的特定事件发生后，系统会调用钩子函数进行处理。不同事件的钩子函数的形式是各不相同的。下面以鼠标钩子函数举例说明钩子函数的原型： <br>LRESULT CALLBACK HookProc(int nCode ,WPARAM wParam,LPARAM lParam) <br>参数wParam和 lParam包含所钩消息的信息，比如鼠标位置、状态，键盘按键等。nCode包含有关消息本身的信息，比如是否从消息队列中移出。 <br>我们先在钩子函数中实现自定义的功能，然后调用函数 CallNextHookEx.把钩子信息传递给钩子链的下一个钩子函数。CallNextHookEx.的原型如下： <br>LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam ) <br>参数 hhk是钩子句柄。nCode、wParam和lParam 是钩子函数。 <br>当然也可以通过直接返回TRUE来丢弃该消息，就阻止了该消息的传递。 <br>2．安装钩子 <br>　　 在程序初始化的时候，调用函数SetWindowsHookEx安装钩子。其函数原型为： <br>HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORD dwThreadId ) <br>参数idHook表示钩子类型，它是和钩子函数类型一一对应的。比如，WH_KEYBOARD表示安装的是键盘钩子，WH_MOUSE表示是鼠标钩子等等。 <br>　　 Lpfn是钩子函数的地址。 <br>　　 HMod是钩子函数所在的实例的句柄。对于线程钩子，该参数为NULL；对于系统钩子，该参数为钩子函数所在的DLL句柄。 <br>　　 dwThreadId 指定钩子所监视的线程的线程号。对于全局钩子，该参数为NULL。 <br>　　 SetWindowsHookEx返回所安装的钩子句柄。 <br>　　 3．卸载钩子 <br>　　 当不再使用钩子时，必须及时卸载。简单地调用函数 BOOL UnhookWindowsHookEx( HHOOK hhk)即可。 </p>
<p>值得注意的是线程钩子和系统钩子的钩子函数的位置有很大的差别。线程钩子一般在当前线程或者当前线程派生的线程内，而系统钩子必须放在独立的动态链接库中，实现起来要麻烦一些。 </p>
<p>线程钩子的编程实例： <br>　　 按照上面介绍的方法实现一个线程级的鼠标钩子。钩子跟踪当前窗口鼠标移动的位置变化信息。并输出到窗口。 <br>　　 (1)在VC＋＋6.0中利用MFC <br>APPWizard（EXE）生成一个不使用文档/视结构的单文档应用mousehook。打开childview.cpp文件，加入全局变量： <br>HHOOK hHook;//鼠标钩子句柄 <br>CPoint point;//鼠标位置信息 <br>CChildView ＊pView; <br>// 鼠标钩子函数用到的输出窗口指针 </p>
<p>　　 在CChildView::OnPaint()添加如下代码： <br>CPaintDC dc(this); <br>char str[256]; <br>sprintf(str,&#8220;x=％d,y=％d",point.x,point.y); <br>//构造字符串 <br>dc.TextOut(0,0,str); //显示字符串 </p>
<p>　　 (2)childview.cpp文件中定义全局的鼠标钩子函数。 <br>LRESULT CALLBACK MouseProc <br>(int nCode, WPARAM wParam, LPARAM lParam) <br>{//是鼠标移动消息 <br>if(wParam==WM_MOUSEMOVE||wParam <br>==WM_NCMOUSEMOVE) <br>{ <br>point=((MOUSEHOOKSTRUCT ＊)lParam)－&gt;pt; <br>//取鼠标信息 <br>pView－&gt;Invalidate(); //窗口重画 <br>} <br>return CallNextHookEx(hHook,nCode,wParam,lParam); <br>//传递钩子信息 <br>} <br>(3)CChildView类的构造函数中安装钩子。 <br>CChildView::CChildView() <br>{ <br>pView=this;//获得输出窗口指针 <br>hHook=SetWindowsHookEx(WH_MOUSE,MouseProc,0,</p>
<p>GetCurrentThreadId()); <br>} <br>(4)CChildView类的析构函数中卸载钩子。 <br>CChildView::～CChildView() <br>{ <br>if(hHook) <br>UnhookWindowsHookEx(hHook); <br>} </p>
<p><br>系统钩子的编程实例： <br>由于系统钩子要用到dll，所以先介绍下win32 dll的特点： <br>Win32 DLL与 Win16 DLL有很大的区别，这主要是由操作系统的设计思想决定的。一方面，在Win16 DLL中程序入口点函数和出口点函数（LibMain和WEP）是分别实现的；而在Win32 DLL中却由同一函数DLLMain来实现。无论何时，当一个进程或线程载入和卸载DLL时，都要调用该函数，它的原型是BOOL WINAPI DllMain <br>(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);，其中，第一个参数表示DLL的实例句柄；第三个参数系统保留；这里主要介绍一下第二个参数，它有四个可能的 值：DLL_PROCESS_ATTACH（进程载入），DLL_THREAD_ATTACH（线程载入），DLL_THREAD_DETACH（线程卸 载），DLL_PROCESS_DETACH（进程卸载），在DLLMain函数中可以对传递进来的这个参数的值进行判别，并根据不同的参数值对DLL进 行必要的初始化或清理工作。举个例子来说，当有一个进程载入一个DLL时，系统分派给DLL的第二个参数为DLL_PROCESS_ATTACH，这时， 你可以根据这个参数初始化特定的数据。另一方面，在Win16环境下，所有应用程序都在同一地址空间；而在Win32环境下，所有应用程序都有自己的私有 空间，每个进程的空间都是相互独立的，这减少了应用程序间的相互影响，但同时也增加了编程的难度。大家知道，在Win16环境中，DLL的全局数据对每个 载入它的进程来说都是相同的；而在Win32环境中，情况却发生了变化，当进程在载入DLL时，系统自动把DLL地址映射到该进程的私有空间，而且也复制 该DLL的全局数据的一份拷贝到该进程空间，也就是说每个进程所拥有的相同的DLL的全局数据其值却并不一定是相同的。因此，在Win32环境下要想在多 个进程中共享数据，就必须进行必要的设置。亦即把这些需要共享的数据分离出来，放置在一个独立的数据段里，并把该段的属性设置为共享。 <br>在 VC6中有三种形式的MFC DLL（在该DLL中可以使用和继承已有的MFC类)可供选择，即Regular statically linked to MFC DLL（标准静态链接MFC DLL）和Regular using the shared MFC DLL（标准动态链接MFC DLL）以及Extension MFC DLL（扩展MFC DLL）。第一种DLL的特点是，在编译时把使用的MFC代码加入到DLL中，因此，在使用该程序时不需要其他MFC动态链接类库的存在，但占用磁盘空间 比较大；第二种DLL的特点是，在运行时，动态链接到MFC类库，因此减少了空间的占用，但是在运行时却依赖于MFC动态链接类库；这两种DLL既可以被 MFC程序使用也可以被Win32程序使用。第三种DLL的特点类似于第二种，做为MFC类库的扩展，只能被MFC程序使用。 <br>下面说说在VC6中全局共享数据的实现 <br>　　 在主文件中，用#pragma data_seg建立一个新的数据段并定义共享数据，其具体格式为： <br>　　 #pragma data_seg （"shareddata") <br>　　 HWND sharedwnd=NULL;//共享数据 <br>　　 #pragma data_seg() <br>　　 仅定义一个数据段还不能达到共享数据的目的，还要告诉编译器该段的属性，有两种方法可以实现该目的（其效果是相同的），一种方法是在.DEF文件中加入如下语句： <br>SETCTIONS shareddata READ WRITE SHARED <br>　　 另一种方法是在项目设置链接选项中加入如下语句： <br>　　 /SECTION:shareddata,rws <br>好了，准备知识已经学完了，让我们开始编写个全局的钩子程序吧！ </p>
<p><br>由于全局钩子函数必须包含在动态链接库中，所以本例由两个程序体来实现。 <br>1．建立钩子Mousehook.DLL <br>　　 (1)选择MFC AppWizard(DLL)创建项目Mousehook； <br>　　 (2)选择MFC Extension DLL（共享MFC拷贝）类型； <br>　　 (3)由于VC5没有现成的钩子类，所以要在项目目录中创建Mousehook.h文件，在其中建立钩子类： <br>　　 class AFX_EXT_CLASS Cmousehook:public CObject <br>　　 { <br>　　 public: <br>　　 Cmousehook(); <br>　　 //钩子类的构造函数 <br>　　 ~Cmousehook(); <br>　　 //钩子类的析构函数 <br>　　 BOOL starthook(HWND hWnd); <br>　　 //安装钩子函数 <br>　　 BOOL stophook(); <br>　　 卸载钩子函数 <br>　　 }; <br>　　 (4)在Mousehook.app文件的顶部加入#include"Mousehook.h"语句； <br>　　 (5)加入全局共享数据变量： <br>　　 #pragma data_seg("mydata") <br>　　 HWND glhPrevTarWnd=NULL; <br>　　 //上次鼠标所指的窗口句柄 <br>　　 HWND glhDisplayWnd=NULL; <br>　　 //显示目标窗口标题编辑框的句柄 <br>　　 HHOOK glhHook=NULL; <br>　　 //安装的鼠标钩子句柄 <br>　　 HINSTANCE glhInstance=NULL; <br>　　 //DLL实例句柄 <br>　　 #pragma data_seg() <br>　　 (6)在DEF文件中定义段属性： <br>　　 SECTIONS <br>　　 mydata READ WRITE SHARED <br>　　 (7)在主文件Mousehook.cpp的DllMain函数中加入保存DLL实例句柄的语句： <br>　　 DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) <br>　　 { <br>　　 //如果使用lpReserved参数则删除下面这行 <br>　　 UNREFERENCED_PARAMETER(lpReserved); <br>　　 if (dwReason == DLL_PROCESS_ATTACH) <br>　　 { <br>　　 TRACE0("MOUSEHOOK.DLL Initializing!\n"); <br>　　 //扩展DLL仅初始化一次 <br>　　 if (!AfxInitExtensionModule(MousehookDLL, hInstance)) <br>　　 return 0; <br>　　 new CDynLinkLibrary(MousehookDLL); <br>　　 //把DLL加入动态MFC类库中 <br>　　 glhInstance=hInstance; <br>　　 //插入保存DLL实例句柄 <br>　　 } <br>　　 else if (dwReason == DLL_PROCESS_DETACH) <br>　　 { <br>　　 TRACE0("MOUSEHOOK.DLL Terminating!\n"); <br>　　 //终止这个链接库前调用它 <br>　　 AfxTermExtensionModule(MousehookDLL); <br>　　 } <br>　　 return 1; <br>　　 } <br>　　 (8)类Cmousehook的成员函数的具体实现： <br>　　 Cmousehook::Cmousehook() <br>　　 //类构造函数 <br>　　 { <br>　　 } <br>　　 Cmousehook::~Cmousehook() <br>　　 //类析构函数 <br>　　 { <br>　　 stophook(); <br>　　 } <br>　　 BOOL Cmousehook::starthook(HWND hWnd) <br>　　 //安装钩子并设定接收显示窗口句柄 <br>　　 { <br>　　 BOOL bResult=FALSE; <br>　　 glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0); <br>　　 if(glhHook!=NULL) <br>　　 bResult=TRUE; <br>　　 glhDisplayWnd=hWnd; <br>　　 //设置显示目标窗口标题编辑框的句柄 <br>　　 return bResult; <br>　　 } <br>　　 BOOL Cmousehook::stophook() <br>　　 //卸载钩子 <br>　　 { <br>　　 BOOL bResult=FALSE; <br>　　 if(glhHook) <br>　　 { <br>　　 bResult= UnhookWindowsHookEx(glhHook); <br>　　 if(bResult) <br>　　 { <br>　　 glhPrevTarWnd=NULL; <br>　　 glhDisplayWnd=NULL;//清变量 <br>　　 glhHook=NULL; <br>　　 } <br>　　 } <br>　　 return bResult; <br>　　 } <br>　　 (9)钩子函数的实现： <br>　　 LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam) <br>　　 { <br>　　 LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *) lparam; <br>　　 if (nCode&gt;=0) <br>　　 { <br>　　 HWND glhTargetWnd=pMouseHook-&gt;hwnd; <br>　　 //取目标窗口句柄 <br>　　 HWND ParentWnd=glhTargetWnd; <br>　　 while (ParentWnd !=NULL) <br>　　 { <br>　　 glhTargetWnd=ParentWnd; <br>　　 ParentWnd=GetParent(glhTargetWnd); <br>　　 //取应用程序主窗口句柄 <br>　　 } <br>　　 if(glhTargetWnd!=glhPrevTarWnd) <br>　　 { <br>　　 char szCaption[100]; <br>　　 GetWindowText(glhTargetWnd,szCaption,100); <br>　　 //取目标窗口标题 <br>　　 if(IsWindow(glhDisplayWnd)) <br>　　 SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption); <br>　　 glhPrevTarWnd=glhTargetWnd; <br>　　 //保存目标窗口 <br>　　 } <br>　　 } <br>　　 return CallNextHookEx(glhHook,nCode,wparam,lparam); <br>　　 //继续传递消息 <br>　　 } <br>　　 (10)编译项目生成mousehook.dll。 </p>
<p>2．创建钩子可执行程序 <br>　　 (1)用MFC的AppWizard(EXE)创建项目Mouse； <br>　　 (2)选择&#8220;基于对话应用&#8221;并按下&#8220;完成&#8221;键； <br>　　 (3)编辑对话框，删除其中原有的两个按钮，加入静态文本框和编辑框，用鼠标右键点击静态文本框，在弹出的菜单中选择&#8220;属性&#8221;，设置其标题为&#8220;鼠标所在的窗口标题&#8221;； </p>
　　 (4)在Mouse.h中加入对Mousehook.h的包含语句#Include"..\Mousehook\Mousehook.h"； <br>　　 (5)在CMouseDlg.h的CMouseDlg类定义中添加私有数据成员： <br>　　 CMouseHook m_hook;//加入钩子类作为数据成员 <br>　　 (6)修改CmouseDlg::OnInitDialog()函数： <br>　　 BOOL CMouseDlg::OnInitDialog() <br>　　 { <br>CDialog::OnInitDialog(); <br>　　 ASSERT((IDM_ABOUTBOX &amp; 0xFFF0) == IDM_ABOUTBOX); <br>　　 ASSERT(IDM_ABOUTBOX &lt;0xF000); <br>　　 CMenu* pSysMenu = GetSystemMenu(FALSE); <br>　　 if (pSysMenu != NULL) <br>　　 { <br>　　 CString strAboutMenu; <br>　　 strAboutMenu.LoadString(IDS_ABOUTBOX); <br>　　 if (!strAboutMenu.IsEmpty()) <br>　　 { <br>　　 pSysMenu-&gt;AppendMenu(MF_SEPARATOR); <br>　　 pSysMenu-&gt;AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); <br>　　 } <br>　　 } <br>　　 SetIcon(m_hIcon, TRUE);//Set big icon <br>　　 SetIcon(m_hIcon, FALSE);//Set small icon <br>　　 //TODO: Add extra initialization here <br>　　 CWnd * pwnd=GetDlgItem(IDC_EDIT1); <br>　　 //取得编辑框的类指针 <br>　　 m_hook.starthook(pwnd-&gt;GetSafeHwnd()); <br>　　 //取得编辑框的窗口句柄并安装钩子 <br>　　 return TRUE; <br>　　 //return TRUE unless you set the focus to a control <br>　　 } <br>　　 (7)链接DLL库，即把..\Mousehook\debug\Mousehook.lib加入到项目设置链接标签中； <br>　　 (8)编译项目生成可执行文件； <br>　　 (9)把Mousehook.DLL拷贝到..\mouse\debug目录中； <br>　　 (10)先运行几个可执行程序，然后运行Mouse.exe程序，把鼠标在不同窗口中移动，在Mouse.exe程序窗口中的编辑框内将显示出鼠标所在的应用程序主窗口的标题。<br>
<img src ="http://www.cppblog.com/iniwf/aggbug/112529.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> 2010-04-14 10:11 <a href="http://www.cppblog.com/iniwf/archive/2010/04/14/112529.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>驱动和应用层的三种通信方式</title><link>http://www.cppblog.com/iniwf/archive/2010/04/07/111829.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Wed, 07 Apr 2010 02:08:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/07/111829.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/111829.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/07/111829.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/111829.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/111829.html</trackback:ping><description><![CDATA[转自：<a href="http://bbs.pediy.com/showthread.php?t=57666">http://bbs.pediy.com/showthread.php?t=57666</a><br><font color=#333333>标 题:</font><font color=#000000> 【原创】【成果3.5】驱动和应用层的三种通信方式</font><font color=#666666><br><font color=#333333>作 者:</font> <font color=#000000>sislcb</font><br><font color=#333333>时 间:</font> 2008-01-04,11:57:54<br><font color=#333333>链 接:</font> http://bbs.pediy.com/showthread.php?t=57666<br></font><br>驱动程序和客户应用程序经常需要进行数据交换，但我们知道驱动程序和客户应用程序可能不在同一个地址空间，因此操作系统必须解决两者之间的数据交换。<br>驱动层和应用层通信，主要是靠DeviceIoControl函数，下面是该函数的原型：<br>BOOL&nbsp;DeviceIoControl&nbsp;(&nbsp;<br>HANDLE&nbsp;hDevice,&nbsp;//&nbsp;设备句柄&nbsp;<br>DWORD&nbsp;dwIoControlCode,&nbsp;//&nbsp;IOCTL请求操作代码&nbsp;<br>LPVOID&nbsp;lpInBuffer,&nbsp;//&nbsp;输入缓冲区地址&nbsp;<br>DWORD&nbsp;nInBufferSize,&nbsp;//&nbsp;输入缓冲区大小&nbsp;<br>LPVOID&nbsp;lpOutBuffer,&nbsp;//&nbsp;输出缓冲区地址&nbsp;<br>DWORD&nbsp;nOutBufferSize,&nbsp;//&nbsp;输出缓冲区大小&nbsp;<br>LPDWORD&nbsp;lpBytesReturned,&nbsp;//&nbsp;存放返回字节数的指针&nbsp;<br>LPOVERLAPPED&nbsp;lpOverlapped&nbsp;//&nbsp;用于同步操作的Overlapped结构体指针&nbsp;<br>);<br><br>dwIoControlCode<br>要进行操作的控制码。驱动程序可以通过CTL_CODE宏来组合定义一个控制码，并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层，irpStack-&gt;Parameters.DeviceIoControl.IoControlCode表示了这个控制码。<br><br>IOCTL请求有四种缓冲策略，下面一一介绍。&nbsp;<br>1、&nbsp;输入输出缓冲I/O(METHOD_BUFFERED)<br>2、&nbsp;直接输入缓冲输出I/O(METHOD_IN_DIRECT)<br>3、&nbsp;缓冲输入直接输出I/O(METHOD_OUT_DIRECT)<br>4、&nbsp;上面三种方法都不是(METHOD_NEITHER)<br><br>为了对这些类型更详细的描述，请看msdn上的解释，我抄录如下：<br><br><strong>"缓冲"方法</strong>(METHOD_BUFFERED)<br>备注：在下面的讨论中，"输入"表示数据从用户模式的应用程序到驱动程序，"输出"表示数据从驱动程序到应用程序。<br><br>对于读取请求，I/O&nbsp;管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区。IRP&nbsp;中的&nbsp;SystemBuffer&nbsp;字段包含系统地址。UserBuffer&nbsp;字段包含初始的用户缓冲区地址。当完成请求时，I/O&nbsp;管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。对于写入请求，会分配一个系统缓冲区并将&nbsp;SystemBuffer&nbsp;设置为地址。用户缓冲区的内容会被复制到系统缓冲区，但是不设置&nbsp;UserBuffer。对于&nbsp;IOCTL&nbsp;请求，会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区，并将&nbsp;SystemBuffer&nbsp;设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer&nbsp;字段设置为用户模式输出缓冲区地址。内核模式驱动程序应当只使用系统缓冲区，且不应使用&nbsp;UserBuffer&nbsp;中存储的地址。<br><br>对于&nbsp;IOCTL，驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时，I/O&nbsp;系统将输出数据从系统缓冲区复制到用户缓冲区。<br><br><strong>"直接"方法</strong>(METHOD_IN/OUT_DIRECT)<br>对于读取和写入请求，用户模式缓冲区会被锁定，并且会创建一个内存描述符列表&nbsp;(MDL)。MDL&nbsp;地址会存储在&nbsp;IRP&nbsp;的&nbsp;MdlAddress&nbsp;字段中。SystemBuffer&nbsp;和&nbsp;UserBuffer&nbsp;均没有任何含义。但是，驱动程序不应当更改这些字段的值。<br><br>对于&nbsp;IOCTL&nbsp;请求，如果在&nbsp;METHOD_IN_DIRECT&nbsp;和&nbsp;METHOD_OUT_DIRECT&nbsp;中同时有一个输出缓冲区，则分配一个系统缓冲区（SystemBuffer&nbsp;又有了地址）并将输入数据复制到其中。如果有一个输出缓冲区，且它被锁定，则会创建&nbsp;MDL&nbsp;并设置&nbsp;MdlAddress。UserBuffer&nbsp;字段没有任何含义。<br><br><strong>"两者都不"方法</strong>(METHOD_NEITHER)<br>对于读取和写入请求，UserBuffer&nbsp;字段被设置为指向初始的用户缓冲区。不执行任何其他操作。SystemAddress&nbsp;和&nbsp;MdlAddress&nbsp;没有任何含义。对于&nbsp;IOCTL&nbsp;请求，I/O&nbsp;管理器将&nbsp;UserBuffer&nbsp;设置为初始的用户输出缓冲区，而且，它将当前&nbsp;I/O&nbsp;栈位置的&nbsp;Parameters.DeviceIoControl.Type3InputBuffer&nbsp;设置为用户输入缓冲区。利用该&nbsp;I/O&nbsp;方法，由驱动程序来确定如何处理缓冲区：分配系统缓冲区或创建&nbsp;MDL。<br><br>通常，驱动程序在访问用户数据时不应当将&nbsp;UserBuffer&nbsp;字段用作地址，即使当用户缓冲区被锁定时也是如此。这是由于在调用驱动程序时，在系统中可能看不到调用用户的地址空间。（对于该规则的一个例外是，在最高层驱动程序将&nbsp;IRP&nbsp;向下传递到较低层的驱动程序之前，它可能需要使用&nbsp;UserBuffer&nbsp;来复制数据。）如果使用"直接"或"两者都不"方法，在创建&nbsp;MDL&nbsp;之后，驱动程序可以使用&nbsp;MmGetSystemAddressForMdl&nbsp;函数来获取有效的系统地址以访问用户缓冲区。<br><br><br>在驱动层，依传输类型的不同，输入缓冲区的位置亦不同，见下表。<br>传输类型&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;位置<br>METHOD_IN_DIRECT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irp-&gt;AssociatedIrp.SystemBuffer<br>METHOD_OUT_DIRECT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irp-&gt;AssociatedIrp.SystemBuffer<br>METHOD_BUFFERED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irp-&gt;AssociatedIrp.SystemBuffer<br>METHOD_NEITHER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irpStack-&gt;Parameters.DeviceIoControl.Type3InputBuffer<br><br>在驱动层，依传输类型的不同，输出缓冲区的位置亦不同，见下表。<br>传输类型&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;位置<br>METHOD_IN_DIRECT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irp-&gt;MdlAddress<br>METHOD_OUT_DIRECT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irp-&gt;MdlAddress<br>METHOD_BUFFERED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irp-&gt;AssociatedIrp.SystemBuffer<br>METHOD_NEITHER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;irp-&gt;UserBuffer<br><br>所以只要确定了传输方式后，就可以根据各自的位置来读取和写入数据，从而实现应用层和驱动的通信。<br>下面看驱动层对ioctl控制码的处理代码：<br><br>
<div style="MARGIN: 5px 20px 20px">
<div style="MARGIN-BOTTOM: 2px" class=smallfont>代码:</div>
<pre style="BORDER-BOTTOM: 1px inset; TEXT-ALIGN: left; BORDER-LEFT: 1px inset; PADDING-BOTTOM: 6px; BACKGROUND-COLOR: #dedfdf; MARGIN: 0px; PADDING-LEFT: 6px; WIDTH: 850px; PADDING-RIGHT: 6px; OVERFLOW: auto; BORDER-TOP: 1px inset; BORDER-RIGHT: 1px inset; PADDING-TOP: 6px" dir=ltr>//METHOD_OUT_DIREC方式
NTSTATUS&nbsp;COMM_DirectOutIo(PIRP&nbsp;Irp,&nbsp;PIO_STACK_LOCATION&nbsp;pIoStackIrp,&nbsp;UINT&nbsp;*sizeofWrite)
{
&nbsp;&nbsp;&nbsp;&nbsp;NTSTATUS&nbsp;status&nbsp;=&nbsp;STATUS_UNSUCCESSFUL;
&nbsp;&nbsp;&nbsp;&nbsp;PVOID&nbsp;pInputBuffer,&nbsp;pOutputBuffer;
&nbsp;&nbsp;ULONG&nbsp;&nbsp;outputLength,&nbsp;inputLength;
&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_DirectOutIo\r\n");
&nbsp;&nbsp;&nbsp;&nbsp;outputLength&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.OutputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;inputLength&nbsp;&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.InputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;pInputBuffer&nbsp;=&nbsp;Irp-&gt;AssociatedIrp.SystemBuffer;
&nbsp;&nbsp;&nbsp;&nbsp;pOutputBuffer&nbsp;=&nbsp;NULL;
&nbsp;&nbsp;&nbsp;&nbsp;if(Irp-&gt;MdlAddress)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pOutputBuffer&nbsp;=&nbsp;MmGetSystemAddressForMdlSafe(Irp-&gt;MdlAddress,&nbsp;NormalPagePriority);
&nbsp;&nbsp;&nbsp;&nbsp;if(pInputBuffer&nbsp;&amp;&amp;&nbsp;pOutputBuffer)
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_DirectOutIo&nbsp;UserModeMessage&nbsp;=&nbsp;'%s'",&nbsp;pInputBuffer);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RtlCopyMemory(pOutputBuffer,&nbsp;pInputBuffer,&nbsp;outputLength);
&nbsp;&nbsp;&nbsp;&nbsp;*sizeofWrite&nbsp;=&nbsp;outputLength;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;status&nbsp;=&nbsp;STATUS_SUCCESS;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;status;
}
//&nbsp;METHOD_IN_DIRECT
NTSTATUS&nbsp;COMM_DirectInIo(PIRP&nbsp;Irp,&nbsp;PIO_STACK_LOCATION&nbsp;pIoStackIrp,&nbsp;UINT&nbsp;*sizeofWrite)
{
&nbsp;&nbsp;&nbsp;&nbsp;NTSTATUS&nbsp;status&nbsp;=&nbsp;STATUS_UNSUCCESSFUL;
&nbsp;&nbsp;&nbsp;&nbsp;PVOID&nbsp;pInputBuffer,&nbsp;pOutputBuffer;
&nbsp;&nbsp;ULONG&nbsp;&nbsp;outputLength,&nbsp;inputLength;
&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_DirectInIo\r\n");
&nbsp;&nbsp;&nbsp;&nbsp;outputLength&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.OutputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;inputLength&nbsp;&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.InputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;pInputBuffer&nbsp;=&nbsp;Irp-&gt;AssociatedIrp.SystemBuffer;
&nbsp;&nbsp;&nbsp;&nbsp;pOutputBuffer&nbsp;=&nbsp;NULL;
&nbsp;&nbsp;&nbsp;&nbsp;if(Irp-&gt;MdlAddress)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pOutputBuffer&nbsp;=&nbsp;MmGetSystemAddressForMdlSafe(Irp-&gt;MdlAddress,&nbsp;NormalPagePriority);
&nbsp;&nbsp;&nbsp;&nbsp;if(pInputBuffer&nbsp;&amp;&amp;&nbsp;pOutputBuffer)
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_DirectInIo&nbsp;UserModeMessage&nbsp;=&nbsp;'%s'",&nbsp;pInputBuffer);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RtlCopyMemory(pOutputBuffer,&nbsp;pInputBuffer,&nbsp;outputLength);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*sizeofWrite&nbsp;=&nbsp;outputLength;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;status&nbsp;=&nbsp;STATUS_SUCCESS;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;status;
}
//&nbsp;METHOD_BUFFERED
NTSTATUS&nbsp;COMM_BufferedIo(PIRP&nbsp;Irp,&nbsp;PIO_STACK_LOCATION&nbsp;pIoStackIrp,&nbsp;UINT&nbsp;*sizeofWrite)
{
&nbsp;&nbsp;&nbsp;&nbsp;NTSTATUS&nbsp;status&nbsp;=&nbsp;STATUS_UNSUCCESSFUL;
&nbsp;&nbsp;&nbsp;&nbsp;PVOID&nbsp;pInputBuffer,&nbsp;pOutputBuffer;
&nbsp;&nbsp;ULONG&nbsp;&nbsp;outputLength,&nbsp;inputLength;
&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_BufferedIo\r\n");
&nbsp;&nbsp;outputLength&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.OutputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;inputLength&nbsp;&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.InputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;pInputBuffer&nbsp;=&nbsp;Irp-&gt;AssociatedIrp.SystemBuffer;
&nbsp;&nbsp;&nbsp;&nbsp;pOutputBuffer&nbsp;=&nbsp;Irp-&gt;AssociatedIrp.SystemBuffer;
&nbsp;&nbsp;&nbsp;&nbsp;if(pInputBuffer&nbsp;&amp;&amp;&nbsp;pOutputBuffer)
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_BufferedIo&nbsp;UserModeMessage&nbsp;=&nbsp;'%s'",&nbsp;pInputBuffer);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RtlCopyMemory(pOutputBuffer,&nbsp;pInputBuffer,&nbsp;outputLength);
&nbsp;&nbsp;&nbsp;&nbsp;*sizeofWrite&nbsp;=&nbsp;outputLength;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;status&nbsp;=&nbsp;STATUS_SUCCESS;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;status;
}
//&nbsp;METHOD_NEITHER
NTSTATUS&nbsp;COMM_NeitherIo(PIRP&nbsp;Irp,&nbsp;PIO_STACK_LOCATION&nbsp;pIoStackIrp,&nbsp;UINT&nbsp;*sizeofWrite)
{
&nbsp;&nbsp;&nbsp;&nbsp;NTSTATUS&nbsp;status&nbsp;=&nbsp;STATUS_UNSUCCESSFUL;
&nbsp;&nbsp;&nbsp;&nbsp;PVOID&nbsp;pInputBuffer,&nbsp;pOutputBuffer;
&nbsp;&nbsp;ULONG&nbsp;&nbsp;outputLength,&nbsp;inputLength;
&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_NeitherIo\r\n");
&nbsp;&nbsp;outputLength&nbsp;&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.OutputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;inputLength&nbsp;&nbsp;&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.InputBufferLength;
&nbsp;&nbsp;&nbsp;&nbsp;pInputBuffer&nbsp;&nbsp;=&nbsp;pIoStackIrp-&gt;Parameters.DeviceIoControl.Type3InputBuffer;
&nbsp;&nbsp;&nbsp;&nbsp;pOutputBuffer&nbsp;=&nbsp;Irp-&gt;UserBuffer;
&nbsp;&nbsp;&nbsp;&nbsp;if(pInputBuffer&nbsp;&amp;&amp;&nbsp;pOutputBuffer)
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;DbgPrint("COMM_NeitherIo&nbsp;UserModeMessage&nbsp;=&nbsp;'%s'",&nbsp;pInputBuffer);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RtlCopyMemory(pOutputBuffer,&nbsp;pInputBuffer,&nbsp;outputLength);
&nbsp;&nbsp;&nbsp;&nbsp;*sizeofWrite&nbsp;=&nbsp;outputLength;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;status&nbsp;=&nbsp;STATUS_SUCCESS;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;status;
}
代码比较简单，都是取得输入的数据，然后把数据直接拷贝到输出，传输给应用层。
应用层的代码：
procedure&nbsp;TfrmMain.Send_Recv_Data(AInData:&nbsp;String;&nbsp;var&nbsp;AOutData:String;
&nbsp;&nbsp;IoctlCode:&nbsp;DWORD);
var
&nbsp;&nbsp;dwReturn:&nbsp;DWORD;
&nbsp;&nbsp;inData:array[0..1023]&nbsp;of&nbsp;char;
&nbsp;&nbsp;outData:array[0..1023]&nbsp;of&nbsp;char;
begin
&nbsp;&nbsp;StrPCopy(inData,&nbsp;AInData);
&nbsp;&nbsp;if&nbsp;m_hCommDevice&nbsp;&lt;&gt;&nbsp;0&nbsp;then
&nbsp;&nbsp;begin
&nbsp;&nbsp;&nbsp;&nbsp;DeviceIoControl(m_hCommDevice,&nbsp;IoctlCode,&nbsp;@inData,&nbsp;&nbsp;Length(inData),&nbsp;@outData,&nbsp;Length(outData),&nbsp;dwReturn,&nbsp;nil);
&nbsp;&nbsp;&nbsp;&nbsp;AOutData&nbsp;:=&nbsp;StrPas(@outData);
&nbsp;&nbsp;end;
end;
</pre>
</div>
上面是进行发送和接受的过程。<br>需要通信，只要如下做：<br>
<div style="MARGIN: 5px 20px 20px">
<div style="MARGIN-BOTTOM: 2px" class=smallfont>代码:</div>
<pre style="BORDER-BOTTOM: 1px inset; TEXT-ALIGN: left; BORDER-LEFT: 1px inset; PADDING-BOTTOM: 6px; BACKGROUND-COLOR: #dedfdf; MARGIN: 0px; PADDING-LEFT: 6px; WIDTH: 850px; PADDING-RIGHT: 6px; OVERFLOW: auto; BORDER-TOP: 1px inset; BORDER-RIGHT: 1px inset; PADDING-TOP: 6px" dir=ltr>procedure&nbsp;TfrmMain.&nbsp;btnDirect_IN_IOClick&nbsp;(Sender:&nbsp;TObject);
var
&nbsp;&nbsp;outData:String;
begin
&nbsp;&nbsp;Send_Recv_Data(Trim(edtDirect_in_in.Text),&nbsp;outData,&nbsp;IOCTL_COMM_DIRECT_IN_IO);
&nbsp;&nbsp;edtDirect_in_out.Text&nbsp;:=&nbsp;outData;
end;
</pre>
</div>
这是&nbsp;direct_in方式通信，其他通信方式类似，大家可以参考代码了，这里就不列举了，由于代码比较简单，我就不多说了，大家还是看代码吧，很好明白。最后，给个测试图：<br><br>应用层：<br><img style="MARGIN: 2px; CURSOR: pointer" onmouseover=pointer border=0 alt="" src="http://bbs.pediy.com/attachment.php?attachmentid=10929&amp;d=1199418875" width=614 onload="if(this.width>screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('http://bbs.pediy.com/attachment.php?attachmentid=10929&amp;d=1199418875')}}"><br>驱动层:<br><img style="MARGIN: 2px; CURSOR: pointer" onmouseover=pointer border=0 alt="" src="http://bbs.pediy.com/attachment.php?attachmentid=10930&amp;d=1199418875" width=614 onload="if(this.width>screen.width*0.6) {this.width=screen.width*0.6;this.alt='';this.onmouseover=this.style.cursor='pointer'; this.onclick=function(){window.open('http://bbs.pediy.com/attachment.php?attachmentid=10930&amp;d=1199418875')}}"><br><br>代码：<br><a title="" href="http://bbs.pediy.com/attachment.php?attachmentid=10931&amp;d=1199419054"><font color=#000000>驱动和应用层通信.rar</font></a><a href="javascript:whodownloaded(10931)"><font color=#000000>[谁下载?]</font></a> 
<img src ="http://www.cppblog.com/iniwf/aggbug/111829.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> 2010-04-07 10:08 <a href="http://www.cppblog.com/iniwf/archive/2010/04/07/111829.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>File System Filter Driver Tutorial</title><link>http://www.cppblog.com/iniwf/archive/2010/04/02/111361.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Fri, 02 Apr 2010 01:23:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/02/111361.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/111361.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/02/111361.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/111361.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/111361.html</trackback:ping><description><![CDATA[转自：<a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx">http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx</a><br><br>
<ul class=download>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/FsFilter.zip">Download source code - 9.42 KB</a> </li>
</ul>
<h2>Contents</h2>
<ol>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#_Toc244423396">Introduction</a>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#_Toc244423398">Creating a simple File System Filter Driver</a>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#_Toc244423419">How to install a driver</a>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#_Toc244423426">Running a sample</a>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#_Toc244423427">Improvements</a>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#_Toc244423428">Conclusion</a>
    <li><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#_Toc244423429">Useful references</a> </li>
</ol>
<h2><a id=_Toc244423396 name=_Toc244423396>Introduction</a></h2>
<p>This tutorial will show you how to develop a simple file system filter driver. The demo driver will print the names of opening files to the debug output.</p>
<p>The article requires basic Windows driver development and C/C++ knowledge. However, it may also be interesting to people without Windows driver development experience.</p>
<h3><a id=_Toc244423397 name=_Toc244423397>What is a file system filter driver?</a></h3>
<p>A file system filter driver is called on every file system I/O operation (create, read, write, rename, and etc.), and thus it can modify the file system behavior. File system filter drivers are almost similar to legacy drivers, but they require some special steps to do. Such drivers are used by anti-viruses, security, backup, and snapshot software.</p>
<h2><a id=_Toc244423398 name=_Toc244423398>Creating a simple File System Filter Driver</a></h2>
<h3><a id=_Toc244423399 name=_Toc244423399>Before starting</a></h3>
<p>To build a driver, you need WDK or the IFS Kit. You can get them from <a href="http://www.microsoft.com/whdc/devtools/downloads.mspx">Microsoft&#8217;s website</a>. Also, you have to set an environment variable <code>%WINDDK%</code> to the path where you have installed the WDK/IFS Kit.</p>
<p><strong>Be careful:</strong> Even a small error in the driver may cause a BSOD or system instability.</p>
<h3><a id=_Toc244423400 name=_Toc244423400>Main.c</a></h3>
<h4><a id=_Toc244423401 name=_Toc244423401>Driver entry</a></h4>
<p>This is the entry point of any driver. The first thing that we do is to store <code>DriverObject</code> to a global variable (we will need it later).</p>
<div style="WIDTH: 100%" id=premain0 class=small-text><img style="CURSOR: pointer" id=preimg0 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="0"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse0 preid="0"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="0"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre0 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>/////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> Global data
</span>
PDRIVER_OBJECT   g_fsFilterDriverObject = NULL;
<span class=code-SummaryComment>///</span><span class=code-comment>/////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> DriverEntry - Entry point of the driver
</span>
NTSTATUS DriverEntry(
__inout PDRIVER_OBJECT  DriverObject,
__in    PUNICODE_STRING RegistryPath
)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG    i      = <span class=code-digit>0</span>;
<span class=code-comment>//</span><span class=code-comment>ASSERT(FALSE); // This will break to debugger
</span>
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment> Store our driver object.
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
g_fsFilterDriverObject = DriverObject;
...
}</pre>
<h4><a id=_Toc244423402 name=_Toc244423402>Set IRP dispatch table</a></h4>
<p>The next step is to populate the IRP dispatch table with function pointers to IRP handlers. In our filter driver, there is a generic pass-through IRP handler (which sends the request further). And, we will need a handler for <code>IRP_MJ_CREATE</code> to retrieve the names of the opening files. The implementation of the IRP handlers will be described later.</p>
<div style="WIDTH: 100%" id=premain1 class=small-text><img style="CURSOR: pointer" id=preimg1 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="1"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse1 preid="1"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="1"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre1 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> DriverEntry - Entry point of the driver
</span>
NTSTATUS DriverEntry(
__inout PDRIVER_OBJECT  DriverObject,
__in    PUNICODE_STRING RegistryPath
)
{
...
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment>  Initialize the driver object dispatch table.
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
<span class=code-keyword>for</span> (i = <span class=code-digit>0</span>; i <span class=code-keyword>&lt;</span>= IRP_MJ_MAXIMUM_FUNCTION; ++i)
{
DriverObject-<span class=code-keyword>&gt;</span>MajorFunction[i] = FsFilterDispatchPassThrough;
}
DriverObject-<span class=code-keyword>&gt;</span>MajorFunction[IRP_MJ_CREATE] = FsFilterDispatchCreate;
...
}</pre>
<h4><a id=_Toc244423403 name=_Toc244423403>Set Fast-IO dispatch table</a></h4>
<p>A file system filter driver must have the fast-IO dispatch table. If you&#8217;ve forgot to set up the fast-IO dispatch table, it will lead to system crash. Fast-IO is an alternative way to initiate I/O operations (and it&#8217;s faster than IRP). Fast-IO operations are always synchronous. If the Fast-IO handler returns <code>FALSE</code>, it means the Fast-IO way is impossible and an IRP will be created.</p>
<div style="WIDTH: 100%" id=premain2 class=small-text><img style="CURSOR: pointer" id=preimg2 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="2"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse2 preid="2"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="2"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre2 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> Global data
</span>
FAST_IO_DISPATCH g_fastIoDispatch =
{
<span class=code-keyword>sizeof</span>(FAST_IO_DISPATCH),
FsFilterFastIoCheckIfPossible,
...
};
<span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> DriverEntry - Entry point of the driver
</span>
NTSTATUS DriverEntry(
__inout PDRIVER_OBJECT  DriverObject,
__in    PUNICODE_STRING RegistryPath
)
{
...
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment> Set fast-io dispatch table.
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
DriverObject-<span class=code-keyword>&gt;</span>FastIoDispatch = &amp;g_fastIoDispatch;
...
}</pre>
<h4><a id=_Toc244423404 name=_Toc244423404>Register a notification for file system changes</a></h4>
<p>We should track the file system being activated/deactivated to perform attaching/detaching of our file system filter driver. How to start tracking file system changes is shown below.</p>
<div style="WIDTH: 100%" id=premain3 class=small-text><img style="CURSOR: pointer" id=preimg3 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="3"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse3 preid="3"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="3"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre3 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> DriverEntry - Entry point of the driver
</span>
NTSTATUS DriverEntry(
__inout PDRIVER_OBJECT  DriverObject,
__in    PUNICODE_STRING RegistryPath
)
{
...
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment>  Registered callback routine for file system changes.
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
status = IoRegisterFsRegistrationChange(DriverObject,
FsFilterNotificationCallback);
<span class=code-keyword>if</span> (!NT_SUCCESS(status))
{
<span class=code-keyword>return</span> status;
}
...
}</pre>
<h4><a id=_Toc244423405 name=_Toc244423405>Set</a> driver unload routine</h4>
<p>The last part of the driver initialization sets an unload routine. Setting the driver unload routine makes the driver unloadable, and you can load/unload it multiple times without system restart. However, this driver is made unloadable only for debugging purpose, because file system filters can&#8217;t be unloaded safely. <strong>Never do this in production code.</strong></p>
<div style="WIDTH: 100%" id=premain4 class=small-text><img style="CURSOR: pointer" id=preimg4 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="4"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse4 preid="4"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="4"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre4 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> DriverEntry - Entry point of the driver
</span>
NTSTATUS DriverEntry(
__inout PDRIVER_OBJECT  DriverObject,
__in    PUNICODE_STRING RegistryPath
)
{
...
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment> Set driver unload routine (debug purpose only).
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
DriverObject-<span class=code-keyword>&gt;</span>DriverUnload = FsFilterUnload;
<span class=code-keyword>return</span> STATUS_SUCCESS;
}</pre>
<h4><a id=_Toc244423406 name=_Toc244423406>Driver unload implementation</a></h4>
<p>Driver unload routine is responsible for cleaning up and deallocation of resources. First of all, unregister the notification for file system changes.</p>
<div style="WIDTH: 100%" id=premain5 class=small-text><img style="CURSOR: pointer" id=preimg5 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="5"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse5 preid="5"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="5"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre5 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> Unload routine
</span>
VOID FsFilterUnload(
__in PDRIVER_OBJECT DriverObject
)
{
...
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment>  Unregistered callback routine for file system changes.
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
IoUnregisterFsRegistrationChange(DriverObject, FsFilterNotificationCallback);
...
}</pre>
<p>Then loop through the devices we created, detach and delete them. Wait for 5 seconds to let all outstanding IRPs to be completed. As it was mentioned before, this is a debug only solution. It works in the majority of cases, but there is no guarantee for all.</p>
<div style="WIDTH: 100%" id=premain6 class=small-text><img style="CURSOR: pointer" id=preimg6 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="6"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse6 preid="6"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="6"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre6 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> Unload routine
</span>
VOID FsFilterUnload(
__in PDRIVER_OBJECT DriverObject
)
{
...
<span class=code-keyword>for</span> (;;)
{
IoEnumerateDeviceObjectList(
DriverObject,
devList,
<span class=code-keyword>sizeof</span>(devList),
&amp;numDevices);
<span class=code-keyword>if</span> (<span class=code-digit>0</span> == numDevices)
{
<span class=code-keyword>break</span>;
}
numDevices = min(numDevices, RTL_NUMBER_OF(devList));
<span class=code-keyword>for</span> (i = <span class=code-digit>0</span>; i <span class=code-keyword>&lt;</span> numDevices; ++i)
{
FsFilterDetachFromDevice(devList[i]);
ObDereferenceObject(devList[i]);
}
KeDelayExecutionThread(KernelMode, FALSE, &amp;interval);
}
}</pre>
<h3><a id=_Toc244423407 name=_Toc244423407>IrpDispatch.c</a></h3>
<h4><a id=_Toc244423408 name=_Toc244423408>Dispatch pass-through</a></h4>
<p>This IRP handler does nothing except passing requests further to the next driver. We have the next driver object stored in our device extension.</p>
<div style="WIDTH: 100%" id=premain7 class=small-text><img style="CURSOR: pointer" id=preimg7 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="7"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse7 preid="7"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="7"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre7 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>//////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> PassThrough IRP Handler
</span>
NTSTATUS FsFilterDispatchPassThrough(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP           Irp
)
{
PFSFILTER_DEVICE_EXTENSION pDevExt =
(PFSFILTER_DEVICE_EXTENSION)DeviceObject-<span class=code-keyword>&gt;</span>DeviceExtension;
IoSkipCurrentIrpStackLocation(Irp);
<span class=code-keyword>return</span> IoCallDriver(pDevExt-<span class=code-keyword>&gt;</span>AttachedToDeviceObject, Irp);
}</pre>
<h4><a id=_Toc244423409 name=_Toc244423409>Dispatch create</a></h4>
<p>This IRP handler is invoked on every create file operation. We will grab a filename from <code>PFILE_OBJECT</code> and print it to the debug output. Then, we call the pass-through handler described above. Pay attention to the fact that a valid file name exists in <code>PFILE_OBJECT</code> only while the create file operation is being performed! Also there are relative opens, and opens by ID. Retrieving file names in those cases is beyond the scope of this article.</p>
<div style="WIDTH: 100%" id=premain8 class=small-text><img style="CURSOR: pointer" id=preimg8 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="8"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse8 preid="8"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="8"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre8 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> IRP_MJ_CREATE IRP Handler
</span>
NTSTATUS FsFilterDispatchCreate(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP           Irp
)
{
PFILE_OBJECT pFileObject = IoGetCurrentIrpStackLocation(Irp)-<span class=code-keyword>&gt;</span>FileObject;
DbgPrint(<span class=code-string>"</span><span class=code-string>%wZ\n"</span>, &amp;pFileObject-<span class=code-keyword>&gt;</span>FileName);
<span class=code-keyword>return</span> FsFilterDispatchPassThrough(DeviceObject, Irp);
}</pre>
<h3><a id=_Toc244423410 name=_Toc244423410>FastIo.c</a></h3>
<p>To test the Fast-IO dispatch table validity for the next driver, we will use the following helper macro (not all of the Fast-IO routines must be implemented by the underlying file system, so we have to be sure about that):</p>
<div style="WIDTH: 100%" id=premain9 class=small-text><img style="CURSOR: pointer" id=preimg9 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="9"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse9 preid="9"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="9"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre9 lang=C++><span class=code-comment>//</span><span class=code-comment>  Macro to test if FAST_IO_DISPATCH handling routine is valid
</span><span class=code-preprocessor>#define</span> VALID_FAST_IO_DISPATCH_HANDLER(_FastIoDispatchPtr, _FieldName) \
(((_FastIoDispatchPtr) != NULL) &amp;&amp; \
(((_FastIoDispatchPtr)-<span class=code-keyword>&gt;</span>SizeOfFastIoDispatch) <span class=code-keyword>&gt;</span>= \
(FIELD_OFFSET(FAST_IO_DISPATCH, _FieldName) + <span class=code-keyword>sizeof</span>(<span class=code-keyword>void</span> *))) &amp;&amp; \
((_FastIoDispatchPtr)-<span class=code-keyword>&gt;</span>_FieldName != NULL))</pre>
<h4><a id=_Toc244423411 name=_Toc244423411>Fast-IO pass-through</a></h4>
<p>Passing through Fast-IO requests requires writing a lot of code (in contrast to passing through IRP requests) because each Fast-IO function has its own set of parameters. A typical pass-through function is shown below:</p>
<div style="WIDTH: 100%" id=premain10 class=small-text><img style="CURSOR: pointer" id=preimg10 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="10"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse10 preid="10"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="10"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre10 lang=C++><span class=code-SDKkeyword>BOOLEAN</span> FsFilterFastIoQueryBasicInfo(
__in PFILE_OBJECT       FileObject,
__in <span class=code-SDKkeyword>BOOLEAN</span>            Wait,
__out PFILE_BASIC_INFORMATION Buffer,
__out PIO_STATUS_BLOCK  IoStatus,
__in PDEVICE_OBJECT     DeviceObject
)
{
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment>  Pass through logic for this type of Fast I/O
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
PDEVICE_OBJECT    nextDeviceObject =
((PFSFILTER_DEVICE_EXTENSION)DeviceObject-<span class=code-keyword>&gt;</span>DeviceExtension)-<span class=code-keyword>&gt;</span>AttachedToDeviceObject;
PFAST_IO_DISPATCH fastIoDispatch   = nextDeviceObject-<span class=code-keyword>&gt;</span>DriverObject-<span class=code-keyword>&gt;</span>FastIoDispatch;
<span class=code-keyword>if</span> (VALID_FAST_IO_DISPATCH_HANDLER(fastIoDispatch, FastIoQueryBasicInfo))
{
<span class=code-keyword>return</span> (fastIoDispatch-<span class=code-keyword>&gt;</span>FastIoQueryBasicInfo)(
FileObject,
Wait,
Buffer,
IoStatus,
nextDeviceObject);
}
<span class=code-keyword>return</span> FALSE;
}</pre>
<h4><a id=_Toc244423412 name=_Toc244423412>Fast-IO detach device</a></h4>
<p>This is a special Fast-IO request which we have to handle ourselves and not call the next driver. We have to detach our filter device from the file system device stack and delete our device. That can be done easily by the following code:</p>
<div style="WIDTH: 100%" id=premain11 class=small-text><img style="CURSOR: pointer" id=preimg11 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="11"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse11 preid="11"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="11"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre11 lang=C++>VOID FsFilterFastIoDetachDevice(
__in PDEVICE_OBJECT     SourceDevice,
__in PDEVICE_OBJECT     TargetDevice
)
{
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment>  Detach from the file system's volume device object.
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
IoDetachDevice(TargetDevice);
IoDeleteDevice(SourceDevice);
}</pre>
<h3><a id=_Toc244423413 name=_Toc244423413>Notification.c</a></h3>
<p>A typical file system consists of a control device and volume devices. A volume device is attached to the storage device stack. The control device is registered as a file system.</p>
<p><img alt=01-fsDevices.png src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/01-fsDevices.png" width=389 height=492 complete="true"></p>
<div class=Caption>Figure 1 - Devices of the typical file system</div>
<p>We have a callback which is invoked for all active file systems and whenever a file system has either registered or unregistered itself as an active one. This is a good place to attach/detach our filter device. When a file system activates itself, we attach to its control device (only if we are not already attached), enumerate its volume devices, and attach to them too. On file system deactivation, we examine the file system control device stack, find our device, and detach it. Detaching from the file system volume devices is performed in the <code>FsFilterFastIoDetachDevice</code> routine described earlier.</p>
<div style="WIDTH: 100%" id=premain12 class=small-text><img style="CURSOR: pointer" id=preimg12 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="12"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse12 preid="12"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="12"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre12 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>/////////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> This routine is invoked whenever a file system has either registered or
</span><span class=code-comment>//</span><span class=code-comment> unregistered itself as an active file system.
</span>
VOID FsFilterNotificationCallback(
__in PDEVICE_OBJECT DeviceObject,
__in <span class=code-SDKkeyword>BOOLEAN</span>        FsActive
)
{
<span class=code-comment>//</span><span class=code-comment>
</span>    <span class=code-comment>//</span><span class=code-comment>  Handle attaching/detaching from the given file system.
</span>    <span class=code-comment>//</span><span class=code-comment>
</span>
<span class=code-keyword>if</span> (FsActive)
{
FsFilterAttachToFileSystemDevice(DeviceObject);
}
<span class=code-keyword>else</span>
{
FsFilterDetachFromFileSystemDevice(DeviceObject);
}
}</pre>
<h3><a id=_Toc244423414 name=_Toc244423414>AttachDetach.c</a></h3>
<p>This file contains helper routines for attaching, detaching, and checking whether our filter is already attached.</p>
<h4><a id=_Toc244423415 name=_Toc244423415>Attaching</a></h4>
<p>To perform attaching, we create a new device object with the device extension (call <code>IoCreateDevice</code>) and the propagate device object flags from the device object we are trying to attach to (<code>DO_BUFFERED_IO</code>, <code>DO_DIRECT_IO</code>, <code>FILE_DEVICE_SECURE_OPEN</code>). Then, we call <code>IoAttachDeviceToDeviceStackSafe</code> in a loop with a delay in the case of failure. It is possible for this attachment request to fail because the device object has not finished initialization. This situation can occur if we try to mount the filter that was loaded as the volume only. When attaching is finished, we save the &#8220;attached to&#8221; device object to the device extension and clear the <code>DO_DEVICE_INITIALIZING</code> flag. The device extension is shown below:</p>
<div style="WIDTH: 100%" id=premain13 class=small-text><img style="CURSOR: pointer" id=preimg13 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="13"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse13 preid="13"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="13"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre13 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> Structures
</span>
<span class=code-keyword>typedef</span> <span class=code-keyword>struct</span> _FSFILTER_DEVICE_EXTENSION
{
PDEVICE_OBJECT AttachedToDeviceObject;
} FSFILTER_DEVICE_EXTENSION, *PFSFILTER_DEVICE_EXTENSION;</pre>
<h4><a id=_Toc244423416 name=_Toc244423416>Detaching</a></h4>
<p>Detaching is quite simple. Get the &#8220;attached to&#8221; device object from the device extension and call <code>IoDetachDevice</code> and <code>IoDeleteDevice</code>.</p>
<div style="WIDTH: 100%" id=premain14 class=small-text><img style="CURSOR: pointer" id=preimg14 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="14"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse14 preid="14"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="14"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre14 lang=C++><span class=code-keyword>void</span> FsFilterDetachFromDevice(
__in PDEVICE_OBJECT DeviceObject
)
{
PFSFILTER_DEVICE_EXTENSION pDevExt =
(PFSFILTER_DEVICE_EXTENSION)DeviceObject-<span class=code-keyword>&gt;</span>DeviceExtension;
IoDetachDevice(pDevExt-<span class=code-keyword>&gt;</span>AttachedToDeviceObject);
IoDeleteDevice(DeviceObject);
}</pre>
<h4><a id=_Toc244423417 name=_Toc244423417>Checking whether our device is attached</a></h4>
<p>To check whether we are attached to a device, we have to iterate through the device stack (using <code>IoGetAttachedDeviceReference</code> and <code>IoGetLowerDeviceObject</code>) and search for our device there. We distinguish our devices by comparing the device driver object with our driver object (<code>g_fsFilterDriverObject</code>).</p>
<div style="WIDTH: 100%" id=premain15 class=small-text><img style="CURSOR: pointer" id=preimg15 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="15"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse15 preid="15"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="15"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre15 lang=C++><span class=code-SummaryComment>///</span><span class=code-comment>///////////////////////////////////////////////////////////////////////
</span><span class=code-comment>//</span><span class=code-comment> Misc
</span>
<span class=code-SDKkeyword>BOOLEAN</span> FsFilterIsMyDeviceObject(
__in PDEVICE_OBJECT DeviceObject
)
{
<span class=code-keyword>return</span> DeviceObject-<span class=code-keyword>&gt;</span>DriverObject == g_fsFilterDriverObject;
}</pre>
<h3><a id=_Toc244423418 name=_Toc244423418>Sources and makefile</a></h3>
<p>Sources and makefile files are used by the build utility to build the driver. It contains project settings and source file names.</p>
<p>Sources file contents:</p>
<div style="WIDTH: 100%" id=premain16 class=small-text><img style="CURSOR: pointer" id=preimg16 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="16"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse16 preid="16"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="16"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre16 lang=C++>TARGETNAME  = FsFilter
TARGETPATH  = obj
TARGETTYPE  = DRIVER
DRIVERTYPE  = FS
SOURCES     = \
Main.c \
IrpDispatch.c \
AttachDetach.c \
Notification.c \
FastIo.c</pre>
<p>The makefile is standard:</p>
<div style="WIDTH: 100%" id=premain17 class=small-text><img style="CURSOR: pointer" id=preimg17 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="17"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse17 preid="17"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="17"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre17 lang=text>!include $(NTMAKEENV)\makefile.def</pre>
<p>The MSVC makefile project build command line is:</p>
<div style="WIDTH: 100%" id=premain18 class=small-text><img style="CURSOR: pointer" id=preimg18 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="18"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse18 preid="18"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="18"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre18 lang=text>call $(WINDDK)\bin\setenv.bat $(WINDDK) chk wxp
cd /d $(ProjectDir)
build.exe &#8211;I</pre>
<h2><a id=_Toc244423419 name=_Toc244423419>How to install a driver</a></h2>
<h3><a id=_Toc244423420 name=_Toc244423420>SC.EXE overview</a></h3>
<p>We will use <em>sc.exe</em> (sc &#8211; service control) to manage our driver. It is a command-line utility that can be used to query or modify the database of installed services. It is shipped with Windows XP and higher, or you can find it in the Windows SDK/DDK.</p>
<h3><a id=_Toc244423421 name=_Toc244423421>Install</a></h3>
<p>To install the driver, call:</p>
<div style="WIDTH: 100%" id=premain19 class=small-text><img style="CURSOR: pointer" id=preimg19 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="19"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse19 preid="19"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="19"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre19 lang=text>sc create FsFilter type= filesys binPath= c:\FSFilter.sys</pre>
<p>A new service entry will be created with the name <em>FsFilter</em>; the service type will be file system, and the binary path, <em>c:\FsFilter.sys</em>.</p>
<h3><a id=_Toc244423422 name=_Toc244423422>Start</a></h3>
<p>To start the driver, call:</p>
<div style="WIDTH: 100%" id=premain20 class=small-text><img style="CURSOR: pointer" id=preimg20 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="20"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse20 preid="20"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="20"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre20 lang=text>sc start FsFilter</pre>
<p>This starts a service named <em>FsFilter</em>.</p>
<h3><a id=_Toc244423423 name=_Toc244423423>Stop</a></h3>
<p>To stop the driver, call:</p>
<div style="WIDTH: 100%" id=premain21 class=small-text><img style="CURSOR: pointer" id=preimg21 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="21"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse21 preid="21"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="21"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre21 lang=text>sc stop FsFilter</pre>
<p>This stop the service named <em>FsFilter</em>.</p>
<h3><a id=_Toc244423424 name=_Toc244423424>Uninstall</a></h3>
<p>And to uninstall, call:</p>
<div style="WIDTH: 100%" id=premain22 class=small-text><img style="CURSOR: pointer" id=preimg22 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="22"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse22 preid="22"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="22"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre22 lang=text>sc delete FsFilter</pre>
<p>This instructs the service manager to delete the service entry with the name <em>FsFilter</em>.</p>
<h3><a id=_Toc244423425 name=_Toc244423425>Resulting script</a></h3>
<p>All those commands are put into a single batch file to make driver testing easier. Here is the listing of the <em>Install.cmd</em> command file:</p>
<div style="WIDTH: 100%" id=premain23 class=small-text><img style="CURSOR: pointer" id=preimg23 src="http://www.codeproject.com/images/minus.gif" width=9 height=9 preid="23"><span style="MARGIN-BOTTOM: 0px; CURSOR: pointer" id=precollapse23 preid="23"> Collapse</span><img style="MARGIN-LEFT: 35px" src="http://www.codeproject.com/images/copy_16.png" width=16 height=16><a href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial.aspx#" preid="23"> Copy Code</a></div>
<pre style="MARGIN-TOP: 0px" id=pre23 lang=text>sc create FsFilter type= filesys binPath= c:\FsFilter.sys
sc start FsFilter
pause
sc stop FsFilter
sc delete FsFilter
pause</pre>
<h2><a id=_Toc244423426 name=_Toc244423426>Running a sample</a></h2>
<p>This is the most interesting part. To demonstrate the file system filter work, we will use <a href="http://download.sysinternals.com/Files/DebugView.zip">Sysinternals DebugView for Windows</a> to monitor the debug output, and <a href="http://www.osronline.com/article.cfm?article=97">OSR Device Tree</a> to see the devices and drivers.</p>
<p>So, build the driver. Then, copy the build output file <em>FsFilter.sys</em> and the install the script <em>Install.cmd</em> to the root of the disk <em>C</em>.</p>
<p><img alt=02-diskC.PNG src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/02-diskC.PNG" width=518 height=331 complete="true"></p>
<div class=Caption>Figure 2 - The driver and the install script on the disk.</div>
<p>Run <em>Install.cmd</em>. It will install and start the driver and then wait for user input.</p>
<p><img alt=03-install.PNG src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/03-install.PNG" width=600 height=297 complete="true"></p>
<div class=Caption>Figure 3 - The driver is successfully installed and started.</div>
<p>Now start the DebugView utility.</p>
<p><img alt=04-debugView.PNG src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/04-debugView.PNG" width=600 height=327 complete="true"></p>
<div class=Caption>Figure 4 - Monitoring the debug output.</div>
<p>We can see what files are being opened! Our filter works. Now, run the device tree utility and locate our driver there.</p>
<p><a title="Click to enlarge" href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/05-deviceTree01.PNG"><img border=0 src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/05-deviceTree01_small.PNG" width=600 height=313 complete="true"></a></p>
<div class=Caption>Figure 5 - Our filter driver in the device tree.</div>
<p>We can see numerous devices created by our driver. Now let&#8217;s open the NTFS driver and look at the device tree:</p>
<p><a title="Click to enlarge" href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/06-deviceTree02.PNG"><img border=0 src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/06-deviceTree02_small.PNG" width=600 height=313 complete="true"></a></p>
<div class=Caption>Figure 6 - Our filter is attached to NTFS.</div>
<p>We are attached. Let&#8217;s take a look at the other file systems.</p>
<p><a title="Click to enlarge" href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/07-deviceTree03.PNG"><img border=0 src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/07-deviceTree03_small.PNG" width=600 height=313 complete="true"></a></p>
<div class=Caption>Figure 7 - Our filter is attached to the other file systems too.</div>
<p>And finally, press any key to move our install script forward. It will stop and uninstall the driver.</p>
<p><img alt=08-uninstall.PNG src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/08-uninstall.PNG" width=600 height=297 complete="true"></p>
<div class=Caption>Figure 8 - The driver is stopped and uninstalled.</div>
<p>Refresh the device tree list by pressing F5:</p>
<p><a title="Click to enlarge" href="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/09-deviceTree04.PNG"><img border=0 src="http://www.codeproject.com/KB/system/fs-filter-driver-tutorial/09-deviceTree04_small.PNG" width=600 height=313 complete="true"></a></p>
<div class=Caption>Figure 9 - Our filter device isnot in the device tree.</div>
<p>Our filter is gone. The system is running as before.</p>
<h2><a id=_Toc244423427 name=_Toc244423427>Improvements</a></h2>
<p>The sample driver lacks a commonly required functionality of attaching to the newly arrived volumes. It is done so to make the driver as easy to understand as possible. You can write a <code>IRP_MJ_FILE_SYSTEM_CONTROL</code> handler of your own to track the newly arrived volumes.</p>
<h2><a id=_Toc244423428 name=_Toc244423428>Conclusion</a></h2>
<p>This tutorial showed how to create a simple file system filter driver, and how to install, start, stop, and uninstall it from a command line. Also, some file system filter driver aspects were discussed. We saw the file system device stack with the attached filters, and learned how to monitor the debug output from the driver. You may use the provided sources as a skeleton for your own file system filter driver or modify its behavior.</p>
<h2><a id=_Toc244423429 name=_Toc244423429>Useful references</a></h2>
<ol>
    <li><a href="http://msdn.microsoft.com/en-us/library/ms793575.aspx">File System Filter Drivers</a>
    <li><a href="http://www.osronline.com/section.cfm?section=20">Content for File System or File System Filter Developers</a>
    <li><a href="http://www.amazon.com/Windows-System-Internals-Classic-Reprints/dp/0976717514">Windows NT File System Internals (OSR Classic Reprints) (Paperback)</a>
    <li>sfilter DDK sample </li>
</ol>
<p>Read more Driver Development tips at the <a href="http://www.apriorit.com/case-studies.html">Apriorit Case Studies section</a>.</p>
<!-- Main Page Contents End -->
<img src ="http://www.cppblog.com/iniwf/aggbug/111361.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> 2010-04-02 09:23 <a href="http://www.cppblog.com/iniwf/archive/2010/04/02/111361.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WDM驱动程序入门</title><link>http://www.cppblog.com/iniwf/archive/2010/03/24/110471.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Wed, 24 Mar 2010 10:29:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/03/24/110471.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/110471.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/03/24/110471.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/110471.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/110471.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自http://www.luocong.com/articles/show_article.asp?Article_ID=11WDM驱动程序入门（1）——HelloWDMWDM驱动程序是一种很新的东西，相信很多人都跟我一样，对它很感兴趣，但是又找不到学习的切入点。究其原因，还是因为WDM是一种非常&#8220;死板板&#8221;的程序，它一运行就是工作在系统的底层RING&nbsp;0处，...&nbsp;&nbsp;<a href='http://www.cppblog.com/iniwf/archive/2010/03/24/110471.html'>阅读全文</a><img src ="http://www.cppblog.com/iniwf/aggbug/110471.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> 2010-03-24 18:29 <a href="http://www.cppblog.com/iniwf/archive/2010/03/24/110471.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PCI设备驱动开发</title><link>http://www.cppblog.com/iniwf/archive/2010/03/24/110470.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Wed, 24 Mar 2010 10:26:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/03/24/110470.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/110470.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/03/24/110470.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/110470.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/110470.html</trackback:ping><description><![CDATA[转自<a href="http://hi.baidu.com/techofchaos/blog/item/efee77eb272e88dbd439c922.html">http://hi.baidu.com/techofchaos/blog/item/efee77eb272e88dbd439c922.html</a><br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><span style="COLOR: #000000">1</span><span style="COLOR: #000000">.&nbsp;PCI&nbsp;简介<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">PCI&nbsp;总线标准是一种将系统外部设备连接起来的总线标准，是&nbsp;PC&nbsp;中最重要的总线，实际上是系统的各个部分如何交互的接口。传输速率可达到&nbsp;133MB</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">s。在当前的&nbsp;PC&nbsp;体系结构中，几乎所有的外部设备采用的各种各样的接口总线，均是通过桥接电路挂接到&nbsp;PCI&nbsp;系统上。在这种&nbsp;PCI&nbsp;系统中，&nbsp;Host</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">PCI&nbsp;桥称为北桥，连接主处理器总线到基础&nbsp;PCI&nbsp;局部总线。&nbsp;PCI&nbsp;与其他总线的接口称为南桥，其中南桥还通常含有中断控制器、IDE&nbsp;控制器、USB&nbsp;控制器和&nbsp;DMA&nbsp;控制器等。南桥和北桥组成主板的芯片组。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">.&nbsp;PCI配置空间<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">每个PCI设备都有自己的配置空间，用于支持即插即用，使之满足现行的系统配置结构。下面对PCI配置空间做一下简要介绍。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">配&nbsp;置空间是一容量为256字节并具有特定结构的地址空间。这个空间又分为头标区和设备有关区两部分。头标区的长度是64字节，每个设备都必须配置该区的寄存&nbsp;器。该区中的各个字段用来唯一地识别设备。其余的192字节因设备而异。配置空间的头标区64个字节的使用情况如图1示。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">为了实现即插即用，系统可根据硬件资源的使用情况，为PCI设备分配新的资源。因此编写设备驱动程序重点是获得基址寄存器（Base&nbsp;Address）和中断干线寄存器的内容。配置空间共有六个基址寄存器和一个中断干线寄存器，具体用法如下：<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">PCI&nbsp;Base&nbsp;Address&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;寄存器：系统利用此寄存器为PCI接口芯片的配置寄存器分配一段PCI地址空间，通过这段地址我们可以以内存映射的形式访问PCI接口芯片的配置寄存器。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">PCI&nbsp;Base&nbsp;Address&nbsp;1寄存器：系统利用此寄存器为&nbsp;PCI&nbsp;接口芯片的配置寄存器分配一段PCI地址空间，通过这段地址我们可以以I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O的形式访问PCI接口芯片的配置寄存器。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">PCI&nbsp;Base&nbsp;Address&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">、</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">、</span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">、5寄存器：系统BIOS利用这些寄存器分配PCI地址空间以支持PCI接口芯片的局部配置寄存器0、</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">、</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">、3的访问。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">在所有基址寄存器中，第0位均为只读位，表示这段地址映射到存储器空间还是I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O空间，如果是&#8220;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&#8221;表示映射到I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O空间，如果是&#8220;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&#8221;则表示映射到存储器空间。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">中断干线寄存器（Interrupt&nbsp;Line）：用于说明中断线的连接情况，这个寄存器的值与标准8259的IRQ编号（</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">~</span><span style="COLOR: #000000">15</span><span style="COLOR: #000000">）对应。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">表1&nbsp;PCI配置空间<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">.设备初始化<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">PCI&nbsp;设备驱动程序要完成识别&nbsp;PCI&nbsp;器件、寻找&nbsp;PCI&nbsp;硬件的资源和对&nbsp;PCI&nbsp;器件中断的服务。在驱动程序初始化过程中，使用&nbsp;HalGetBusData（）函数完&nbsp;成寻找&nbsp;PCI&nbsp;设备的工作。在初始化过程中，使用器件识别号（Device&nbsp;ID）和厂商识别号（Vendor&nbsp;ID），通过遍历总线上的所有设备，寻找到指定的PCI设备，并获取设备的总线号，器件号与功能号。通过这些配置信息，可以在系统中寻址该设备的资源配置&nbsp;列表。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">在此之后，驱动程序需要从配置空间获取硬件的参数。PCI设备的中断号、端口地址的范围（I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O）方式、存储器的地址与映射&nbsp;方式等，都可以从硬件资源列表数据结构中获取。在Windows&nbsp;NT中，调用HalAssignSlotResources（）函数来获得指定设备的资源列表数据结构指针，然后通过遍历该列表中的所有资源描述符，获取&nbsp;该设备的I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O端口基地址与长度，中断的中断级、中断向量与模式，存储器基地址与长度等硬件资源数据。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">我们设计的DMA通信采用总线主控方式进行通信，在&nbsp;设备初始化时需要对DMA适配器进行初始化，使用HalGetAdapter（）获得操作系统分配的适配器对象指针。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">示例代码如下：<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;遍历总线，获得指定设备的总线号，器件号与功能号</span><span style="COLOR: #008000"><br><img id=Codehighlighter1_1630_2391_Open_Image onclick="this.style.display='none'; Codehighlighter1_1630_2391_Open_Text.style.display='none'; Codehighlighter1_1630_2391_Closed_Image.style.display='inline'; Codehighlighter1_1630_2391_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_1630_2391_Closed_Image onclick="this.style.display='none'; Codehighlighter1_1630_2391_Closed_Text.style.display='none'; Codehighlighter1_1630_2391_Open_Image.style.display='inline'; Codehighlighter1_1630_2391_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(&nbsp;busNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;busNumber&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;MAX_PCI_BUSES;&nbsp;busNumber</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;)&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_1630_2391_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1630_2391_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_1703_2388_Open_Image onclick="this.style.display='none'; Codehighlighter1_1703_2388_Open_Text.style.display='none'; Codehighlighter1_1703_2388_Closed_Image.style.display='inline'; Codehighlighter1_1703_2388_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_1703_2388_Closed_Image onclick="this.style.display='none'; Codehighlighter1_1703_2388_Closed_Text.style.display='none'; Codehighlighter1_1703_2388_Open_Image.style.display='inline'; Codehighlighter1_1703_2388_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif"></span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(&nbsp;deviceNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;deviceNumber&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;PCI_MAX_DEVICES;deviceNumber</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;)&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_1703_2388_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1703_2388_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">slotNumber.u.bits.DeviceNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;deviceNumber;<br><img id=Codehighlighter1_1832_2385_Open_Image onclick="this.style.display='none'; Codehighlighter1_1832_2385_Open_Text.style.display='none'; Codehighlighter1_1832_2385_Closed_Image.style.display='inline'; Codehighlighter1_1832_2385_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_1832_2385_Closed_Image onclick="this.style.display='none'; Codehighlighter1_1832_2385_Closed_Text.style.display='none'; Codehighlighter1_1832_2385_Open_Image.style.display='inline'; Codehighlighter1_1832_2385_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif"></span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(&nbsp;functionNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;functionNumber&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;PCI_MAX_FUNCTION;&nbsp;functionNumber</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;)&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_1832_2385_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1832_2385_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">slotNumber.u.bits.FunctionNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;functionNumber;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">HalGetBusData(PCIConfiguration,&nbsp;busNumber,&nbsp;slotNumber.u.AsULONG,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><img id=Codehighlighter1_1987_2028_Open_Image onclick="this.style.display='none'; Codehighlighter1_1987_2028_Open_Text.style.display='none'; Codehighlighter1_1987_2028_Closed_Image.style.display='inline'; Codehighlighter1_1987_2028_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_1987_2028_Closed_Image onclick="this.style.display='none'; Codehighlighter1_1987_2028_Closed_Text.style.display='none'; Codehighlighter1_1987_2028_Open_Image.style.display='inline'; Codehighlighter1_1987_2028_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">pciData,</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(ULONG))&nbsp;)&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_1987_2028_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1987_2028_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">deviceNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;PCI_MAX_DEVICES;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img id=Codehighlighter1_2078_2091_Open_Image onclick="this.style.display='none'; Codehighlighter1_2078_2091_Open_Text.style.display='none'; Codehighlighter1_2078_2091_Closed_Image.style.display='inline'; Codehighlighter1_2078_2091_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_2078_2091_Closed_Image onclick="this.style.display='none'; Codehighlighter1_2078_2091_Closed_Text.style.display='none'; Codehighlighter1_2078_2091_Open_Image.style.display='inline'; Codehighlighter1_2078_2091_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif"></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(pciData.VendorID&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;PCI_INVALID_VENDORID&nbsp;)&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_2078_2091_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_2078_2091_Open_Text><span style="COLOR: #000000">{&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #0000ff">continue</span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(&nbsp;(&nbsp;VendorId&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;PCI_INVALID_VENDORID&nbsp;)&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_2205_2217_Open_Image onclick="this.style.display='none'; Codehighlighter1_2205_2217_Open_Text.style.display='none'; Codehighlighter1_2205_2217_Closed_Image.style.display='inline'; Codehighlighter1_2205_2217_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_2205_2217_Closed_Image onclick="this.style.display='none'; Codehighlighter1_2205_2217_Closed_Text.style.display='none'; Codehighlighter1_2205_2217_Open_Image.style.display='inline'; Codehighlighter1_2205_2217_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif">(&nbsp;pciData.VendorID&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;VendorId&nbsp;</span><span style="COLOR: #000000">||</span><span style="COLOR: #000000">&nbsp;pciData.DeviceID&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;DeviceId&nbsp;))&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_2205_2217_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_2205_2217_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif"></span><span style="COLOR: #0000ff">continue</span><span style="COLOR: #000000">;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif">}</span></span><span style="COLOR: #000000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">pPciDeviceLocation</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">BusNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;busNumber;&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">pPciDeviceLocation</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">SlotNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;slotNumber;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">pPciDeviceLocation&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">PciDeviceList</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">List[</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">count];<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">status&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;STATUS_SUCCESS;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif">}</span></span><span style="COLOR: #000000">&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif">}</span></span><span style="COLOR: #000000">&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;获取设备的资源列表数据指针</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">status&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;HalAssignSlotResources(RegistryPath,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ClassUnicodeString,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">DriverObject,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">DeviceObject,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterfaceType,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">BusNumber,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">SlotNumber,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">pCmResourceList&nbsp;);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">.&nbsp;I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O端口访问<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">在&nbsp;PC机上，I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O寻址方式与内存寻址方式不同，所以处理方法也不同。I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O空间是一个64K字节的寻址空间，I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O寻址没有实模式与保护模式之分，在各种&nbsp;模式下寻址方式相同。在Windows&nbsp;NT下，系统不允许处于Ring3级的用户程序和用户模式驱动程序直接使用I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O指令，对I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O端口进行访问，任何对I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O的操作都需要借助内核模式驱动&nbsp;来完成。在访问I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O端口时，使用READ_PORT_XXX与WRITE_PORT_XXX函数来进行读写。I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O端口基地址使用从配置空间基址寄存器&nbsp;PCI&nbsp;Base&nbsp;Address&nbsp;1中返回的I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O端口基地址。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">示例代码如下：<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">RegValue&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;READ_PORT_ULONG(pBaseAddr</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">RegOffSet);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">WRITE_PORT_ULONG(pBaseAddr</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;RegOffset,&nbsp;RegValue);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">.&nbsp;设备内存访问<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">Winsows&nbsp;工作在32位保护模式下，保护模式与实模式的根本区别在于CPU寻址方式上的不同，这也是Windows驱动程序设计中需要着重解决的问题。&nbsp;Windows采用了分段、分页机制，使得一个程序可以很容易地在物理内存容量不一样的、配置范围差别很大的计算机上运行，编程人员使用虚拟存储器可以写&nbsp;出比任何实际配置的物理存储器都大得多的程序。每个虚拟地址由16位的段选择字和32位段偏移量组成。通过分段机制，系统由虚拟地址产生线性地址。再通过&nbsp;分页机制，由线性地址产生物理地址。线性地址被分割成页目录(Page&nbsp;Directory)、页表(Page&nbsp;Table)和页偏移(Offset)三个部分。当建立一个新的Win32进程时，操作系统会为它分配一块内存，并建立它自己的页目录、页表，页目录的地&nbsp;址也同时放入进程的现场信息中。当计算一个地址时，系统首先从CPU控制器CR3中读出页目录所在的地址，然后根据页目录得到页表所在的地址，再根据页表&nbsp;得到实际代码／数据页的页帧，最后再根据页偏移访问特定的单元。硬件设备读写的是物理内存，但应用程序读写的是虚拟地址，所以存在着将物理内存地址映射到&nbsp;用户程序线性地址的问题。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">从物理内存到线性地址的转换是驱动程序需要完成的工作，可以在初始化驱动程序的进行。在已经获得设备的存&nbsp;储器基地址后，首先调用HalTranslateBusAddress()函数将总线相关的内存地址转换成系统的物理地址，然后调用&nbsp;MmMapIoSpace()函数将系统的物理地址映射到线性地址空间。在需要访问设备内存时，调用READ_REGISTER_XXX()与&nbsp;WRITE_REGISTER_XXX&nbsp;()函数来进行，基地址使用前面映射后的线性地址。在设备卸载时，调用MmUnmapIoSpace()断开设备内存与线性地址空间的映射。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">示例代码如下：<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">HalTranslateBusAddress(InterfaceType,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">BusNumber,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">BaseAddress</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">RangeStart,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">addressSpace,&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">cardAddress)<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">BaseAddress</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">MappedRangeStart&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;MmMapIoSpace(cardAddress,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">BaseAddress</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">RangeLength,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">MmCached&nbsp;);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&#8230;&#8230;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">RegValue&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;READ_REGISTER_ULONG(pRegister);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">WRITE_REGISTER_ULONG(pRegister,&nbsp;pInBuf</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">RegValue);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&#8230;&#8230;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">MmUnmapIoSpace(pBaseAddress</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">MappedRangeStart,&nbsp;pBaseAddress</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">RangeLength&nbsp;);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">6</span><span style="COLOR: #000000">.&nbsp;中断处理<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">中&nbsp;断的设置、响应与调用在驱动程序中完成。设置中断应该在设备创建时完成，使用从CmResourceTypeInterrupt描述符中提取的参数，先调&nbsp;用HalGetInterruptVector()将与总线有关的中断向量参数转换为系统的中断向量，然后调用IoConnectInterrupt()&nbsp;指定中断服务，注册中断服务函数ISR（Interrupt&nbsp;Service&nbsp;Routine）的函数指针。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">当硬件设备产生中断时，系统&nbsp;会自动调用ISR函数来响应中断。ISR函数运行的中断请求级较高，主要完成对硬件设备中断的清除，不适合执行过多的代码。在传输大块数据时，需要使用延&nbsp;迟过程调用（Delay&nbsp;Process&nbsp;Call，DPC）机制。例如，使用PCI设备进行DMA通信时，在ISR函数中完成对指定设备中断的判断以及清除中断，在退出ISR前，调用DPC函&nbsp;数；在DPC函数中，完成DMA通信的过程，并将数据返回给用户程序。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">示例代码如下：<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">DeviceExtension</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptLevel&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;partialData</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">u.Interrupt.Level;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">DeviceExtension</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptVector&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;partialData</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">u.Interrupt.Vector;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">DeviceExtension</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptAffinity&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;partialData</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">u.Interrupt.Affinity;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(partialData</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Flags&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;CM_RESOURCE_INTERRUPT_LATCHED)<br><img id=Codehighlighter1_4857_4901_Open_Image onclick="this.style.display='none'; Codehighlighter1_4857_4901_Open_Text.style.display='none'; Codehighlighter1_4857_4901_Closed_Image.style.display='inline'; Codehighlighter1_4857_4901_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_4857_4901_Closed_Image onclick="this.style.display='none'; Codehighlighter1_4857_4901_Closed_Text.style.display='none'; Codehighlighter1_4857_4901_Open_Image.style.display='inline'; Codehighlighter1_4857_4901_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif"></span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_4857_4901_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_4857_4901_Open_Text><span style="COLOR: #000000">{<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">DeviceExtension</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptMode&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Latched;<br><img id=Codehighlighter1_4908_4960_Open_Image onclick="this.style.display='none'; Codehighlighter1_4908_4960_Open_Text.style.display='none'; Codehighlighter1_4908_4960_Closed_Image.style.display='inline'; Codehighlighter1_4908_4960_Closed_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif"><img style="DISPLAY: none" id=Codehighlighter1_4908_4960_Closed_Image onclick="this.style.display='none'; Codehighlighter1_4908_4960_Closed_Text.style.display='none'; Codehighlighter1_4908_4960_Open_Image.style.display='inline'; Codehighlighter1_4908_4960_Open_Text.style.display='inline';" align=top src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif">}</span></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;</span><span style="BORDER-BOTTOM: #808080 1px solid; BORDER-LEFT: #808080 1px solid; BACKGROUND-COLOR: #ffffff; DISPLAY: none; BORDER-TOP: #808080 1px solid; BORDER-RIGHT: #808080 1px solid" id=Codehighlighter1_4908_4960_Closed_Text><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_4908_4960_Open_Text><span style="COLOR: #000000">{&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif">DeviceExtension</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptMode&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;LevelSensitive;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif">}</span></span><span style="COLOR: #000000">&nbsp;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&#8230;&#8230;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">vector&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;HalGetInterruptVector(pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterfaceType,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">BusNumber,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptLevel,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptVector,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">irql,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">affinity&nbsp;);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">status&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;IoConnectInterrupt(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptObject,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">(PKSERVICE_ROUTINE)PciDmaISR,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">DeviceObject,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">NULL,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">vector,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">irql,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">irql,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterruptMode,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">TRUE,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">affinity,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">FALSE&nbsp;);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">7</span><span style="COLOR: #000000">.&nbsp;DMA通信过程<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">DMA通信在驱动程序中实现，需要多个例程才能完成一次DMA通信。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">）&nbsp;DriverEntry例程<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">构造DEVICE_DESCRIPTION结构，并调用HalGetAdapter，找到与设备关联的Adapter对象，并将返回的Adapter对象的地址和映射寄存器的数目保存在设备扩展的数据结构中。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">示例代码：<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;申请DMA的适配器对象</span><span style="COLOR: #008000"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">deviceDescription.Version&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;DEVICE_DESCRIPTION_VERSION;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.Master&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;TRUE;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.ScatterGather&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ScatterGather;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.DemandMode&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;FALSE;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.AutoInitialize&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;FALSE;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.Dma32BitAddresses&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;TRUE;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.BusNumber&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">BusNumber;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.InterfaceType&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">InterfaceType;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">deviceDescription.MaximumLength&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">MaxTransferLength;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">pDevExt</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">AdapterObject&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;HalGetAdapter(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">deviceDescription,<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">numberOfMapRegisters<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">);<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">&#8230;&#8230;<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">）Start&nbsp;I</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">O例程<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">该例程请求Adapter对象的拥有权，然后把其余的工作留给AdapterControl回调例程。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">a)&nbsp;调用KeFlushIoBuffers从CPU的Cache把数据清到物理内存，然后计算映射寄存器的数目和用户缓冲区的大小，及在第一次设备操作中传输的字节数。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">b)&nbsp;调用MmGetMdlVirtualAddress，从MDL中恢复用户缓冲区的虚地址，并存入设备扩展数据结构中。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">c)&nbsp;调用IoAllocateAdapterChannel请求Adapter对象的拥有权。如果调用成功，其余的设置工作由AdapterControl例程去做；如果失败了，则完成本次IRP包处理，开始处理下一个IRP。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">)&nbsp;AdapterControl例程<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">该例程完成初始化DMA控制器，并启动设备的工作。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">a)&nbsp;调用IoMapTransfer，装入Adapter对象的映射寄存器。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">b)&nbsp;向设备发送合适的命令开始传输操作。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">c)&nbsp;返回值KeepObject保留Adapter对象的拥有权。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">4</span><span style="COLOR: #000000">）中断服务（ISR）例程<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">在设备中断时，由系统调用。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">a)&nbsp;向硬件设备发出中断响应的指令。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">b)&nbsp;调用IoRequestDpc在驱动程序的DpcForIsr中继续处理该请求。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">c)&nbsp;返回TRUE，表示已经服务了本次中断。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"><br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif"></span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">)DpcForIsr例程<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">由ISR在每个部分数据传输操作的结束时触发，完成当前IRP请求。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">a)&nbsp;调用IoFlushAdapterBuffers，清除Adapter对象的Cache中的任何剩余数据。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">b)&nbsp;调用IoFreeMapRegisters，释放所使用的映射寄存器。<br><img align=top src="http://www.cppblog.com/Images/OutliningIndicators/None.gif">c)&nbsp;检查有未传完的剩余数据，如果有，则计算下次设备操作中需要传输的字节数，调用IoMapTransfer重设映射寄存器，并启动设备；如果没有剩余数据，则完成当前IRP请求，并开始下一个请求。&nbsp;</span></div>
<img src ="http://www.cppblog.com/iniwf/aggbug/110470.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> 2010-03-24 18:26 <a href="http://www.cppblog.com/iniwf/archive/2010/03/24/110470.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hook导入表 —— 实现挂钩FreeLibaray和HOOK延迟加载模块的API</title><link>http://www.cppblog.com/iniwf/archive/2009/08/30/94827.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 30 Aug 2009 13:57:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/30/94827.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/94827.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/30/94827.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/94827.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/94827.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Hook导入表&nbsp;——&nbsp;实现挂钩FreeLibaray和HOOK延迟加载模块的API&nbsp;　　最近在研究Windows&nbsp;Ring3上的API&nbsp;Hook，对HOOK导入表这种方法进行了研究。HOOK导入表所用的C++类大同小异，不同的就是如何实现HOOK一个延迟加载的模块中的函数，以及FreeLibaray某个函数之后再次LoadLibaray加载这个模块...&nbsp;&nbsp;<a href='http://www.cppblog.com/iniwf/archive/2009/08/30/94827.html'>阅读全文</a><img src ="http://www.cppblog.com/iniwf/aggbug/94827.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-08-30 21:57 <a href="http://www.cppblog.com/iniwf/archive/2009/08/30/94827.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RDP协议简要分析</title><link>http://www.cppblog.com/iniwf/archive/2009/08/06/92381.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Thu, 06 Aug 2009 01:42:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/06/92381.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/92381.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/06/92381.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/92381.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/92381.html</trackback:ping><description><![CDATA[转自：<font  face="song" size="6"><span  style="border-collapse: collapse; font-size: 19px; font-weight: 800;">http://blog.chinaunix.net/u/23353/showart_1856736.html</span></font><div><font  face="song" size="6"><span  style="border-collapse: collapse; font-size: 19px; font-weight: 800;"><span  style="font-size: 12px; font-weight: normal; "><table border="0" cellspacing="0" cellpadding="0" width="100%" style="border-collapse: collapse; word-wrap: break-word; "><tbody><tr style="font: normal normal normal 12px/normal song, Verdana; "><td height="25" align="center" style="font: normal normal normal 12px/normal song, Verdana; "><font color="#02368D" style="font-size: 14pt; "><strong><strong style="color: black; background-color: rgb(255, 255, 102); ">RDP</strong><strong style="color: black; background-color: rgb(160, 255, 255); ">协议</strong>简要<strong style="color: black; background-color: rgb(153, 255, 153); ">分析</strong></strong></font><br style="font: normal normal normal 12px/normal song, Verdana; "></td></tr><tr style="font: normal normal normal 12px/normal song, Verdana; "><td bgcolor="#D2DEE2" height="1" style="font: normal normal normal 12px/normal song, Verdana; "></td></tr><tr style="font: normal normal normal 12px/normal song, Verdana; "><td bgcolor="#FFFFFF" height="1" style="font: normal normal normal 12px/normal song, Verdana; "></td></tr><tr style="font: normal normal normal 12px/normal song, Verdana; "><td align="center" style="font: normal normal normal 12px/normal song, Verdana; "><table border="0" cellspacing="0" cellpadding="0" width="100%" style="border-collapse: collapse; word-wrap: break-word; "><tbody><tr style="font: normal normal normal 12px/normal song, Verdana; "><td width="100%" style="font: normal normal normal 12px/normal song, Verdana; "><div id="art" width="100%" style="margin-top: 15px; margin-right: 15px; margin-bottom: 15px; margin-left: 15px; "><div>上周六简要<strong style="color: black; background-color: rgb(153, 255, 153); ">分析</strong>了一下<strong style="color: black; background-color: rgb(255, 255, 102); ">RDP</strong><strong style="color: black; background-color: rgb(160, 255, 255); ">协议</strong>,在windows server 2008上对其进行了扩展，有了remote app，在其他系统上可以远程访问windows server 2008的应用程序，只需要一个简单的配置程序，并在server上注册访问。</div><div>其实微软真是很厉害阿，有了remote app这种 方式，移动设备完全可以利用wifi接入网络，这样可以弥补移动设备计算能力相对弱的问题，在复杂的计算由server完成，然后把结果(包括音频、视频等)传输给终端设备，也就是<strong style="color: black; background-color: rgb(255, 255, 102); ">rdp</strong>&nbsp;client设备，不错的解决方案。</div><div>写了一个简要的代码<strong style="color: black; background-color: rgb(153, 255, 153); ">分析</strong>笔记。</div><div><table bordercolor="#dddddd" cellspacing="0" cellpadding="0" width="360" align="center" border="1" style="border-collapse: collapse; "><tbody><tr height="60" style="font: normal normal normal 12px/normal song, Verdana; "><td align="middle" width="60" style="font: normal normal normal 12px/normal song, Verdana; "><img alt="" src="http://control.cublog.cn/fileicon/pdf.gif" border="0"></td><td style="font: normal normal normal 12px/normal song, Verdana; "><table cellspacing="0" cellpadding="0" width="100%" border="0" style="border-collapse: collapse; "><tbody><tr height="20" style="font: normal normal normal 12px/normal song, Verdana; "><td align="middle" width="40" style="font: normal normal normal 12px/normal song, Verdana; ">文件:</td><td style="font: normal normal normal 12px/normal song, Verdana; ">Rdesktop<strong style="color: black; background-color: rgb(160, 255, 255); ">协议</strong>简要<strong style="color: black; background-color: rgb(153, 255, 153); ">分析</strong>.pdf</td></tr><tr height="20" style="font: normal normal normal 12px/normal song, Verdana; "><td align="middle" width="40" style="font: normal normal normal 12px/normal song, Verdana; ">大小:</td><td style="font: normal normal normal 12px/normal song, Verdana; ">168KB</td></tr><tr height="20" style="font: normal normal normal 12px/normal song, Verdana; "><td align="middle" width="40" style="font: normal normal normal 12px/normal song, Verdana; ">下载:</td><td style="font: normal normal normal 12px/normal song, Verdana; "><a href="http://blogimg.chinaunix.net/blog/upfile2/090309190705.pdf" style="text-decoration: underline; color: rgb(0, 68, 182); ">下载</a></td></tr></tbody></table></td></tr></tbody></table></div><div><br></div><div>下面为参考信息：<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">1. RDesktop网站&nbsp;<a href="http://www.rdesktop.org/" style="text-decoration: underline; color: rgb(0, 68, 182); ">http://www.rdesktop.org/</a>&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">2.&nbsp; 这里有如何配置RemoteAPP的说明<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;<a href="http://servers.pconline.com.cn/skills/0711/1168847_4.html" style="text-decoration: underline; color: rgb(0, 68, 182); ">http://servers.pconline.com.cn/skills/0711/1168847_4.html</a></div></div></td></tr></tbody></table></td></tr></tbody></table></span></span></font></div><img src ="http://www.cppblog.com/iniwf/aggbug/92381.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-08-06 09:42 <a href="http://www.cppblog.com/iniwf/archive/2009/08/06/92381.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>djyos 嵌入式实时操作系统</title><link>http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 01 Aug 2009 06:29:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/91889.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/91889.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/91889.html</trackback:ping><description><![CDATA[<a href="http://www.djyos.com/">http://www.djyos.com/</a>
<img src ="http://www.cppblog.com/iniwf/aggbug/91889.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-08-01 14:29 <a href="http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源项目：MaxWit Linux 、g-bios</title><link>http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 01 Aug 2009 06:27:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/91888.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/91888.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/91888.html</trackback:ping><description><![CDATA[<a href="http://www.maxwit.com/msg.php/22.html">http://www.maxwit.com/msg.php/22.html</a>
<img src ="http://www.cppblog.com/iniwf/aggbug/91888.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-08-01 14:27 <a href="http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Android源码</title><link>http://www.cppblog.com/iniwf/archive/2009/08/01/91886.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 01 Aug 2009 06:26:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/01/91886.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/91886.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/01/91886.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/91886.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/91886.html</trackback:ping><description><![CDATA[<a href="http://source.android.com/">http://source.android.com/</a>
<img src ="http://www.cppblog.com/iniwf/aggbug/91886.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-08-01 14:26 <a href="http://www.cppblog.com/iniwf/archive/2009/08/01/91886.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《使用开源软件-自己动手写操作系统》Rev 2发布</title><link>http://www.cppblog.com/iniwf/archive/2009/08/01/91885.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 01 Aug 2009 06:22:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/01/91885.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/91885.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/01/91885.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/91885.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/91885.html</trackback:ping><description><![CDATA[<a href="http://blog.solrex.cn/articles/category/cs/open/writeos">http://blog.solrex.cn/articles/category/cs/open/writeos</a>
<img src ="http://www.cppblog.com/iniwf/aggbug/91885.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-08-01 14:22 <a href="http://www.cppblog.com/iniwf/archive/2009/08/01/91885.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下PCI设备驱动程序开发</title><link>http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Thu, 02 Jul 2009 03:19:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/89053.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/89053.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/89053.html</trackback:ping><description><![CDATA[转自<a href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html">http://www.ibm.com/developerworks/cn/linux/l-pci/index.html</a><br><br>
<p>2004 年 3 月 09 日</p>
<blockquote>PCI是一种广泛采用的总线标准，它提供了许多优于其它总线标准（如EISA）的新特性，目前已经成为计算机系统中应用最为广泛，并且最为通用的总线标准。Linux的内核能较好地支持PCI总线，本文以Intel 386体系结构为主，探讨了在Linux下开发PCI设备驱动程序的基本框架。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name=1><span class=atitle>一、PCI总线系统体系结构</span></a></p>
<p>PCI是外围设备互连（Peripheral Component Interconnect）的简称，作为一种通用的总线接口标准，它在目前的计算机系统中得到了非常广泛的应用。PCI提供了一组完整的总线接口规范，其目的是描述如何将计算机系统中的外围设备以一种结构化和可控化的方式连接在一起，同时它还刻画了外围设备在连接时的电气特性和行为规约，并且详细定义了计算机系统中的各个不同部件之间应该如何正确地进行交互。 </p>
<p>无论是在基于Intel芯片的PC机中，或是在基于Alpha芯片的工作站上，PCI毫无疑问都是目前使用最广泛的一种总线接口标准。同旧式的ISA总线不同，PCI将计算机系统中的总线子系统与存储子系统完全地分开，CPU通过一块称为PCI桥（PCI-Bridge）的设备来完成同总线子系统的交互，如图1所示。 </p>
<br><a name=N10045><strong>图1 PCI子系统的体系结构</strong></a><br><img alt="" src="http://www.ibm.com/developerworks/cn/linux/l-pci/images/1.jpg"> <br>
<p>由于使用了更高的时钟频率，因此PCI总线能够获得比ISA总线更好的整体性能。PCI总线的时钟频率一般在25MHz到33MHz范围内，有些甚至能够达到66MHz或者133MHz，而在64位系统中则最高能达到266MHz。尽管目前PCI设备大多采用32位数据总线，但PCI规范中已经给出了64位的扩展实现，从而使PCI总线能够更好地实现平台无关性，现在PCI总线已经能够用于IA-32、Alpha、PowerPC、SPARC64和IA-64等体系结构中。 </p>
<p>PCI总线具有三个非常显著的优点，使得它能够完成最终取代ISA总线这一历史使命： <br>
<ul>
    <li>在计算机和外设间传输数据时具有更好的性能；
    <li>能够尽量独立于具体的平台；
    <li>可以很方便地实现即插即用。 </li>
</ul>
<p>&#160;</p>
<p>图2是一个典型的基于PCI总线的计算机系统逻辑示意图，系统的各个部分通过PCI总线和PCI-PCI桥连接在一起。从图中不难看出，CPU和RAM需要通过PCI桥连接到PCI总线0（即主PCI总线），而具有PCI接口的显卡则可以直接连接到主PCI总线上。PCI-PCI桥是一个特殊的PCI设备，它负责将PCI总线0和PCI总线1（即从PCI主线）连接在一起，通常PCI总线1称为PCI-PCI桥的下游（downstream），而PCI总线0则称为PCI-PCI桥的上游（upstream）。图中连接到从PCI总线上的是SCSI卡和以太网卡。为了兼容旧的ISA总线标准，PCI总线还可以通过PCI-ISA桥来连接ISA总线，从而能够支持以前的ISA设备。图中ISA总线上连接着一个多功能I/O控制器，用于控制键盘、鼠标和软驱。 </p>
<br><a name=N10069><strong>图2 PCI系统示意图</strong></a><br><img alt="" src="http://www.ibm.com/developerworks/cn/linux/l-pci/images/2.jpg"> <br>
<p>在此我只对PCI总线系统体系结构作了概括性介绍，如果读者想进一步了解，David A Rusling在The Linux Kernel（http://tldp.org/LDP/tlk/dd/pci.html）中对Linux的PCI子系统有比较详细的介绍。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="35" sizcache="2">
    <tbody sizset="35" sizcache="1">
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right sizset="36" sizcache="2">
    <tbody sizset="37" sizcache="2">
        <tr align=right sizset="37" sizcache="2">
            <td sizset="37" sizcache="2"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0 sizset="37" sizcache="2">
                <tbody sizset="37" sizcache="1">
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html#main" cmImpressionSent="1"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=2><span class=atitle>二、Linux驱动程序框架</span></a></p>
<p>Linux将所有外部设备看成是一类特殊文件，称之为&#8220;设备文件&#8221;，如果说系统调用是Linux内核和应用程序之间的接口，那么设备驱动程序则可以看成是Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件在实现上的细节，使得应用程序可以像操作普通文件一样来操作外部设备。 </p>
<p><a name=N10080><span class=smalltitle><strong><font face=Arial>1. 字符设备和块设备</font></strong></span></a></p>
<p>Linux抽象了对硬件的处理，所有的硬件设备都可以像普通文件一样来看待：它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作，而驱动程序的主要任务也就是要实现这些系统调用函数。Linux系统中的所有硬件设备都使用一个特殊的设备文件来表示，例如，系统中的第一个IDE硬盘使用/dev/hda表示。每个设备文件对应有两个设备号：一个是主设备号，标识该设备的种类，也标识了该设备所使用的驱动程序；另一个是次设备号，标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致，否则用户进程将无法访问到设备驱动程序。 </p>
<p>在Linux操作系统下有两类主要的设备文件：一类是字符设备，另一类则是块设备。字符设备是以字节为单位逐个进行I/O操作的设备，在对字符设备发出读写请求时，实际的硬件I/O紧接着就发生了，一般来说字符设备中的缓存是可有可无的，而且也不支持随机访问。块设备则是利用一块系统内存作为缓冲区，当用户进程对设备进行读写请求时，驱动程序先查看缓冲区中的内容，如果缓冲区中的数据能满足用户的要求就返回相应的数据，否则就调用相应的请求函数来进行实际的I/O操作。块设备主要是针对磁盘等慢速设备设计的，其目的是避免耗费过多的CPU时间来等待操作的完成。一般说来，PCI卡通常都属于字符设备。 </p>
<p>所有已经注册（即已经加载了驱动程序）的硬件设备的主设备号可以从/proc/devices文件中得到。使用mknod命令可以创建指定类型的设备文件，同时为其分配相应的主设备号和次设备号。例如，下面的命令： </p>
<p sizset="38" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="38" sizcache="2">
    <tbody sizset="38" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>[root@gary root]# mknod  /dev/lp0  c  6  0
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>将建立一个主设备号为6，次设备号为0的字符设备文件/dev/lp0。当应用程序对某个设备文件进行系统调用时，Linux内核会根据该设备文件的设备类型和主设备号调用相应的驱动程序，并从用户态进入到核心态，再由驱动程序判断该设备的次设备号，最终完成对相应硬件的操作。 </p>
<p><a name=N10099><span class=smalltitle><strong><font face=Arial>2. 设备驱动程序接口</font></strong></span></a></p>
<p>Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口，这是通过include/linux/fs.h中的数据结构file_operations来完成的： </p>
<p sizset="39" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="39" sizcache="2">
    <tbody sizset="39" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>struct file_operations {
            struct module *owner;
            loff_t (*llseek) (struct file *, loff_t, int);
            ssize_t (*read) (struct file *, char *, size_t, loff_t *);
            ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
            int (*readdir) (struct file *, void *, filldir_t);
            unsigned int (*poll) (struct file *, struct poll_table_struct *);
            int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
            int (*mmap) (struct file *, struct vm_area_struct *);
            int (*open) (struct inode *, struct file *);
            int (*flush) (struct file *);
            int (*release) (struct inode *, struct file *);
            int (*fsync) (struct file *, struct dentry *, int datasync);
            int (*fasync) (int, struct file *, int);
            int (*lock) (struct file *, int, struct file_lock *);
            ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
            ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
            ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
            unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
            <!-- code sample is too wide -->};
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>当应用程序对设备文件进行诸如open、close、read、write等操作时，Linux内核将通过file_operations结构访问驱动程序提供的函数。例如，当应用程序对设备文件执行读操作时，内核将调用file_operations结构中的read函数。 </p>
<p><a name=N100AC><span class=smalltitle><strong><font face=Arial>2. 设备驱动程序模块</font></strong></span></a></p>
<p>Linux下的设备驱动程序可以按照两种方式进行编译，一种是直接静态编译成内核的一部分，另一种则是编译成可以动态加载的模块。如果编译进内核的话，会增加内核的大小，还要改动内核的源文件，而且不能动态地卸载，不利于调试，所有推荐使用模块方式。 </p>
<p>从本质上来讲，模块也是内核的一部分，它不同于普通的应用程序，不能调用位于用户态下的C或者C++库函数，而只能调用Linux内核提供的函数，在/proc/ksyms中可以查看到内核提供的所有函数。 </p>
<p>在以模块方式编写驱动程序时，要实现两个必不可少的函数init_module( )和cleanup_module( )，而且至少要包含&lt;linux/krernel.h&gt;和&lt;linux/module.h&gt;两个头文件。在用gcc编译内核模块时，需要加上-DMODULE -D__KERNEL__ -DLINUX这几个参数，编译生成的模块（一般为.o文件）可以使用命令insmod载入Linux内核，从而成为内核的一个组成部分，此时内核会调用模块中的函数init_module( )。当不需要该模块时，可以使用rmmod命令进行卸载，此进内核会调用模块中的函数cleanup_module( )。任何时候都可以使用命令来lsmod查看目前已经加载的模块以及正在使用该模块的用户数。 </p>
<p><a name=N100BB><span class=smalltitle><strong><font face=Arial>3. 设备驱动程序结构</font></strong></span></a></p>
<p>了解设备驱动程序的基本结构（或者称为框架），对开发人员而言是非常重要的，Linux的设备驱动程序大致可以分为如下几个部分：驱动程序的注册与注销、设备的打开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。 </p>
<ul>
    <li><strong>驱动程序的注册与注销</strong>
    <p>向系统增加一个驱动程序意味着要赋予它一个主设备号，这可以通过在驱动程序的初始化过程中调用register_chrdev( )或者register_blkdev( )来完成。而在关闭字符设备或者块设备时，则需要通过调用unregister_chrdev( )或unregister_blkdev( )从内核中注销设备，同时释放占用的主设备号。 </p>
    <li><strong>设备的打开与释放</strong>
    <p>打开设备是通过调用file_operations结构中的函数open( )来完成的，它是驱动程序用来为今后的操作完成初始化准备工作的。在大部分驱动程序中，open( )通常需要完成下列工作： </p>
    <ol>
        <li>检查设备相关错误，如设备尚未准备好等。
        <li>如果是第一次打开，则初始化硬件设备。
        <li>识别次设备号，如果有必要则更新读写操作的当前位置指针f_ops。
        <li>分配和填写要放在file-&gt;private_data里的数据结构。
        <li>使用计数增1。 </li>
    </ol>
    <p>释放设备是通过调用file_operations结构中的函数release( )来完成的，这个设备方法有时也被称为close( )，它的作用正好与open( )相反，通常要完成下列工作： </p>
    <ol>
        <li>使用计数减1。
        <li>释放在file-&gt;private_data中分配的内存。
        <li>如果使用计算为0，则关闭设备。 </li>
    </ol>
    <li><strong>设备的读写操作</strong>
    <p>字符设备的读写操作相对比较简单，直接使用函数read( )和write( )就可以了。但如果是块设备的话，则需要调用函数block_read( )和block_write( )来进行数据读写，这两个函数将向设备请求表中增加读写请求，以便Linux内核可以对请求顺序进行优化。由于是对内存缓冲区而不是直接对设备进行操作的，因此能很大程度上加快读写速度。如果内存缓冲区中没有所要读入的数据，或者需要执行写操作将数据写入设备，那么就要执行真正的数据传输，这是通过调用数据结构blk_dev_struct中的函数request_fn( )来完成的。 </p>
    <li><strong>设备的控制操作</strong>
    <p>除了读写操作外，应用程序有时还需要对设备进行控制，这可以通过设备驱动程序中的函数ioctl( )来完成。ioctl( )的用法与具体设备密切关联，因此需要根据设备的实际情况进行具体分析。 </p>
    <li><strong>设备的中断和轮询处理</strong> <br>
    <p>对于不支持中断的硬件设备，读写时需要轮流查询设备状态，以便决定是否继续进行数据传输。如果设备支持中断，则可以按中断方式进行操作。 </p>
    </li>
</ul>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="40" sizcache="2">
    <tbody sizset="40" sizcache="1">
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right sizset="41" sizcache="2">
    <tbody sizset="42" sizcache="2">
        <tr align=right sizset="42" sizcache="2">
            <td sizset="42" sizcache="2"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0 sizset="42" sizcache="2">
                <tbody sizset="42" sizcache="1">
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html#main" cmImpressionSent="1"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=3><span class=atitle>三、PCI驱动程序实现</span></a></p>
<p><a name=N1011D><span class=smalltitle><strong><font face=Arial>1. 关键数据结构</font></strong></span></a></p>
<p>PCI设备上有三种地址空间：PCI的I/O空间、PCI的存储空间和PCI的配置空间。CPU可以访问PCI设备上的所有地址空间，其中I/O空间和存储空间提供给设备驱动程序使用，而配置空间则由Linux内核中的PCI初始化代码使用。内核在启动时负责对所有PCI设备进行初始化，配置好所有的PCI设备，包括中断号以及I/O基址，并在文件/proc/pci中列出所有找到的PCI设备，以及这些设备的参数和属性。 </p>
<p>Linux驱动程序通常使用结构（struct）来表示一种设备，而结构体中的变量则代表某一具体设备，该变量存放了与该设备相关的所有信息。好的驱动程序都应该能驱动多个同种设备，每个设备之间用次设备号进行区分，如果采用结构数据来代表所有能由该驱动程序驱动的设备，那么就可以简单地使用数组下标来表示次设备号。 </p>
<p>在PCI驱动程序中，下面几个关键数据结构起着非常核心的作用： </p>
<ul sizset="43" sizcache="2">
    <li sizset="43" sizcache="2"><strong>pci_driver</strong>
    <p>这个数据结构在文件include/linux/pci.h里，这是Linux内核版本2.4之后为新型的PCI设备驱动程序所添加的，其中最主要的是用于识别设备的id_table结构，以及用于检测设备的函数probe( )和卸载设备的函数remove( )： </p>
    <p sizset="43" sizcache="2">
    <table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="43" sizcache="2">
        <tbody sizset="43" sizcache="1">
            <tr>
                <td class=code-outline>
                <pre class=displaycode>struct pci_driver {
                struct list_head node;
                char *name;
                const struct pci_device_id *id_table;
                int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);
                void (*remove) (struct pci_dev *dev);
                int  (*save_state) (struct pci_dev *dev, u32 state);
                int  (*suspend)(struct pci_dev *dev, u32 state);
                int  (*resume) (struct pci_dev *dev);
                int  (*enable_wake) (struct pci_dev *dev, u32 state, int enable);
                };
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br></p>
    <li sizset="44" sizcache="2"><strong>pci_dev</strong>
    <p>这个数据结构也在文件include/linux/pci.h里，它详细描述了一个PCI设备几乎所有的硬件信息，包括厂商ID、设备ID、各种资源等： </p>
    <p sizset="44" sizcache="2">
    <table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="44" sizcache="2">
        <tbody sizset="44" sizcache="1">
            <tr>
                <td class=code-outline>
                <pre class=displaycode>struct pci_dev {
                struct list_head global_list;
                struct list_head bus_list;
                struct pci_bus  *bus;
                struct pci_bus  *subordinate;
                void        *sysdata;
                struct proc_dir_entry *procent;
                unsigned int    devfn;
                unsigned short  vendor;
                unsigned short  device;
                unsigned short  subsystem_vendor;
                unsigned short  subsystem_device;
                unsigned int    class;
                u8      hdr_type;
                u8      rom_base_reg;
                struct pci_driver *driver;
                void        *driver_data;
                u64     dma_mask;
                u32             current_state;
                unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
                unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
                unsigned int    irq;
                struct resource resource[DEVICE_COUNT_RESOURCE];
                struct resource dma_resource[DEVICE_COUNT_DMA];
                struct resource irq_resource[DEVICE_COUNT_IRQ];
                char        name[80];
                char        slot_name[8];
                int     active;
                int     ro;
                unsigned short  regs;
                int (*prepare)(struct pci_dev *dev);
                int (*activate)(struct pci_dev *dev);
                int (*deactivate)(struct pci_dev *dev);
                };
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br></p>
    </li>
</ul>
<p><a name=N1014F><span class=smalltitle><strong><font face=Arial>2. 基本框架</font></strong></span></a></p>
<p>在用模块方式实现PCI设备驱动程序时，通常至少要实现以下几个部分：初始化设备模块、设备打开模块、数据读写和控制模块、中断处理模块、设备释放模块、设备卸载模块。下面给出一个典型的PCI设备驱动程序的基本框架，从中不难体会到这几个关键模块是如何组织起来的。 </p>
<p sizset="45" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="45" sizcache="2">
    <tbody sizset="45" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>/* 指明该驱动程序适用于哪一些PCI设备 */
            static struct pci_device_id demo_pci_tbl [] __initdata = {
            {PCI_VENDOR_ID_DEMO, PCI_DEVICE_ID_DEMO,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEMO},
            {0,}
            };
            /* 对特定PCI设备进行描述的数据结构 */
            struct demo_card {
            unsigned int magic;
            /* 使用链表保存所有同类的PCI设备 */
            struct demo_card *next;
            /* ... */
            }
            /* 中断处理模块 */
            static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
            {
            /* ... */
            }
            /* 设备文件操作接口 */
            static struct file_operations demo_fops = {
            owner:      THIS_MODULE,   /* demo_fops所属的设备模块 */
            read:       demo_read,    /* 读设备操作*/
            write:      demo_write,    /* 写设备操作*/
            ioctl:      demo_ioctl,    /* 控制设备操作*/
            mmap:       demo_mmap,    /* 内存重映射操作*/
            open:       demo_open,    /* 打开设备操作*/
            release:    demo_release    /* 释放设备操作*/
            /* ... */
            };
            /* 设备模块信息 */
            static struct pci_driver demo_pci_driver = {
            name:       demo_MODULE_NAME,    /* 设备模块名称 */
            id_table:   demo_pci_tbl,    /* 能够驱动的设备列表 */
            probe:      demo_probe,    /* 查找并初始化设备 */
            remove:     demo_remove    /* 卸载设备模块 */
            /* ... */
            };
            static int __init demo_init_module (void)
            {
            /* ... */
            }
            static void __exit demo_cleanup_module (void)
            {
            pci_unregister_driver(&amp;demo_pci_driver);
            }
            /* 加载驱动程序模块入口 */
            module_init(demo_init_module);
            /* 卸载驱动程序模块入口 */
            module_exit(demo_cleanup_module);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>上面这段代码给出了一个典型的PCI设备驱动程序的框架，是一种相对固定的模式。需要注意的是，同加载和卸载模块相关的函数或数据结构都要在前面加上__init、__exit等标志符，以使同普通函数区分开来。构造出这样一个框架之后，接下去的工作就是如何完成框架内的各个功能模块了。 </p>
<p><a name=N10162><span class=smalltitle><strong><font face=Arial>3. 初始化设备模块</font></strong></span></a></p>
<p>在Linux系统下，想要完成对一个PCI设备的初始化，需要完成以下工作：</p>
<ul>
    <li>检查PCI总线是否被Linux内核支持；
    <li>检查设备是否插在总线插槽上，如果在的话则保存它所占用的插槽的位置等信息。
    <li>读出配置头中的信息提供给驱动程序使用。 </li>
</ul>
<p>当Linux内核启动并完成对所有PCI设备进行扫描、登录和分配资源等初始化操作的同时，会建立起系统中所有PCI设备的拓扑结构，此后当PCI驱动程序需要对设备进行初始化时，一般都会调用如下的代码： </p>
<p sizset="46" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="46" sizcache="2">
    <tbody sizset="46" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int __init demo_init_module (void)
            {
            /* 检查系统是否支持PCI总线 */
            if (!pci_present())
            return -ENODEV;
            /* 注册硬件驱动程序 */
            if (!pci_register_driver(&amp;demo_pci_driver)) {
            pci_unregister_driver(&amp;demo_pci_driver);
            return -ENODEV;
            }
            /* ... */
            return 0;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>驱动程序首先调用函数pci_present( )检查PCI总线是否已经被Linux内核支持，如果系统支持PCI总线结构，这个函数的返回值为0，如果驱动程序在调用这个函数时得到了一个非0的返回值，那么驱动程序就必须得中止自己的任务了。在2.4以前的内核中，需要手工调用pci_find_device( )函数来查找PCI设备，但在2.4以后更好的办法是调用pci_register_driver( )函数来注册PCI设备的驱动程序，此时需要提供一个pci_driver结构，在该结构中给出的probe探测例程将负责完成对硬件的检测工作。 </p>
<p sizset="47" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="47" sizcache="2">
    <tbody sizset="47" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>
            static int __init demo_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
            {
            struct demo_card *card;
            /* 启动PCI设备 */
            if (pci_enable_device(pci_dev))
            return -EIO;
            /* 设备DMA标识 */
            if (pci_set_dma_mask(pci_dev, DEMO_DMA_MASK)) {
            return -ENODEV;
            }
            /* 在内核空间中动态申请内存 */
            if ((card = kmalloc(sizeof(struct demo_card), GFP_KERNEL)) == NULL) {
            printk(KERN_ERR "pci_demo: out of memory\n");
            return -ENOMEM;
            }
            memset(card, 0, sizeof(*card));
            /* 读取PCI配置信息 */
            card-&gt;iobase = pci_resource_start (pci_dev, 1);
            card-&gt;pci_dev = pci_dev;
            card-&gt;pci_id = pci_id-&gt;device;
            card-&gt;irq = pci_dev-&gt;irq;
            card-&gt;next = devs;
            card-&gt;magic = DEMO_CARD_MAGIC;
            /* 设置成总线主DMA模式 */
            pci_set_master(pci_dev);
            /* 申请I/O资源 */
            request_region(card-&gt;iobase, 64, card_names[pci_id-&gt;driver_data]);
            return 0;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N1018B><span class=smalltitle><strong><font face=Arial>4. 打开设备模块</font></strong></span></a></p>
<p>在这个模块里主要实现申请中断、检查读写模式以及申请对设备的控制权等。在申请控制权的时候，非阻塞方式遇忙返回，否则进程主动接受调度，进入睡眠状态，等待其它进程释放对设备的控制权。 </p>
<p sizset="48" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="48" sizcache="2">
    <tbody sizset="48" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int demo_open(struct inode *inode, struct file *file)
            {
            /* 申请中断，注册中断处理程序 */
            request_irq(card-&gt;irq, &amp;demo_interrupt, SA_SHIRQ,
            card_names[pci_id-&gt;driver_data], card)) {
            /* 检查读写模式 */
            if(file-&gt;f_mode &amp; FMODE_READ) {
            /* ... */
            }
            if(file-&gt;f_mode &amp; FMODE_WRITE) {
            /* ... */
            }
            /* 申请对设备的控制权 */
            down(&amp;card-&gt;open_sem);
            while(card-&gt;open_mode &amp; file-&gt;f_mode) {
            if (file-&gt;f_flags &amp; O_NONBLOCK) {
            /* NONBLOCK模式，返回-EBUSY */
            up(&amp;card-&gt;open_sem);
            return -EBUSY;
            } else {
            /* 等待调度，获得控制权 */
            card-&gt;open_mode |= f_mode &amp; (FMODE_READ | FMODE_WRITE);
            up(&amp;card-&gt;open_sem);
            /* 设备打开计数增1 */
            MOD_INC_USE_COUNT;
            /* ... */
            }
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N1019B><span class=smalltitle><strong><font face=Arial>5. 数据读写和控制信息模块</font></strong></span></a></p>
<p>PCI设备驱动程序可以通过demo_fops 结构中的函数demo_ioctl( )，向应用程序提供对硬件进行控制的接口。例如，通过它可以从I/O寄存器里读取一个数据，并传送到用户空间里： </p>
<p sizset="49" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="49" sizcache="2">
    <tbody sizset="49" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int demo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
            <!-- code sample is too wide -->{
            /* ... */
            switch(cmd) {
            case DEMO_RDATA:
            /* 从I/O端口读取4字节的数据 */
            val = inl(card-&gt;iobae + 0x10);
            /* 将读取的数据传输到用户空间 */
            return 0;
            }
            /* ... */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>事实上，在demo_fops里还可以实现诸如demo_read( )、demo_mmap( )等操作，Linux内核源码中的driver目录里提供了许多设备驱动程序的源代码，找那里可以找到类似的例子。在对资源的访问方式上，除了有I/O指令以外，还有对外设I/O内存的访问。对这些内存的操作一方面可以通过把I/O内存重新映射后作为普通内存进行操作，另一方面也可以通过总线主DMA（Bus Master DMA）的方式让设备把数据通过DMA传送到系统内存中。 </p>
<p><a name=N101AE><span class=smalltitle><strong><font face=Arial>6. 中断处理模块</font></strong></span></a></p>
<p>PC的中断资源比较有限，只有0~15的中断号，因此大部分外部设备都是以共享的形式申请中断号的。当中断发生的时候，中断处理程序首先负责对中断进行识别，然后再做进一步的处理。 </p>
<p sizset="50" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="50" sizcache="2">
    <tbody sizset="50" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
            {
            struct demo_card *card = (struct demo_card *)dev_id;
            u32 status;
            spin_lock(&amp;card-&gt;lock);
            /* 识别中断 */
            status = inl(card-&gt;iobase + GLOB_STA);
            if(!(status &amp; INT_MASK))
            {
            spin_unlock(&amp;card-&gt;lock);
            return;  /* not for us */
            }
            /* 告诉设备已经收到中断 */
            outl(status &amp; INT_MASK, card-&gt;iobase + GLOB_STA);
            spin_unlock(&amp;card-&gt;lock);
            /* 其它进一步的处理，如更新DMA缓冲区指针等 */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N101BE><span class=smalltitle><strong><font face=Arial>7. 释放设备模块</font></strong></span></a></p>
<p>释放设备模块主要负责释放对设备的控制权，释放占用的内存和中断等，所做的事情正好与打开设备模块相反：</p>
<p sizset="51" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="51" sizcache="2">
    <tbody sizset="51" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int demo_release(struct inode *inode, struct file *file)
            {
            /* ... */
            /* 释放对设备的控制权 */
            card-&gt;open_mode &amp;= (FMODE_READ | FMODE_WRITE);
            /* 唤醒其它等待获取控制权的进程 */
            wake_up(&amp;card-&gt;open_wait);
            up(&amp;card-&gt;open_sem);
            /* 释放中断 */
            free_irq(card-&gt;irq, card);
            /* 设备打开计数增1 */
            MOD_DEC_USE_COUNT;
            /* ... */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N101CE><span class=smalltitle><strong><font face=Arial>8. 卸载设备模块</font></strong></span></a></p>
<p>卸载设备模块与初始化设备模块是相对应的，实现起来相对比较简单，主要是调用函数pci_unregister_driver( )从Linux内核中注销设备驱动程序： </p>
<p sizset="52" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="52" sizcache="2">
    <tbody sizset="52" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static void __exit demo_cleanup_module (void)
            {
            pci_unregister_driver(&amp;demo_pci_driver);
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="53" sizcache="2">
    <tbody sizset="53" sizcache="1">
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right sizset="54" sizcache="2">
    <tbody sizset="55" sizcache="2">
        <tr align=right sizset="55" sizcache="2">
            <td sizset="55" sizcache="2"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0 sizset="55" sizcache="2">
                <tbody sizset="55" sizcache="1">
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html#main" cmImpressionSent="1"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=4><span class=atitle>四、小结</span></a></p>
<p>PCI总线不仅是目前应用广泛的计算机总线标准，而且是一种兼容性最强、功能最全的计算机总线。而Linux作为一种新的操作系统，其发展前景是无法估量的，同时也为PCI总线与各种新型设备互连成为可能。由于Linux源码开放，因此给连接到PCI总线上的任何设备编写驱动程序变得相对容易。本文介绍如何编译Linux下的PCI驱动程序，针对的内核版本是2.4。 </p>
<br><br>
<p><a name=resources><span class=atitle>参考资料 </span></a></p>
<ul>
    <li>David A Rusling在 <a href="http://tldp.org/LDP/tlk/dd/pci.html" cmImpressionSent="1"><u><font color=#0000ff>The Linux Kernel</font></u></a>中对Linux的PCI子系统进行了比较详细的介绍。 <br><br>
    <li><a href="http://tldp.org/HOWTO/PCI-HOWTO.html" cmImpressionSent="1"><u><font color=#0000ff>Linux PCI-HOWTO</font></u></a>是了解Linux下PCI设备的最好读物。 <br><br>
    <li>毛德操，胡希明，Linux内核源代码情景分析，杭州：浙江大学出版社，2001<br><br>
    <li>Alessandro Rubini,，Linux Device Drivers（2nd Edition） USA：O&#8217;Reilly，2001<br><br>
    <li>Tomshanley，DonAderson，PCI系统结构（第四版），北京：电子工业出版社，2000<br></li>
</ul>
<br><br>
<p><a name=author><span class=atitle>关于作者</span></a></p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="56" sizcache="2">
    <tbody sizset="56" sizcache="1">
        <tr>
            <td colSpan=3><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></td>
        </tr>
        <tr vAlign=top align=left>
            <td>
            <p>&#160;</p>
            </td>
            <td><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></td>
            <td width="100%">
            <p>肖文鹏，北京理工大学计算机系的一名硕士研究生，主要从事操作系统和分布式计算环境的研究，喜爱Linux和Python。你可以通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#120;&#105;&#97;&#111;&#119;&#112;&#64;&#50;&#54;&#51;&#46;&#110;&#101;&#116;&#63;&#99;&#99;&#61;" cmImpressionSent="1"><u><font color=#0000ff>xiaowp@263.net</font></u></a>与他取得联系。 </p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/89053.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-07-02 11:19 <a href="http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DirectDraw 与 DirectInput 的游戏编程体验</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86490.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:54:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86490.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86490.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86490.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86490.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86490.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Abstract/DirectDraw">http://dev.gameres.com/Program/Abstract/DirectDraw</a>与DirectInput的游戏编程体验.htm<br><br>
<table id=AutoNumber2 style="BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 width="85%" border=0>
    <tbody>
        <tr>
            <td width="100%">
            <p align=center><font color=#ffffee size=4><strong>DirectDraw 与 DirectInput 的游戏编程体验</strong></font></p>
            </td>
        </tr>
        <tr>
            <td width="100%">　</td>
        </tr>
        <tr>
            <td width="100%"><a href="file:///C:/Documents%2520and%2520Settings/lanier/妗岄潰/寰呭鐞嗘暟鎹甛寰愪紭鏂囩珷%25202003%5B1%5D.10.23/projects.zip"><u><font color=#0000ff>源代码下载（3个工程）</font></u></a> <font color=#ff0000>(编辑者：链接失效)</font>
            <p>　　我想关于这个主题的文章，不算少，但也不算太多。但大多是分别介绍 DirectDraw 与 DirectInput，而并没有将其结合起来，也许你会问：&#8220;分开与合并起来并没有本质区别啊！&#8221;。其实的确没有本质区别，但那样使那些最初对游戏编程报有极大热情的爱好者感到非常失望，因为这其中的一个并不能完全满足他们的要求，并且使其感到巨大的阻力，从而失去信心。所以本文将 DirectDraw 与 DirectInput结合起来去讲一个主题就是&#8220;游戏编程&#8221;，请注意是&#8220;游戏编程&#8221;，当然这只是一个简单的桌面游戏，但这已经与先前有很大的不同了，这已不是简单的 DirectDraw或 DirectInput编程。我想你现在应该能够体会出其中的区别了。</p>
            <p><font color=#ff0000>　　声明：在这之前需要你具有一定的 WIN32 API 函数的知识，并且可以熟练使用。和 DirectDraw的知识，关于DirectDraw可以参见 www.frontfree.net 中的 &lt;&lt;动画程序编写——DirectDraw之旅&gt;&gt; 1-3)，或其它文章。最后是 c++ 语言，当然也要包括面向对象的那部分。在 Visual C++ .NET 编译环境下进行开发的。</font></p>
            <p>　　首先 ，我们还是先简要复习一下DirectDraw的概念吧！<br>DirectDraw本质上是显存管理程序。它最重要的性能是允许程序员直接在显存里存储和操纵位图。它使你能够利用视频硬件bliter（位块传输器）在显存内部进行位图的blit（位块传输）。用视频硬件的blitter从显存向显存进行blit比从内存向显存更快。这在64位显卡向显存提供64位数据路径的今天显得尤其重要，硬件独立于促CPU进行位块传输操作，使得CPU得以继续工作。另外DirectDraw支持显卡的其他硬件加速特性，例如对精灵和z -buffering的硬件支持。</p>
            <p><strong>DirectDraw的工作原理</strong></p>
            <p>我们这里还是用图表方式展现给大家吧!</p>
            <p><img height=433 src="http://dev.gameres.com/Program/Abstract/DDandDIgame/image001.gif" width=505> </p>
            <p>　　细心的朋友可以很明显地注意到图示中的右上角的图解中说明，表面对象有两个宽度，一个是WIDTH，一个是PITCH。WIDTH就是创建表面时所给出的那个宽度，而PITCH是表面的实际宽度，是按字节算的。在许多显卡上，PITCH和WIDTH是相等的，比如在640x480的高彩模式下，PITCH为1280。而在某些显卡上，PITCH比WIDTH要大。比如在640x480的256色模式下，当WIDTH是640时，PITCH为1024而不是640，这些显卡这样做是为了更好地进行数据对齐来提高性能或达到其它目的。所以，我们在实际编程时，为了保证程序的兼容性，必须按PITCH处理。 但这些硬件的底层问题，我们不用太关心，只要稍有了解就可以了。</p>
            <p><strong>下面我们再简要叙述一下，如何使用 DirectX 9.0 中提供的 DirectDraw 类库来创建对象并使用操作对象。</strong></p>
            <p>宏定义在先，定义删除指针和释放对象的宏</p>
            <table class=code cellSpacing=1 cellPadding=0 width="100%" bgColor=#e0e0e0 border=0>
                <tbody>
                    <tr>
                        <td>
                        <p class=lc><font color=#0000ff>#define</font> <font color=#000000>SAFE_DELETE(p) { </font><font color=#0000ff>if</font><font color=#000000>(p) { </font><font color=#0000ff>delete</font><font color=#000000> (p); (p)=NULL; } }</font><br><font color=#0000ff>#define</font><font color=#000000> SAFE_RELEASE(p) { </font><font color=#0000ff>if</font><font color=#000000>(p) { (p)-&gt;Release(); (p)=NULL; } }</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>先创建一个 CDisplay 的全局对象<br>CDisplay就是ddutil.h中定义的类，用于处理表面之间的拷贝翻页等操作的类，再次定义一个全局变量，用于以后对指向的表面之间进行操作</p>
            <table class=lc cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>CDisplay* g_pDisplay = NULL;</font></td>
                    </tr>
                </tbody>
            </table>
            <p>然后创建表面，当然可以创建很多的表面，这些表面都是离屏表面，在更新画面时，都可以用 CDisplay 类的对象中的方法，将其拷贝到后备缓冲区表面上。只要创建离屏表面，就要用到 CSurface 类。CSurface也是ddutil.h头文件中定义的类，用于对表面本身进行操作，如设置色彩键码，在此定义的图画指针。<br>　</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>CSurface* g_pBackSurface = NULL;</font></td>
                    </tr>
                </tbody>
            </table>
            <p>DirectX 中就一共用这两个类封装了 DirectDraw 对象的大部分操作，如果你觉得这还不能满足要求，那么你也可以在程序中用 DirectDraw API 函数编写程序，不过在本文中不再介绍。</p>
            <p>这之后，我们会用到 InitDirectDraw 函数。这个函数是我们自己创建的。在此函数中作所有的 DirectDraw 的对象初始化工作。</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>HRESULT InitDirectDraw( HWND hWnd )<br>{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT hr; </font><font color=#006600>//接受返回值，其实是long型变量</font> <br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPDIRECTDRAWPALETTE pDDPal = NULL; </font><font color=#006600>//定义程序中的调色板</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int</font><font color=#000000> iSprite;</font> <font color=#006600>//定义与sprite个数有关的计数器</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_pDisplay = </font><font color=#0000ff>new</font> <font color=#000000>CDisplay();</font> <font color=#006600>//动态开辟一个CDisplay类</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;CreateFullScreenDisplay( hWnd, SCREEN_WIDTH, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SCREEN_HEIGHT, SCREEN_BPP ) ) )</font> <font color=#006600>/*设置程序为全屏，并且 g_pDisplay 就是动态开辟一个CDisplay类的指针，而在这个类的域中，有一个DirectDraw主表面指针，和一个后备缓冲区表面的指针。在从我建议你可以先去阅读一下 ddutil.h 和 ddutil.cpp 文件。*/</font><br>&nbsp;&nbsp;<font color=#000000>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBox( hWnd, TEXT("This display card does not support 1024x768x8. "),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TEXT("DirectDraw Sample"), MB_ICONERROR | MB_OK );</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font><font color=#000000> hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;CreatePaletteFromBitmap( &amp;pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) )</font> <font color=#006600>//顾名思义，就是从bmp图片中获得调色板值，并赋值在pDDPal结构指针所指向的结构体中。</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#0000ff>if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;SetPalette( pDDPal ) ) )</font> <font color=#006600>//用刚才从IDB_DIRECTX中获得的调色板制来设置程序调色板</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE( pDDPal );</font><font color=#006600>//释放指针，在用过后，一定要释放，这是良好的编程习惯</font><br><font color=#006600>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 用IDB_WINXP图片创建一个表面，并用g_pBackSurface指向这个表面<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;CreateSurfaceFromBitmap( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;g_pBackSurface, MAKEINTRESOURCE( IDB_WINXP ), <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SCREEN_WIDTH, SCREEN_HEIGHT ) ) )</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font><font color=#000000> hr;</font><font color=#006600>//设置色彩键码为黑色，0代表黑色，这样在表面的拷贝过程中黑色像素的点将不会被拷贝，这样可以产生镂空效果。当然你可以任意设置关键颜色，而颜色的表示法可以用 RGB 宏定义。例如 红色：RGB( 255,0,0 ), 黑色 RGB( 255,255,255 )<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = g_pBackSurface-&gt;SetColorKey( RGB( 255,255,255 ) ) ) )</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>S_OK;<br>}</font></td>
                    </tr>
                </tbody>
            </table>
            <p>下面的函数是用于更新画面的。<br>　</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>HRESULT DisplayFrame()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_pDisplay-&gt;Clear( 0 ); </font><font color=#006600>//清空后备缓冲区表面</font><br><font color=#006600>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//将g_pBackSurface所指向的图片拷贝到后备缓冲区表面</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_pDisplay-&gt;Blt( 0, 0, g_pBackSurface, NULL );</font><font color=#006600>//最关键的地方在这里，请看下面的语句，只要我们一执行翻页操作，就可以将改动了的图像了显示在屏幕上了<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#0000ff>if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;Present()</font> <font color=#006600>/*翻页操作*/</font><font color=#000000>) )</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>S_OK;</font><br><font color=#000000>}</font></td>
                    </tr>
                </tbody>
            </table>
            <p>下面的函数是用于在程序失去焦点时调用的。</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>HRESULT RestoreSurfaces()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPDIRECTDRAWPALETTE pDDPal = NULL;</font> <font color=#006600>/*当程序失去焦点，要保存当前的画面，请注意这里，g_pDisplay-&gt;GetDirectDraw()函数返回的才是真正的 DirectDraw 对象 */<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#0000ff>if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;GetDirectDraw()-&gt;RestoreAllSurfaces() ) )</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;</font><font color=#006600>//在此我们还要重新创建调色板<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;CreatePaletteFromBitmap( &amp;pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) )</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;</font><font color=#006600>//重新设置调色板<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#0000ff>if</font><font color=#000000>( FAILED( hr = g_pDisplay-&gt;SetPalette( pDDPal ) ) )</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE( pDDPal );</font><font color=#006600>//重新画出图画<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#0000ff>if</font><font color=#000000>( FAILED( hr = g_pLogoSurface-&gt;DrawBitmap( MAKEINTRESOURCE( IDB_WINXP ),<br>SPRITE_DIAMETER, SPRITE_DIAMETER ) ) )</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#0000ff>return</font> <font color=#000000>hr;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>S_OK;<br>}</font></td>
                    </tr>
                </tbody>
            </table>
            <p>下面这个函数是释放表面指针所用的。</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>VOID FreeDirectDraw()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_DELETE( g_pBackSurface );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAFE_DELETE( g_pDisplay );<br>}</font></td>
                    </tr>
                </tbody>
            </table>
            <p>我们的回顾到此结束，下面我们开始本文要介绍的一个关键技术，DirectInput 的使用。<br>游戏编程可不仅仅是图形程序的开发工作，实际上包含了许多方面，本文所要讲述的就是关于如何使用 DirectInput 来对键盘编程的问题。<br>而我们为什么要选择用 DirectInput 来处理游戏中的输入问题呢？其实用 Win32 API 函数也完全可以处理这些工作，例如其中，有一个 </p>
            <p>GetAsyncKeyState() 的函数可以返回一个指定键的当前状态是按下还是松开。这个函数还能返回该指定键在上次调用 GetAsyncKeyState() 函数以后，是否被按下过。虽然这个函数听上去很不错，但需要我们自己轮换查询每个键盘的状态。而在 DirectInput 中我们已经可以脱离这些烦琐的工作，只因它的功能更强大。</p>
            <p>由于本文重点在二者的结合，故在此只介绍 DirectInput 中比较简单的，而且最容易上手的立即模式的工作方式。<br>而这里我们要用到 DirectInput 的 API 函数。有人会问，为什么在 DirectDraw 中用 DirectX 提供的类库编程，而对于 DirectInput 却直接使用要用其 API 函数呢，是因为没有提供 DirectInput 的类库吗？不是！而是因为使用类库并不很方便而且不灵活。</p>
            <p>OK，让我们开始我们游戏编程的第二部——DirectInput编程。</p>
            <p>前面讲 DirectDraw 时，并没有提到，微软是按 COM 来设计DirectX的，所以就有了一个 DIRECTINPUT 对象来表示输入设备，而某个具体的设备由 DIRECTINPUTDEVICE 对象来表示。也许会感到很无奈，怎么游戏编程需要这么多的知识啊，其实您也无需烦恼，只要知道一下就可以了，其实这并不；影响您的设计，而且就算您不知道，也同样可以驾驭DIRECTINPUT。</p>
            <p>实际的建立过程是先创建一个 DIRECTINPUT 对象，然后在通过此对象的 CreateDevice 方法来创建 DIRECTINPUTDEVICE 对象。</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=lc><font color=#0000ff>#include</font><font color=#000000> &lt;dinput.h&gt;</font><br><font color=#0000ff>#define</font> <font color=#000000>DINPUT_BUFFERSIZE 16<br>LPDIRECTINPUT lpDirectInput; </font><font color=#006600>// DirectInput 对象实际上是一个com对象</font><br><font color=#000000>LPDIRECTINPUTDEVICE lpKeyboard; </font><font color=#006600>// DirectInput 设备<br></font><font color=#000000>BOOL InitDInput(HWND hWnd)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT hr;</font><font color=#006600>// 创建一个 DIRECTINPUT 对象<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = DirectInputCreate(hInstanceCopy, DIRECTINPUT_VERSION, &amp;lpDirectInput, NULL)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><font color=#006600>// 失败提示或处理</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font> <font color=#0000ff>&nbsp;return</font> <font color=#000000>hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><font color=#006600>// 创建一个 DIRECTINPUTDEVICE 界面<br>//参数 GUID_SysKeyboard 指明了建立的是键盘对象</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = lpDirectInput-&gt;CreateDevice(GUID_SysKeyboard, &amp;lpKeyboard, NULL)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>// 失败提示或处理</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><font color=#006600>// 设定为通过一个 256 字节的数组返回查询状态值</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED(hr = lpKeyboard-&gt;SetDataFormat(&amp;c_dfDIKeyboard)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>// 失败提示或处理</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font><font color=#000000> hr;</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><font color=#006600>// 设定协作模式为独占模式和前台模式，独占模式表面本程序在运行中占有所有键盘资源，而前台模式指出当程序具有焦点时才可以占有键盘资源</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( hr = lpKeyboard-&gt;SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>// 失败提示或处理</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><br><font color=#006600>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设定缓冲区大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 如果不设定，缓冲区大小默认值为 0，程序就只能按立即模式工作<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 如果要用缓冲模式工作，必须使缓冲区大小超过 0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 在此，我们没有必要设定，因为我们就用立即模式工作（还有一种缓冲模式）,所有我们将其注调了</font></p>
                        <p><font color=#006600>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* DIPROPDWORD property;</font></p>
                        <p><font color=#006600>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property.diph.dwSize = sizeof(DIPROPDWORD);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property.diph.dwHeaderSize = sizeof(DIPROPHEADER);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property.diph.dwObj = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property.diph.dwHow = DIPH_DEVICE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;property.dwData = DINPUT_BUFFERSIZE;</font></p>
                        <p class=lc><font color=#006600>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( FAILED(hr = lpKeyboard-&gt;SetProperty(DIPROP_BUFFERSIZE, &amp;property.diph)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 失败<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>return FALSE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</font><br><font color=#006600>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//此处是关键，我们要通过这个函数来锁定键盘，记住，所有的DirectInput资源在使用前都要锁定，在此即获得键盘资源，在知识我们刚才设定的键盘模式才能起作用<br></font><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hr = lpKeyboard-&gt;Acquire();</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000> FAILED(hr)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>// 失败</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font><font color=#000000> FALSE;</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font> <font color=#0000ff>return</font> <font color=#000000>TRUE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>在这段代码中，我们首先定义了 lpDirectInput 和 lpKeyboard 两个指针，前者指向 DIRECTINPUT 对象，后者指向一个 </p>
            <p>DIRECTINPUTDEVICE 界面。其顺序就是这样的。这和其它COM对象的使用方法都一样，即先创建 COM 对象，然后创建界面，然后再获得<br>硬件资源，然后使用资源，然后释放。</p>
            <p>通过 DirectInputCreate(), 我们为 lpDirectInput 创建了一个 DIRECTINPUT 对象。然后我们调用 CreateDevice 来建立一个DIRECTINPUTDEVICE 界面。<br>　　<br>完成这些工作以后，我们便调用 DIRECTINPUTDEVICE 对象的 Acquire 方法来激活对设备的访问权限。在此要特别说明一点，任何一个 </p>
            <p>DIRECTINPUT 设备，如果未经 Acquire，是无法进行访问的。还有，当系统切换到别的进程时，必须用 Unacquire 方法来释放访问权限，在系统切换回本进程时再调用 Acquire 来重新获得访问权限。</p>
            <p>立即模式的数据查询</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>HRESULT ReadImmediateData( HWND hWnd )<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT hr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BYTE diks[256];</font> <font color=#006600>// 创建键盘状态数据缓冲区存取键盘信息</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int</font> <font color=#000000>i; </font><font color=#006600>// 计数器</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( NULL == g_pKeyboard ) </font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>S_OK;</font><font color=#006600>// 键盘状态数据缓冲区清0</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#000000>ZeroMemory( &amp;diks, </font><font color=#0000ff>sizeof</font><font color=#000000>(diks) );</font><font color=#006600>// 获得键盘所有键的信息，这只是检查一次</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#000000>hr = g_pKeyboard-&gt;GetDeviceState( </font><font color=#0000ff>sizeof</font><font color=#000000>(diks), &amp;diks );</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED(hr) ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>// 如果键盘资源丢失，我们要重新获得</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hr = g_pKeyboard-&gt;Acquire();</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while</font><font color=#000000>( hr == DIERR_INPUTLOST ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hr = g_pKeyboard-&gt;Acquire();</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>S_OK; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><font color=#006600>// 进行一下轮循，处理键盘信息。</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for</font><font color=#000000>( i = 0; i &lt; 256; i++ ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( diks[i] &amp; 0x80 ) </font><font color=#006600>//记录此键的状态，低字节最高位是 1 表示按下，0 表示松开，一般用 diks[i]&amp;0x80 来测试</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;switch</font><font color=#000000>(i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>//我们可以通过测试计数器i，来判断是哪个键被按下了。<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>//我们提供几个数据 UP：200 down：208 left：203 right：205 enter：28 space：57<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>//其实你可以用DirectX中的Samples\C++\DirectInput\Bin\Keyboard.exe程序来测试，只不过那是用<br></font><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>//16进制显示的。</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case</font> <font color=#000000>200:</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case</font> <font color=#000000>0xc8:</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font> <font color=#000000>S_OK;<br>}</font></td>
                    </tr>
                </tbody>
            </table>
            <p>请注意，上面的这段代码只是一个示例，重在使你明白其原理，但并不能满足游戏的需求，因为这其中只查询了一次键盘的全部信息，做了一次轮循，而在游戏中要周期性地查询，并轮循，这就需要你自己用Win32 API函数 SetTimer和 KillTimer 设置初始化 DirectInput 对象函数中在相应的地方设置计计时器，让windows定时向程序发送 WM_TIMER消息，你要通过此消息进行周期性地键盘查询，并在相应的地方解除计时器。</p>
            <p>最后一个函数是用于释放指针或DirectInput对象的</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#0000ff>void</font><font color=#000000> ReleaseDInput(void)<br>{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font> <font color=#000000>(lpDirectInput)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>(lpKeyboard)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>// Always unacquire the device before calling Release().</font><br><font color=#0000ff>&nbsp;</font><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpKeyboard-&gt;Unacquire();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpKeyboard-&gt;Release();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpKeyboard = NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpDirectInput-&gt;Release();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpDirectInput = NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</font></td>
                    </tr>
                </tbody>
            </table>
            <p>在这些函数中的注释很明确，关键在于理解其原理，而怎样将他们融入到 Win32 API 程序的基本框架中的，在&lt;&lt;动画程序编写——DirectDraw之旅&gt;&gt; 1-3中的示例代码中已经解释得很明确了，在此不再赘述。不过我们提供其中的代码示例下载。同时你也可以去仔细阅读DirectX 8.0 SDK 包中的 <font color=#99ccff>samples\ multimedia\ directdraw\ fullscreenmode</font> 或<font color=#006600> </font><font color=#99ccff>\ samples\ multimedia\ directdraw\ windowedmode</font> 这两个工程中的文件，因为为了我们的示例也是照这两个工程改编过来的，读者可以通过仔细阅读代码和对比我们的更改，而更加了解 DirectDraw的运行运行原理。（请注意：是 DirectX 8.0 SDK 包中的示例，而在 9.0 中 DirectX SDK 已经不提供 DirectDraw的示例代码了）</p>
            <p>我们就用这七个函数就已经可以创造出一个小游戏了。</p>
            <p>我们下面就要利用&lt;&lt;动画程序编写——DirectDraw之旅&gt;&gt; 1-3 中所用的代码进行进一部的游戏开发。<br>我们先展示一下 DirectX中<font color=#99ccff>\ samples\ multimedia\ directdraw\ windowedmode </font>工程中的截图<br>在这个动画中有黑色背景，并有很多 DirectX 精灵在漂浮。</p>
            <p><img height=301 src="http://dev.gameres.com/Program/Abstract/DDandDIgame/image002.jpg" width=400><br><br>这是一个全屏的动画程序，而我们在&lt;&lt;动画程序编写——DirectDraw之旅&gt;&gt; 1-3其中做的改动就是为其加了一个背景，改屏幕分辨率 640&#215;480 为 1024&#215;768.注意，因为我们应用的是全屏模式，即可以独占显存资源，所以我们可以更改屏幕的分辨率。这只是做的小小的改动，而我们的目的只在于让大家更加深入了解。且看下面的这副截图：</p>
            <p><img height=300 src="http://dev.gameres.com/Program/Abstract/DDandDIgame/image004.jpg" width=400></p>
            <p>而我们还要继续深入编程，我们的思路是，先将程序由先前的全屏程序改编成一个windows的窗口程序，然后将其所有的界面翻新，并改编 DirectX精灵为许多小蘑菇在漂浮，还要加入DirectInput 的组建，用键盘控制一个小娃娃。可以上下左右，并可以斜向飞行。<br>我们先将此动画的截图展现给大家</p>
            <p><img height=320 src="http://dev.gameres.com/Program/Abstract/DDandDIgame/image006.jpg" width=400></p>
            <p>怎么样，你有什么想法，是想说：&#8220;唉，这还不好办，就是又多加了一个！&#8221;，但不要光看截图，不要忘记，我们一定让她动起来，并且是可以控制的，这就不是那么简单的事了！<br>什么？若有人看到这里感到有些迷茫和泄气，不禁想问：&#8220;你说了这么多，那么源代码在那里呢！，光给我们几个函数，又能做什么呢？&#8221;，如果你这么想，你也不要太急迫。我们还是先分析一下程序框架吧。</p>
            <p>不过，还有一件重要的事情，我还是要重申一边。一定要将 DirectX 的头文件价，和lib文件夹加入到 Visual C++.NET 的默认目录中去，这样编译器就可以正确地找到它们了。<br>如果你不会加入，就请通过工具栏上的 Tool -&gt; Option&#8230; 打开Option 对话框，设置如图：</p>
            <p><img height=320 src="http://dev.gameres.com/Program/Abstract/DDandDIgame/image008.jpg" width=550><br><br>好了，这样我们的准备工作就算已经做好了。</p>
            <p>来看看我们的工程文件结构吧，还有工程中的资源。</p>
            <p><img height=328 src="http://dev.gameres.com/Program/Abstract/DDandDIgame/image010.jpg" width=211> <img height=329 src="http://dev.gameres.com/Program/Abstract/DDandDIgame/image011.jpg" width=214></p>
            <p>在工程资源中我们的 ID 号是都用的字符串表示的，笔者认为这样更加方便。<br>我想对于工程文件中的 ddutil.cpp 和 dxutil.cpp 文件，读者如果了解有些 DirectDraw编程是不会感到陌生的，我们只是将其引入到我们的工程中了。而我们自己实际编程的文件是<br>outfly.cpp 文件。</p>
            <p>我们的程序叙述如下：<br>首先进行宏定义，结构设置，和全局变量的声明。<br>后在 WinMain （windows程序的入口点）中首先初始化一切需要初始化的物件（有windows窗口，DirectDraw对象，和 DirectInput对象），在此我们就调用前文讲过的函数，但要有写改动，读者在会在后面看到的。然后进入消息循环，在其中没有消息时，程序会自动更新画面，在有消息时处理消息。<br>当遇到 WM_QUIT 消息后，结束整个程序。</p>
            <p>我们在一些地方有一些小小的改动，我们来看看吧。</p>
            <p><strong>1</strong> 我们在 HRESULT InitDirectInput( HWND hWnd )<br>函数中的开始加入了</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>KillTimer( hWnd, 0 ); <br>FreeDirectInput();</font></td>
                    </tr>
                </tbody>
            </table>
            <p>关掉上一次使用的计时器，并释放 DirectInput 设备。<br>而在最后加入了</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#000000>SetTimer( hWnd, 0, 1000 / 100, NULL );</font></td>
                    </tr>
                </tbody>
            </table>
            <p>用来重新设置计时器。</p>
            <p><strong>2</strong> 我们在主窗口的消息处理函数中加入了</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0><font color=#0000ff>case</font><font color=#000000> WM_ACTIVATE: </font><font color=#006600>//当程序先失去焦点，而现在有重新得到焦点时，要重新锁定键盘资源</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( WA_INACTIVE != wParam &amp;&amp; g_pKeyboard )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#006600>// Make sure the device is acquired, if we are gaining focus.</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_pKeyboard-&gt;Acquire();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#0000ff>case</font> <font color=#000000>WM_TIMER:</font><font color=#006600> //因为设置了计时器所以要处理此消息</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( FAILED( ReadImmediateData( hWnd ) ) )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;KillTimer( hWnd, 0 ); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBox( NULL, _T("Error reading input state. ")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_T("The sample will now exit."), <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_T("Keyboard"), MB_ICONERROR | MB_OK );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#0000ff>case</font> <font color=#000000>WM_DESTROY:</font><font color=#006600>// Cleanup and close the app</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FreeDirectDraw();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FreeDirectInput();</font> <font color=#006600>// 释放资源</font><br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PostQuitMessage( 0 );</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</font><font color=#000000> 0L;</font></td>
                    </tr>
                </tbody>
            </table>
            <p><strong>3</strong> 在HRESULT ReadImmediateData( HWND hWnd ) 函数中进行了这样的处理，来时时改变小娃娃的坐标。</p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=lc bgColor=#e0e0e0><font color=#0000ff>for</font><font color=#000000>( i = 0; i &lt; 256; i++ ) <br>{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( diks[i] &amp; 0x80 ) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;switch</font><font color=#000000>(i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case</font> <font color=#000000>200: </font><font color=#006600>//上键</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( g_me.fPosY &gt; g_me.fVelY)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_me.fPosY -= g_me.fVelY;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#000000>&nbsp;g_me.fPosY = 0;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case</font> <font color=#000000>208: </font><font color=#006600>//下键</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( g_me.fPosY &lt;= WINDOW_HEIGHT - SPRITE_DIAMETER - g_me.fVelY)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_me.fPosY += g_me.fVelY;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#000000>&nbsp;&nbsp;&nbsp;g_me.fPosY = WINDOW_HEIGHT- SPRITE_DIAMETER;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case</font> <font color=#000000>203:</font><font color=#006600>//左键</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( g_me.fPosX &gt; g_me.fVelX)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_me.fPosX -= g_me.fVelX;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#000000>&nbsp;&nbsp;&nbsp;g_me.fPosX = 0;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case</font> <font color=#000000>205:</font><font color=#006600>//右键</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</font><font color=#000000>( g_me.fPosX &lt;= WINDOW_WIDTH - SPRITE_DIAMETER - g_me.fVelX)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g_me.fPosX += g_me.fVelX;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;g_me.fPosX = WINDOW_WIDTH- SPRITE_DIAMETER;</font><br><font color=#0000ff>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break</font>;<br><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</font></td>
                    </tr>
                </tbody>
            </table>
            <p>这些只是其中一些比较重要的改动，还有许多改动，读者会在实际的程序中看到的。如果你觉得：&#8220;啊！到这里就结束了，可是我还是感到似乎莫不到头绪，就这样草草收尾了？&#8221;，其实文章并没有结束，重头戏还在后面呢，那就不是我的工作了，而是看你有没有耐心去仔细阅读代码了，因为想要把握程序的整体，与其让我将代码放在文章中，还不如读者自己在编译器中自己运行实践一下好，其实我们已经在第二个工程代码中有过详细的解释。但记住一定要按照顺序阅读 工程1，工程2 ，工程3。工程1就是 DirectX中提供的原代码，工程2就是我们改了一个背景的工程，而3就是我们讨论的工程。</p>
            </td>
        </tr>
        <tr>
            <td width="100%">　</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86490.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-06-01 23:54 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86490.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DirectDraw编程基础</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86488.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:43:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86488.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86488.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86488.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86488.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86488.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DDrawBase1.htm">http://dev.gameres.com/Program/Visual/2D/DDrawBase1.htm</a><br><br>
<table id=table2 style="BORDER-COLLAPSE: collapse" cellPadding=0 width="100%" border=0>
    <tbody>
        <font face=Arial>
        <tr>
            <td bgColor=#f7ebde>
            <p align=center><font color=#000000><strong>DirectDraw编程基础</strong></font></p>
            </td>
            <td width=24 background=../../../../images1/frame/right.gif>　</td>
        </tr>
        <tr>
            <td width=21 background=../../../../images1/frame/left.gif>　</td>
            <td bgColor=#f7ebde><font class=NewsContent2><br>　　本文面向有几个月学习编程经历的初学者：看过C++的教程，看的懂基本的C++语法;有点点VC使用经验，知道怎么去组建一个工程；理解一些windows编程的基本概念，比如窗口、消息循环等；还有，不懂的地方会去查资料：）。<br>　　看过几本关于DirectDraw的书，这些书都不错，在此感谢她们的作者。美中不足的是这些书的部分起点较高，虽然我们仍然能够清晰的理解一些概念，但在组织这些文件上会有不少困惑。在此我重申一下书中的概念，也借此梳理一下自己的思路。废话少说，言归正传。<br>首先说一些不可不说的东西。我认为它们不可不提，是因为这些东西也许太基础，高手们往往忽略这些东西对新手的作用。作为一个新手，我觉得掌握程序的框架及组织方法，比多熟悉几个APIs更迫切一些。Now lets begin：<br>　　写一个游戏程序，要熟悉其流程，另外要锻炼组织程序文件的能力。对新手来说，我建议按部就班的来处理及分析要写的程序，不主张这个时候你在搞思维跳跃。这是个良好的习惯，当然也有利于我们尽快掌握编程的思想方法。下面来看一个概括的流程及相应的程序框架 <br><br>（框架显示不出来。。）<br><br>　　那么，如何利用上面的流程来构建我们的大体程序框架呢？<br><br>　　我们已经知道一些windows编程方面的东西了，也许你还比较了解MFC。我们这里不提倡用MFC，尽管它封装了好多有用的模式，但对我们编游戏来说，倒是累赘了。好，接着说。既然采用windowsAPI，可以建立个文件WinMain.cpp来处理windows编程中有关窗口的一些问题。这样，我们在该文件中应该完成创建窗口，处理基本消息（比如按&#8220;esc&#8221;退出等），控制程序退出等。游戏过程中窗口的消息是不是也要在这处理呢？当然，不过游戏当中的窗口就不仅是windows窗口了，显示部分要靠DirectDraw来控制，那么我们只好在WinMain.cpp中调用相关的模块来处理。这么看来，在WinMain.cpp中几乎囊括了整个流程,不错，它就控制了程序的整个框架，为你的程序内核提供了一个平台。平台有了，那么下一步，GameMain.cpp要诞生了，这个主要用来控制整个游戏的各个组件，协调各部分工作，完成游戏设置初始化，游戏中消息循环，控制游戏退出。你的才华就在这儿来尽情的发挥了。一般，游戏程序会有几个固定的组件的：显示，音乐，信息输入。在DirectX中提供了很方便的组件DirectDraw，DirectSound和DirectMusic，DirectInput。相应的我们建立MyDirectDraw.cpp，MyDirectAudio.cpp，MyDirectInput.cpp来控制各部分组件的相应功能。<br>显然，这3部分都是为GameMain.cpp服务的，被GameMain.cpp调用。那么我们可以看出我们的程序应该包括的文件及其包含关系为：<br><br>（图表显示不出来了，555）<br><br>　　程序文件怎么去组织，应该由这个表可以看出来。这么一看，我们发现，WinMain.cpp好像是一个投资者，提供开发平台，他只关注整个项目总的进程，不关注细节。GameMain.cpp好像个项目负责人，整个项目的细节过程由他来策划，来控制，向上与WinMain.cpp交互，来完成项目，向下协调MyDirectDraw.cpp，MyDirectAudio.cpp，MyDirectInput.cpp之间的工作。MyDirectDraw.cpp，MyDirectAudio.cpp，MyDirectInput.cpp这三个家伙就是员工了，负责各自的工作，完成相应的功能给GameMain.cpp。<br><br>　　组织程序应该就是这么个思路，当然具体问题具体分析。那么我们下面来开始看DirectDraw部分了。<br><br>　　首先，做准备工作，安装DirectX SDK，在VC中添加dxguid.lib和ddraw.lib（本来不想说这个，看到有个教程，它少加了dxguid.lib，郁闷了我好一阵子，害人颇深感觉）这样，directdraw程序才能通过编译。提一下，dxguid.lib中定义了DirectX中会用到的所有全局句柄，ddraw.lib是DirectDraw使用的函数库。<br><br>下面就可以写代码了，这里我们当然主要看MyDirectDraw.cpp该怎么写了<br>为此，我选出了几个源代码，做参考研究，它们会与本文一起打包。<br>我还是习惯先从整体上鸟瞰一下：<br><br>　　一般，在MyDirectDraw.cpp(注意不要忘记引用头文件ddraw.h)中至少要有两部分：初始化和结束。先看初始化，所谓初始化无非是个准备工作，需要的东西定义创建出来摆在手边以备后用。来看看初始化函数intMyDirectDrawInit（void）该怎么写。首先定义一个指向DirectDraw对象的指针，创建DirectDraw对象，查询以获取最新的DirectDraw接口，设置协作等级，设置显示模式。通过这些步骤可以创建一个黑色的屏幕了，也就是说已经开辟了我们需要的空间了，当然DirectDraw程序的初始化不会这么简单。要操作2d图形，我们还要接着创建主页面和缓冲页面以及离屏页面，总之根据需要，凡是需要在操作前需要准备好的东西都可以放在这里。那么结束 int MyDirectDrawShut（void）就应该释放我们开辟的东西，一般要释放主页面指针，和DirectDraw接口等。<br><br>大体就是这么个样子，go on，该细一点了，呵呵<br><br><font color=#000000>先定义指针：LPDIRECTDRAW lpDDraw_temp；代表整个显示系统<br>创建对象： if (FAILED(DirectDrawCreate(NULL, &amp;lpDDraw_temp, NULL)))<br>{<br>&nbsp;&nbsp;&nbsp; MessageBox(NULL,TEXT("Direct Draw Create error!"),<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>TEXT("Wrong!"),MB_OK);<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>return(0);<br>}<br></font><br>这里用了一个FAILED宏来检测是否创建成功，这可以帮我们跟踪错误。<br><br>函数DirectDrawCreate(NULL, &amp;lpDDraw_temp, NULL)完成创建，第一个参数是显示驱动的全局唯一标志符，这里null表示目前的显示设备；第二个参数用来接受创建出来的DirectDraw对象地址，这里用&amp;lpDDraw_temp接受；第三个参数？不要问，就给它null，不想惹麻烦的话。<br><br><font color=#000000>查询DirectDraw接口：if(FAILED(lpDDraw_temp-&gt;QueryInterface(IID_IDirectDraw7, (LPVOID *)&amp;lpDDraw7)))<br>{<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>MessageBox(NULL,TEXT("DirectDraw QueryInterface error!"),<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>TEXT("Wrong!"),MB_OK);<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>return(0);<br>}</font><br><br>通过QueryInterface()方法来获取新接口，这里是IDirectDraw7而不是IDirectDraw8，指向IDirectDraw7的指针放在lpDDraw7中，这是个全局变量，可以这样定义LPDIRECTDRAW7 lpDDraw7＝NULL；<br><br>顺便说一下，一般情况下你是应该知道你使用的接口的，这和SDK有关，所以说这一步不是必须的。<br><br><font color=#000000>设置协作等级： if (FAILED(lpDDraw7-&gt;SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)))<br>{<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>MessageBox(NULL,TEXT("DirectDraw SetCooperativeLevel error!"),<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>TEXT("Wrong!"),MB_OK);<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>return(0);<br>}</font><br><br>决定你这个程序和windows的关系，它向windows申请所用资源，比如它要全屏，独占等。第一个参数是主窗口句柄，就是你WinMain（）中创建的那个了，第二个参数有几个控制标志，常用的用法如下：<br><br><font color=#000000>DDSCL_FULLSCREEN：全屏模式，必须和DDSCL_EXCLUSIVE同时使用<br>DDSCL_EXCLUSIVE：请求独占级别，须和DDSCL_FULLSCREEN同时使用<br>DDSCL_ALLOWREBOOT：允许系统检测ctrl+alt+del按键消息（这很有用）</font><br><br>我想，这三个就够用了，其他的就先不用管了<br><font color=#000000>设置显示模式：if(FAILED(lpDDraw7-&gt;SetDisplayMode(800, 600, 16,0,0)))<br>{<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>MessageBox(NULL,TEXT("DirectDraw SetDisplayMode error!"),<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>TEXT("Wrong!"),MB_OK);<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>return(0);<br>}<br></font><br>游戏中要使用的显示模式可能和用户当前显示模式不一样，要在此统一设置SetDisplayMode（）强制使用它设置的模式，它的前三个参数很容易懂吧，第四个，用0表示使用默认的刷新率，第五个参数这里是0，有书上说必须用DDSDM_STANDVGAMODE（可以理解，只是不知道这个0什么意思，我想应该是default的意思吧。<br><br>到此为止，我想已经创建出来我们需要的空间了，以后，随着我们要求的提高，再逐步完善初始化函数，now看看结束函数：<br><font color=#000000>释放接口： if (lpDDraw7)<br>{<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>lpDDraw7-&gt;Release();<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>lpDDraw7 = NULL;<br>}<br></font><br>以后还要释放主页面，缓冲页面等，需要注意一点的是一定要释放你申请的资源，这是个好习惯，更应该注意的一点是先创建的一定要后释放，因为后创建的可能是在先创建的环境下工作的。<br><br>到此为止，我们只是做好了最基础的准备工作，什么还都不能做呢<br>想做点什么吗？歇会吧，说点不得不说的题外话：<br><br>那么我们来看看颜色吧。有关色彩，分这么几种，256色（8位的），16位增强色，24位真彩和32位真彩。256色估计很少用了，16位目前还是主流，所以我们着重看一下16位增强色，通常16位增强色有两种格式：5.5.5和5.6.5，一般用RGB表示法表示。其中：<br>5.5.5格式，最高位为Alpha位，表示是不是透明，其余15位表示颜色，红绿蓝各5位，这种格式可以表示32786种颜色。通过宏<br><br><font color=#000000>＃define _RGB16BIT555(r,g,b)((b%32)+((g%32)&lt;&lt;5)+((r%32)&lt;&lt;10))来转变成5.5.5格式<br></font><br>对5.6.5格式，显然，红蓝各5位，绿6位，这样可以表示65536种颜色，同样，宏<br><br><font color=#000000>＃define _RGB16BIT565(r,g,b) ((b%32)+((g%64)&lt;&lt;6)+((r%32)&lt;&lt;11))来转变成5.6.5格式<br></font><br>中间的移位我也搞不清楚是怎么回事，姑且先不看了，看的越多可能越胡涂哦<br>那么到底该用哪种格式？看机器了，大部分可以用5.6.5，当然你可以检测一下，至于怎么检测嘛，我就不说了，查查相关资料就可以了。24位呢？红绿蓝各8位呗，32位？添个Alpha位，其余同24位。好了颜色就说到这里。<br><br>下面想干嘛？想在屏幕上搞点颜色出来，参看附的源代码code1<br>　　你会不会发现我们还应该在上面的基础上添点什么？对，应该在初始化函数里创建页面,也就是DirectDrawSurface对象，那它和DirectDraw对象什么区别？DirectDraw对象，我们知道是表示整个显示系统，也就是你的显卡和显屏构成的那个系统，你能在显示器屏幕上直接画点东西吗？不行，显屏上的东西是通过显存和内存操作把里面的东西显示出来，那么相对应于显屏，内存中就应该有一张矩形白纸供你作画，然后才能把它在显屏上显示。那张白纸就是DirectDrawSurface对象，代表了显存或内存里的一个连续的线性的数据区。这个数据区可以被代表显示硬件的DirectDraw对象所识别和确认。一般，可以创建的页面有4种，我们常用的有主页面（primary surface）和离屏页面（offscreen plain）先说主页面，就是一块显存，在主页面中的图形会显示到屏幕中，直接在主页面上操作会有个问题，数据一多，图象就会不连续，为此可以采用缓冲技术，即建立一个Back buffer（后台缓冲），说白了，就是在内存中再开辟一块区域，和主页面的区域对应，这样就可以不直接操作主页面，先把数据写入到这里，然后通过换页成为可见。离屏页面不同了，它是和主页面一模一样的画面，但是它永远不在屏幕上表现出来，通常被用来存储位图，用于将后来的位图图象Blit到主页面或后台缓冲上。那么，我们来看一下这几个页面在工作当中的位置及作用：<br><br>（此处有一图表，显示不出来）<br><br>这样，我们大体了解了页面的作用，那么初始化时就应该创建好，以等待到时对页面的操作。于是我们的初始化函数中就应该再添加：<br><br><font color=#000000>memset(&amp;ddsd,0,sizeof(ddsd));<br>ddsd.dwSize=sizeof(ddsd);<br>//设置dwFlags，告诉DirectDraw哪些成员可用<br>ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;<br>//定义ddsCaps.dwCaps，请求一个带后台缓冲的主页面<br>ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;<br>//定义设置后台缓冲的数量为1<br>ddsd.dwBackBufferCount = 1;<br>//创建主页面<br>if (FAILED(lpDDraw7-&gt;CreateSurface(&amp;ddsd, &amp;lpDDprimary, NULL)))<br>{<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>MessageBox(NULL,TEXT("DirectDraw Create primary Surface error!"),<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>TEXT("Wrong!"),MB_OK);<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>return(0);<br>}<br>//设置ddsCaps.dwCaps<br>ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;<br>//连接主页面及后台缓冲<br>if (FAILED(lpDDprimary-&gt;GetAttachedSurface(&amp;ddsd.ddsCaps, &amp;lpDDback)))<br>{<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>MessageBox(NULL,TEXT("DirectDraw Create back Surface error!"),<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>TEXT("Wrong!"),MB_OK);<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>return(0);<br>}</font><br><br>在这里，我们要定义几个全局变量：<br><font color=#000000>extern LPDIRECTSURFACE7 lpDDprimary;<br>extern LPDIRECTSURFACE7 lpDDback;<br>extern DDSURFACEDESC2 ddsd;</font><br><br>这是一个指向主页面的指针，一个指向后台缓冲的指针，和一个页面描述结构。不用说，这些定义你可以放在MyDirectDraw.h中。通过填充ddsd结构的成员来申明你所想创建的页面的类型。这里我们没创建离屏页面。用主页面及后台缓冲可以完成一些相对简单，数据不是很多的图形显示，数据过于复杂，就应该创建离屏页面了。<br><br>相应的，在结束时，除了释放DirectDraw7接口外，还要依次释放后台缓冲指针和主页面指针。还是提醒一下，先创建的一定要后释放，不然你会死的很难堪的。怎么去Release这些东西，看看code1中的代码，很容易明白的。<br><br>顺便我们看一下如何创建离屏页面，看下面代码：<br><font color=#000000>DDSURFACEDESC2 ddsd;<br>LPDIRECTSURFACE7 lpDDopl; //这两个定义不用说了吧<br>memset(&amp;ddsd,0,sizeof(ddsd)); //清空结构内容<br>ddsd.dwSize=sizeof(ddsd); //设置大小<br>ddsd.dwFlags = DDSD_CAPS |DDSD_HEIGHT|DDSD_WIDTH;<br>ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;//指定页面类型 <br>ddsd.dwWidth=600;<br>ddsd.dwHeight=800; //设置离屏页面大小<br>if (FAILED(lpDDraw7-&gt;CreateSurface(&amp;ddsd, &amp;lpDDopl, NULL)))<br>{<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>MessageBox(NULL,TEXT("DirectDraw Create offscreen plain error!"),<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>TEXT("Wrong!"),MB_OK);<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>return(0);<br>} //创建离屏页面</font><br><br>Okay！离屏页面就创建好了，说一下，因为离屏页面是个独立的页面，不隶属于任何其他页面，所以你必须指定它的大小。<br><br>关于页面的创建我们就说到这，到这儿，是不是有一种万事具备，只欠东风的感觉啊？<br><br>抬头一看，天亮了，该睡觉了，睡醒咱们再接着说，先去呼呼了。<br><br>&#8230;&#8230;n小时后&#8230;&#8230;<br><br>好了，既然只欠东风，我们就来说东风。<br><br>简单的画图，我们可以参看code1（在屏幕上打点）<br><br>　　有关页面的运用的位图的操作（作图也就这两个东西）我还组织不起来，无法把理解到的东西组织到程序中（汗！还没真正理解，就好意思在这说）我也在学嘛，多理解几遍，说不定就能够组织了，那么，那么，我们只能像我看过的几本资料一样，来拆开来说了，开始照单全收的抄书。希望抄完后，能有点组织的眉目。<br><br>　　从以前那个页面表，可以看出，这里的操作无非是载入位图，贴图，翻页显示，以及对画面进行剪贴。那我们一步步来说吧。这里就事论事，就模块论模块，代码段和以前的文件没多大关联了。大家看不明白了不要骂我，理解万岁。<br><br>先看载入位图，即将位图load到离屏页面中,要通过windows的HDC来进行存取，用windowsAPI配合DirectX来完成。我们看代码段：<br><br><font color=#000000>HDC hdc，hdc1； //声明HDC对象，hdc用来存储位图，hdc1代表离屏页面的DC<br>HBITMAP bitmap； //声明HBITMAP对象<br>hdc=::CreateCompatatibleDC(NULL)；//建立与目前显示模式兼容的DC（参数为null）<br>bitmap=(HBITMAP)::LoadImage(NULL,&#8221;bgroud.bmp&#8221;,IMAGE_BITMAP,640,480,LR_LOADFROMFILE);<br>//加载640*480的位图<br>::SelectObject(hdc,bitmap); //使用windows函数设置hdc中的内容为bitmap</font><br><br>现在把位图加载到了DC中，下面就要把DC中的位图贴到离屏页面中了<br><br><font color=#000000>LPDIRECTSURFACE7 lpDDopl; //这个定义不用说了吧<br>HRESULT result；//干嘛用的？往下看<br>lpDDopl-&gt;GetSurfaceDesc(&amp;ddsd);//ddsd和我们前面定义过的一样<br>result= lpDDopl-&gt;GetDC(&amp;hdc1);//用GetDC（）来取得离屏页面的DC<br>if(result!=DD_OK)<br><font face=Arial>&nbsp;&nbsp;&nbsp; </font>MessageBox(&#8220;取得暂存区DC失败&#8221;);//是否取得成功，了解result做这个用<br>：：Bitblt（hdc1，0，0，ddsd.dwWidth,ddsd.dwHeight,hdc,0,0,SRCCOPY）;<br>//这个就是贴图用的windows函数<br>lpDDopl-&gt;releaseDC(hdc1);//释放离屏页面的DC，一定要释放</font><br><br>到此我们已经把位图贴到离屏页面中了，下面应该把离屏页面DC中的位图填充到back buffer中，然后通过换页显示出来。先来了解两个DirectDraw的贴图函数Blt和BltFast。这两个函数的原型在老王翻译的directx开发手册中有详细说明，在我主页上可以down到，你可以查阅一下。这里我简单说一下：<br><br><font color=#000000>HRESULT Blt( LPRECT lpDestRect, //目标页面的区域，lpDestRect定义其左上右下点坐标<br>LPDIRECTDRAWSURFACE7 lpDDSrcSurface,//源页面指针<br>LPRECT lpSrcRect, //源页面的区域<br>DWORD dwFlags,//控制标志，详见老王的手册<br>LPDDBLTFX lpDDBltFx)//图形变换的信息结构，详情请自己查阅<br>HRESULT BltFast( DWORD dwX, //目的区域左上x坐标<br>DWORD dwY, //目的区域左上y坐标<br>LPDIRECTDRAWSURFACE7 lpDDSrcSurface，//源页面指针<br>LPRECT lpSrcRect, //源页面的区域<br>DWORD dwTrans，//转换参数，见老王手册</font><br><br>这两者的差别就是Blt多了图形放缩功能，但是BltFast效率较高，如何选用已经很清楚了。调用这两个函数中的一个就能够实现从离屏页面到back buffer的贴图，代码如下：<br><br><font color=#000000>lpDDback-&gt;BltFast(0,0,lpDDopl,CRect(0,0,640,480),DDBLTFAST_WAIT);</font><br><br>//lpDDback是我们以前声明过的后台缓冲，CRect（&#8230;）是个CRect类的对象，如果我们已声明了一个CRect rect；这里就可用&amp;rect来代替<br>单看贴图这步操作，还是很easy的。<br>看起来好像离显示只有一步之遥了啊，right，只要翻页（flip）一下就okay了<br><br>先看翻页函数：<br><font color=#000000>HRESULT Flip( LPDIRECTDRAWSURFACE7 lpDDDestSurface,//你想翻到的目标页<br>DWORD dwFlags) //通常设为DDFLIP_WAIT</font><br><br>说明一下：第一个参数为null时，表示翻到目前页面的所连接的下一个页面。当换页对象是可见的页面，比如主页面换页链，进行换页的Flip函数与系统CPU是异步执行的。这就是说，在这些可见的页面上，调用Flip函数，它只是简单的告诉显示硬件该进行换页了，并不需要等待换页操作在硬件设备中实际完成后才返回。这是因为显示硬件（显示器）只有在完成一次垂直刷新后才能进行一次换页。所以，Flip函数调用成功，并不意味着换页已经完成，在实际的换页操作进行之前，对即将成为主页面的后台缓存是不能锁定和进行Blit操作的。要让Flip函数成为与系统CPU同步的操作，在调用时指定DDFLIP_WAIT标志即可<br><br>代码同样简单：<br><font color=#000000>lpDDprimary-&gt;Flip（NULL,DDFLIP_WAIT）;</font><br><br>　　小功告成，到这儿我们已经把一个指定的位图bgroud.bmp在屏幕上显示出来了，这个就可以作为你的游戏的背景图，比如潜水艇游戏的那张大海图。<br><br>需要说明一下的是，如果我们要在哪个页面上操作（一般是back buffer），最好操作前先锁定，用完再解锁，防止其他GDI程序的干扰。举个例子:<br><font color=#000000>lpDDback-&gt;Lock(NULL,&amp;ddsd,DDLOCK-WAIT|DDLOCK_SURFACEMEMORYPTR, NULL); //锁定后台缓冲<br>lpDDback-&gt;Unlock(NULL);//解锁后台缓冲</font><br><br>把这两句代码分别添加到相应位置即可。<br><br>　　再往下我们该做什么了？背景有了，应该引进我们的精灵了（精灵这个术语真是可爱），然后想想看如何能让我们的精灵动起来。老实说，到这，我快崩溃了。下面的内容应该属于陌生的部分吧（如果前面的内容我还有点熟悉的话）。好在我这个人是很执着的，所以只有继续硬着头皮往下写了，理解不到位的地方还请大家包涵，同时希望大家指教。<br><br>先做做准备工作，去吃饭先，休息一会再来&#8230;&#8230;&#8230;&#8230;<br><br>&#8230;&#8230;又是n个小时&#8230;&#8230;<br><br>有饭吃的日子真爽啊，珍惜吧，朋友们：）深吸一口气，let&#8217;s go on<br><br>运用DirectDraw来做动画，我们把不同的图片载入到离屏页面中，然后定时贴入到back buffer中，翻页显示就出现动画效果了。来看个例程：<br><br><strong>（待续） From：<a href="http://blog.gameres.com/show.asp?blogID=269&amp;column=0"><u><font color=#800080>活着为了游戏 — GameRes Blog</font></u></a></strong></font></td>
        </tr>
        </font>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86488.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-06-01 23:43 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86488.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DirectX 5.0 最新游戏编程指南</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86487.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:41:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86487.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86487.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86487.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86487.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86487.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DirectX5T.htm">http://dev.gameres.com/Program/Visual/2D/DirectX5T.htm</a><br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <p align=center><font face=Tahoma color=#ffffcc><strong>DirectX 5.0 最新游戏编程指南</strong></font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">　
                        <div align=center>
                        <center>
                        <table cellSpacing=0 cellPadding=0 width="95%" border=0>
                            <tbody>
                                <tr>
                                    <td width="100%"><font face=Tahoma size=2>DirectX是为Visual C++的用户准备的，因此要编制DirectDraw游戏程序，最好对VC要有一定的了解。不愿意使用VC的用户也可以利用Arakelian Soft公司开发的专门针对Visual Basic5.0用户的ActiveX控件DirectStudio98或Tegosoft公司的TegoSoft ActiveX for Visual Basic。不过，如果想充分发挥DirectX的性能，并且希望保持程序的兼容，那么最好还是使用Visual C++。<br>为了叙述方便，假定已经安装了DirectX5.0 SDK 和Visual C++ 5.0，其目录分别是C:\DX5SDK和C:\Program Files\DevStudio。如果你使用了另一种编译器或安装到了其它目录下，必须将下面的例子做适当的修改才能运行。有人安装了DirectX SDK后却不知怎样使用，因为它是基于Visual C++的，却没有一个界面友好的集成开发环境，因此必须对Visual C++进行适当的配制。<br><br><strong>一 配置DirectX SDK</strong><br>1.1 配置Microsoft Developer Studio<br>为了编译DirectX SDK提供的例子，需要打开一个新的project workspace，插入适当的文件，设置环境变量使得编译器能够找到需要的链接库和包含文件，下面描述了设置的全部过程。启动Microsoft Developer Studio，安装下述步骤创建工程：<br>.在File菜单，选择New；<br>.在New对话框中选择Project中的Win32 Application，在Project Name输入DDEX1<br>.在Location文本框输入放置工程文件的位置，点OK按钮<br>.一个新的DDEX1 Classes文件夹就出现在workspace窗口的左边了。<br>创建了工程后，需要使用如下步骤向工程插入适当的文件：<br>.在Project菜单选择Add toProject|Files<br>.浏览到C:\DX5SDK\SDK\SAMPLES\DDEX1 目录，选择所有的文件<br>.选择OK，该目录下的DDEX1.CPP、DDEX1.RC、RESOURCE.H就加入到工程了。<br>然后设置包含文件的路径：<br>.在Tools菜单，选择Options，就弹出Options对话框<br>.选择Directories ，在Show Directories For列表框选择Include files<br>.在Directories:列表框双击列表底部的空白行，输入C:\DX5SDK\SDK\INC.<br>.同样再加入另一个路径C:\DX5SDK\SDK\SAMPLES\MISC<br>.选择OK按钮，<br>设置链接库目录：<br>.在Show Directories For列表框选择Library files<br>.在Directories:列表框双击底部空白行，输入C:\DX5SDK\SDK\LIB.<br>.单击OK按钮。<br>最后设置建立应用程序时链接的模块：<br>.在Project菜单单击Settings.<br>.选择Link<br>.在Category下拉框选择General.<br>.在Object/Library模块列表框加入Ddraw.lib和Winmm.lib.<br>.单击OK.<br>1.2 配制NMAKE路径<br>　　有时候命令行的方式比集成环境更加方便，所以许多有经验的程序员更愿意用命令行的方式来建立应用程序。下面是包含文件和链接库模块的路径：<br>@echo off<br>set PATH=C:\Program Files\DevStudio\SharedIDE\Bin;<br>C:\Program Files\DevStudio\Vc\Bin;%PATH%set INCLUDE=C:\Program Files\DevStudio\Vc\include;<br>C:\Program Files\DevStudio\Vc\Mfc\include;C:\DX5SDK\SDK\INC;%INCLUDE%set LIB= C:\Program Files\DevStudio\SharedIDE\Vc\lib;<br>C:\Program Files\DevStudio\Vc\Mfc\lib; C:\DX5SDK\SDK\LIB;%LIB%set INIT= C:\Program Files\DevStudio;%INIT%将上述内容加入Autoexec.bat。在例子的目录下输入<br>NMAKE<br>将会在当前目录下创建一个DEBUG目录，并将生成的可执行文件放在该目录下。<br>为了在学习的过程中熟悉DirectX SDK，我们将按照DirectX SDK提供的范例程序的顺序由浅入深，循序渐进。<br>1.3 为Borland C++5.0配置DirectX SDK<br>尽管DirectX 5 SDK是主要为Visual C++用户准备的，但Microsoft并未忘记众多的BorlandC++用户，所以在DirectX SDK中也提供了DirectX的Borland C++库。不过，可能出于竞争的缘故（猜测而已），安装后的DirectX SDK中并没有Borland C++库。这就需要用户自己来处理这一恼人的问题了。我们知道，DirectX 5 SDK是以一个IDX5SDK.EXE发布的，运行IDX5SDK后，它先将压缩的文件全部解压到某个目录下（如D:\DX5SDK），然后再运行该目录下的SETUP.EXE安装DirectX SDK（假设目录为C:\DX5SDK）。实际上，在解压后的目录中包含了一个D:\DX5SDK\SDK\LIB\BORLANDC目录，该目录下就是Borland C++的链接库文件。但在SETUP安装时，安装程序并没有把该目录复制到安装目录中。解决方法很简单，即SETUP安装完成后，再建立一个C:\DX5SDK\SDK\LIB\Borland，将目录D:\DX5SDK\SDK\LIB\Borland下的所有文件都复制到C:\DX5SDK\SDK\LIB\Borland目录下。然后在Borland C++5.0的集成环境中如同配置Visual C++5.0那样配置工程文件。<br><br><strong>二 第一个DirectDraw实例</strong><br>要使用DirectDraw，首先必须创建DirectDraw对象的一个实例来表征计算机上的显示适配卡，然后使用接口方法来处理对象。另外还需要创建一个或多个DirectDrawSurface对象的实例来显示游戏。DDEX1首先创建一个DirectDraw对象，再创建一个主表面（primary<br>surface）和一个后台缓冲区（back buffer），然后在表面之间转换。DDEXx例子都是用C++写成的，如果你使用的是C编译器，必须将代码做适当改动，至少要加入虚表和指向接口方法的指针。<br>2.1 首先初始化DirectDraw对象<br>DDEX1 程序在doInit函数包含了DirectDraw 的初始化代码：<br><br></font><font face=宋体 color=#99ccff size=2>// 创建主DirectDraw 对象<br>ddrval = DirectDrawCreate( NULL, &amp;lpDD, NULL );<br>if( ddrval == DD_OK )<br>{<br>&nbsp;&nbsp;&nbsp; // 获取独占模式<br>&nbsp;&nbsp;&nbsp; ddrval = lpDD-&gt;SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE| DDSCL_FULLSCREEN );<br>&nbsp;&nbsp;&nbsp; if(ddrval == DD_OK )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddrval = lpDD-&gt;SetDisplayMode(640, 480, 8 );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( ddrval == DD_OK )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建带有一个后台缓冲区的主表面<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddsd.dwSize = sizeof( ddsd );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |DDSCAPS_FLIP |DDSCAPS_COMPLEX;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddsd.dwBackBufferCount = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddrval = lpDD-&gt;CreateSurface( &amp;ddsd, &amp;lpDDSPrimary, NULL );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( ddrval == DD_OK )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //获取后台缓冲区的指针<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddscaps.dwCaps = DDSCAPS_BACKBUFFER;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddrval=lpDDSPrimary-&gt;GetAttachedSurface(&amp;ddscaps,&amp;lpDDSBack);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( ddrval == DD_OK )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 画出一些文本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (lpDDSPrimary-&gt;GetDC(&amp;hdc) == DD_OK)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetBkColor( hdc, RGB( 0, 0, 255 ) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetTextColor( hdc, RGB( 255, 255, 0 ) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextOut( hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDDSPrimary-&gt;ReleaseDC(hdc);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (lpDDSBack-&gt;GetDC(&amp;hdc) == DD_OK)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetBkColor( hdc, RGB( 0, 0, 255 ) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetTextColor( hdc, RGB( 255, 255, 0 ) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextOut( hdc, 0, 0, szBackMsg, lstrlen(szBackMsg) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDDSBack-&gt;ReleaseDC(hdc);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 创建翻转页面的计时器if( SetTimer( hwnd, TIMER_ID, TIMER_RATE, NULL ) )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return TRUE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>}<br><br>wsprintf(buf, "Direct Draw Init Failed (%08lx)\n", ddrval );<br>............</font><font face=Tahoma size=2><br><br>下面详细说明初始化DirectDraw 对象的创建和准备表面的每一步骤。<br><br>2.2 创建DirectDraw 对象<br>创建DirectDraw 对象的一个实例，应该用DirectDrawCreate API 函数，也可以用COM中的CoCreateInstance函数。DirectDrawCreate用一个全局统一标志符GUID(Globally<br>Unique IDentifier)来表征显示设备，在大多数情况下GUID为NULL(使用系统的缺省显示设备，既&#8220;空设备&#8221;）；指针指向DirectDraw对象的地址；第三个参数总是NULL(供将来扩展使用)。下述代码表明了如何创建一个DirectDraw对象，并且检验是否成功。<br><br></font><font face=宋体 color=#99ccff size=2>ddrval = DirectDrawCreate( NULL, &amp;lpDD, NULL );<br>if( ddrval == DD_OK )<br>{<br>&nbsp;&nbsp;&nbsp; // lpDD is是合法的DirectDraw 对象<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; // DirectDraw对象不能被创建<br>}</font><font face=Tahoma size=2><br><br>2.3 设置显示模式<br>设置DirectDraw 应用程序的显示模式需要两步：首先调用IDirectDraw::SetCooperativeLevel方法来设定该模式下的要求，一旦确定了要求，再用IDirectDraw::SetDisplayMode方法来选择显示分辨率。<br>在改变显示分辨率之前，还必须通过IDirectDraw::SetCooperativeLevel方法来指定DDSCL_EXCLUSIVE和DDSCL_FULLSCREEN标志。<br>　　这样能使游戏程序完全控制显示设备，其它的应用程序不能同时共享显示设备。DDSCL_FULLSCREEN标志表示将程序设为全屏模式。下面的代码显示了如何使用IDirectDraw::SetCooperativeLevel方法：<br><br></font><font face=宋体 color=#99ccff size=2>HRESULT ddrval;<br>LPDIRECTDRAW lpDD; // already created by DirectDrawCreate<br>ddrval = lpDD-&gt;SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);<br>if( ddrval == DD_OK )<br>{<br>&nbsp;&nbsp;&nbsp; // 全屏独占方式设置成功<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; // 调用不成功，但程序仍然能继续运行<br>}<br></font><font face=Tahoma size=2><br>如果IDirectDraw::SetCooperativeLevel不返回DD_OK，你仍然可以运行该程序，但不是全屏模式，有时可能产生一些无法预料的错误。因此你应该显示一条错误信息，让用户知道发生了什么事，由用户来决定是否继续运行游戏。<br>使用IDirectDraw::SetCooperativeLevel时，必须向窗口 (HWND)传送一个句柄，让窗口决定何时非正常地终止应用程序。例如，若发生了GP错误或GDI被翻转(flip)到了后台缓冲区，用户就无法访问当前屏幕。为了避免这种情况，DirectDraw有一个后台等待进程，它俘获所有发往该窗口的消息，用这些消息来确定应用程序何时终止。如果创建了新的窗口，必须确定该窗口为活动的，否则，就会有一系列的事件无法继续工作。<br><br>2.4 改变显示模式<br>一旦选择了应用程序的工作模式，就可以使用IDirectDraw::SetDisplayMode方法来改变显示模式，下面的代码将显示模式设置成640x480x256：<br><br></font><font face=宋体 color=#99ccff size=2>HRESULT ddrval;<br>LPDIRECTDRAW lpDD; // already created<br>ddrval = lpDD-&gt;SetDisplayMode( 640, 480, 8 );<br>if( ddrval == DD_OK )&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; // 改变模式成功<br>}<br>else&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; // 显示模式不能改变 // 系统可能不支持该模式<br>}</font><font face=Tahoma size=2><br><br>当设定显示模式时，应该确保如果用户的设备不支持更高的分辨率，应用程序应该返回系统支持的标准模式。如果显示示配卡不支持设计的分辨率，IDirectDraw::SetDisplayMode返回一个DDERR_INVALIDMODE错误值。因此，在设置分辨率时，应该先用IDirectDraw::EnumDisplayMode方法检测用户的显示设备的性能。<br><br>2.5 创建可翻转表面(Flippable Surface)<br>设定了显示模式后，必须创建放置应用程序的表面。在DDEX1例中，我们使用IDirectDraw::SetCooperativeLevel方法将程序设成独占全屏模式，然后可以创建翻转表面。如果使用IDirectDraw::SetCooperativeLevel设成DDSCL_NORMAL模式，就只能创建块写方式的表面了。<br><br>2.6 定义表面要求<br>创建可翻转表面的的第一步是在DDSURFACEDESC结构中定义表面的要求。下面的代码描述了结构的定义及创建可翻转表面所需要的标志：<br><br></font><font face=宋体 color=#99ccff size=2>// 创建带有一个后台缓冲区的主表面<br>ddsd.dwSize = sizeof( ddsd );<br>ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;<br>ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |DDSCAPS_COMPLEX;<br>ddsd.dwBackBufferCount = 1;</font><font face=Tahoma size=2><br><br>例中，成员变量dwSize被设为DDSURFACEDESC结构的大小。dwFlags标志指定DDSURFACEDESC结构中哪些域可存放有效信息。<br>在DDEX1例中，dwFlags指出了需要使用DDSCAPS结构(DDSD_CAPS)并创建一个后台缓冲区<br>(DDSD_BACKBUFFERCOUNT)。dwCaps指出 DDSCAPS结构会用到的标志，本例中它指定了一个主表面(DDSCAPS_PRIMARYSURFACE)，一个翻转表面(DDSCAPS_FLIP)和一个复杂表面(DDSCAPS_COMPLEX)。<br>　　最后，程序指定了一个后台缓冲区。后台缓冲区是背景图像和人物将写入的位置，它可以转化为主表面。<br>本例中，后台缓冲区的数目为1，事实上，只要有足够的显示内存，可以创建任意多个后台缓冲区，一般每1M的显示内存只能用来创建一个后台缓冲区。表面的内存既可以是显示内存，也可以是系统内存。<br>DirectDraw在使用完了显示内存时（例如在仅有1M的显示内存创建了2个后台缓冲区）会自动使用系统内存。你可以通过将DDSCAPS结构中的dwCaps设定为DDSCAPS_SYSTEMMEMORY或DDSCAPS_VIDEOMEMORY来指定只使用系统内存或只使用显示内存。如果指定了DDSCAPS_VIDEOMEMORY又没有足够的显示内存来创建表面，IDirectDraw::CreateSurface<br>将返回一个DDERR_OUTOFVIDEOMEMORY错误。<br><br>2.7 创建表面<br>填完了DDSURFACEDESC结构，就可以使用该结构和lpDD了，lpDD是用DirectDrawCreate<br>方法创建的DirectDraw 对象的指针，下面的代码显示了这一过程：<br><br></font><font face=宋体 color=#99ccff size=2>ddrval = lpDD-&gt;CreateSurface( &amp;ddsd, &amp;lpDDSPrimary, NULL);<br>if( ddrval == DD_OK )&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; // lpDDSPrimary points to new surface<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; // surface was not created return FALSE;<br>}</font><font face=Tahoma size=2><br><br>如果调用成功，IDirectDraw::CreateSurface函数就返回指向主表面的指针lpDDSPrimary。若主表面指针可用，就可以调用IDirectDrawSurface::GetAttachedSurface 方法取得后台缓冲区的指针，如下所示：<br><br></font><font face=宋体 color=#99ccff size=2>ddscaps.dwCaps = DDSCAPS_BACKBUFFER;ddrval = lpDDSPrimary-&gt;GetAttachedSurface( &amp;ddcaps, &amp;lpDDSBack);<br>if( ddrval == DD_OK )&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; // lpDDSBack points to the back buffer<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; return FALSE;<br>} </font><font face=Tahoma size=2><br><br>如果IDirectDrawSurface::GetAttachedSurface调用成功，通过提供主表面的地址和设置DDSCAPS_BACKBUFFER标志，lpDDSBack 参数就指向后台缓冲区。<br><br>2.8 着色表面<br>创建了主表面和后台缓冲区后，DDEX1使用标准的WindowsGDI 函数将一些文本提交到主表面和后台缓冲区，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>if (lpDDSPrimary-&gt;GetDC(&amp;hdc) == DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; SetBkColor( hdc, RGB( 0, 0, 255 ) );<br>&nbsp;&nbsp;&nbsp; SetTextColor( hdc, RGB( 255, 255, 0 ) );<br>&nbsp;&nbsp;&nbsp; TextOut( hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));<br>&nbsp;&nbsp;&nbsp; lpDDSPrimary-&gt;ReleaseDC(hdc);<br>}<br>if (lpDDSBack-&gt;GetDC(&amp;hdc) == DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; SetBkColor( hdc, RGB( 0, 0, 255 ) );<br>&nbsp;&nbsp;&nbsp; SetTextColor( hdc, RGB( 255, 255, 0 ) );<br>&nbsp;&nbsp;&nbsp; TextOut( hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));<br>&nbsp;&nbsp;&nbsp; lpDDSBack-&gt;ReleaseDC(hdc);<br>}</font><font face=Tahoma size=2><br><br>例中使用了IDirectDrawSurface::GetDC方法来设备上下文的句柄且锁定该表面。如果不想使用要求句柄的Windows函数，还可以使用IDirectDrawSurface::Lock和IDirectDrawSurface::Unlock方法来锁定和解锁后台缓冲区。锁定表面的内存（不管是整个表面还是其中的一部分）能确保你的应用程序和系统不会同时访问这块内存。另外，除非给内存解锁，程序不能翻转表面。本例在锁定表面后使用Windows GDI 函数SetBkColor来设置背景颜色，使用SetTextColor来设置文本颜色，然后使用TextOut将文本输出到表面。当文本写入缓冲区后，例中使用了IDirectDrawSurface::ReleaseDC方法来解锁表面并释放句柄。良好的习惯是，向后台缓冲区写数据完成后，马上调用IDirectDrawSurface::ReleaseDC或IDirectDrawSurface::Unlock。<br>一般来讲，当向表面写数据时，该表面就是后台缓冲区，然后将缓冲区翻转成主表面显示出来。在DDEX1中，第一次翻转表面之前有一个重要的延迟。于是DDEX1就将数据写入主缓冲区，避免开始显示时有太长的时间间隔。后面将会讲到，DDEX1只在WM_TIMER期间向后台写数据。初始化函数或标题头可能会写入主缓冲区。应该注意的是，一旦使用IDirectDrawSurface::Unlock对表面解锁，指向表面的指针就变成无效，必须再次使用IDirectDrawSurface::Lock方法才能获取该表面内存的有效指针。<br><br>2.9 写表面及翻转表面<br>完成了初始化后，DDEX1开始处理消息循环。在消息循环的过程中，完成锁定后台缓冲区——写入新的文本——解锁后台缓冲区——翻转表面的过程。WM_TIMER包含了写数据和翻转表面的大部分代码。<br>WM_TIMER消息的前半部分用于向后台缓冲区写数据，&#8220;phase&#8221;变量决定是写主缓冲区消息还是写后台缓冲区消息。如果phase为1，表示写主缓冲区的消息，然后将phase改变为0；若为0，表示写后台缓冲区的消息，然后将phase改变为1。注意，两种情况中的消息都是写向后台缓冲区。后台缓冲区写入了消息后，使用IDirectDrawSurface::ReleaseDC方法解锁。下面的代码实现了这一点：<br><br></font><font face=宋体 color=#99ccff size=2>case WM_TIMER:<br>// Flip surfaces<br>if( bActive )&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; if (lpDDSBack-&gt;GetDC(&amp;hdc)== DD_OK)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetBkColor( hdc, RGB( 0, 0, 255 ) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetTextColor( hdc, RGB( 255, 255, 0 ) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( phase )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextOut( hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; phase = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextOut( hdc, 0, 0, szBackMsg, lstrlen(szBackMsg) );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; phase = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDDSBack-&gt;ReleaseDC(hdc);<br>&nbsp;&nbsp;&nbsp; }</font><font face=Tahoma size=2><br><br>表面内存解锁后，使用IDirectDrawSurface::Flip方法将后台缓冲区翻转成主表面，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>while( 1 )&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; HRESULT ddrval;<br>&nbsp;&nbsp;&nbsp; ddrval = lpDDSPrimary-&gt;Flip( NULL, 0 );<br>&nbsp;&nbsp;&nbsp; if( ddrval == DD_OK )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; if( ddrval == DDERR_SURFACELOST )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ddrval = lpDDSPrimary-&amp;g&gt;val != DD_OK )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; if( ddrval != DDERR_WASSTILLDRAWING )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; }<br>}</font><font face=Tahoma size=2><br><br>例中，lpDDSPrimary指明了主表面及其后台缓冲区。调用IDirectDrawSurface::Flip方法后，主表面和后表面交换。调用成功后，返回DD_OK，程序终止While循环；如果返回DDERR_SURFACELOST，表明可能是表面丢失，需要用IDirectDrawSurface::Restore方法恢复该表面，若恢复成功，就再一次调用IDirectDrawSurface::Flip方法；如果失败，程序终止While循环并返回一个错误值。另外，如前所述，即使调用IDirectDrawSurface::Flip成功，交换也不是立即完成，它将等到系统中在此之前的表面交换都完成后才进行。例如，前一次的表面翻转还未发生时，IDirectDrawSurface::Flip就返回DERR_WASSTILLDRAWING。本例中，IDirectDrawSurface::Flip继续循环直到返回DD_OK.。<br><br>2.10 释放DirectDraw 对象<br>当按了F12后，DDEX1程序在退出之前先处理WM_DESTROY消息，该消息调用了finiObjects函数，而finiObjects函数包含了所有的Iunknown Release的调用，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>static void finiObjects( void )<br>{<br>&nbsp;&nbsp;&nbsp; if( lpDD != NULL )&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( lpDDSPrimary != NULL)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDDSPrimary-&gt;Release();lpDDSPrimary = NULL; } lpDD-&gt;Release();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDD = NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>} /* finiObjects */</font><font face=Tahoma size=2><br><br>程序检测DirectDraw对象的指针(lpDD)和DirectDrawSurface对象的指针(lpDDSPrimary)<br>是否等于NULL，本例中显然不为NULL。然后DDEX1调用 IDirectDrawSurface::Release方法将DirectDrawSurface对象的参考值减1，这将会使得其参考值变为0，DirectDrawSurface 对象就被释放了，DirectDrawSurface的指针被设为NULL值，然后撤消。程序又调用IDirectDraw::Release就DirectDraw对象的参考值减1变为0，释放DirectDraw对象及其指针。<br>上述的DDEX1是DirectDraw最基本的应用，它首先创建DirectDraw对象和DirectDrawSurface对象，创建一个主表面及其后台缓冲区，将文本输出到后台缓冲区，然后转化表面。第二个例子DDEX2扩展了DDEX1的功能，它可以将一个位图文件调入后台缓冲区。第三个例子DDEX3则更进一步，除了一个主表面及后台缓冲区外，还创建了两个屏外表面，将位图调入每一个屏外表面，然后使用IDirectDrawSurface::BltFast方法将一个屏外表面的内容位块传输到后台缓冲区，然后翻转表面并将另一个屏外表面的内容位块传输到后台缓冲区。下面将详细讨论这些功能。<br><br>2.11 将位图调入表面<br>如DDEX1中一样，doInit函数是DDEX2的初始化函数，两者的实质一样，一直到下面的代码：<br><br></font><font face=宋体 color=#99ccff size=2>lpDDPal = DDLoadPalette(lpDD, szBackground);<br>if (lpDDPal == NULL)<br>&nbsp;&nbsp;&nbsp; goto error;<br>ddrval = lpDDSPrimary-&gt;SetPalette(lpDDPal);<br>if( ddrval != DD_OK )<br>&nbsp;&nbsp;&nbsp; goto error;<br>// load a bitmap into the back buffer.<br>ddrval = DDReLoadBitmap(lpDDSBack, szBackground);<br>if( ddrval != DD_OK )<br>&nbsp;&nbsp;&nbsp; goto error;</font><font face=Tahoma size=2><br><br>代码的第一行从函数DDLoadPalette返回一个值，该函数在C:\DX5SDK\SDK\SAMPLES\MISC中的Ddutil.cpp文件中，因此编译DDEX2时需要将Ddutil.cpp和Ddutil.h加入过程。大部分的DirectDraw程序都需要该文件。在DDEX2中，DDLoadPalette函数从Back.bmp文件中创建一个DirectDrawPalette对象。DDLoadPalette函数首先检查用于创建调色板的文件或资源十分存在，如果不存在，就创建一个缺省调色板。在DDEX2中，它从位图文件中抽取调色板信息并存储在由ape指向的结构，然后如下创建DirectDrawPalette对象：<br><br></font><font face=宋体 color=#99ccff size=2>pdd-&gt;CreatePalette(DDPCAPS_8BIT, ape, &amp;ddpal, NULL);<br>return ddpal;</font><font face=Tahoma size=2><br><br>当IDirectDraw::CreatePalette方法返回时，ddpal就指向该DirectDrawPalette对象。ape是指向一个结构的指针，该结构能包含2/4/16/256个线性的实体，实体的数目由IDirectDraw::CreatePalette调用的dwFlags参数决定。本例中，dwFlags设定为DDPCAPS_8BIT，这表示该结构中有256个实体，每个实体有四个字节（红色、绿色、蓝色和标志字节）。<br><br>2.12 设置调色板，将位图调入后台缓冲区<br>创建了调色板之后，可以通过调用IDirectDrawSurface::SetPalette方法将DirectDrawPalette 对象的指针ddpal传送给主表面，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>ddrval = lpDDSPrimary-&gt;SetPalette(lpDDPal);<br>if( ddrval != DD_OK )<br>&nbsp;&nbsp;&nbsp; // SetPalette failed</font><font face=Tahoma size=2><br><br>调用了IDirectDrawSurface::SetPalette方法之后，DirectDrawPalette对象就和DirectDrawSurface对象挂(hook)在一起了，需要改变调色板时，只需要创建一个新的调色板对其进行设置就可以了。<br>　　DirectDrawPalette对象同DirectDrawSurface对象挂在一起后，DDEX2使用以下代码将Back.bmp文件装入后台缓冲区：<br><br></font><font face=宋体 color=#99ccff size=2>// load a bitmap into the back buffer.<br>ddrval = DDReLoadBitmap(lpDDSBack, szBackground);<br>if( ddrval != DD_OK )<br>&nbsp;&nbsp;&nbsp; // Load failed</font><font face=Tahoma size=2><br><br>DDReLoadBitmap是Ddutil.cpp中的另一个函数，它将位图从文件或资源中调入已经存在的DirectDraw表面。在本例中，它将szBackground指向的Back.bmp装入lpDDSBack指向的后台缓冲区。DDReLoadBitmap调用DDCopyBitmap函数将文件拷贝到后台缓冲区并延展为适当的尺寸。DDCopyBitmap函数将位图拷入内存，使用GetObject函数获取位图的大小，然后用下述代码获取将要放置位图的后台缓冲区的大小：<br><br></font><font face=宋体 color=#99ccff size=2>// get size of surface.<br>ddsd.dwSize = sizeof(ddsd);<br>ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;<br>pdds-&gt;GetSurfaceDesc(&amp;ddsd);</font><font face=Tahoma size=2><br><br>　　ddsd 是指向DDSURFACEDESC结构的指针，该结构储存了DirectDraw表面的当前描述。DDSURFACEDESC结构的成员描述了由DDSD_HEIGHT 和DDSD_WIDTH指定了表面的高和宽。IDirectDrawSurface::GetSurfaceDesc方法使用合适的值调入结构，例中高为480，宽为640。DDCopyBitmap函数锁定表面并将位图拷贝到后台缓冲区，然后用StretchBlt函数对位图进行拉伸或压缩，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>if ((hr = pdds-&gt;GetDC(&amp;hdc)) == DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; StretchBlt(hdc, 0, 0, ddsd.dwWidth,ddsd.dwHeight, hdcImage, x, y, dx, dy, SRCCOPY);<br>&nbsp;&nbsp;&nbsp; pdds-&gt;ReleaseDC(hdc);<br>}</font><font face=Tahoma size=2><br><br>2.13 从屏外表面位块传输<br>DDEX2同DDEX1基本相同。DDEX2打开一个位图文件并将它送往后台缓冲区，然后翻转后台缓冲区和主表面。但这对显示位图并不特别理想，DDEX3扩展了DDEX2的功能，它加入了两个屏外缓冲区，每个缓冲区都存储一个位图。下面是DDEX3中的doInit函数的一部分，功能是创建两个屏外缓冲区：<br><br></font><font face=宋体 color=#99ccff size=2>// Create an offscreen bitmap.<br>ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;<br>ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;<br>ddsd.dwHeight = 480;<br>ddsd.dwWidth = 640;<br>ddrval = lpDD-&gt;CreateSurface( &amp;ddsd, &amp;lpDDSOne, NULL);<br>if( ddrval != DD_OK )<br>{<br>&nbsp;&nbsp;&nbsp; return initFail(hwnd);<br>}<br>// Create another offscreen bitmap. ddrval = lpDD-&gt;CreateSurface( &amp;ddsd, &amp;lpDDSTwo, NULL); if( ddrval != DD_OK )<br>{<br>&nbsp;&nbsp;&nbsp; return initFail(hwnd);<br>} </font><font face=Tahoma size=2><br><br>从代码中可以看到，dwFlags指明了程序使用DDSCAPS结构，并设置缓冲区的高和宽。由DDSCAPS结构中的DDSCAPS_OFFSCREEN标志指定该表面是屏外缓冲区，在DDSURFACEDESC结构中将高和宽设为480和640，然后使用IDirectDraw::CreateSurface方法来创建表面。因为两个屏外表面的大小一样，创建第二个缓冲区只需要再运行一次IDirectDraw::CreateSurface即可（当然要用不同的指针）。你还可以在DDSCAPS中设置DDSCAPS_SYSTEMMEMORY或DDSCAPS_VIDEOMEMORY 来指定屏外缓冲区放在显示内存还是系统内存。将位图存放在显示内存可以加快后台缓冲区与屏外表面之间的数据传输速度，这在位图动画中非常重要。但是，如果你为屏外缓冲区指定了DDSCAPS_VIDEOMEMORY又没有足够的显示内存调入整个位图，当创建该表面时，程序就会返回一个DDERR_OUTOFVIDEOMEMORY的错误值。<br><br>2.14 将位图文件调入屏外表面<br>创建了两个屏外表面后，DDEX3使用了InitSurfaces函数将位图从Frntback.bmp文件装入到两个表面。InitSurfaces函数使用了DDCopyBitmap函数调入两个位图，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>// Load our bitmap resource.<br>hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), szBitmap, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);<br>if (hbm == NULL)<br>&nbsp;&nbsp;&nbsp; return FALSE;<br>DDCopyBitmap(lpDDSOne, hbm, 0, 0, 640, 480);<br>DDCopyBitmap(lpDDSTwo, hbm, 0, 480, 640, 480);<br>DeleteObject(hbm);<br>return TRUE;</font><font face=Tahoma size=2><br><br>Frntback.bmp文件由两部分组成，一半在上，一半在下。DDCopyBitmap函数将上半部分调入第一个屏外表面lpDDSOne，下半部分调入第二个表面lpDDSTwo。<br><br>2.15 将屏外表面位位块传输到后台缓冲区<br>WM_TIMER包含了写表面和翻转表面的代码。在DDEX3中，它选择适当的屏外表面，并将其位块传输到后台缓冲区，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>rcRect.left = 0;<br>rcRect.top = 0;<br>rcRect.right = 640;<br>rcRect.bottom = 480;<br>if(phase)<br>{<br>&nbsp;&nbsp;&nbsp; pdds = lpDDSTwo;<br>&nbsp;&nbsp;&nbsp; phase = 0;<br>}<br>else {<br>&nbsp;&nbsp;&nbsp; pdds = lpDDSOne;<br>&nbsp;&nbsp;&nbsp; phase = 1;<br>}<br><br>while( 1 )<br>{<br>&nbsp;&nbsp;&nbsp; ddrval = lpDDSBack-&gt;BltFast(0, 0, pdds, &amp;rcRect, FALSE );<br>&nbsp;&nbsp;&nbsp; if( ddrval ==DD_OK )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>}</font><font face=Tahoma size=2><br><br>&#8220;phase&#8221;决定了准备将哪一个屏外表面位块传输到后台缓冲区，然后调用IDirectDrawSurface::BltFast方法将选定的屏外表面位块传输到后台缓冲区，从左上角的位置0,0开始。rcRect指向定义了屏外表面的左上角和右下角的RECT结构。最后一个参数设为FALSE或0，指明不使用特殊的传输标志。一旦屏外表面被传送到后台缓冲区，就可以利用前面的方法将后台缓冲区和主表面相互翻转了。<br><br><strong>三、创建动画</strong><br>上面的例子都只是将数据写入后台缓冲区，然后将后台缓冲区与主表面翻转，其速度并不太快。下面的例子DDEX4和DDEX5优化了实时功能，使看起来更象一个真正的游戏。DDEX4显示了怎样为表面设置Color key，怎样使用IDirectDrawSurface::BltFast方法将屏外表面各部分拷贝到后台缓冲区以产生动画。DDEX5加入了读取调色板并在动画运行时改变调色板的功能。<br><br>3.1 Color Key和位图动画<br>在DDEX3例中描述了将位图放入屏外缓冲区的一种主要方式。DDEX4则使用了将背景和一系列的精灵(sprite，本例中精灵是圆环)装入屏外表面的技术，然后使用,IDirectDrawSurface::BltFast方法将屏外表面的各部分拷贝到后台缓冲区。doInit函数除了具有前面例子中的功能外，还包括了为精灵设置Color key的代码。Color key是用于设置透明度的颜色值。当使用硬件块写方式时，矩型区域内除了设为color key的像素，其它的像素都被块写，由此在表面上产生非矩型的精灵。设置color key的代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>// Set the color key for this bitmap (black)<br>// NOTE this bitmap has black as entry 255 in the color table.<br>// ddck.dwColorSpaceLowValue = 0xff;<br>// ddck.dwColorSpaceHighValue = 0xff;<br>// lpDDSOne-&gt;SetColorKey( DDCKEY_SRCBLT, &amp;ddck);<br>// if we did not want to hard code the palette index (0xff)<br>// we can also set the color key like so...<br>DDSetColorKey(lpDDSOne, RGB(0,0,0));<br>return TRUE;</font><font face=Tahoma size=2><br><br>例中给出了设置color key的两种不同方法。第一种方法是注释内的3行，先设定DDCOLORKEY<br>结构中color key的范围，再调用IDirectDrawSurface::SetColorKey方法将color key 设置成黑色（假定位图在颜色表中以黑色作为调色板索引项255）。第二种方法是调用DDSetColorKey函数设置颜色的RGB值来选择color key，黑色就是RGB(0,0,0)。DDSetColorKey 函数调用了DDColorMatch函数，DDColorMatch存储放置于lpDDSOne表面的位图的0,0位置像素的颜色值，然后用提供的RGB值赋给0,0位置的像素，并将该颜色值屏蔽。<br>　　完成了这一步骤后，原来的颜色就可重新放回0,0处并用正确的Color Key调用DDSetColorKey函数，调用成功后，color key就放入DDCOLORKEY 结构中的成员变量dwColorSpaceLowValue ，同时也拷贝到dwColorSpaceHighValue成员，然后再调用IDirectDrawSurface::SetColorKey设置Color key。 CLR_INVALID是DDSetColorKey 和DDColorMatch函数中另一个有用的变量。如果在DDSetColorKey中以该值作为color key，位图左上角的像素就会作为color key使用。要想实现这一功能，需要调入位图文件All.bmp，将0,0处的像素值该为黑色，保存更改，然后如下改变对DDSetColorKey的调用： DDSetColorKey(lpDDSOne, CLR_INVALID);重新编译DDEX4，DDEX4就会使用0,0处的像素值作为color key了。<br><br>3.2 DDEX4中的动画<br>DDEX4利用All.bmp中的红色圆环调用updateFrame 函数来创建一个简单的动画。动画由圆环的3个位置组成。例子通过比较Win32中的GetTickCount和上次该函数开始运行的时间来判断是否重画哪个圆环，然后使用IDirectDrawSurface::BltFast方法将背景从屏外表面lpDDSOne位块传输到后台缓冲区，然后再使用已经设定好的color key将圆环块写入后台缓冲区。在所有的圆环都块写到后台缓冲区后，调用IDirectDrawSurface::Flip方法翻转后台缓冲区和主表面。<br><br>3.3 动态改变调色板<br>DDEX5描述了任何在程序运行时动态地改变调色板，尽管在游戏中这并不总是用到。DirectDraw确实能很好地控制调色板。DDEX5中的下述代码将All.bmp文件的下半部分中的调色板装入：<br><br></font><font face=宋体 color=#99ccff size=2>// First, set all colors as unused<br>for(i=0; i&lt;256; i++)<br>{<br>&nbsp;&nbsp;&nbsp; torusColors[i] = 0;<br>}<br>// lock the surface and scan the lower part (the torus area)<br>// and remember all the index's we find.<br>ddsd.dwSize = sizeof(ddsd);<br>while (lpDDSOne-&gt;Lock(NULL, &amp;ddsd, 0, NULL) == DDERR_WASSTILLDRAWING);<br>// Now search through the torus frames and mark used colors<br>for( y=480; y&lt;480+384; y++ )<br>{<br>&nbsp;&nbsp;&nbsp; for( x=0; x&lt;640; x++ )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; torusColors[((BYTE*)ddsd.lpSurface)[y*ddsd.lPitch+x]] = 1;<br>&nbsp;&nbsp;&nbsp; }<br>}<br>lpDDSOne-&gt;Unlock(NULL);</font><font face=Tahoma size=2><br><br>　　数组torusColors用于指定All.bmp中的下半部分调色板的索引值，数组在使用之前都初始化为0。然后锁定屏外表面来检测某颜色索引值是否已用。数组torusColors开始于位图的第0行第480列，数组中的颜色索引值由位图表面放置于内存的位置的一个字节决定，该位置由DDSURFACEDESC 结构中的lpSurface成员变量来决定，lpSurface指向对应于位图（0,480）处的内存地址(y*lPitch+x)。数组中设定的颜色索引值用来检测调色板中哪些颜色被替换。因为背景和红色圆环之间没有公用的颜色，所以只有那些同圆环联在一起的颜色值才会被替换。<br><br>3.4 替换调色板<br>DDEX5中的updateFrame函数同DDEX4中的基本相同，先将背景块写入后台缓冲区，再将3个红色圆环块写到前景。但在翻转表面之前，updateFrame用doInit函数创建的调色板索引值来改变主表面的调色板，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>// Change the palette<br>if(lpDDPal-&gt;GetEntries( 0, 0, 256, pe ) != DD_OK)&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; return;<br>}<br><br>for(i=1; i&lt;256; i++)<br>{<br>&nbsp;&nbsp;&nbsp; if(!torusColors[i])&nbsp;<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; pe[i].peRed = (pe[i].peRed+2) % 256;<br>&nbsp;&nbsp;&nbsp; pe[i].peGreen = (pe[i].peGreen+1) %256;<br>&nbsp;&nbsp;&nbsp; pe[i].peBlue = (pe[i].peBlue+3) % 256;<br>}<br><br>if(lpDDPal-&gt;SetEntries( 0, 0, 256, pe) != DD_OK)<br>&nbsp;&nbsp;&nbsp; return;<br></font><font face=Tahoma size=2><br>IDirectDrawPalette::GetEntries方法在DirectDrawPalette对象中查询调色板的值，因为pe指向的调色板实体的值有效，方法就返回DD_OK，程序继续运行。然后循环检测torusColors在初始化中是否被设为1，如果索引值被设为1，由pe指向的调色板的红色、绿色、蓝色的值就被替换。在所有的被标记的调色板实体替换完毕后，再调用IDirectDrawPalette::SetEntries方法来真正改变DirectDrawPalette中的实体。如果该调色板已经设给主表面，上面的改变就会立即完成。完成了这一工作，剩下的就是同前面一样的翻转表面了。<br><br><strong>四 使用覆盖表面</strong><br>　　本例将使用DirectX SDK包含的Mosquito范例程序一步一步地说明怎样在程序中使用DirectDraw和硬件支持的覆盖表面。Mosquito使用覆盖表面的翻转链而没有位块传输到主表面将运动位图显示在桌面上。Mosquito程序调整覆盖表面的特征以适应硬件的限制。<br><br>4.1 创建一个主表面<br>要使用覆盖表面，必须先要初始化一个主表面，覆盖表面将显示在该主表面上。Mosquito用如下代码创建了一个主表面：<br><br></font><font face=宋体 color=#99ccff size=2>// Zero-out the structure and set the dwSize member.<br>ZeroMemory(&amp;ddsd, sizeof(ddsd));<br>ddsd.dwSize = sizeof(ddsd);<br>// Set flags and create a primary surface.<br>ddsd.dwFlags = DDSD_CAPS;<br>ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;<br>ddrval = g_lpdd-&gt;CreateSurface(&amp;ddsd, &amp;g_lpddsPrimary,NULL );</font><font face=Tahoma size=2><br><br>程序先初始化将要使用的DDSURFACEDESC结构，然后设定适当的标志调用IDirectDraw2::CreateSurface方法创建主表面。在对该方法的调用中，第一个参数是描述将要创建的表面的DDSURFACEDESC结构的指针；第二个参数是一个变量的指针，如果调用成功，该变量将接收IDirectDrawSurface接口的指针；第三个参数设为NULL表明没有COM集合。<br><br>4.2 检测硬件对覆盖的支持<br>初始化DirectDraw后，需要检测设备是否支持覆盖表面。因为DirectDraw不能仿真覆盖，所以如果硬件不支持覆盖，就不能继续下面的工作。你可以用IDirectDraw2::GetCaps方法获取硬件设备驱动程序的能力检测覆盖支持。在调用该方法之后，查看DDCAPS结构中的dwFlags成员是否包含有DDCAPS_OVERLAY标志。若有就表明支持覆盖，否则就不支持。<br>下面的代码是Mosquito程序中的一部分，它表明了怎样检测硬件的覆盖支持能力：<br><br></font><font face=宋体 color=#99ccff size=2>BOOL AreOverlaysSupported()<br>{<br>&nbsp;&nbsp;&nbsp; DDCAPS capsDrv;<br>&nbsp;&nbsp;&nbsp; HRESULT ddrval;<br>&nbsp;&nbsp;&nbsp; // Get driver capabilities to determine Overlay support.<br>&nbsp;&nbsp;&nbsp; ZeroMemory(&amp;capsDrv, sizeof(capsDrv));<br>&nbsp;&nbsp;&nbsp; capsDrv.dwSize = sizeof(capsDrv);<br>&nbsp;&nbsp;&nbsp; ddrval = g_lpdd-&gt;GetCaps(&amp;capsDrv, NULL);<br>&nbsp;&nbsp;&nbsp; if (FAILED(ddrval))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br>&nbsp;&nbsp;&nbsp; // Does the driver support overlays in the current mode?<br>&nbsp;&nbsp;&nbsp; // (Currently the DirectDraw emulation layer does not support overlays.<br>&nbsp;&nbsp;&nbsp; // Overlay related APIs will fail without hardware support).<br>&nbsp;&nbsp;&nbsp; if (!(capsDrv.dwCaps &amp; DDCAPS_OVERLAY))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br><br>&nbsp;&nbsp;&nbsp; return TRUE;<br>}</font><font face=Tahoma size=2><br><br>程序首先调用IDirectDraw2::GetCaps方法获取设备驱动程序的能力。第一个参数是DDCAPS结构的地址指针；因为程序不需要关仿真的信息，所以第二个参数就设为NULL。获取驱动程序的能力后，程序使用了逻辑&#8220;与&#8221;来检查dwFlags成员是否包含有DDCAPS_OVERLAY标志。若否，程序返回FALSE表明失败。若是，就返回TRUE表明显示设备支持覆盖表面。<br><br>4.3 创建一个覆盖表面<br>如果知道显示设备支持覆盖表面，就可以创建一个。因为没有指明设备怎样支持覆盖表面的标准，所以不能够期望创建任意大小的像素格式的表面。另外，也不要期望第一次创建覆盖表面就会成功。因此，必须作好准备进行多次创建的尝试，直到有一个能够工作为止。<br>Mosquito程序在创建表面时遵循&#8220;best case to worst case&#8221;的原则，首先尝试创建一个三缓冲页翻转复杂覆盖表面。如果尝试失败，程序就改变方法尝试用其它通用的迅速格式来配置。下面的代码就是这一思路的表现：<br><br></font><font face=宋体 color=#99ccff size=2>ZeroMemory(&amp;ddsdOverlay, sizeof(ddsdOverlay));<br>ddsdOverlay.dwSize = sizeof(ddsdOverlay);<br>ddsdOverlay.dwFlags= DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH|DDSD_BACKBUFFERCOUNT| DDSD_PIXELFORMAT;<br>ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP|DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;<br>ddsdOverlay.dwWidth =320;<br>ddsdOverlay.dwHeight =240;<br>ddsdOverlay.dwBackBufferCount=2;<br>// Try to create an overlay surface using one of the pixel formats in our<br>// global list.<br>i=0;<br>do{<br>&nbsp;&nbsp;&nbsp; ddsdOverlay.ddpfPixelFormat=g_ddpfOverlayFormats[i];<br>&nbsp;&nbsp;&nbsp; // Try to create the overlay surface<br>&nbsp;&nbsp;&nbsp; ddrval = g_lpdd-&gt;CreateSurface(&amp;ddsdOverlay,&amp;g_lpddsOverlay, NULL);<br>} while( FAILED(ddrval) &amp;&amp; (++i &lt; NUM_OVERLAY_FORMATS));</font><font face=Tahoma size=2><br><br>程序设置DDSURFACEDESC结构中的标志和值以反映三缓冲页翻转复杂覆盖表面，然后执行循环。在循环中，程序尝试用各种常用的像素格式创建要求的表面。如果尝试成功，循环就终止。如果尝试失败，说明很有可能是显示硬件没有足够的显示内存支持三缓冲的方案或者硬件根本就不支持翻转覆盖表面。在这种情况下，在最小要求的配置下使用一个单一的非翻转覆盖表面，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>// If we failed to create a triple buffered complex overlay surface, try<br>// again with a single non-flippable buffer.<br>if(FAILED(ddrval))<br>{<br>&nbsp;&nbsp;&nbsp; ddsdOverlay.dwBackBufferCount=0;<br>&nbsp;&nbsp;&nbsp; ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY| DDSCAPS_VIDEOMEMORY;<br>&nbsp;&nbsp;&nbsp; ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT;<br>&nbsp;&nbsp;&nbsp; // Try to create the overlay surface<br>&nbsp;&nbsp;&nbsp; ddrval = g_lpdd-&gt;CreateSurface(&amp;ddsdOverlay,&amp;g_lpddsOverlay, NULL);<br>&nbsp;&nbsp;&nbsp; i=0;<br>&nbsp;&nbsp;&nbsp; do<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddsdOverlay.ddpfPixelFormat=g_ddpfOverlayFormats[i];ddrval = g_lpdd-&gt;CreateSurface(&amp;ddsdOverlay, &amp;g_lpddsOverlay, NULL);<br>&nbsp;&nbsp;&nbsp; }while( FAILED(ddrval)&amp;&amp; (++i &lt; NUM_OVERLAY_FORMATS) ); // We couldn't create an overlay surface. Exit, returning failure.<br>&nbsp;&nbsp;&nbsp; if (FAILED(ddrval))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br>}</font><font face=Tahoma size=2><br><br>　　上面的代码对DDSURFACEDESC结构中的标志和值复原来反映一个单一的非翻转覆盖表面，然后通过像素格式的循环尝试创建表面。如果创建表面成功，循环就停止。如果不成功，程序返回FALSE表明创建表面失败。在成功地创建覆盖表面之后，就可将位图装入其中以供显示。<br><br>4.4 显示覆盖表面<br>创建了覆盖表面之后就可以显示它了。通常，硬件在用于显示覆盖的矩形的位置和像素格式上加上对齐约束。另外，还需要经常通过调整目的矩形的宽度来说明最小要求的拉伸因子以成功地显示覆盖表面。Mosquito程序按照以下的步骤准备和显示覆盖表面。<br><br>4.4.1、检测显示的最小要求<br>大部分的显示硬件在显示覆盖时都会加上约束。你必须很仔细地调整覆盖使之满足这些约束。可以通过调用IDirectDraw2::GetCaps方法获得有关这些约束的信息。该方法填充的结构DDCAPS包含了有关覆盖能力和使用约束的信息。不同硬件的约束是不同的，因此必须始终要查看包含在dwFlags成员的标志以确定附加的是哪一种约束。<br>　　Mosquito程序开始时先获取硬件的能力，然后采用基于最小拉伸因子的方法，如下所示：<br><br></font><font face=宋体 color=#99ccff size=2>// Get driver capabilities<br>ddrval = g_lpdd-&gt;GetCaps(&amp;capsDrv, NULL);<br>if (FAILED(ddrval))<br>&nbsp;&nbsp;&nbsp; return FALSE;<br><br>// Check the minimum stretch and set the local variable accordingly.<br>if(capsDrv.dwCaps &amp; DDCAPS_OVERLAYSTRETCH)<br>&nbsp;&nbsp;&nbsp; uStretchFactor1000 = (capsDrv.dwMinOverlayStretch&gt;1000) ? capsDrv.dwMinOverlayStretch : 1000;<br>else<br>&nbsp;&nbsp;&nbsp; uStretchFactor1000 = 1000;</font><font face=Tahoma size=2><br><br>上面的代码调用IDirectDraw2::GetCaps方法获取硬件的能力。在本例中，第一个参数是DDCAPS结构的指针；第二个参数是NULL，表明了不需要获取有关仿真的信息。程序在一个临时变量中保留了最小拉伸因子以备以后之用。如果驱动程序报告出的拉伸因子大于1000，就表明驱动程序要求所有的目的矩形沿X轴的方向拉伸。例如，若拉伸因子是1.3，源矩形宽320个像素，目的矩形就必须至少要有416（320X1.3=416）个像素的宽。如果驱动程序报告出的拉伸因子小于1000，就表明驱动程序能够显示比源矩形小的覆盖，但不能伸展覆盖。<br>下面的代码是测定描述驱动程序的大小对齐约束的值：<br></font><font face=宋体 color=#99ccff size=2><br>// Grab any alignment restrictions and set the local variables acordingly.<br>uSrcSizeAlign = (capsDrv.dwCaps &amp; DDCAPS_ALIGNSIZESRC)?capsDrv.dwAlignSizeSrc:0;<br>uDestSizeAlign= (capsDrv.dwCaps &amp; DDCAPS_ALIGNSIZESRC)</font><font face=Tahoma size=2><br><br>?capsDrv.dwAlignSizeDest:0; 例中使用了更多的临时变量来保存从dwAlignSizeSrc和dwAlignSizeDest成员中获得的大小对齐约束。这些值提供了有关像素宽度对齐约束的信息，并且在以后设定源矩形和目的矩形的大小时需要用到。源矩形和目的矩形必须是这些值的倍数。<br>最后，程序测定描述目的矩形边界对齐约束的值：<br><br></font><font face=宋体 color=#99ccff size=2>// Set the "destination position alignment" global so we won't have to<br>// keep calling GetCaps() every time we move the overlay surface.<br>if (capsDrv.dwCaps &amp; DDCAPS_ALIGNBOUNDARYDEST)<br>&nbsp;&nbsp;&nbsp; g_dwOverlayXPositionAlignment= capsDrv.dwAlignBoundaryDest;<br>else<br>&nbsp;&nbsp;&nbsp; g_dwOverlayXPositionAlignment= 0;</font><font face=Tahoma size=2><br><br>上面的代码使用了一个全局变量来保存目的矩形边界约束的值，该值是从dwAlignBoundaryDest成员中的得来的，在以后程序重新放置覆盖时将会用到。你必须设定目的矩形左上角的X坐标在像素格式上同该值对齐。也就是说，如果该值是4，就只能指定左上角的X坐标为0，4，8，12等像素宽的目的矩形。Mosquito程序首先在0，0处显示覆盖，于是在第一次显示覆盖之前就不需要获取约束信息。但是因为不同应用程序的实现过程可能不同，所以你可能需要在显示覆盖之前检查这些信息以调整目的矩形。<br><br>4.4.2、设置源矩形和目的矩形<br>在获得了驱动程序的覆盖约束之后，就应该设定有关源矩形和目的矩形的值，确保能够正确显示覆盖。下面的代码就设定了源矩形的特征：<br><br></font><font face=宋体 color=#99ccff size=2>// Set initial values in the source RECT.<br>rs.left=0;<br>rs.top=0;<br>rs.right = 320;<br>rs.bottom = 240;<br>// Apply size alignment restrictions, if necessary.<br>if (capsDrv.dwCaps &amp; DDCAPS_ALIGNSIZESRC &amp;&amp;uSrcSizeAlign)<br>&nbsp;&nbsp;&nbsp; rs.right -= rs.right % uSrcSizeAlign;</font><font face=Tahoma size=2><br><br>上面的代码设置了包含整个表面大小的初始值。如果设备驱动程序要求大小对齐，程序就调整源矩形来保证。程序调整了源矩形的宽度使之比初始值要小，这是因为如果不是完全重新创建表面，就不能够扩展宽度。<br>在设定了源矩形的大小后，需要设置和调整目的矩形的大小。这一过程需要稍微多一点的工作，因为目的矩形可能需要先拉伸再调整以符合大小对齐约束。下面的代码根据最小拉伸因子来设置和调整目的矩形的大小：<br><br></font><font face=宋体 color=#99ccff size=2>// Set up the destination RECT, starting with the source RECT values.<br>// We use the source RECT dimensions instead of the surface dimensions in<br>// case they differ.<br>rd.left=0;<br>rd.top=0;<br>rd.right = (rs.right*uStretchFactor1000+999)/1000;<br>// (Adding 999 avoids integer truncation problems.)<br>// (This isn't required by DDraw, but we'll stretch the<br>// height, too, to maintain aspect ratio).<br>rd.bottom = rs.bottom*uStretchFactor1000/1000;</font><font face=Tahoma size=2><br><br>前面的代码先设置目的矩形的左上角位置，再根据最小拉伸因子设定目的矩形的宽度。根据拉伸因子调整矩形时，注意程序在宽度和拉伸因子的乘积上又加了999，这是为了避免出现整数截断，整数截断会导致矩形同最小拉伸因子的要求不一致。程序在拉伸宽度后也拉伸了矩形的高度。不过，对高度的拉伸并不是必须的，这里只是为了保持位图的长宽比率避免出现失真的现象。<br>拉伸目的矩形后，程序对其进行调整以保持和大小对齐约束一致，下面是相应的代码：<br><br></font><font face=宋体 color=#99ccff size=2>// Adjust the destination RECT's width to comply with any imposed<br>// alignment restrictions.<br>if (capsDrv.dwCaps &amp; DDCAPS_ALIGNSIZEDEST &amp;&amp;uDestSizeAlign)<br>&nbsp;&nbsp;&nbsp; rd.right = (int)((rd.right+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;</font><font face=Tahoma size=2><br><br>程序检测硬件能力的标志，查看驱动程序是否加了矩形大小对齐约束。如果是，就增加目的矩形的宽度使之满足大小对齐约束。这里对矩形的调整是扩展其宽度而不能减少其宽度，因为减少宽度可能会导致目的矩形比最小拉伸因子要求的还要小，从而引起显示覆盖表面失败。<br><br>4.4.3、显示覆盖表面 在设置了源矩形和目的矩形后，就可以显示覆盖了。如果显示覆盖之前的准备工作正确，显示覆盖会很简单。Mosquito程序用如下的代码来显示覆盖：<br><br></font><font face=宋体 color=#99ccff size=2>&nbsp;// Set the flags we'll send to UpdateOverlay dwUpdateFlags = DDOVER_SHOW | DDOVER_DDFX;<br>// Does the overlay hardware support source color keying?<br>// If so, we can hide the black background around the image.<br>// This probably won't work with YUV formats<br>if (capsDrv.dwCKeyCaps &amp; DDCKEYCAPS_SRCOVERLAY)<br>&nbsp;&nbsp;&nbsp; dwUpdateFlags |= DDOVER_KEYSRCOVERRIDE;<br>// Create an overlay FX structure so we can specify a source color key.<br>// This information is ignored if the DDOVER_SRCKEYOVERRIDE flag isn't set.<br>ZeroMemory(&amp;ovfx, sizeof(ovfx));<br>ovfx.dwSize = sizeof(ovfx);<br>ovfx.dckSrcColorkey.dwColorSpaceLowValue=0; // Specify black as the color key<br>ovfx.dckSrcColorkey.dwColorSpaceHighValue=0;<br>// Call UpdateOverlay() to displays the overlay on the screen.<br>ddrval = g_lpddsOverlay-&gt;UpdateOverlay(&amp;rs, g_lpddsPrimary, &amp;rd, dwUpdateFlags, &amp;ovfx);<br>if(FAILED(ddrval))<br>&nbsp;&nbsp;&nbsp; return FALSE;</font><font face=Tahoma size=2><br><br>程序开始在临时变量dwUpdateFlags中设定了DDOVER_SHOW和DDOVER_DDFX标志，指明该覆盖是第一次显示，硬件应该使用包含在DDOVERLAYFX结构中的效果信息完成这一工作。然后，程序检查DDCAPS结构确定覆盖是否支持源Color Key。如果是，DDOVER_KEYSRCOVERRIDE标志就包含在dwUpdateFlags变量中利用源Color Key，程序也据此设置Color Key。<br>准备工作完成之后，程序调用IDirectDrawSurface3::UpdateOverlay方法来显示覆盖。在对该方法的调用中，第一个参数和第三个参数是已调整的源矩形和目的矩形的地址。第二个参数是覆盖显示在其上的主表面的地址。第四个参数是包括了放置于此前准备的dwUpdateFlags变量中的标志。第五个参数是DDOVERLAYFX结构的地址，该结构中的成员将设定同那些标志相匹配。<br>如果硬件只支持一个覆盖表面而且该表面正在使用，UpdateOverlay方法就会失败，并返回DDERR_OUTOFCAPS。另外，有可能硬件报告出的最小拉伸因子过小，在UpdateOverlay方法失败后，你就需要尝试减少目的矩形的宽度来应付这种可能性。不过，这种情况很少发生，在Mosquito中也只是简单地返回一个错误信息。<br><br>4.5 更新覆盖的显示位置<br>显示覆盖表面之后，有时可能就不需要对覆盖左其它的操作了。但有些软件还需要重新放置覆盖，改变覆盖的显示位置。Mosquito程序就使用IDirectDrawSurface3::SetOverlayPosition方法重新放置覆盖，代码如下：<br><br></font><font face=宋体 color=#99ccff size=2>// Set X- and Y-coordinates<br>...... // We need to check for any alignment restrictions on the X position<br>// and align it if necessary.<br>if (g_dwOverlayXPositionAlignment)<br>&nbsp;&nbsp;&nbsp; dwXAligned = g_nOverlayXPos- g_nOverlayXPos % g_dwOverlayXPositionAlignment;<br>else<br>&nbsp;&nbsp;&nbsp; dwXAligned = g_nOverlayXPos;<br>// Set the overlay to its new position.<br>ddrval = g_lpddsOverlay-&gt;SetOverlayPosition(dwXAligned,g_nOverlayYPos);<br>if (ddrval == DDERR_SURFACELOST)<br>{<br>&nbsp;&nbsp;&nbsp; if (!RestoreAllSurfaces())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>}</font><font face=Tahoma size=2><br><br>程序开始对齐矩形以满足可能存在的任何目的矩形边界对齐约束。当程序此前调用IDirectDraw2::GetCaps方法时，全局变量g_dwOverlayXPositionAlignment已经设定为同DDCAPS结构中dwAlignBoundaryDest成员所报告出的值相等。如果存在目的矩形约束，程序就据此调整新的X坐标为像素对齐的。若不满足要求，覆盖表面就不能显示。<br>在完成了对X坐标的调整之后，程序调用IDirectDrawSurface3::SetOverlayPosition方法重新放置覆盖。在调用中第一个参数是对齐的新的X坐标，第二个参数的新的Y坐标。这些值表明了覆盖左上角新的位置。这里并不需要取得宽度和高度信息，因为DirectDraw在开始用IDirectDrawSurface3::UpdateOverlay方法显示覆盖时就已经获得了表面大小的信息。如果因为一个或多个表面丢失而引起的重新放置覆盖表面的失败，Mosquito程序就调用一个应用定义的函数来恢复这些表面并重新装入它们的位图。<br>注意，不要使用太靠近目标表面的右、下边界的坐标。因为IDirectDraw2::SetOverlayPosition方法并不执行剪切功能，所以使用那些可能导致覆盖超出目标表面边界的坐标会引起调用的失败，并返回DDERR_INVALIDPOSITION。<br><br>4.6 隐藏覆盖表面<br>如果不再需要一个覆盖表面或只想不让覆盖可见，就可以设定适当的标志调用IDirectDrawSurface3::UpdateOverlay方法来隐藏该覆盖表面。Mosquito用以下代码隐藏覆盖表面并准备关闭应用程序：<br><br></font><font face=宋体 color=#99ccff size=2>void DestroyOverlay()<br>{<br>&nbsp;&nbsp;&nbsp; if (g_lpddsOverlay)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Use UpdateOverlay() with the DDOVER_HIDE flag to remove an overlay<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // from the display.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_lpddsOverlay-&gt;UpdateOverlay(NULL,g_lpddsPrimary, NULL, DDOVER_HIDE, NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_lpddsOverlay-&gt;Release();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_lpddsOverlay=NULL;<br>&nbsp;&nbsp;&nbsp; }<br>}</font><font face=Tahoma size=2><br><br>在调用IDirectDrawSurface3::UpdateOverlay时，对源矩形和目的矩形指定了NULL，因为在隐藏覆盖的过程中不需要源矩形和目的矩形。<br>同理，第五个参数也被指定为NULL是因为不使用覆盖效果。第二个参数是目标表面的指针。最后，程序在第四个参数使用DDOVER_HIDE标志表明该覆盖将从视口中取消。<br>　　程序在隐藏覆盖之后，释放了它的IDirectDrawSurface3接口，并且将全局变量设为NULL使之变得无效。对于Mosquito程序来说，覆盖就不再需要了。如果在应用程序中还需要使用该覆盖，就只需简单地隐藏覆盖，而不要释放它，然后在需要的时候再重新显示。<br><br><strong>五 DirectDraw中其它的DirectDraw范例</strong><br>要熟练掌握有关DirectDraw的应用，还应该多研究包含在DirectX<br>SDK:之中的下述范例。<br>1、Stretch<br>描述了怎样在一个窗口中创建一个非独占模式的动画，它具有剪切位块传输和拉伸剪切位块传输的功能。<br>2、Donut<br>描述了多个独占模式的应用同非独占模式应用之间的交互。<br>3、Wormhole<br>该范例描述了详细的调色板动画。<br>4、Dxview<br>详细描述了怎样获取显示硬件的能力。<br>　 其它的还有Duel、Iklowns、Foxbear、Palette和Flip2d等，只要多这些范例多加分析，掌握DirectX最基本的技术DirectDraw是不难的。</font></td>
                                </tr>
                            </tbody>
                        </table>
                        </center></div>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86487.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-06-01 23:41 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86487.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何实现游戏截屏功能</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86486.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:33:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86486.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86486.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86486.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86486.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86486.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/jietu.htm">http://dev.gameres.com/Program/Visual/2D/jietu.htm</a><br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <p align=center><font face=Tahoma color=#ffffcc><strong>如何实现游戏截屏功能</strong></font> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <div align=center>
                        <center>
                        <table cellSpacing=0 cellPadding=0 width="95%" border=0>
                            <tbody>
                                <tr>
                                    <td width="100%"><font face=Tahoma size=2><br>　　现在很多游戏都提供一种截屏的功能，用来截取游戏中的画面，那么这是如何实现的呢？其实就是把游戏当前屏幕的数据存成一个图片文件；在这里我给出一段源程序，它实现了DirectDraw（16位模式）下的的游戏截屏。生成的文件格式为ＢＭＰ，程序有比较详细的注释，请各位自己看吧。<br><br>　　现在很多游戏都提供一种截屏的功能，用来截取游戏中的画面，那么这是如何实现的呢？其实就是把游戏当前屏幕的数据存成一个图片文件；在这里我给出一段源程序，它实现了DirectDraw（16位模式）下的的游戏截屏。生成的文件格式为ＢＭＰ，程序有比较详细的注释，请各位自己看吧。<br><br></font><font face=宋体 color=#99ccff size=2>bool Is555; // 是否是565模式，这个变量需要用者填写<br><br>// 功能：将一个16位的DirectDraw表面，存为一张24位BMP位图 （传入主表面即截屏）<br>// 输入：表面指针，输出的文件名<br>// 输出：是否成功<br>bool SaveToBitmapFile(LPDIRECTDRAWSURFACE lpSurface, char* filename)<br>{<br>&nbsp;&nbsp;&nbsp; WORD* lpBuffer; // 表面指针<br>&nbsp;&nbsp;&nbsp; int nPitch; // 表面跨距<br>&nbsp;&nbsp;&nbsp; int nWidth, nHeight; // 表面宽高<br><br>&nbsp;&nbsp;&nbsp; // 打开文件s<br>&nbsp;&nbsp;&nbsp; FILE* fp;<br>&nbsp;&nbsp;&nbsp; if( (fp=fopen(filename, "wb")) != NULL )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 锁定表面<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DDSURFACEDESC ddsd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ddsd.dwSize = sizeof(ddsd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HRESULT ddrval = lpSurface-&gt;Lock( NULL, &amp;ddsd, DDLOCK_WAIT, NULL );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( ddrval == DD_OK )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpBuffer = (WORD *)ddsd.lpSurface;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nWidth = ddsd.dwWidth;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nHeight = ddsd.dwHeight;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nPitch = ddsd.lPitch &gt;&gt; 1; //lPitch以Byte为单位，GraphPitch以WORD为单位。所以GraphPitch = lPitch / 2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 保存文件头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BITMAPFILEHEADER FileHeader;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileHeader.bfType = 'BM';<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileHeader.bfSize = nWidth * nHeight * 3 + 0x36;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileHeader.bfReserved1 = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileHeader.bfReserved2 = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileHeader.bfOffBits = 0x36;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fwrite(&amp;FileHeader, sizeof(BITMAPFILEHEADER), 1, fp);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 保存文件信息<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BITMAPINFOHEADER Header;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biSize = sizeof(BITMAPINFOHEADER); // 结构的大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biWidth = nWidth; // 宽<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biHeight = nHeight; // 高<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biPlanes = 1; // 固定<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biBitCount = 24; // 颜色数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biCompression = BI_RGB; // 是否压缩<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biSizeImage = nWidth * nHeight * 3; // 图片的大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biXPelsPerMeter = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biYPelsPerMeter = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biClrUsed = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Header.biClrImportant = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fwrite(&amp;Header, Header.biSize, 1, fp);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 写入具体内容(从下向上存放)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fseek(fp, 0x36, SEEK_SET);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WORD word;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpBuffer += nWidth * (nHeight - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i</font><nheight; i++)<br><font face=宋体 color=#99ccff size=2> {<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><font face=宋体 color=#99ccff size=2>for(int j=0; j</font><nwidth; j++)<br><font face=宋体 color=#99ccff size=2> {<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>word = *lpBuffer;<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>fputc( GetBlue( word ), fp); // 蓝<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>fputc( GetGreen( word ), fp); // 绿<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>fputc( GetRed( word ), fp); // 红<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>lpBuffer++;<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>}<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>lpBuffer -= nPitch*2; // 指针转到上一行的开始<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>}<br><br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>fclose(fp);<br><br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>// 解锁表面<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>ddrval = lpSurface-&gt;Unlock( NULL );<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>return true;<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>}<br><br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>return false;<br>}<br><br>inline unsigned char GetRed(WORD color)<br>{<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>if( Is555 )<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>return (color&gt;&gt;7) &amp; 0xff;<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>else<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>return (color&gt;&gt;8) &amp; 0xff;<br>}<br><br>inline unsigned char GetGreen(WORD color)<br>{<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>if( Is555 )<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>return (color&gt;&gt;2) &amp; 0xff;<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>else<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>return (color&gt;&gt;3) &amp; 0xff;<br>}<br><br>inline unsigned char GetBlue(WORD color)<br>{<br></font><font face=宋体 color=#99ccff size=2>&nbsp;&nbsp;&nbsp; </font><nheight; i++)<br><nwidth; j++)<br><font face=宋体 color=#99ccff size=2>return (color &amp; 0x1f) &lt;&lt; 3;<br>}<br></font><font face=Tahoma size=2><br>------------------------<br>金点时空<br>softboy<br>2001.5.9<br>http://www.gpgame.com<br>------------------------<br></font></td>
                                </tr>
                            </tbody>
                        </table>
                        </center></div>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86486.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-06-01 23:33 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DirectDraw编程技巧</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86485.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:30:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86485.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86485.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86485.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86485.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86485.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/articles.asp?style=a&amp;page=2">http://dev.gameres.com/articles.asp?style=a&amp;page=2</a><a href="http://dev.gameres.com/Program/Visual/2D/DrawAdd.htm"><br>http://dev.gameres.com/Program/Visual/2D/DrawAdd.htm</a><br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <p align=center><strong><font face=Tahoma color=#ffffcc>DirectDraw编程技巧</font></strong> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <div align=center>
                        <center>
                        <table cellSpacing=0 cellPadding=0 width="95%" border=0>
                            <tbody>
                                <tr>
                                    <td width="100%"><font face=Tahoma size=2><br>使用IDirectDrawSurface::Lock( )就能让我们随心所欲，因为此函数可以允许我们直接修改页面。<br>　　Lock( )函数的用法如下：<br><br></font><font face=宋体 color=#99ccff size=2>HRESULT Lock(<br>&nbsp;&nbsp;&nbsp; LPRECT lpDestRect,<br>&nbsp;&nbsp;&nbsp; LPDDSURFACEDESC lpDDSurfaceDesc,<br>&nbsp;&nbsp;&nbsp; DWORD dwFlags,<br>&nbsp;&nbsp;&nbsp; HANDLE hEvent<br>);<br></font><font face=Tahoma size=2><br>　　第一个参数为一个指向某个RECT的指针，它指定将被锁定的页面区域。如果该参数为 NULL，整个页面将被锁定。<br>　　第二个参数为一个 DDSURFACEDESC结构的地址，将被填充页面的相关信息。<br>　　第三个参数，即dwFlags，还是象以前一样给它DDLOCK_WAIT。<br>　　第四个参数规定要为NULL。<br>　　现在举一个例子来说明怎样使用Lock( )，我们的目标是使lpDDSBack半透明地浮现在lpDDSBuffer上。先看看完整的锁屏部分（注意，这一节只讨论24和32位色下如何操作）：<br><br></font><font face=宋体 color=#99ccff size=2>DDSURFACEDESC ddsd, ddsd2; //DirectDraw页面描述<br>ZeroMemory(&amp;ddsd, sizeof(ddsd)); //ddsd用前要清空<br>ddsd.dwSize = sizeof(ddsd); //DirectDraw中的对象都要这样<br>ZeroMemory(&amp;ddsd2, sizeof(ddsd2));<br>ddsd2.dwSize = sizeof(ddsd2);<br>lpDDSBuffer-&gt;Lock(NULL, &amp;ddsd, DDLOCK_WAIT, NULL); //Lock!<br>lpDDSBack-&gt;Lock(NULL, &amp;ddsd2, DDLOCK_WAIT, NULL);<br><br>BYTE *Bitmap = (BYTE*)ddsd.lpSurface; //Lock后页面的信息被存在这里，请注意<br>//这个指针可能每次Lock( )后都不同！<br>BYTE *Bitmap2 = (BYTE*)ddsd2.lpSurface;</font><font face=Tahoma size=2><br><br>　　锁完页面后，Bitmap数组的存储格式是这样的：如为32位色则页面上坐标为(x,y)的点的R/G/B值分别存在Bitmap的y*ddsd.lPitch+x*4，y*ddsd.lPitch+x*4+1，y*ddsd.lPitch+x*4+2处；如为24位色则页面上坐标为(x,y)的点的R/G/B值分别存在Bitmap的y*ddsd.lPitch+x*3，y*ddsd.lPitch+x*3+1，y*ddsd.lPitch+x*3+2处。所以，现在我们就可以发挥想象，做出想要的一切效果了，比如说动态光照(将一目标页面按光照表改变亮度即可)！下面是接下来的代码(32位色时)：<br><br></font><font face=宋体 color=#99ccff size=2>int pos;<br>for (int y=0;y&lt;480; y++)<br>{<br>&nbsp;&nbsp;&nbsp; pos=y*ddsd.lPitch;<br>&nbsp;&nbsp;&nbsp; for (int x=0; x&lt;640; x++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap[pos] =(Bitmap[pos]+Bitmap2[pos])&gt;&gt;1; //改R<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap[pos] =(Bitmap[pos]+Bitmap2[pos])&gt;&gt;1; //改G<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap[pos] =(Bitmap[pos]+Bitmap2[pos])&gt;&gt;1; //改B<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos+=2;//到下一个R处<br>&nbsp;&nbsp;&nbsp; }<br>}<br>lpDDSBack-&gt;Unlock(&amp;ddsd2); //Unlock!<br>lpDDSBuffer-&gt;Unlock(&amp;ddsd);</font><font face=Tahoma size=2><br><br>　　由于使用Lock后DirectDraw要锁定页面，在没有使用Unlock( )前我们是无法用其他办法如Blt来修改页面的。所以用完Lock( )要赶快象上面的程序那样Unlock( )。Unlock的方法很简单，lpDDSXXX-&gt;Unlock(LPDDSURFACEDESC lpDDSurfaceDesc)即可。<br><br>7.2 程序的提速<br>　　上面的程序看起来好象很简单，但运行速度很可能会很慢，即使你直接用汇编重写也不会快多少。原因是读显存非常慢，写显存的速度也比写内存慢。解决这个问题的方法是：<br>(1)把除了主页面外的所有页面放在内存中。（初始化页面时将ddsd.ddsCaps.dwCaps中的 DDSCAPS_OFFSCREENPLAIN后再或( | )一项"DDSCAPS_SYSTEMMEMORY"）这样做的另一个好处是你Lock( )一次后就永远得到了页面指针，而且然后一Unlock( )就又可以使用Blt了，你就拥有了两种改变页面的手段。<br>(2)将后台缓冲改为一个普通的离屏页面。<br>(3)将Flip( )改用BltFast( )函数实现。当然你也可以直接用memcpy( )拷贝，这样做有时候会快一些。注意要一行一行地拷贝，比如说640x480x24位色全屏幕拷贝是这样的：<br><br></font><font face=宋体 color=#99ccff size=2>BYTE *pSrc=(BYTE *)ddsd_src.lpSurface;<br>BYTE *pDest=(BYTE *)ddsd_dest.lpSurface;<br><br>for (int y=0;y&lt;480;y++)<br>{<br>&nbsp;&nbsp;&nbsp; memcpy(pDest, pSrc, 1920); //若为32位色则为2560<br>&nbsp;&nbsp;&nbsp; pSrc+=ddsd_src.lPitch;<br>&nbsp;&nbsp;&nbsp; pDest+=ddsd_dest.lPitch;<br>}</font><font face=Tahoma size=2><br><br>　　看起来好像要重写很多东西，其实改动的部分并不多。这样改了之后整个程序的速度就会快很多，但还不能很好地满足全屏幕特效的要求，因为全屏幕特效实在很耗时间，只有用MMX指令重写速度才能比较快。所以在下面一章中，我们将介绍内嵌汇编和MMX指令。<br><br>7.3 排除错误的技巧<br>　　每个人都会犯错误，在编程中也如此，写完程序后第一次运行就直接通过的情况实在是不多的，偶尔出现一两次都是值得高兴的事。有错当然要改，但很多时候最难的并不是改正错误，而是找到错误，有时候写程序的时间还不如找错误的时间长。为了帮助大家节省一点时间，下面就讲一讲我的一点找错误的经验。<br>　　首先当然要说说常见的错误有哪些，最经常出现的是：漏分号、多分号、漏各种括号、多各种括号、"=="写成了"="（上面的错误看上去很弱智，不过你没犯过吗？）、数组（指针）越界（最常见的错误之一！）、变量越界、指针使用前未赋初值。有一点要注意的是Visual C++显示出的出错的那一行有可能不是真正出错的位置！<br>　　常用的找错办法就是先确认你刚刚改动了哪些语句，然后用/*和*/把可能出错的语句屏障掉，如果运行后还不通过就再扩大范围。即使有一段程序你觉得不可能有什么问题或以前工作正常也要试试将它屏障，有时就是在似乎最不可能出错的地方出了问题。<br>　　还有一种大家都经常用的找错办法就是把一些变量的值显示在屏幕上，或是把程序运行的详细过程存入文件中，出什么问题一目了然。如果再像QuakeIII一样用一个"控制台"显示出来就很酷了。<br>　　象其它编译器一样，Visual C++提供了变量观察（Watch）、单步执行（Step）等常规调试手段，当然你首先需要把工程设为Debug模式，在编译---放置可运行设置中可更改。但可惜的是在DirectDraw程序中这些方法不怎么好用。可能有的人会问我为什么不把程序改为在窗口模式下运行，但在窗口模式下你能Lock吗？你能随意改动显存吗？如果把这些语句屏障掉那可就太麻烦了。不过有两条调试语句可以在DirectDraw程序中调用，第一条就是assert。该语句的使用方法是assert（条件），你可以把它放到需要的地方，当条件不满足时就会显示一个对话框，说明在哪个程序哪一行出现了条件不满足，然后你可以选择继续或是忽略。第二条语句是OutputDebugString(要输出的字符串)，可以在屏幕下方编译窗口的调试那一栏显示这个字符串。<br><br>7.4 C++程序优化技巧<br>　　首先请注意一点：再好的语句上的优化，比如说使用汇编语言，也比不上算法上的优化所带来的巨大效益。算法的设计是国内有些编程人员的不足之处，所以我觉得对这方面不太熟悉的人都应该买本讲数据结构与算法的书来看看，不要抄了别人的A*算法就觉得自己的程序很先进了。在9.4节讲述了几种常用的算法，如果你感兴趣可以看看。<br>下面就转入正题，讲一讲一般的优化技巧吧：<br>(1) 使用内联函数。<br><br>(2)展开循环。<br><br></font><font face=宋体 color=#99ccff size=2>for (i = 0; i &lt; 100; i++)<br>{<br>&nbsp;&nbsp;&nbsp; do_stuff(i);<br>}<br><br></font><font face=宋体 size=2>可以展开成：</font><font face=宋体 color=#99ccff size=2><br>for (i = 0; i &lt; 100; )<br>{<br>&nbsp;&nbsp;&nbsp; do_stuff(i); i++;<br>&nbsp;&nbsp;&nbsp; do_stuff(i); i++;<br>&nbsp;&nbsp;&nbsp; do_stuff(i); i++;<br>&nbsp;&nbsp;&nbsp; do_stuff(i); i++;<br>&nbsp;&nbsp;&nbsp; do_stuff(i); i++;<br>}<br><br></font><font face=宋体 size=2>(3)运算强度减弱。</font><font face=宋体 color=#99ccff size=2><br>x = w % 8;<br>y = x * 33;<br>for (i = 0; i &lt; MAX; i++)<br>{<br>&nbsp;&nbsp;&nbsp; h = 14 * i;<br>&nbsp;&nbsp;&nbsp; cout&lt; }<br><br></font><font face=宋体 size=2>上面的程序这样改动可以大大加快速度：</font><font face=宋体 color=#99ccff size=2><br>x = w &amp; 7;<br>y = (x &lt;&lt; 5) + x; //&lt;&lt;比+的运算优先级低！<br>for (i = h = 0; i &lt; MAX; i++)<br>{<br>&nbsp;&nbsp;&nbsp; cout&lt; h += 14;<br>}</font><font face=Tahoma size=2><br><br>(4)查表。这种方法挺有用，特别是在DirectDraw程序中。比如说我们定义了一个函数f(x)可以返回x*x*x，其中x的范围是0~1，精度为0.001，那么我们可以建立一个数组a[1000]，a[t*1000]存储的是预先计算好的t*t*t的值，以后调用函数就可以用查找数组代替了。<br></font></td>
                                </tr>
                            </tbody>
                        </table>
                        </center></div>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86485.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-06-01 23:30 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86485.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>动画程序编写——DirectDraw之旅（3）</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86484.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:26:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86484.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86484.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86484.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86484.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86484.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DDrawZL_3.htm">http://dev.gameres.com/Program/Visual/2D/DDrawZL_3.htm</a><br><br>
<table id=AutoNumber2 style="BORDER-COLLAPSE: collapse" height=8692 cellSpacing=0 cellPadding=0 width="85%" border=0>
    <tbody>
        <tr>
            <td width="100%" height=32>
            <p align=center><font color=#ffffee><strong>动画程序编写——DirectDraw之旅（3）</strong></font></p>
            </td>
        </tr>
        <tr>
            <td width="100%" height=18></td>
        </tr>
        <tr>
            <td width="100%" height=8564><font size=2>下面我们开始分析源程序：</font>
            <p><font size=2>下载源程序&nbsp; <font color=#ff0000>（编辑者：链接丢失）</font></font></p>
            <p><font size=2>属于Win32程序的基本框架的我们用蓝色标出，而用红色表出的是我们要重点学习的。下面的所有程序都是 FullScreenMode.cpp 文件中的内容，其中&#8220;IDB_DIRECTX&#8221;和&#8220;IDB_WinXP&#8221;都是图片资源的ID号，我想如何向程序中添加资源应该不用我多说了吧:)</font></p>
            <p><font color=#4898df size=2>工程文件：FullScreenMode.cpp</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>#define STRICT<br>#include &lt;windows.h&gt;<br>#include &lt;ddraw.h&gt;<br>#include &lt;mmsystem.h&gt;<br>#include "resource.h"<br>#include "ddutil.h"<br><br></font><font face=宋体 color=#008000 size=2>//定义删除指针和释放对象的宏</font><font face=宋体 color=#000000 size=2><br><br>#define SAFE_DELETE(p) <br>{ <br>　if(p) <br>　{ <br>　 delete (p); <br>　 (p)=NULL; <br>　 } <br>}<br><br>#define SAFE_RELEASE(p) <br>{ <br>　 if(p) <br>　 { <br>　　 (p)-&gt;Release(); <br>　　 (p)=NULL; <br>　 } <br>}<br><br>#define SCREEN_WIDTH 1024 </font><font face=宋体 color=#008000><font size=2>//定义屏幕宽度</font></font><font face=宋体 color=#000000 size=2><br><br>#define SCREEN_HEIGHT 768 </font><font face=宋体 color=#008000><font size=2>//定义屏幕高度</font></font><font face=宋体 color=#000000 size=2><br><br>#define SCREEN_BPP 8 </font><font face=宋体 color=#008000><font size=2>//定义调色板位数</font></font><font face=宋体 color=#000000 size=2><br><br>#define SPRITE_DIAMETER 32 </font><font face=宋体 color=#008000><font size=2>//定义飘动的子画面 的直径（宽度和高度一样）</font></font><font face=宋体 color=#000000 size=2><br><br>#define NUM_SPRITES 10 </font><font face=宋体 color=#008000><font size=2>//定义飘动的子画面 的个数</font></font><font face=宋体 color=#000000 size=2><br><br>#define HELPTEXT TEXT("Press Escape to quit.") </font><font face=宋体 color=#008000><font size=2>//定义文本住表面</font></font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><font size=2>上面出现的这个 TEXT( ) 是一个系统头文件里定义的宏，起作用是检查程序中是否定义了 Unicode ,如果有，就将括号中的文本转化成宽自负，如果没有，则转化成ASCLL 码。以下定义的是子画面飘动时的属性：</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>struct SPRITE_STRUCT<br><br>{<br>　FLOAT fPosX; </font><font face=宋体 color=#008000><font size=2>//sprite当前坐标的x值,如果在初始化时,即为初始坐标x值</font></font><font face=宋体 color=#000000 size=2><br>　FLOAT fPosY; </font><font face=宋体 color=#008000><font size=2>//sprite当前坐标的y值,如果在初始化时,即为初始坐标y值</font></font><font face=宋体 color=#000000 size=2><br>　FLOAT fVelX; </font><font face=宋体 color=#008000><font size=2>//sprite在x轴上的速度</font></font><font face=宋体 color=#000000 size=2><br>　FLOAT fVelY; </font><font face=宋体 color=#008000><font size=2>//sprite在y轴上的速度</font></font><font face=宋体 color=#000000 size=2><br>};<br><br>CDisplay* g_pDisplay = NULL; </font><font face=宋体 color=#008000><font size=2>/*CDisplay就是ddutil.h（我们又新加入的目录中的）中定义的类，用于处理表面之间的拷贝翻页等操作的类，再次定义一个全局变量，用于以后对指向的表面之间进行操作*/</font></font><font face=宋体 color=#000000 size=2><br><br>CSurface* g_pBackSurface = NULL; </font><font face=宋体 color=#008000><font size=2>/* CSurface也是ddutil.h头文件中定义的类，用于对表面本身进行操作，如设置色彩键码，在此定义的是背景图画指针*/</font></font><font face=宋体 color=#000000 size=2><br><br>CSurface* g_pLogoSurface = NULL; </font><font face=宋体 color=#008000><font size=2>/*子画面图画指针*/</font></font><font face=宋体 color=#000000 size=2><br><br>CSurface* g_pTextSurface = NULL; </font><font face=宋体 color=#008000><font size=2>/*背景文本指针*/</font></font><font face=宋体 color=#000000 size=2><br><br>BOOL g_bActive = FALSE; </font><font face=宋体 color=#008000><font size=2>/*定义一个bool形变量，起到一个开关的作用，对程序流程进行控制*/</font></font><font face=宋体 color=#000000 size=2><br><br>DWORD g_dwLastTick; </font><font face=宋体 color=#008000><font size=2>/*用于记录最后一次的系统时间*/</font></font><font face=宋体 color=#000000 size=2><br><br>SPRITE_STRUCT g_Sprite[NUM_SPRITES]; </font><font face=宋体 color=#008000><font size=2>/*定义多个子画面，其中包括多个 运动时的属性*/</font></font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>Function-prototypes</font></strong></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );</font><font face=宋体 color=#008000 size=2>//主窗口消息处理函数</font><font face=宋体 color=#000000 size=2><br><br>HRESULT WinInit( HINSTANCE hInst, int nCmdShow, HWND* phWnd, HACCEL* phAccel );</font><font face=宋体 color=#008000><font size=2>//窗口类设置，注册，并创建窗口</font></font><font face=宋体 color=#000000 size=2><br><br>HRESULT InitDirectDraw( HWND hWnd );<br><br>VOID FreeDirectDraw();<br><br>HRESULT ProcessNextFrame();<br><br>VOID UpdateSprite( SPRITE_STRUCT* pSprite, FLOAT fTimeDelta );<br><br>HRESULT DisplayFrame();<br><br>HRESULT RestoreSurfaces();</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>WinMain()</font></strong></p>
            <p><font size=2>Desc: Entry point to the program. Initializes everything and calls</font></p>
            <p><font size=2>UpdateFrame() when idle from the message pump.</font></p>
            </font><font face=宋体>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font color=#000000 size=2>int APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, int nCmdShow )<br>{<br>　 MSG msg;<br>　<br>　 HWND hWnd;<br>　<br>　 HACCEL hAccel;<br><br>　 ZeroMemory( &amp;g_Sprite, sizeof(SPRITE_STRUCT) * NUM_SPRITES );</font><font color=#008000 size=2>//清空内存</font><font color=#000000 size=2><br>　<br>　 srand( GetTickCount() </font><font color=#008000 size=2>/*用于获取自windows启动以来经历的时间长度（毫秒）*/</font><font color=#000000 size=2> ); </font><font color=#008000 size=2>//设置随机数随时间变化而变化</font><font color=#000000 size=2><br><br>　 if( FAILED( WinInit( hInst, nCmdShow, &amp;hWnd, &amp;hAccel ) ) )<br>　　 return FALSE;<br><br>　 if( FAILED( InitDirectDraw( hWnd ) ) )<br>　 {<br>　　 MessageBox( hWnd, TEXT("DirectDraw init failed. ")<br>　　<br>　　 TEXT("The sample will now exit. "), TEXT("DirectDraw Sample"), <br>　　<br>　　 MB_ICONERROR | MB_OK );<br><br>　　 return FALSE;<br>　 }<br><br>　 g_dwLastTick = timeGetTime();</font><font color=#008000 size=2>//获得当前系统时间</font><font color=#000000 size=2><br><br>　 while( TRUE )<br>　 {<br><br></font><font color=#008000 size=2>// Look for messages, if none are found then <br>// update the state and display it</font><font color=#000000 size=2><br><br>　　 if( PeekMessage( &amp;msg, NULL, 0, 0, PM_NOREMOVE ) )<br>　　 {<br>　　　 if( 0 == GetMessage(&amp;msg, NULL, 0, 0 ) )<br>　　　 {<br><br></font><font color=#008000 size=2>// WM_QUIT was posted, so exit</font><font color=#000000 size=2><br><br>　　　　 return (int)msg.wParam;<br>　　　 }<br><br></font><font color=#008000 size=2>// Translate and dispatch the message</font><font color=#000000 size=2><br><br>　　　 if( 0 == TranslateAccelerator( hWnd, hAccel, &amp;msg ) )<br>　　　 {<br>　　　　 TranslateMessage( &amp;msg ); <br>　　　　 DispatchMessage( &amp;msg );<br>　　　 }<br>　　 }<br>　　 else<br>　　 {<br>　　　 if( g_bActive )<br>　　　 {</font></p>
                        <p class=tabletxt><font color=#008000 size=2>// Move the sprites, blt them to the back buffer, then <br>// flip or blt the back buffer to the primary buffer</font></p>
                        <p class=tabletxt><font color=#000000 size=2>　　　　if( FAILED( ProcessNextFrame() ) </font><font color=#008000 size=2>/*设置下张页面的子画面的位置*/</font><font color=#000000 size=2>)<br>　　　　{<br>　　　　　 SAFE_DELETE( g_pDisplay );<br>　　　　　 <br>　　　　　 MessageBox( hWnd, TEXT("Displaying the next frame failed. ")<br>　　　　　 <br>　　　　　 TEXT("The sample will now exit. "), TEXT("DirectDraw Sample"), </font></p>
                        <p class=tabletxt><font color=#000000 size=2>　　　　　 MB_ICONERROR | MB_OK );<br>　　　　　<br>　　　　　 return FALSE;<br>　　　　 }<br>　　　 }<br>　　　 else<br>　　　 {</font></p>
                        <p class=tabletxt><font color=#008000 size=2>// Make sure we go to sleep if we have nothing else to do</font></p>
                        <p class=tabletxt><font color=#000000 size=2>　　　　　WaitMessage();</font></p>
                        <p class=tabletxt><font color=#008000 size=2>// Ignore time spent inactive </font></p>
                        <p class=tabletxt><font color=#000000 size=2>　　　　　g_dwLastTick = timeGetTime();</font><font color=#008000 size=2>//获得当前系统时间</font><font color=#000000 size=2><br>　　　 }<br>　　 }<br>　 }<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </font><font face=Arial>
            <p><strong><font size=2>WinInit()</font></strong></p>
            <p><font size=2>Desc: Init the window</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>HRESULT WinInit( HINSTANCE hInst, int nCmdShow, HWND* phWnd, HACCEL* phAccel ) <br>{<br>　 WNDCLASS wc;<br>　 HWND hWnd;<br>　 HACCEL hAccel;<br><br></font><font face=宋体 color=#008000><font size=2>// Register the Window Class 设置窗口类</font></font><font face=宋体 color=#000000 size=2><br><br>　 wc.lpszClassName = TEXT("FullScreenMode");<br><br>　 wc.lpfnWndProc = MainWndProc;<br><br>　 wc.style = CS_VREDRAW | CS_HREDRAW;<br><br>　 wc.hInstance = hInst;<br><br>　 wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(IDI_MAIN) );<br><br>　 wc.hCursor = LoadCursor( NULL, IDC_ARROW );<br><br>　 wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);<br><br>　 wc.lpszMenuName = NULL;<br><br>　 wc.cbClsExtra = 0;<br><br>　 wc.cbWndExtra = 0;<br><br>　 if( RegisterClass( &amp;wc ) == 0 </font><font face=宋体 color=#008000><font size=2>/*注册窗口类*/</font></font><font face=宋体 color=#000000 size=2> )<br>　　 return E_FAIL;<br><br></font><font face=宋体 color=#008000><font size=2>// Load keyboard accelerators</font></font><font face=宋体 color=#000000 size=2><br><br>　 hAccel = LoadAccelerators( hInst, MAKEINTRESOURCE(IDR_MAIN_ACCEL) );<br><br></font><font face=宋体 color=#008000><font size=2>// Create and show the main window</font></font><font face=宋体 color=#000000 size=2><br><br>　 hWnd = CreateWindowEx( 0, TEXT("FullScreenMode"), TEXT("DirectDraw FullScreenMode Sample"),<br><br>　 WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,<br><br>　 CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL );<br><br>　 if( hWnd == NULL )<br>　　 return E_FAIL;<br><br>　 ShowWindow( hWnd, nCmdShow );<br><br>　 UpdateWindow( hWnd );<br><br>　 *phWnd = hWnd;<br><br>　 *phAccel = hAccel;<br><br>　 return S_OK;<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>InitDirectDraw()</font></strong></p>
            <p><font size=2>Desc: Create the DirectDraw object, and init the surfaces</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>HRESULT InitDirectDraw( HWND hWnd )<br>{<br>　 HRESULT hr; </font><font face=宋体 color=#008000><font size=2>//接受返回值，其实是long型变量</font></font><font face=宋体 color=#000000 size=2><br><br>　 LPDIRECTDRAWPALETTE pDDPal = NULL; </font><font face=宋体 color=#008000><font size=2>//定义程序中的调色板</font></font><font face=宋体 color=#000000 size=2><br><br>　 int iSprite; </font><font face=宋体 color=#008000><font size=2>//定义与sprite个数有关的计数器</font></font><font face=宋体 color=#000000 size=2><br><br>　 g_pDisplay = new CDisplay();</font><font face=宋体 color=#008000><font size=2>//动态开辟一个CDisplay类</font></font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = g_pDisplay-&gt;CreateFullScreenDisplay( hWnd, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP ) ) ) </font><font face=宋体 color=#008000><font size=2>//设置程序为全屏，并且g_pDisplay就为后备缓冲区表面的句柄，即指向后备缓冲区表面的指针。</font></font><font face=宋体 color=#000000 size=2><br>　 {<br>　　 MessageBox( hWnd, TEXT("This display card does not support 1024x768x8. "),<br>　　<br>　　 TEXT("DirectDraw Sample"), MB_ICONERROR | MB_OK );<br>　　<br>　　 return hr;<br>　 }<br><br></font><font face=宋体 color=#008000><font size=2>// Create and set the palette when in palettized color</font></font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = g_pDisplay-&gt;CreatePaletteFromBitmap( &amp;pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) ) </font><font face=宋体 color=#008000><font size=2>//顾名思义，就是从bmp图片中获得调色板值，并赋值在pDDPal结构指针中。</font></font><font face=宋体 color=#000000 size=2><br>　　 return hr;<br>　<br>　 if( FAILED( hr = g_pDisplay-&gt;SetPalette( pDDPal ) ) ) </font><font face=宋体 color=#008000><font size=2>//用刚才从IDB_DIRECTX中获得的调色板制来设置程序调色板</font></font><font face=宋体 color=#000000 size=2><br>　　 return hr;<br><br>　 SAFE_RELEASE( pDDPal );</font><font face=宋体 color=#008000><font size=2>//释放指针</font></font><font face=宋体 color=#000000 size=2><br><br></font><font face=宋体 color=#008000 size=2>// Create a surface, and draw a bitmap resource on it. <br>// 用IDB_DIRECTX图片创建一个表面，并用g_pLogoSurface指向这个表面</font><font face=宋体 color=#000000 size=2><br>　<br>　 if( FAILED( hr = g_pDisplay-&gt;CreateSurfaceFromBitmap( &amp;g_pLogoSurface, MAKEINTRESOURCE( IDB_DIRECTX ), SPRITE_DIAMETER, SPRITE_DIAMETER ) ) )<br>　　 return hr;<br><br>　 if( FAILED( hr = g_pDisplay-&gt;CreateSurfaceFromBitmap( &amp;g_pBackSurface, MAKEINTRESOURCE( IDB_WINXP ), SCREEN_WIDTH, SCREEN_HEIGHT ) ) )<br>　　 return hr;<br><br></font><font face=宋体 color=#008000><font size=2>// Create a surface, and draw text to it. <br>//创建文本表面<br></font></font><font face=宋体 color=#000000 size=2><br>　 if( FAILED( hr = g_pDisplay-&gt;CreateSurfaceFromText( &amp;g_pTextSurface, NULL, HELPTEXT, RGB(0,0,0), RGB(255, 255, 0) ) ) )<br>　　 return hr;<br><br></font><font face=宋体 color=#008000><font size=2>// Set the color key for the logo sprite to black<br>//设置色彩键码为黑色，0代表黑色，这样在表面的拷贝过程中黑色像素的点将不会被拷贝</font></font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = g_pLogoSurface-&gt;SetColorKey( 0 ) ) )<br>　　 return hr;<br><br>　 if( FAILED( hr = g_pTextSurface-&gt;SetColorKey( 0 ) ) )<br>　　 return hr;<br><br></font><font face=宋体 color=#008000><font size=2>// Init all the sprites. All of these sprites look the same, <br>// using the g_pDDSLogo surface. </font></font><font face=宋体 color=#000000 size=2><br><br>　 for( iSprite = 0; iSprite &lt; NUM_SPRITES; iSprite++ )<br>　 {<br><br></font><font face=宋体 color=#008000><font size=2>// Set the sprite's position and velocity<br>// 设置这些 sprite（小怪物）的初始坐标随机产生<br>　</font></font><font face=宋体 color=#000000 size=2><br>　　 g_Sprite[iSprite].fPosX = (float) (rand() % SCREEN_WIDTH);<br>　　 g_Sprite[iSprite].fPosY = (float) (rand() % SCREEN_HEIGHT); <br><br></font><font face=宋体 color=#008000><font size=2>// 速度也随机产生</font></font><font face=宋体 color=#000000 size=2><br><br>　　 g_Sprite[iSprite].fVelX = 500.0f * rand() / RAND_MAX - 250.0f;<br>　　 g_Sprite[iSprite].fVelY = 500.0f * rand() / RAND_MAX - 250.0f;<br>　 }<br>　 return S_OK;<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>FreeDirectDraw()</font></strong></p>
            <p><font size=2>Release all the DirectDraw objects 释放所有指针。</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>VOID FreeDirectDraw()<br>{<br>　 SAFE_DELETE( g_pBackSurface );<br>　 SAFE_DELETE( g_pLogoSurface );<br>　 SAFE_DELETE( g_pTextSurface );<br>　 SAFE_DELETE( g_pDisplay );<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>MainWndProc()</font></strong></p>
            <p><font size=2>Desc: The main window procedure</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )<br>{<br>　 switch (msg)<br>　 {<br>　　 case WM_COMMAND:<br>　　 switch( LOWORD(wParam) )<br>　　 {<br>　　　 case IDM_EXIT:<br><br></font><font face=宋体 color=#008000><font size=2>// Received key/menu command to exit app</font></font><font face=宋体 color=#000000 size=2><br>　<br>　　　 PostMessage( hWnd, WM_CLOSE, 0, 0 );<br>　<br>　　　 return 0L;<br>　　 }<br>　　 break; // Continue with default processing<br><br>　　 case WM_SETCURSOR:<br><br></font><font face=宋体 color=#008000><font size=2>// Hide the cursor in fullscreen </font></font><font face=宋体 color=#000000 size=2><br>　<br>　　 SetCursor( NULL );<br><br>　　 return TRUE;<br><br>　　 case WM_SIZE:<br><br></font><font face=宋体 color=#008000><font size=2>// Check to see if we are losing our window...</font></font><font face=宋体 color=#000000 size=2><br><br>　　 if( SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam )<br>　　　 g_bActive = FALSE;<br>　　 else<br>　　　 g_bActive = TRUE;<br>　　 <br>　　 if( g_pDisplay )<br>　　　 g_pDisplay-&gt;UpdateBounds();<br><br>　　 break；<br><br>　　 case WM_EXITMENULOOP:<br><br></font><font face=宋体 color=#008000><font size=2>// Ignore time spent in menu</font></font><font face=宋体 color=#000000 size=2><br><br>　　 g_dwLastTick = timeGetTime();<br><br>　　 break;<br><br>　　 case WM_EXITSIZEMOVE:<br><br></font><font face=宋体 color=#008000 size=2>// Ignore time spent resizing</font><font face=宋体 color=#000000 size=2><br><br>　　 g_dwLastTick = timeGetTime();<br><br>　　 break;<br><br>　　 case WM_MOVE:<br><br>　　 if( g_pDisplay )<br>　　　 g_pDisplay-&gt;UpdateBounds();<br><br>　　 break;<br><br>　　 case WM_SYSCOMMAND:<br><br></font><font face=宋体 color=#008000><font size=2>// Prevent moving/sizing and power loss in fullscreen mode</font></font><font face=宋体 color=#000000 size=2><br><br>　　 switch( wParam )<br>　　 {<br>　　　 case SC_MOVE:<br><br>　　　 case SC_SIZE:<br><br>　　　 case SC_MAXIMIZE:<br><br>　　　 case SC_MONITORPOWER:<br><br>　　　 return TRUE;<br>　　 }<br><br>　　 break;<br><br>　　 case WM_DESTROY:<br><br></font><font face=宋体 color=#008000><font size=2>// Cleanup and close the app</font></font><font face=宋体 color=#000000 size=2><br><br>　　 FreeDirectDraw();<br><br>　　 PostQuitMessage( 0 );<br><br>　　 return 0L;<br>　 }<br>　<br>　 return DefWindowProc(hWnd, msg, wParam, lParam);<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>ProcessNextFrame()</font></strong></p>
            <p><font size=2>Desc: Move the sprites, blt them to the back buffer, then </font></p>
            <p><font size=2>flips the back buffer to the primary buffer</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>HRESULT ProcessNextFrame()<br>{<br>　 HRESULT hr;<br><br></font><font face=宋体 color=#008000><font size=2>// Figure how much time has passed since the last time</font></font><font face=宋体 color=#000000 size=2><br><br>　 DWORD dwCurrTick = timeGetTime(); </font><font face=宋体 color=#008000><font size=2>//get current time 获得当前时间</font></font><font face=宋体 color=#000000 size=2><br>　 DWORD dwTickDiff = dwCurrTick - g_dwLastTick; </font><font face=宋体 color=#008000><font size=2>//the difference between current-time (dwCurrTick) and last time (g_dwLastTick)</font></font><font face=宋体 color=#000000 size=2><br><br></font><font face=宋体 color=#008000 size=2>//计算当前时间 (dwCurrTick) 与最后一次时间 (g_dwLastTick) 的差值<br>// Don't update if no time has passed </font><font face=宋体 color=#000000 size=2><br><br>　 if( dwTickDiff == 0 ) </font><font face=宋体 color=#008000><font size=2>//如果时间差值为0,即时间没有变化,则不更新屏幕,而返回</font></font><font face=宋体 color=#000000 size=2><br>　　 return S_OK; <br><br></font><font face=宋体 color=#008000><font size=2>//如果程序运行到这里,而没有在前面返回,则说明时间有变化,则下面将要更新屏幕</font></font><font face=宋体 color=#000000 size=2><br><br>　 g_dwLastTick = dwCurrTick; </font><font face=宋体 color=#008000><font size=2>//使最后时间=当前时间</font></font><font face=宋体 color=#000000 size=2><br><br></font><font face=宋体 color=#008000 size=2>// Move the sprites according to how much time has passed<br>//根据时间的变化来移动子画面,即移动sprites<br></font><font face=宋体 color=#000000 size=2><br>　 for( int iSprite = 0; iSprite &lt; NUM_SPRITES; iSprite++ )<br>　　 UpdateSprite( &amp;g_Sprite[ iSprite ], dwTickDiff / 1000.0f );<br><br></font><font face=宋体 color=#008000><font size=2>// Display the sprites on the screen</font></font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = DisplayFrame() ) )<br>　 {<br>　　 if( hr != DDERR_SURFACELOST )<br>　　　 return hr;<br><br></font><font face=宋体 color=#008000><font size=2>// The surfaces were lost so restore them </font></font><font face=宋体 color=#000000 size=2><br><br>　　 RestoreSurfaces();<br>　 }<br><br>　 return S_OK;<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>UpdateSprite()</font></strong></p>
            <p><font size=2>Desc: Move the sprite around and make it bounce based on how much time has passed ，此函数属于动画的关键算法，就是控制spirte如何移动</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>VOID UpdateSprite( SPRITE_STRUCT* pSprite, FLOAT fTimeDelta )<br>{ <br><br></font><font face=宋体 color=#008000><font size=2>// Update the sprite position</font></font><font face=宋体 color=#000000 size=2><br><br>　 pSprite-&gt;fPosX += pSprite-&gt;fVelX * fTimeDelta;<br>　 pSprite-&gt;fPosY += pSprite-&gt;fVelY * fTimeDelta;<br><br></font><font face=宋体 color=#008000><font size=2>// Clip the position, and bounce if it hits the edge</font></font><font face=宋体 color=#000000 size=2><br><br>　 if( pSprite-&gt;fPosX &lt; 0.0f )<br>　 {<br>　　 pSprite-&gt;fPosX = 0;<br>　　 pSprite-&gt;fVelX = -pSprite-&gt;fVelX;<br>　 }<br>　 <br>　 if( pSprite-&gt;fPosX &gt;= SCREEN_WIDTH - SPRITE_DIAMETER )<br>　 {<br>　　 pSprite-&gt;fPosX = SCREEN_WIDTH - 1 - SPRITE_DIAMETER;<br>　　 pSprite-&gt;fVelX = -pSprite-&gt;fVelX;<br>　 }<br>　<br>　 if( pSprite-&gt;fPosY &lt; 0 )<br>　 {<br>　　 pSprite-&gt;fPosY = 0;<br>　　 pSprite-&gt;fVelY = -pSprite-&gt;fVelY;<br>　 }<br><br>　 if( pSprite-&gt;fPosY &gt; SCREEN_HEIGHT - SPRITE_DIAMETER )<br>　 {<br>　　 pSprite-&gt;fPosY = SCREEN_HEIGHT - 1 - SPRITE_DIAMETER;<br>　　 pSprite-&gt;fVelY = -pSprite-&gt;fVelY;<br>　 } <br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>DisplayFrame()</font></strong></p>
            <p><font size=2>Desc: Blts a the sprites to the back buffer, then flips the back buffer onto the primary buffer.</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>HRESULT DisplayFrame()<br>{<br>　 HRESULT hr;<br><br></font><font face=宋体 color=#008000><font size=2>// Fill the back buffer with black, ignoring errors until the flip</font></font><font face=宋体 color=#000000 size=2><br><br>　 g_pDisplay-&gt;Clear( 0 ); </font><font face=宋体 color=#008000><font size=2>//清空后备缓冲区表面</font></font><font face=宋体 color=#000000 size=2><br><br></font><font face=宋体 color=#008000><font size=2>// Blt the help text on the backbuffer, ignoring errors until the flip<br>//将g_pBackSurface所指向的图片拷贝到后备缓冲区表面</font></font><font face=宋体 color=#000000 size=2><br><br>　 g_pDisplay-&gt;Blt( 0, 0, g_pBackSurface, NULL );<br><br></font><font face=宋体 color=#008000><font size=2>//将g_pTextSurface所指向的文本拷贝到后备缓冲区表面</font></font><font face=宋体 color=#000000 size=2><br><br>　 g_pDisplay-&gt;Blt( 10, 10, g_pTextSurface, NULL );<br><br></font><font face=宋体 color=#008000><font size=2>// Blt all the sprites onto the back buffer using color keying,<br>// ignoring errors until the flip. Note that all of these sprites <br>// use the same DirectDraw surface.<br>//将所有的spirits拷贝到后备缓冲区表面，这里要注意拷贝顺序，后拷贝上的画面会压盖在先拷贝的画面上</font></font><font face=宋体 color=#000000 size=2><br><br>　 for( int iSprite = 0; iSprite &lt; NUM_SPRITES; iSprite++ )<br>　 {<br>　　 g_pDisplay-&gt;Blt( (DWORD)g_Sprite[iSprite].fPosX,(DWORD)g_Sprite[iSprite].fPosY,g_pLogoSurface, NULL );<br>　 }<br><br></font><font face=宋体 color=#008000><font size=2>// We are in fullscreen mode, so perform a flip and return <br>// any errors like DDERR_SURFACELOST<br>//最关键的地方在这里，请看下面的语句，只要我们一执行翻页操作，就可以将改动了的图像了显示在屏幕上了</font></font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = g_pDisplay-&gt;Present() </font><font face=宋体 color=#008000 size=2>/*翻页操作*/</font><font face=宋体 color=#000000 size=2>) )<br>　　 return hr;<br><br>　 return S_OK;<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><strong><font size=2>RestoreSurfaces()</font></strong></p>
            <p><font size=2>Desc: Restore all the surfaces, and redraw the sprite surfaces.</font></p>
            </font>
            <table class=code cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font face=宋体 color=#000000 size=2>HRESULT RestoreSurfaces()<br>{<br>　 HRESULT hr;<br><br>　 LPDIRECTDRAWPALETTE pDDPal = NULL; <br><br>　 if( FAILED( hr = g_pDisplay-&gt;GetDirectDraw()-&gt;RestoreAllSurfaces() ) )<br>　　 return hr;<br><br></font><font face=宋体 color=#008000 size=2>// No need to re-create the surface, just re-draw it.</font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = g_pTextSurface-&gt;DrawText( NULL, HELPTEXT, 0, 0, RGB(0,0,0), RGB(255, 255, 0) ) ) )<br>　　 return hr;<br><br></font><font face=宋体 color=#008000 size=2>// We need to release and re-load, and set the palette again to <br>// redraw the bitmap on the surface. Otherwise, GDI will not <br>// draw the bitmap on the surface with the right palette</font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = g_pDisplay-&gt;CreatePaletteFromBitmap( &amp;pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) )<br>　　 return hr;<br><br>　 if( FAILED( hr = g_pDisplay-&gt;SetPalette( pDDPal ) ) )<br>　　 return hr;<br><br>　 SAFE_RELEASE( pDDPal );<br><br></font><font face=宋体 color=#008000 size=2>// No need to re-create the surface, just re-draw it.</font><font face=宋体 color=#000000 size=2><br><br>　 if( FAILED( hr = g_pLogoSurface-&gt;DrawBitmap( MAKEINTRESOURCE( IDB_DIRECTX ),SPRITE_DIAMETER, SPRITE_DIAMETER ) ) )<br>　　 return hr;<br>　 return S_OK;<br>}</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <font face=Arial>
            <p><font size=2>好了，我们的程序分析就到这了，我想您现在应该大概了解了DirectDraw程序的大概流程了吧，就是先创建DDraw对象，然后进行相关的设置，然后绘制后备缓冲区页，然后执行翻页操作，这样循环，就会产生很好的动画效果了，其实用DirectDraw编程很简单，说白了其实就是：&#8220;几个表面之间拷来拷去&#8221;。只不过这其间可是大有文章可作的哟！</font></p>
            </font></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86484.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-06-01 23:26 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86484.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>动画程序编写——DirectDraw之旅（2）</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86483.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:25:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86483.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86483.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86483.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86483.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86483.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2.htm">http://dev.gameres.com/Program/Visual/2D/DDrawZL_2.htm</a><br><br>
<table id=AutoNumber2 style="BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 width="85%" border=0>
    <tbody>
        <tr>
            <td width="100%">
            <p align=center><font color=#ffffee><strong>动画程序编写——DirectDraw之旅（2）</strong></font></p>
            </td>
        </tr>
        <tr>
            <td width="100%">　</td>
        </tr>
        <tr>
            <td width="100%"><font size=2>&#8220;君欲善其事，必先利其器&#8221;，在编写DirectDraw应用程序之前，我们先要准备好以下工具：</font>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=tabletxt><font size=2>Windows95、Windows98、Windows Me、Windows NT 4.0、Windows 2000 或 Windows XP（其对操作系统没有特殊要求）</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt><font size=2>DirectX 驱动程序(最好是DirectX 8.0以上版本)</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt><font size=2>DirectX 8.0 SDK </font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt><font size=2>Visual C＋＋ 5.0 ，Visual C＋＋ 6.0 或 Visual C＋＋ .NET</font></td>
                    </tr>
                </tbody>
            </table>
            <p><font size=2>Direct SDK包括开发基于DirectX应用程序所需的全部文件，全部安装需要80兆的硬盘空间。其实你只需安装必需的头文文件（.h文件）和库文件（.lib文件）就行了。</font></p>
            <p><font size=2>安装完DirectX SDK，需要通知Visual C＋＋ DirectX SDK的路径。具体做法是：在VC的编译环境中，依次把Tools－Options－Directories中的Show Directories for一栏中的include files和library files中分别填入SDK的include和lib目录，如图所示。</font></p>
            <p><font size=2><img height=280 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_1.gif" width=492></font></p>
            <p><font size=2><img height=419 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_2.gif" width=531></font></p>
            <p><font size=2><img height=422 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_3.gif" width=545></font></p>
            <p><font size=2>再准备两幅bmp格式的位图，其中winXP.bmp作背景，如图；另一幅directx.bmp作为子画面，如图。还要注意一点的是子画面的背景要为黑色(RGB=(0,0,0))，因为在下面的程序中，色彩键码把黑色设为透明色。</font></p>
            <p><font size=2><img height=339 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_4.jpg" width=453></font></p>
            <p><font color=#000000 size=2>winXP.bmp 1024&#215;768&#215;24</font></p>
            <p><font size=2><img height=32 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_5.jpg" width=32></font></p>
            <p><font color=#000000 size=2>directx.bmp 32&#215;32&#215;8</font></p>
            <p><font size=2>进入VC6的编程环境，File－New－Project,选择Win32 Application,输入项目名FullScreenMode ,按下Ok,以后每一步都按其缺省值即可，这样AppWizard就会自动创建一个空项目，下面我们还需要加入一个cpp源文件，并将Win32程序的基本框架拷贝入刚刚创建的cpp源文件中。如下图1、2：</font></p>
            <p><font size=2><img height=479 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_6.gif" width=463></font></p>
            <p><font size=2><img height=479 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_7.gif" width=278></font></p>
            <p><font size=2>然后点击&#8220;文件&#8221;选项卡添加.CPP工程文件。如下图3：</font></p>
            <p><font size=2><img height=391 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_8.gif" width=541></font></p>
            <p><font size=2>其实我们的工程中还要用到DirectX SKD提供的四个文件：</font></p>
            <table cellSpacing=1 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=tabletxt><font size=2>ddutil.h </font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt><font size=2>ddutil.cpp </font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt><font size=2>dxutil.h</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt><font size=2>dxutil.cpp</font></td>
                    </tr>
                </tbody>
            </table>
            <p><font size=2>对于头文件，我们还采用上面添加编译器默认头文件目录的方法让编译器自己去找，如图：</font></p>
            <p><font size=2><img height=417 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_9.gif" width=523></font></p>
            <p><font size=2>上面的目录是我们刚才加入的，而下面的目录是我们这次加入的。而对于 cpp源文件，我们采用将文件直接导入工程的方法，如图：</font></p>
            <p><font size=2><img height=373 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_10.gif" width=379></font></p>
            <p><font size=2><img height=218 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_11.gif" width=346><br><br>文件 ddutil.cpp dxutil.cpp 所在目录为 </font></p>
            <table cellSpacing=1 cellPadding=0 width="100%" bgColor=#000000 border=0>
                <tbody>
                    <tr>
                        <td class=tabletxt width="8%" bgColor=#e0e0e0><font size=2>（9.0版本）</font></td>
                        <td class=tabletxt width="92%" bgColor=#ffffff><font size=2>C:\DXSDK\Samples\C++\Common\Src</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt bgColor=#e0e0e0><font size=2>（8.0版本） </font></td>
                        <td class=tabletxt bgColor=#ffffff><font size=2>C:\DXSDK \samples\multimedia\common\src</font></td>
                    </tr>
                </tbody>
            </table>
            <p><font size=2>所有的工程文件结构如图：</font></p>
            <p><font size=2><img height=270 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_12.gif" width=306></font></p>
            <p><font size=2>下面的所有程序都是 FullScreenMode.cpp 文件中的内容，其中IDB_DIRECTX和IDB_WinXP都是图片资源的ID号，向程序中添加资源的过程如图：</font></p>
            <p><font size=2><img height=480 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_13.gif" width=546> </font></p>
            <p><font size=2><img height=303 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_14.gif" width=541></font></p>
            <p><font size=2><img height=334 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_2_15.gif" width=467></font></p>
            <p><font size=2>其它的资源文件也是这样加入的。请读者自己将图片加入到程序中。</font></p>
            </td>
        </tr>
        <tr>
            <td width="100%">　</td>
        </tr>
        <tr>
            <td width="100%" bgColor=#151631>
            <p align=right><font face=Arial color=#0000ff size=1><a href="http://www.frontfree.net/"><u>放飞技术网</u></a></font><font size=1>&nbsp;&nbsp;</font></p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86483.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-06-01 23:25 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86483.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>动画程序编写——DirectDraw之旅（1）</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86481.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 15:23:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86481.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86481.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86481.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86481.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86481.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DDrawZL_1.htm">http://dev.gameres.com/Program/Visual/2D/DDrawZL_1.htm</a><br><br>
<table id=AutoNumber2 style="BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 width="85%" border=0>
    <tbody>
        <tr>
            <td width="100%">
            <p align=center><font color=#ffffee><strong>动画程序编写——DirectDraw之旅（1）</strong></font></p>
            </td>
        </tr>
        <tr>
            <td width="100%">　</td>
        </tr>
        <tr>
            <td width="100%"><font size=2>DirectDraw——也许大多数人闻所未闻，但当提到 DirectX 恐怕每一个 游戏爱好者都再熟悉不过了,但是只知道那是一个很多游戏都要求的必须安装的程序,再多就无从所知了,那么它到底能为我们的游戏干什么呢,其实它又叫 Game SDK，它最大的特点是直接对硬件的抽象层(HAL)进行操作，利用 此特点可制作出高性能的Windows游戏。<br>http://www. microsoft.com/directx/default.asp。 </font>
            <p><font size=2>DirectDraw就是DirectX5的6个组件之一。DirectX5的其它5个组件分别是：</font></p>
            <table cellSpacing=1 cellPadding=0 width="100%" bgColor=#000000 border=0>
                <tbody>
                    <tr>
                        <td class=tabletxt width="8%" bgColor=#e0e0e0><font color=#000000 size=2>Direct3D</font></td>
                        <td class=tabletxt width="92%" bgColor=#ffffff><font color=#000000 size=2>提供了3D硬件接口。</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt bgColor=#e0e0e0><font color=#000000 size=2>DirectSound</font></td>
                        <td class=tabletxt bgColor=#ffffff><font color=#000000 size=2>立体声和3D声音效果，同时管理声卡的内存。</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt bgColor=#e0e0e0><font color=#000000 size=2>DirectPlay</font></td>
                        <td class=tabletxt bgColor=#ffffff><font color=#000000 size=2>支持开发多人网络游戏，并能处理游戏中网络之间的通信问题。</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt bgColor=#e0e0e0><font color=#000000 size=2>DirectInput</font></td>
                        <td class=tabletxt bgColor=#ffffff><font color=#000000 size=2>为大量的设备提供输入支持。</font></td>
                    </tr>
                    <tr>
                        <td class=tabletxt bgColor=#e0e0e0><font color=#000000 size=2>DirectSetup</font></td>
                        <td class=tabletxt bgColor=#ffffff><font color=#000000 size=2>自动安装DirectX驱动程序。</font></td>
                    </tr>
                </tbody>
            </table>
            <p><font size=2>而DirectDraw则是DirectX的基础，DirectX的其它组件都是建立在它的基础之上的。DirectDraw使用页面切换的方法实现动画，它不仅可以访问系统内存，还可以访问显示内存，这是以往的Windows程序员所不能的。(例如:如果你是用MFC中的CDC类进行绘图,那么它是绝对不会让您直接访问显存的。) 另外，我们利用DirectDraw还可以生成、移动、剪切、转换、合成图像数据，从而编写出各种&#8220;炫丽多彩&#8221;图形的应用程序。</font></p>
            <p><font size=2>您先不要急,我们会在后面附上本文介绍的动画程序原代码,只要将其用vc打开,进行工程的编译创建后,您就会立刻看到效果。并且我们在工程文件中有详细的中文注释,力求将理论与实践紧密联系在一起。</font></p>
            <p><font size=2>首先，让我们先了解一下DirectDraw的三个重要概念</font><font color=#000000><font size=2>——</font><font color=#ff0000 size=2>表面、Bltting、色彩键码</font></font></p>
            <p class=titletxt><font color=#0099ff size=2><strong>1.表面</strong></font></p>
            <p><font size=2>在用DirectDraw编写程序时,我们先要创建若干个图形数据缓冲区，并把这些图形数据装入其中，再进行转换、拉伸、挎贝等操作，并且还可以显示这些缓冲区中的图形数据，这些缓冲区就称为表面。</font></p>
            <p><font size=2>表面可以分为几类。</font></p>
            <p><font color=#0099ff size=2>主表面</font><font size=2>(primary surface)是用户在屏幕上可以看到的，它是显示内存的一部分。所有DirectDraw程序都有主表面，而且只有一个。它在DirectDraw表面对象之前就已经存在了，因此不能改变它的尺寸、格式和位置。</font></p>
            <p><font size=2>主表面有一个很重要的特性——翻页(flip)。页面翻页用于程序中，可以产生相当平滑、不闪烁的动画。一个可以翻页的主表面实际上是两个表面，一个是可见的，一个是不可见的。不可见的表面称为<font color=#0099ff>后备缓冲区</font>。当发生表面翻页时，后备缓冲区就成为可见的，而以前的可见主表面则成为后备缓冲区。 下面我们用图示来向您解释上面的概念:</font></p>
            <p><font size=2><img height=133 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_1_1.gif" width=468><br><br>当翻页后,将原后备缓冲区页中的内容copy入可见主表面页,而同时将原可见主表面页的内容copy入后备缓冲区页。 </font></p>
            <p><font size=2>显示器屏幕虽然每秒中刷新很多次，在此我们假定为85次，但每次都是一遍一遍地读取可见主表面中存储的显示页信息，而你对后备缓冲区的改动不会显示出来，并且也不会影响可见主表面的显示，而只有当施行翻页操作后，两页的内容互换，而你已经完成了的在原后备缓冲区的改动才会显示在屏幕上，而这个互相拷贝的过程几乎是瞬间完成的，这个时间比起每次刷新所用的时间少得多得多，两者几乎差了几乎几十万个数量级。而人眼是根本察觉不到的，所以用这种方法可以不闪烁，平滑，优质的动画效果</font></p>
            <p><font size=2>还有一种表面叫<font color=#0099ff>离屏表面</font>(off_screen surface)，它是不能直接见到的。离屏表面作为存储缓冲区，有助于表面之间的互相切换，它的大小是可以改变的。</font></p>
            <p><font size=2>主表面和离屏表面都分为有调色板的和无调色板的这两类。像素深度为8位(256色)的表面称为有调色板的表面；而像素深度为16位(64K色)、24位(16M色)的像素表面称为无调色板的表面，它们存储实际的色彩值(RGB值)。</font></p>
            <p><font size=2>在本文下面的程序中，我们先使用256位表面即有调色板的表面。</font></p>
            <p><font color=#0099ff size=2><strong>2. Bltting</strong></font></p>
            <p><font size=2>Bltting是用于复制图形的语言，可以将图像从一处拷贝到另一处。例如大家所熟悉的<font color=#0099ff>CDC类</font>(设备描述表类)的BitBlt()就是具有这样功能的函数。</font></p>
            <p><font size=2>在DirectDraw中，典型的blt操作是将离屏表面的内容拷贝到一个后备缓冲区，而一般的blt操作调用一个源表面和一个目标表面，把源表面的内容拷贝到目标表面中，不仅可以整体拷贝源表面，而且还可以拷贝源表面内的任何矩形区域到目标表面的任何位置。blt还支持透明拷贝，就是指表面中的某一像素在blt过程中可以不予以拷贝，而这个像素值是由<font color=#0099ff>色彩键码</font>(DDCOLOR KEY )决定的。如图：</font></p>
            <p><font size=2><img height=177 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_1_2.gif" width=430></font></p>
            <p><font size=2>DirectDraw中有三个支持blt的函数，它们是</font></p>
            <table cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td bgColor=#e0e0e0>
                        <p class=tabletxt><font color=#000000 size=2>Blt()<br>BltBatch()<br>BltFast()</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p><font size=2>Blt( )用得最多，BltFast()的速度比Blt()要快，但功能却很有限，例如不支持拉伸、剪切等操作。</font></p>
            <p><font size=2>还有一个函数BltSurface()，它是DirectWin类的一个成员函数，Blt()、BltFast()更具有适应性，并且使用起来更加简单。例如，当我们把源表面拷贝到目标表面外时需要裁剪，而BltFast()不支持裁剪。这时我们使用BltSurface()函数，它在内部使用Blt()和 BltSurface()函数，并根据情况自动执行裁剪。</font></p>
            <p><font size=2>在此，有人会问，这种页面切换的方法到底好在那里？它到底与用一般的绘图方法的区别在什么地方？而其它的绘图方法为什么会使屏幕闪动呢？下面我们举二个例子：</font><font color=#000000 size=2>（我们仍然用图示的方法给您一个直观的解释）</font></p>
            <p><font size=2>（1） 在一些绘图程序中您会发现其用到了清屏函数，这种方式是使屏幕效果最差的，这种方法使得没有直接从第一个画面切换到第二个画面，而是先用黑色将图形数据缓冲区清空，并且还显示在屏幕上，但这个时间很短。如果反复清屏，则会产生严重的屏幕闪烁。如下图：<br>　</font></p>
            <p><font size=2><img height=157 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_1_3.gif" width=532></font></p>
            <p><font size=2>（2）在一些程序中用异或的方式进行绘图，即先在屏幕上画出第一个图像，然后在画第二个图像之前，再在屏幕上画一遍第一个图像，这样起到清除第一个图像的效果。如果您仔细看，会发现这样就会比上一个方法的画面效果好得多，但这并不是很完美，因为如果反复使用，虽然不会产生全屏幕的闪动，但在所清除图像处会产生闪烁。究其根本原因还是因其没能直接从第一个画面切换到第二个画面。如下图：</font></p>
            <p><font size=2><img height=204 src="http://dev.gameres.com/Program/Visual/2D/DDrawZL_1_4.gif" width=458></font></p>
            <p><font size=2>我想现在您应明白DirectDraw绘图的优势了吧，其实这就是对上述方法中缺点的很好的解决方法。<br><br></font><strong><font color=#0099ff size=2>3.色彩键码</font></strong></p>
            <p><font size=2>DirectDraw 可以把某种颜色或某个范围的颜色指定为一个颜色值，这个颜色值是由DDCOLORKEY结构即色彩键码说明的，DDCORLORKEY结构说明如下：</font></p>
            <table cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=tabletxt bgColor=#e0e0e0><font color=#000000 size=2>typedef struct _DDCOLORKEY<br>{ <br>　DWORD dwColorSpaceLowValue; //颜色范围的低端<br>　DWORD dwColorSpaceHighValue; //颜色范围的高端<br>} DDCOLORKEY;</font></td>
                    </tr>
                </tbody>
            </table>
            <p><font size=2>当我们对表面进行拷贝操作时,表面中哪些像素不被拷贝是由色彩键码决定的。例如当DDCOLORKEY结构的两个分量都为零时，表面内所有置为零的像素都不能被拷贝。又例如，当表面是24位RGB模式时，若想指定RGB=(120，120，120)像素不被拷贝，则应该:</font></p>
            <table cellSpacing=4 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=tabletxt bgColor=#e0e0e0><font color=#000000 size=2>DDCOLORKEY ddck;<br>ddck.dwColorSpaceLowValue=RGB(120,120,120);<br>ddck.dwColorSpaceHighValue=RGB(120,120,120);<br>surf&#8594;SetColorKey(DDCKEY_SRCBLT,＆ddck);</font></td>
                    </tr>
                </tbody>
            </table>
            <p><font size=2>其中SetColorKey()函数是把色彩键码赋给表面surf。这样，在对表面surf的 blt操作期间RGB值为(120,120,120)的像素不能被拷贝。</font></p>
            <p><font size=2>OK，我们现在已经具备基本的DirectDraw的知识，下面我们就来进行实战演练（未完待续......）</font></p>
            </td>
        </tr>
        <tr>
            <td width="100%">　</td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/86481.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-06-01 23:23 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86481.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从头学习DirectDraw</title><link>http://www.cppblog.com/iniwf/archive/2009/06/01/86476.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 01 Jun 2009 14:58:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/06/01/86476.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/86476.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/06/01/86476.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/86476.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/86476.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>http:</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">dev.gameres.com/Program/Visual/2D/fromfirst.htm</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>从头学习DirectDraw<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　在开始学习DirectDraw编程之前，有一些题外话要说明，以下内容均是个人的心得和体会，如果其中有什么谬误之处，敬请谅解，同时个人不对可能造成的后果负责。。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　以下几点是在编制DirectX应用程序时应该注意的：&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>尽管使用VB或DELPHI都可以制作DirectX应用程序，但考虑到代码的效率，还是应使用C或C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">。其中，C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">是面向对象的编程语言，可以使你的程序更易于维护，如果再考虑到代码的兼容性，我推荐使用Microsoft&nbsp;Visual&nbsp;C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">.0集成开发环境。当然，使用Borland&nbsp;C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">.0也是一个不错的选择。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>你应该已经有编制Windows应用程序的经验，以及C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">的基础。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>熟悉你将要使用的编程工具的运用。例如，如何为集成开发环境设置各种参数，以及编译程序和连接程序的命令行参数。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>确认你已下载了微软的DirectX&nbsp;</span><span style="COLOR: #000000">5.0</span><span style="COLOR: #000000">&nbsp;SDK中的相关文件，特别是头文件和库文件，这些都是编程所必需的。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>确认你已经安装了DirectX&nbsp;</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">.0或更高版本的运行时刻库，因为本教程的所有程序均使用了DirectX的新接口（Interface），必须要5.0或更高版本支持。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">--------------------------------------------------------------------------------</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>DirectDraw是什么？<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　DirectDraw是DirectX中的关于视频输入输出的基本部分，使用DirectDraw可以方便地编制出高效的视频处理程序，只要用户的硬件支持DirectDraw，就能保证你的代码可以处理它们。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　简单地说，一个DOS程序员可以方便地直接访问视频显存，从而高效地处理视频动画，而在Windows的32位环境中，使用DirectDraw可以做类似的工作（而且做得更好）。例如，在DOS环境中的320x200x256色图形模式中，你可以通过在地址A000:0000开始处的一片内存区进行直接读写的方式来处理视频操作，而在Windows环境中，DirectDraw也提供一片内存区供你直接读写，使你能更好地完成相似的操作。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　当然，DirectDraw提供的远不止这些，但一个游戏程序员可能更关心如何直接访问视频显存，以及如何高效地完成位块拷贝操作等等。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">--------------------------------------------------------------------------------</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>让我们从消息循环开始<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　DirectX最初是为游戏开发而推出的，编制游戏的程序员都很贪婪，他们会尽量榨取系统资源，并试图让自己的程序永远具有最高的效率。但Windows是一个多任务的操作系统，当它发现所有的程序都处于空闲时，便会减少给这些程序的资源，其中之一就是开始清理交换文件，为了让自己的程序给Windows以始终繁忙的假象，不妨用一些新的代码来代替常规的方法。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>这是常规的消息循环处理<br><img id=Codehighlighter1_1432_1625_Open_Image onclick="this.style.display='none'; Codehighlighter1_1432_1625_Open_Text.style.display='none'; Codehighlighter1_1432_1625_Closed_Image.style.display='inline'; Codehighlighter1_1432_1625_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1432_1625_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1432_1625_Closed_Text.style.display='none'; Codehighlighter1_1432_1625_Open_Image.style.display='inline'; Codehighlighter1_1432_1625_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(GetMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">msg,NULL,NULL,NULL))</span><span id=Codehighlighter1_1432_1625_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1432_1625_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TranslateMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">msg);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatchMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">msg);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;msg.wParam;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">---------------</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是改进的消息循环处理<br><img id=Codehighlighter1_1875_2655_Open_Image onclick="this.style.display='none'; Codehighlighter1_1875_2655_Open_Text.style.display='none'; Codehighlighter1_1875_2655_Closed_Image.style.display='inline'; Codehighlighter1_1875_2655_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1875_2655_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1875_2655_Closed_Text.style.display='none'; Codehighlighter1_1875_2655_Open_Image.style.display='inline'; Codehighlighter1_1875_2655_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(;;)</span><span id=Codehighlighter1_1875_2655_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1875_2655_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_1971_2243_Open_Image onclick="this.style.display='none'; Codehighlighter1_1971_2243_Open_Text.style.display='none'; Codehighlighter1_1971_2243_Closed_Image.style.display='inline'; Codehighlighter1_1971_2243_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1971_2243_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1971_2243_Closed_Text.style.display='none'; Codehighlighter1_1971_2243_Open_Image.style.display='inline'; Codehighlighter1_1971_2243_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(PeekMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">msg,NULL,NULL,NULL,PM_REMOVE))</span><span id=Codehighlighter1_1971_2243_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1971_2243_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(msg.message</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">WM_QUIT)&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TranslateMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">msg);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatchMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">msg)<br><img id=Codehighlighter1_2248_2605_Open_Image onclick="this.style.display='none'; Codehighlighter1_2248_2605_Open_Text.style.display='none'; Codehighlighter1_2248_2605_Closed_Image.style.display='inline'; Codehighlighter1_2248_2605_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_2248_2605_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2248_2605_Closed_Text.style.display='none'; Codehighlighter1_2248_2605_Open_Image.style.display='inline'; Codehighlighter1_2248_2605_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #0000ff">else</span><span id=Codehighlighter1_2248_2605_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_2248_2605_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(AppPaused)&nbsp;WaitMessage();<br><img id=Codehighlighter1_2379_2555_Open_Image onclick="this.style.display='none'; Codehighlighter1_2379_2555_Open_Text.style.display='none'; Codehighlighter1_2379_2555_Closed_Image.style.display='inline'; Codehighlighter1_2379_2555_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_2379_2555_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2379_2555_Closed_Text.style.display='none'; Codehighlighter1_2379_2555_Open_Image.style.display='inline'; Codehighlighter1_2379_2555_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span id=Codehighlighter1_2379_2555_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_2379_2555_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;这里进行任何不基于消息循环的处理<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;例如动画制作</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;msg.wParam;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　上例中，新的消息循环处理方式使得Windows认为程序始终繁忙，从而提高了程序的性能。注意，其中的AppPaused变量为真时表示程序未在前台运行，该变量的取值为WM_ACTIVATEAPP消息的wParam参数。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">--------------------------------------------------------------------------------</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>第一个DirectDraw程序<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　现在咱们开始编制第一个DirectDraw的应用程序，来示例如何建立一个全屏幕独占方式的应用程序，并利用HDC来实现简单的文本输出。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　这个示例与微软的示例程序不同，它使用了DirectX&nbsp;</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">.0中的新接口，在制作最终可执行程序时应确保为LINK程序指定ddraw.lib和dxguid.lib这两个文件。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　示例程序的可执行程序由VC&nbsp;</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">.0生成，只在基于Intel奔腾芯片、Windows95环境的机器上才能运行。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　下面重点介绍一下建立和销毁DirectDraw基本对象的方法，以及如何请求新接口。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>首先来说明一下以后要用到的术语：&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>DirectDraw基本对象。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>这个对象的实体由DirectX建立，程序只能通过指向该对象实体的指针来访问它，这一点与Delphi十分相似，在Delphi中，一个TLabel类型的变量实际只是一个指针，它指向一个TLabel类的对象。建立DirectDraw基本对象的函数是DirectDrawCreate()，该函数可在内部建立一个DirectDraw对象实体，并将指向该实体的指针返回给应用程序，以后的所有操作都基于这个指针。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>主平面。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>可以将主平面简单地理解为&#8220;视频显存区&#8221;，任何直接对主平面的访问都立刻反映到屏幕上。主平面也是一个对象（程序员只能使用指向该对象的指针），该对象封装了几乎所有的输入输出操作，包括直接访问、利用设备相关（DC）访问以及位块拷贝（Blt）操作等等。主平面可以附带一个或多个&#8220;后台缓冲区&#8221;。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>后台缓冲区。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>后台缓冲区同样也是一个对象，也同样只提供一个指向该对象的指针供程序员使用。后台缓冲区与主平面的操作方式几乎一样，不同的是所有对后台缓冲区的操作不直接反映在屏幕上，例如，你可以在后台缓冲区中放置好图片，然后通过位块拷贝操作（或&#8220;弹出&#8221;操作）将后台缓冲区中的内容快速映射到主平面（即屏幕），这种操作方式可以避免出现不愉快的闪烁现象。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>位块拷贝。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>位块拷贝是将一块矩形区域从一个平面拷贝到另一个平面，可以进行缩放、旋转、镜像、透明等附加动作。DirectDraw提供的位块拷贝方法效率十分高，程序员勿需自行编制代码来完成相似工作。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&#8220;弹出&#8221;操作。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Flip这个单词有反转、弹出等含义，在DirectDraw中，可以将其理解为迅速互换两个平面，这种互换操作并不是内容的互换，而只是指针的互换，因此其速度比位块拷贝快得多。例如，在全屏幕独占方式下，可以利用&#8220;弹出&#8221;操作快速地将后台缓冲区与主平面互换（指针的互换），从而获得高速的视频动画效果。只有具有可&#8220;弹出&#8221;属性的平面才能使用&#8220;弹出&#8221;操作。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>释放。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>在DirectX中，各个对象均有Release方法，这个方法（成员函数）并不见得就是销毁对象，它只是对内部计数器进行递减操作。例如，用DirectDrawCreate()函数建立了DirectDraw基本对象后，该对象的内部计数器就置为值1，如果此时再调用该对象的Release方法，就会使内部计数器减去1，若结果为零则该对象被销毁，但若在调用Release方法之前又为该对象请求新的接口（如IDirectDraw2），则内部计数器又会加一，需要两次Release方法才能完全销毁该对象。&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　在示例程序一中，从WinMain函数开始，在登记了窗口类并建立了程序主窗口后，就开始建立DirectDraw的基本对象了：<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>ddrval&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;DirectDrawCreate(NULL,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">_lpDD,&nbsp;NULL);<br><img id=Codehighlighter1_4461_4565_Open_Image onclick="this.style.display='none'; Codehighlighter1_4461_4565_Open_Text.style.display='none'; Codehighlighter1_4461_4565_Closed_Image.style.display='inline'; Codehighlighter1_4461_4565_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_4461_4565_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_4461_4565_Closed_Text.style.display='none'; Codehighlighter1_4461_4565_Open_Image.style.display='inline'; Codehighlighter1_4461_4565_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ddrval</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">DD_OK)</span><span id=Codehighlighter1_4461_4565_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_4461_4565_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;失败</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　函数DirectDrawCreate在成功时返回DD_OK（零），若是失败则返回一个32位（HRESULT）的错误代码。不止是这个函数，所有DirectX对象的成员函数（方法）均采用这种方式来返回表示成功或失败的值。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　不必去管该函数的第一个和第三个参数，现在只需要把它们设为空值即可，而第二个参数是一个指针，这个指针指向了一个LPDIRECTDRAW类型的变量，而该变量实际上也是一个指针，如果函数成功则指向IDirectDraw对象实体</span><span style="COLOR: #000000">--------</span><span style="COLOR: #000000">是不是有一点绕来绕去的？<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　当基本对象成功建立后，就需要将它&#8220;变&#8221;成较新的对象以增进性能，这也正是本示例程序为什么需要5.0版本的DirectX来支持的缘故。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>ddrval</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">_lpDD</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">QueryInterface(IID_IDirectDraw2,&nbsp;(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lpDD);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_lpDD</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Release();<br><img id=Codehighlighter1_5079_5183_Open_Image onclick="this.style.display='none'; Codehighlighter1_5079_5183_Open_Text.style.display='none'; Codehighlighter1_5079_5183_Closed_Image.style.display='inline'; Codehighlighter1_5079_5183_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_5079_5183_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_5079_5183_Closed_Text.style.display='none'; Codehighlighter1_5079_5183_Open_Image.style.display='inline'; Codehighlighter1_5079_5183_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(ddrval</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">DD_OK)</span><span id=Codehighlighter1_5079_5183_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_5079_5183_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;失败</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　下一步是利用新建立的基本对象来建立与该基本对象相关联的主平面，这个主平面实际上就是可视的屏幕，所有对主平面的访问都直接反映在屏幕。在建立主平面之前我们首先将基本对象的属性设为全屏幕独占方式，这样才能改变显示模式，以及建立可&#8220;弹出&#8221;的平面集。具体的设置方法参见源程序。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　在设置了全屏幕独占方式并设置了适当的显示模式之后，程序建立了一个主平面，这个主平面附带一个后台缓冲区，程序员可以使用&#8220;设备相关把柄（HDC）&#8221;或直接访问的方式（以后再讲）来对后台缓冲区进行图形操作，所有对后台缓冲区的操作并不能反映在屏幕上，在图形操作完成后，可以利用&#8220;弹出&#8221;操作来将后台缓冲区&#8220;弹&#8221;至主平面，从而在屏幕上显示出来。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　本示例程序在进行图形操作示例时，首先获取后台缓冲区的HDC，然后用标准的GDI函数来绘制文本，一旦绘制完毕，就立刻释放刚获取的HDC，因为任何一个平面的HDC被获取后，该平面就被隐式地锁定，无法进行位块拷贝或弹出操作，只有当该HDC被释放后，一个隐式的Unlock才被调用，程序才能对该平面进行前述操作。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　完成后台缓冲区的图形绘制后，程序调用主平面的成员函数（方法）Flip来将后台缓冲区弹至主平面，你将看到屏幕内容已被改变。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>　　这是我个人搞的一个小封装，可用于非C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">的编程语言（如Delphi）。使用此封装库可比较容易地编制DirectDraw应用程序。此封装为静态链接库，提供Visual&nbsp;C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">和BorlandC</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">两种版本，其中包括：<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Visual&nbsp;C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">版库文件<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Borland&nbsp;C</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">版库文件<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;相关的头文件<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;两个示例源程序<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;说明文档（HLP文档格式）<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<img src ="http://www.cppblog.com/iniwf/aggbug/86476.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-06-01 22:58 <a href="http://www.cppblog.com/iniwf/archive/2009/06/01/86476.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DircetDraw c/c++ 使用指导(四)</title><link>http://www.cppblog.com/iniwf/archive/2009/05/25/85700.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 25 May 2009 06:38:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/05/25/85700.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/85700.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/05/25/85700.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/85700.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/85700.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DirectDraw_4.htm">http://dev.gameres.com/Program/Visual/2D/DirectDraw_4.htm</a><br><br>
<table height=211 cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td width="21%" height=49><a href="http://www.gameres.com/"><img height=47 alt=中国游戏技术资源网 src="http://dev.gameres.com/images/titlesmall.jpg" width=200 border=0></a></td>
            <td width="45%" height=49></td>
            <td width="34%" height=49></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3 height=18><font face=Tahoma size=2>&nbsp;</font></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3 height=21>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%" bgColor=#1d2532>
                        <p align=right><font face=Tahoma size=2>译：310cdt　</font> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3 height=41>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%"><font face=Tahoma size=2>&nbsp;</font></td>
                    </tr>
                    <tr>
                        <td width="100%">
                        <p align=center><font face=Tahoma color=#ffffdd><strong>DircetDraw c/c++ 使用指导(四)</strong></font> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3 height=61>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <div align=center>
                        <table cellSpacing=0 cellPadding=0 width="95%" border=0>
                            <tbody>
                                <tr>
                                    <td width="100%"><font face=Tahoma size=2><br>ddex4(不过这两天不知怎的服务器不怎么好使,过两天可能会好)</font>
                                    <p><font face=Tahoma size=2><strong>tutorial4:</strong>色彩键码和位图动画<br>第三篇指导的例子ddex3演示了一个简单的在翻转页面前把位图放到离屏缓存的行为.这篇指导的例子将利用前面描述的技术,装载一个背景图片和一系列的精灵(译者注:一般放置小幅的不断改变的图片)到离屏表面中.然后,使用IDirectDrawSurface7::BltFast方法拷贝离屏表面的一部分到后台缓冲,以次,产生一个简单的位图动画.<br>ddex4用到的位图文件是all.bmp,它包含了背景和60个连续的黑背景的旋转的红圈.DDEx4包含了新的函数,用来为旋转红圈精灵设置色彩键码.然后,例子从离屏表面中拷贝适当的精灵到后台缓冲.<br>DDEx4中新添的函数演示如下:<br><strong>step1:</strong>设置色彩键码<br><strong>step2:</strong>建立一个简单的动画<br>(译者注:色彩键码就是设置了的,在从离屏表面向缓冲拷贝的时候不拷贝的色彩,从而形成透明的样子)</font></p>
                                    <p><font face=Tahoma size=2><strong>step1:</strong>设置色彩键码<br>在其他例子的基础上,ddex4的doInit函数包含了设置精灵色彩键码的代码.色彩键码是用来设置一个色彩值,这个色彩值是用于透明的.当系统使用硬件块移动支持时,所有的像素中除了设为色彩键码的颜色的,都将被块移动(blit)到缓存.这样就创建了一个不是矩形样子的精灵.下面的代码演示了DDEx4中如何设置色彩键码的.</font></p>
                                    <p><font face=宋体 color=#99ccff size=2>// Set the color key for this bitmap (black).<br>DDSetColorKey(lpDDSOne, RGB(0,0,0));<br>&nbsp;<br>return TRUE;</font><font face=Tahoma size=2><br><br>在DDSetColorKey函数的调用的时候,你可以设置你想设的颜色的RGB值来设置色彩键码.黑色的RGB值是(0,0,0).DDSetColorKey函数调用了DDColorMatch函数.(两个函数都在ddutil.cpp中) DDColorMatch函数储存了在lpDDSOne表面中的位图的(0,0)位置的像素的颜色值.然后,他把位图的(0,0)位置的像素设为你提供的那个颜色.最后,它求了颜色值和每个像素色彩位数的掩码(异或).使(0,0)位置变回了原来的颜色,当这些都做完后,函数调用完返回到DDSetColorKey中.返回了色彩键码的值(dw)值.放在了DDCOLORKEY结构的成员dwColorSpaceLowValue中,并被拷贝到dwColorSpaceHighValue成员中.随后IDirectDrawSurface7::SetColorKey的调用设置了色彩键码.<br>你可能已经注意到了DDSetColorKey和DDColorMatch函数中的CLR_INVALID了.如果在DDEx4中你把CLR_INVALID当作色彩键码传递给DDSetColorKey函数,位图中的左上角(0,0)像素将被当作色彩键码.DDEx4中这个意义不大,因为位图的(0,0)位置是灰色的.你要是想看看怎样在ddex4中让(0,0)位置作为色彩键码,你可以用绘图软件打开all.bmp文件.然后把(0,0)这个点改为黑色.一定要保证你把修改保存好了(这个不好看出来).然后,你可以把DDSetColorKey的调用改成下面这样:<br><br></font><font face=宋体 color=#99ccff size=2>DDSetColorKey(lpDDSOne, CLR_INVALID);</font></p>
                                    <p><font face=Tahoma size=2>重新编译ddex4,保证包含了新位图的资源文件也重新编译过了.这样,ddex4将用那个现在已经被改为黑色的(0,0)点作为色彩键码了.</font></p>
                                    <p><font face=Tahoma size=2><br><strong>step2:</strong>创建一个简单的动画<br>ddex4用到了updateFrame例子函数创建了一个简单的动画,用的是all.bmp中包含的那些红圈.这个动画由三个呈三角形位置的以不同速度转动的红圈组成.例子中通过比较win32 GetTickCount函数和上一次调用GetTickCount函数到现在的毫秒数来决定是否重绘精灵.然后,使用IDirectDrawSurface7::BltFast方法把背景从离屏表面(lpDDSOne)中块移动(blit)到后台缓存,并且把精灵们也块移动到后台缓存,这是要用到你刚才设置的色彩键码决定哪些像素是透明的.当精灵们移动到后台缓存后,就调用IDirectDrawSurface7::Flip方法翻转页面.<br>注意当你用IDirectDrawSurface7::BltFast方法块移动背景的时候,dwTrans参数定义了传送的参数是DDBLTFAST_NOCOLORKEY.这个标示了将是一个没有透明的普通块移动.后来,当红圈被块移动的时候,dwTrans参数设为DDBLTFAST_SRCCOLORKEY.这个标示了块移动将用到定义了的色彩键码,以实现透明的效果.在这个例子中,是对lpDDSOne缓存.<br>在这个例子中,每次调用updateFrame函数都将重绘全部背景.优化这个例子的一个方法是,只重绘红圈旋转所引起的背景发生变化的那一小部分.因为放置红圈精灵的位置和大小都是不变得,所以,你可以很简单的修改ddex4以达到这个优化.</font><font face=Tahoma color=#c0c0c0 size=2><br>　</font></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </div>
                        </td>
                    </tr>
                    </center>
                    <tr>
                        <td width="100%" bgColor=#1d2532><font face=Tahoma size=2>　</font></td>
                    </tr>
                    <tr>
                        <td width="100%"><font face=Tahoma size=2>&nbsp;</font></td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3 height=21></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/85700.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-05-25 14:38 <a href="http://www.cppblog.com/iniwf/archive/2009/05/25/85700.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DircetDraw c/c++ 使用指导(三)</title><link>http://www.cppblog.com/iniwf/archive/2009/05/25/85699.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 25 May 2009 06:33:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/05/25/85699.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/85699.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/05/25/85699.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/85699.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/85699.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DirectDraw_3.htm">http://dev.gameres.com/Program/Visual/2D/DirectDraw_3.htm</a><br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td width="21%"><a href="http://www.gameres.com/"><img height=47 alt=中国游戏技术资源网 src="http://dev.gameres.com/images/titlesmall.jpg" width=200 border=0></a></td>
            <td width="45%"></td>
            <td width="34%"></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3><font face=Tahoma>&nbsp;</font></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%" bgColor=#1d2532>
                        <p align=right><font face=Tahoma size=2>译：310cdt</font><font face=Tahoma>　</font> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%"><font face=Tahoma>&nbsp;</font></td>
                    </tr>
                    <tr>
                        <td width="100%">
                        <p align=center><strong><font face=Tahoma color=#ffffdf>DircetDraw c/c++ 使用指导(三)</font></strong> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <div align=center>
                        <table cellSpacing=0 cellPadding=0 width="95%" border=0>
                            <tbody>
                                <tr>
                                    <td width="100%"><font face=Tahoma size=2><br>这几篇指导的例程我放到网上了</font>
                                    <p><font face=Tahoma size=2><a style="COLOR: #ff9933" href="http://brizy310cdt.home.chinaren.com/ddex1.zip"><u>ddex1</u></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face=Tahoma color=#c0c0c0 size=2><a style="COLOR: #ff9933" href="http://brizy310cdt.home.chinaren.com/ddex2.zip"><u>ddex2</u></a></font><font face=Tahoma size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font face=Tahoma color=#c0c0c0 size=2><a style="COLOR: #ff9933" href="http://brizy310cdt.home.chinaren.com/ddex3.zip"><u>ddex3</u></a><br></font><font face=Tahoma size=2>tutorial3:从一个离屏表面(off-screen surface)的块移动(blt)</font></p>
                                    <p><font face=Tahoma size=2>在ddex2中,把一张位图放到了后台缓存中,然后在缓存与主平面间翻啊翻...其实这不是显示位图的一般行为.在这一篇中(例子是ddex3)将要通过包含两个离屏表面来扩展ddex2的能力.两个位图-一个是第偶数次显示的,一个是奇数次显示-存放在这两个离屏表面.例子中,用IDirectDrawSurface7::BltFast方法把离屏表面的内容拷贝到后台缓存中去,然后翻转,然后再把下一个离屏表面拷到缓存...<br>(译者注:这一篇只是演示离屏表面和blit的方法,实际上,离屏表面使用的时候大部分是用来存放精灵,实现动画的.存放的都是小的图片,是一个特殊的平面)<br>ddex3新的函数以下面3个步骤演示:<br>step1:建立离屏表面<br>step2:载入位图到离屏表面<br>step3:块移动(blit)离屏表面到后台缓冲</font></p>
                                    <p><font face=Tahoma size=2><br>step1:建立离屏表面<br>下面的代码是加入到doInit函数中创建两个离屏表面.<br><br></font><font face=宋体 color=#99ccff size=2>// Create an off-screen bitmap.<br>ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;<br>ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;<br>ddsd.dwHeight = 480;<br>ddsd.dwWidth = 640;<br>ddrval = lpDD-&gt;CreateSurface(&amp;ddsd, &amp;lpDDSOne, NULL);<br>if(ddrval != DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; return initFail(hwnd);<br>}<br>&nbsp;<br>// Create another off-screen bitmap.<br>ddrval = lpDD-&gt;CreateSurface(&amp;ddsd, &amp;lpDDSTwo, NULL);<br>if(ddrval != DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; return initFail(hwnd);<br>}</font><font face=Tahoma size=2><br><br>dwFlags成员定义了程序将使用DDSCAPS结构,并且将要设置离屏表面的高和宽.这个表面因为标示了DDSCAPS_OFFSCREEN在DDSCAPS结构中,所以将会是一个完全的离屏的缓存.高和宽分别设为480 和640.随后,表面经过IDirectDraw7::CreateSurface方法创建.<br>因为两个离屏表面大小相同,就又不同的指针调用了IDirectDraw7::CreateSurface又一次,创建了另一个.<br>你可以通过在DDSCAPS结构中定义DDSCAPS_SYSTEMMEMORY或DDSCAPS_VIDEOMEMORY来决定将离屏表面放在系统内存还是显存中.放在显存中,你可以加快离屏表面到后台缓存的移动速度.将用到位图的动画的时候,这将十分的重要.但是,如果你使用DDSCAPS_VIDEOMEMORY并且没有足够的显存区存放那个位图了,当你试图创建这个表面的时候,将会返回一个DDERR_OUTOFVIDEOMEMORY错误.</font></p>
                                    <p><font face=Tahoma size=2>&nbsp;</font></p>
                                    <p><font face=Tahoma size=2>step2:载入位图到离屏表面<br>当两个离屏表面创建好后,DDEx3用InitSurface函数将frntback.bmp文件的位图装入到表面中.InitSurface函数用到了ddutil.cpp文件中的DDCopyBitmap函数去装载位图.如下:<br><br></font><font face=宋体 color=#99ccff size=2>// Load the bitmap resource.<br>hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), szBitmap,<br>&nbsp;&nbsp;&nbsp; IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);<br>&nbsp;<br>if (hbm == NULL)<br>&nbsp;&nbsp;&nbsp; return FALSE;<br>&nbsp;<br>DDCopyBitmap(lpDDSOne, hbm, 0, 0,&nbsp;&nbsp; 640, 480);<br>DDCopyBitmap(lpDDSTwo, hbm, 0, 480, 640, 480);<br>DeleteObject(hbm);<br>&nbsp;<br>return TRUE;</font><font face=Tahoma size=2><br><br>如果你用看图或画图的软件看过frntback.bmp文件了,你就知道,这个文件包括两个图,一个在另一个的上面.DDCopyBitmap函数把这个位图从分界的地方分成两部分.一个装入到第一个离屏表面(lpDDSOne),另一个在第二个离屏表面(lpDDSTwo).</font></p>
                                    <p><font face=Tahoma size=2><br>step3:块移动(blit)离屏表面到后台缓冲<br>WM_TIMER消息触发的代码包括写入表面和翻转表面.在DDEx3中,他包含了下面的代码去选择适当的离屏表面,然后把他块移动(blit)到后台缓存.<br><br></font><font face=宋体 color=#99ccff size=2>rcRect.left = 0;<br>rcRect.top = 0;<br>rcRect.right = 640;<br>rcRect.bottom = 480;<br>if(phase)<br>{<br>&nbsp;&nbsp;&nbsp; pdds = lpDDSTwo;<br>&nbsp;&nbsp;&nbsp; phase = 0;<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; pdds = lpDDSOne;<br>&nbsp;&nbsp;&nbsp; phase = 1;<br>}<br>while(1)<br>{<br>&nbsp;&nbsp;&nbsp; ddrval = lpDDSBack-&gt;BltFast(0, 0, pdds, &amp;rcRect, FALSE);<br>&nbsp;&nbsp;&nbsp; if(ddrval == DD_OK)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; }<br>}</font><font face=Tahoma size=2><br><br>phase变量决定了到底是哪一个离屏表面将被移动到后台缓存.IDirectDrawSurface7::BltFast方法的调用是用来把所选的离屏表面块移动到后台缓存的.在(0,0)处开始,左上角.reRect参数指向一个RECT结构,他定义了移动出来的离屏表面的左上和右下角.最后的一个参数设为FALSE(0),标示没有使用特殊的传输标示符.<br>根据你的程序的需要,你可以使用IDirectDrawSurface7::Blt 或者IDirectDrawSurface7::BltFast 方法实现块移动.如果你的离屏表面在显存中,你最好使用IDirectDrawSurface7::BltFast 方法.这样做,如果你使用的是硬件块移动支持的系统,你不会获得更快的速度,但是,如果使用的硬件模仿的块移动,你将会少用10%的时间.所以,你应该使用IDirectDrawSurface7::BltFast方法去做所有的在显存中的块移动.如果你从系统内存中移动,或者需要特别的硬件标志,那就只好使用IDirectDrawSurface7::Blt了.<br>当完成的从离屏表面到后台缓存的块移动,后台缓存就和主平面之间像前面几篇一样不停的翻啊翻....</font><font face=Tahoma color=#c0c0c0 size=2><br>　</font></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </div>
                        </td>
                    </tr>
                    </center>
                    <tr>
                        <td width="100%" bgColor=#1d2532><font face=Tahoma>　</font></td>
                    </tr>
                    <tr>
                        <td width="100%"><font face=Tahoma>&nbsp;</font></td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/85699.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-05-25 14:33 <a href="http://www.cppblog.com/iniwf/archive/2009/05/25/85699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DircetDraw c/c++ 使用指导(二)</title><link>http://www.cppblog.com/iniwf/archive/2009/05/25/85698.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 25 May 2009 06:32:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/05/25/85698.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/85698.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/05/25/85698.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/85698.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/85698.html</trackback:ping><description><![CDATA[<a href="http://dev.gameres.com/Program/Visual/2D/DirectDraw_2.htm">http://dev.gameres.com/Program/Visual/2D/DirectDraw_2.htm</a><br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td width="21%"><a href="http://www.gameres.com/"><img height=47 alt=中国游戏技术资源网 src="http://dev.gameres.com/images/titlesmall.jpg" width=200 border=0></a></td>
            <td width="45%"></td>
            <td width="34%"></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3><font face=Tahoma>&nbsp;</font></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%" bgColor=#1d2532>
                        <p align=right><font face=Tahoma size=2>译：310cdt</font><font face=Tahoma>　</font> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%"><font face=Tahoma>&nbsp;</font></td>
                    </tr>
                    <tr>
                        <td width="100%">
                        <p align=center><strong><font face=Tahoma color=#ffffee>DircetDraw c/c++ 使用指导(二)</font></strong> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <div align=center>
                        <table cellSpacing=0 cellPadding=0 width="95%" border=0>
                            <tbody>
                                <tr>
                                    <td width="100%"><font face=Tahoma size=2><br><strong>Tutorial2:在缓存中载入位图</strong></font><strong> </strong>
                                    <p><font face=Tahoma size=2>这个例子讨论在ddex1的基础上进行扩展.ddex2(例程在msdn上就有,搜索...)讲包含载入位图文件的函数.新的功能靠以下的步骤实现:<br>step1:创建调色板<br>step2:设置调色板<br>step3:在缓存中载入位图<br>step4:翻转平面.<br>像在ddex1中一样,在初始化函数中初始化了ddex2.<br>不同的代码如下:<br><br></font><font face=宋体 color=#99ccff size=2>lpDDPal = DDLoadPalette(lpDD, szBackground);<br>&nbsp;<br>if (lpDDPal == NULL)<br>&nbsp;&nbsp;&nbsp; goto error;<br>&nbsp;<br>ddrval = lpDDSPrimary-&gt;SetPalette(lpDDPal);<br>&nbsp;<br>if(ddrval != DD_OK)<br>&nbsp;&nbsp;&nbsp; goto error;<br>&nbsp;<br>// Load a bitmap into the back buffer.<br>ddrval = DDReLoadBitmap(lpDDSBack, szBackground);<br>&nbsp;<br>if(ddrval != DD_OK)<br>&nbsp;&nbsp;&nbsp; goto error;</font></p>
                                    <p><font face=Tahoma size=2>Step1:建立调色板<br>在ddex2中,首先用如下代码建立调色板.<br><br></font><font face=宋体 color=#99ccff size=2>lpDDPal = DDLoadPalette(lpDD, szBackground);<br>&nbsp;<br>if (lpDDPal == NULL)<br>&nbsp;&nbsp;&nbsp; goto error;</font><font face=Tahoma size=2><br><br>DDLoadPalette这个函数是在\Dxsdk\sdk\samples\misc\ddutil.cpp中的公共directdraw函数.很多directdraw的例子(sdk包中的)都用到这个文件.重要的是,它包含了载入调色板和位图的函数,无论是从文件还是资源.为了不重复的写代码,就把他放在了一个可以重复使用的文件中.确信你在编译ddexn是包含了这文件.<br>(以下内容在ddutial.cpp中)ddex2中,DDLoadPalette函数从back.bmp文件创建了DirectDrawPalette对象.DDLoadPalette函数判断创建调色板的文件或资源是否存在.如果不是的话,就创建一个默认的调色板.在ddex2中,他从位图文件提取调色板信息并储存在一个ape指向的结构中.<br>DDEx2随后创建了DirectDrawPalette对象,如下:<br><br></font><font face=宋体 color=#99ccff size=2>pdd-&gt;CreatePalette(DDPCAPS_8BIT, ape, &amp;ddpal, NULL);<br>return ddpal;</font><font face=Tahoma size=2><br><br>当IDirectDraw7::CreatePalette方法返回,ddpal参数指向从DDLoadPalette函数返回的DirectDrawPalette对象.<br>ape参数是一个指针,指向一个能包含或者2或4或16或256个线性组织的纪录的结构.记录的数目依靠于CreatePalette方法中的dwFlags参数.在上面的情况下,dwFlags参数设为DDPCAPS_8BIT.这表示结构中有256个记录.每一条记录包含4字节(分别是红,绿,兰的通道和一个标志位).</font></p>
                                    <p><font face=Tahoma size=2>Step2:设置调色板<br>创建完调色板以后,通过指针调用主平面的IDirectDrawSurface7::SetPalette方法,如下:<br><br></font><font face=宋体 color=#99ccff size=2>ddrval = lpDDSPrimary-&gt;SetPalette(lpDDPal);<br>&nbsp;<br>if(ddrval != DD_OK)<br>&nbsp;&nbsp;&nbsp; goto error;// SetPalette failed.</font><font face=Tahoma size=2><br><br>在你调用完IDirectDrawSurface7::SetPalette方法之后,DirectDrawPalette对象就与DirectDrawSurface对象联系起来了.什么时候你想改变调色板了,可以简单的创建一个新的调色板,然后设置一下就可以了.(虽然这篇指导用了这些步骤,其实还有其他的方法改变调色板,以后的例子中将会演示)<br><br>Step3:在缓存区载入位图<br>DirectDrawPalette对象与DirectDrawSurface对象联系起来之后,DDEx2用下面的代码在缓存中载入位图back.bmp<br><br></font><font face=宋体 color=#99ccff><font size=2>// Load a bitmap into the back buffer.<br>ddrval = DDReLoadBitmap(lpDDSBack, szBackground);<br>&nbsp;<br>if(ddrval != DD_OK)<br>&nbsp;&nbsp;&nbsp; // Load failed.<br></font></font><font face=Tahoma size=2><br>DDReLoadBitmap是另一个在Ddutil.cpp中的函数.他载入一个位图文件或资源到一个已经存在的DirectDraw平面.(你也可以使用DDLoadBitmap函数创建一个平面然后载入.函数也在ddutil.cpp中.)在ddex2中,他载入由szBackground(ID)指向的back.bmp文件到由lpDDSBack(指针)指向的后台缓存.DDReLoadBitmap函数调用DDCopyBitmap函数将文件拷贝到缓存,并拉伸到适当的大小.<br>DDCopyBitmap函数将位图拷贝到内存中,用GetObject函数恢复位图的大小.然后是由下面的代码将位图调整到将要放位图的缓存的大小.<br><br></font><font face=宋体 color=#99ccff><font size=2>// Get the size of the surface.<br>ddsd.dwSize = sizeof(ddsd);<br>ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;<br>pdds-&gt;GetSurfaceDesc(&amp;ddsd);<br></font></font><font face=Tahoma size=2><br>ddsd是一个DDSURFACEDESC2结构的指针.这个结构保存了现在的DirectDraw平面的描述.在这种情况下,DDSURFACEDESC2的成员描述了平面的高和宽,由DDSD_HEIIGHT和DDSD_WIDTH标示的.IDirectDrawSurface7::GetSurfaceDesc方法的调用把属性值装入了这个结构.在DDEX2中,值将被设为高480,宽640.<br>DDCopyBitmap函数给平面加锁然后把位图拷贝到缓存中,用StretchBlt函数拉伸或压缩.如下:<br><br></font><font face=宋体 color=#99ccff><font size=2>if ((hr = pdds-&gt;GetDC(&amp;hdc)) == DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, x, y,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dx, dy, SRCCOPY);<br>&nbsp;&nbsp;&nbsp; pdds-&gt;ReleaseDC(hdc);<br>}</font></font></p>
                                    <p><font face=Tahoma size=2>Step4:翻转页面<br>ddex2中翻转页面的部分与ddex1中的十分的相像.所不同的是:当平面丢失时(DDERR_SURFACELOST),在平面储存后,必须用DDReLoadBitmap函数将位图重新载入缓存.<br>　</font></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </div>
                        </td>
                    </tr>
                    </center>
                    <tr>
                        <td width="100%" bgColor=#1d2532><font face=Tahoma>　</font></td>
                    </tr>
                    <tr>
                        <td width="100%"><font face=Tahoma>&nbsp;</font></td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/85698.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-05-25 14:32 <a href="http://www.cppblog.com/iniwf/archive/2009/05/25/85698.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DircetDraw c/c++ 使用指导(一) </title><link>http://www.cppblog.com/iniwf/archive/2009/05/25/85697.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 25 May 2009 06:29:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/05/25/85697.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/85697.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/05/25/85697.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/85697.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/85697.html</trackback:ping><description><![CDATA[<p><a href="http://dev.gameres.com/Program/Visual/2D/DirectDraw_1.htm">http://dev.gameres.com/Program/Visual/2D/DirectDraw_1.htm</a><br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td width="21%"><a href="http://www.gameres.com/"><img height=47 alt=中国游戏技术资源网 src="http://dev.gameres.com/images/titlesmall.jpg" width=200 border=0></a></td>
            <td width="45%"></td>
            <td width="34%"></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3><font face=Tahoma size=2>&nbsp;</font></td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%" bgColor=#1d2532>
                        <p align=right><font face=Tahoma size=2>译：310cdt　</font> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%"><font face=Tahoma size=2>&nbsp;</font></td>
                    </tr>
                    <tr>
                        <td width="100%">
                        <p align=center><strong><font face=Tahoma color=#ffffdf>DircetDraw c/c++ 使用指导(一)</font></strong> </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </center></div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3>
            <div align=center>
            <center>
            <table cellSpacing=0 cellPadding=0 width="90%" border=0>
                <tbody>
                    <tr>
                        <td width="100%">
                        <div align=center>
                        <table cellSpacing=0 cellPadding=0 width="95%" border=0>
                            <tbody>
                                <tr>
                                    <td width="100%"><font face=Tahoma size=2><br>边看边译,译完就拿了上来,见笑了</font>
                                    <p><font face=Tahoma size=2>这是一系列的DirectDraw的指南,教你一步步的去构建一个简单的DirectDraw应用.这个指南用到了sdk包提供的很多DirectDraw的例子.这些例子展示了怎样设置DirectDraw,怎样用DirectDraw方法实现一般任务:</font></p>
                                    <p><font face=Tahoma size=2><br>注意:这些指南中的例子是用c++写的.如果你使用的是c编译器,请进行适当的改变,以能进行成功的编译.你需要把vtable和this指针添加到接口方法中.</font></p>
                                    <p><font face=Tahoma size=2>&nbsp;</font></p>
                                    <p><strong><font face=Tahoma size=2>1.DirectDraw基础用法</font></strong></p>
                                    <p><font face=Tahoma size=2>要使用DirectDraw,你必须先创建一个代表计算机显示接口的DirectDraw实例.然后,你就可以通过接口的方法来操纵这个对象.你可能会需要创建一个或更多的DirectDraw平面对象(DirectDraw surface object)的实例<br>来在图形设备上显示你的应用程序.</font></p>
                                    <p><font face=Tahoma size=2>为了演示这个,例子DDEx1 sample((SDK root)\Samples\Multimedia\DDraw\Src\Ddex1)演示了以下几步:</font></p>
                                    <p><font face=Tahoma size=2>step 1:创建一个DirectDraw对象<br>想创建一个DirectDraw对象的实例,你的应用程序必须在InitApp函数中使用DirectDrawCreateEx函数,就像例程ddex1中一样.DirectDrawCreateEx函数包括4个参数.第一个是:显示设备全局唯一标识(GUID).GUID大部分情况下是设为NULL,选择系统默认的显示设备.第二个参数是:包含的是标示DirectDraw对象是否建立的指针的地址.第三个参数是IDirectDraw7接口的参考标示.必须设为IID_IDirectDraw7.第四个参数是设置为NULL的,是为了将来的扩展做准备的.<br>以下的例子展示了怎样创建DirectDraw对象,并判断创建是否成功.<br><br></font><font face=宋体 color=#99ccff size=2>hRet = DirectDrawCreateEx(NULL, (VOID**)&amp;g_pDD, IID_IDirectDraw7, NULL);<br>if(hRet == DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; // g_pDD is a valid DirectDraw object.<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; // The DirectDraw object could not be created.<br>}</font></p>
                                    <p><font face=Tahoma size=2>&nbsp;</font></p>
                                    <p><font face=Tahoma size=2>step2:决定程序的行为<br>在你改变显示方法前,你必须至少在IDirectDraw7::SetCooperativeLevel函数中指定dwFlags参数(DDSCL_EXCLUSIVE 和 DDSCL_FULLSCREEN).这样,你的应用程序就得到了显示设备的全部控制权,其他的程序不能共享.DDSCL_FULLSCREEN让你的应用程序在全屏幕模式下运行.你的程序覆盖了桌面,并且只有你的程序能在屏幕上输出.但是,桌面仍然是有效的.(按ALT+TAB切换到桌面)</font></p>
                                    <p><font face=Tahoma size=2>下面的例子演示了SetCooperativeLevel函数的用法.<br><br></font><font face=宋体 color=#99ccff size=2>HRESULT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hRet;<br>LPDIRECTDRAW7 g_pDD;&nbsp;&nbsp; // already created by DirectDrawCreateEx</font></p>
                                    <p><font face=宋体 color=#99ccff size=2>hRet = g_pDD-&gt;SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);<br>if (hRet != DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; // Exclusive mode was successful.<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; // Exclusive mode was not successful.<br>&nbsp;&nbsp;&nbsp; // The application can still run, however.<br>}</font></p>
                                    <p><font face=Tahoma size=2>如果 SetCooperativeLevel不返回DD_OK,你可以指定为DDSCL_NORMAL继续运行你的程序.但是,你的程序不再是独占模式,而且,可能会无法完成你的一部分要求.在这种情况下,你最好显示一个对话框让用户决定是否要继续.<br>如果你设置的是全屏幕独占合作级别,你必须把程序的窗口句柄传递给SetCooperativeLevel,这样可以让Windows有能力决定你的程序是否异常终止.</font></p>
                                    <p><font face=Tahoma size=2>&nbsp;</font></p>
                                    <p><font face=Tahoma size=2>step3 :改变显示模式<br>接下来,你可以用IDirectDraw7::SetDisplayMode函数来改变显示模式.下面的例子将演示怎样把显示模式设置为640 &#215; 480 &#215; 8 bits per pixel (bpp).<br><br></font><font face=宋体 color=#99ccff size=2>HRESULT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hRet;<br>LPDIRECTDRAW7 g_pDD;&nbsp;&nbsp; // already created by DirectDrawCreateEx</font></p>
                                    <p><font face=宋体 color=#99ccff size=2>hRet = g_pDD-&gt;SetDisplayMode(640, 480, 8, 0, 0);<br>if (hRet != DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; // The display mode changed successfully.<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; // The display mode cannot be changed.<br>&nbsp;&nbsp;&nbsp; // The mode is either not supported or<br>&nbsp;&nbsp;&nbsp; // another application has exclusive mode.<br>}</font></p>
                                    <p><font face=Tahoma size=2>当你设置显示模式的时候,你应该确定用户的硬件是否支持这样的模式.你可以设置一个能被大部分显示适配器支持的标准的模式.例如:你的程序可以以640 &#215; 480 &#215; 8作为备选模式,设计成为可以在所有系统上运行.<br>注意:IDirectDraw7::SetDisplayMode返回一个DDERR INVALIDMODE错误值,如果显示适配器无法切换到想要的模式时.你可以在试图改变显示模式前,用IDirectDraw7::EnumDisplayModes函数看一下用户显示适配器的能力.</font></p>
                                    <p><font face=Tahoma size=2><br>step4:创建交换页(flipping surface)<br>设置完显示模式以后,你应该创建一个平面.例程:DDEx1用IDirectDraw7::SetCooperativeLevel 设置为独占模式.所以,你可以创建交换页(flipping surfaces).如果你设置的是DDSCL_NORMAL模式,你可以创建一个可以块移动(blit)的平面.创建交换页(flipping surfaces)需要以下步骤:<br>(4.1)定义需求的平面:<br>第一步是以DDSURFACEDESC2结构定义一个需求的平面.下面的例子演示了结构的定义和标志位的设定:<br><br></font><font face=宋体 color=#99ccff size=2>// Create the primary surface with one back buffer.<br>ZeroMemory(&amp;ddsd, sizeof(ddsd));<br>ddsd.dwSize = sizeof(ddsd);<br>ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;<br>ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DDSCAPS_COMPLEX;<br>ddsd.dwBackBufferCount = 1;</font></p>
                                    <p><font face=Tahoma size=2>在这个例子中,dwSize成员是DDSURFACEDESC2结构的大小.这是防止你用到的DirectDraw方法返回无效成员的错误.(dwSize是准备给将来的DDSURFACEDESC2结构的扩展用的)<br>dwFlags成员决定的DDSURFACEDESC2结构中那些成员将被填充有效的信息.例如在DDEx1中,dwFlags被设为你想要用DDSCAPS结构(DDSD_CAPS)和你想创建一个后台缓冲(back buffer)(DDSD_BACKBUFFERCOUNT)<br>dwCaps成员在例子中标示一个将要在DDSCAPS结构中使用的标志位.在这种情况下,他指定一个主平面(primary surface DDSCAPS_PRIMARYSURFACE),一个交换页(flipping surface DDSCAPS_FLIP),一个合成表面(complex surface DDSCAPS_COMPLEX).<br>最后,例子指定了一个后台缓冲.后台缓冲就是实际的绘图操作先在那里完成,然后,再快速的翻动(flip)到主平面(primary surface)上.在DDEx1中,后台缓冲的数目是1.其实,你要你的显存允许,你想建几个就建几个.你想知道更多的关于创建大于1块缓冲的信息,可以去看&nbsp; "triple buffering".<br>创建的"平面"占用的存储空间,可以是系统内存也可以是显存.如果应用程序使用的空间超出了显存,DirectDraw就会使用系统内存.(例如你指定多块缓存在一个仅有1MB显存的是配器上).你也可以这样设置DDSCAPS结构的dwCaps成员,设成DDSCAPS_VIDEOMEMEORY或DDCAPS_SYSTEMMEMORY以达到只用显存或只用内存.(如指定用显存,而显存不够,IDirectDraw7::CreateSurface返回一个DDERR_OUTOFVIDEOMEMORY错误)<br>(4.2)创建平面<br>在填充完DDSURFACEDESC2结构后,你就可以用他和g_pDD指针(DirectDrawCreateEx函数创建的DirectDraw对象的指针)调用IDirectDraw7::CreateSurface方法,就像下面 :<br><br></font><font face=宋体 color=#99ccff size=2>hRet = g_pDD-&gt;CreateSurface(&amp;ddsd, &amp;g_pDDSPrimary, NULL);<br>if (hRet != DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; // g_pDDSPrimary points to the new surface.<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; // The surface was not created.<br>&nbsp;&nbsp;&nbsp; return FALSE;<br>}</font><font face=Tahoma size=2><br><br>g_pDDSPrimary参数将指向由CreateSurface函数返回的主平面(primary surface)的地址,如果调用成功的话.<br>指向主平面(primary surface)的指针有效后,就可以调用IDirectDrawSurface7::GetAttachedSurface方法去得到一个指向缓冲的指针.如下:<br><br></font><font face=宋体 color=#99ccff size=2>ZeroMemory(&amp;ddscaps, sizeof(ddscaps));<br>ddscaps.dwCaps = DDSCAPS_BACKBUFFER;<br>hRet = g_pDDSPrimary-&gt;GetAttachedSurface(&amp;ddscaps, &amp;g_pDDSBack);<br>if (hRet != DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp; // g_pDDSBack points to the back buffer.<br>}<br>else<br>{<br>&nbsp;&nbsp;&nbsp; return FALSE;<br>}</font><font face=Tahoma size=2><br><br>如果IDirectDrawSurface7::GetAttachedSurface调用成功,g_pDDSBack参数将指向缓存区.</font></p>
                                    <p><font face=Tahoma size=2>&nbsp;</font></p>
                                    <p><font face=Tahoma size=2>step5:在平面上输出<br>在主平面(primary surface)和后台缓冲(back buffer)创建完成后,例子DDEx1用windows标准GDI函数输出了一些文本在缓冲上.如下:<br><br></font><font face=宋体 color=#99ccff size=2>if (g_pDDSBack-&gt;GetDC(&amp;hdc) == DD_OK)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetBkColor(hdc, RGB(0, 0, 255));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetTextColor(hdc, RGB(255, 255, 0));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (phase)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetClientRect(hWnd, &amp;rc);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetTextExtentPoint(hdc, szMsg, lstrlen(szMsg), &amp;size);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextOut(hdc, (rc.right - size.cx) / 2, (rc.bottom - size.cy) / 2,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; szMsg, sizeof(szMsg) - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextOut(hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; phase = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextOut(hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; phase = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_pDDSBack-&gt;ReleaseDC(hdc);<br>}</font></p>
                                    <p><font face=Tahoma size=2>这个例子使用the IDirectDrawSurface7::GetDC方法获得设备上下文的句柄,并且,为了准备写入,将缓冲上锁.如果你不准备用需要设备上下文句柄的windows函数,你可以使用IDirectDrawSurface7::Lock&nbsp; IDirectDrawSurface7::Unlock方法加解锁.<br>接下来,phase变量决定了应该输出到主缓存消息(primary buffer message)还是后台缓存消息(back buffer message).如果phase=1,输出<br>主缓存消息,且设phase=0.如果phase=0,输出后台缓存消息,且设phase=1.<br>当消息输出到缓存区后,用IDirectDrawSurface7::ReleaseDC 方法将缓存区解锁.<br>对创建平面的内存上锁,是保证你的程序和系统不能同时对此内存进行存取.这防止你写入"平面"内存事发生错误.另外,不对"平面"内存解锁,你的程序将无法翻转平面.<br>平面被加锁之后,例子中用了标准windowsGDI函数:SetBkColor设置背景色,SetTextColor设置在背景上显示的字的颜色,用TextOut在"表面"上输出文字和背景色.<br>当文字被输出到缓存之后,例子应用IDirectDrawSurface7::ReleaseDC方法解锁"表面"并且释放句柄.不论在什么时候你的程序完成了对缓存的写入,一定要调用IDirectDrawSurface7::ReleaseDC 或IDirectDrawSurface7::Unlock之一,具体用哪个,由你的程序而定.不解锁,你的程序将不能翻转平面.<br>注意:用IDirectDrawSurface7::Unlock对"平面"解锁后,指向"平面"的指针将会无效.你必须再用IDirectDrawSurface7::lock去获得一个有效的指针.</font></p>
                                    <p><font face=Tahoma size=2>&nbsp;</font></p>
                                    <p><font face=Tahoma size=2>step:6 翻转平面<br>在DDEx1中,WM_TIMER消息引发从缓存到主平面的翻转.当"平面"内存解锁后,你就可以用IDirectDrawSurface7::Flip方法实现从缓存到主平面的翻转.如下:<br><br></font><font face=宋体 color=#99ccff size=2>case WM_TIMER:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Update and flip surfaces<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (g_bActive &amp;&amp; TIMER_ID == wParam)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UpdateFrame(hWnd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (TRUE)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hRet = g_pDDSPrimary-&gt;Flip(NULL, 0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (hRet == DD_OK)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (hRet == DDERR_SURFACELOST)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hRet = g_pDDSPrimary-&gt;Restore();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (hRet != DD_OK)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (hRet != DDERR_WASSTILLDRAWING)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;</font></p>
                                    <p><font face=Tahoma size=2>在例子中,g_pDDSPrimary参数指示主平面和与他连接的缓存.当IDirectDrawSurface7::Flip被调用,前后平面将会交换(只是交换指针,没有实际的数据交换).如果翻转成功,返回DD_OK,跳出循环.<br>如果翻转返回的是DDERR_SURFACELOST值,就试图用IDirectDrawSurface7::Restore 方法保存平面.如果保存成功,程序循环到调用IDirectDrawSurface7::Flip,再试一次.如果保存不成功,程序跳出循环,返回一个错误.<br>注意:当你调用IDirectDrawSurface7::Flip 时,翻转动作并不能马上完成.如果,前一个翻转动作还没有完成,IDirectDrawSurface7::Flip 会返回DDERR_WASSTILLDRAWING.在这个例子中,IDirectDrawSurface7::Flip会一直调用直到返回DD_OK.</font></p>
                                    <p><font face=Tahoma size=2><br>step7:释放DirectDraw对象<br>当你按下F12键时,例子DDEx1将在退出程序前处理WM_DESTROY消息.这个消息将调用ReleaseAllObjects函数,这个函数包括了多个Release调用.像下面一样:<br><br></font><font face=宋体 color=#99ccff size=2>static void<br>ReleaseAllObjects(void)<br>{<br>&nbsp;&nbsp;&nbsp; if (g_pDD != NULL)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (g_pDDSPrimary != NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_pDDSPrimary-&gt;Release();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_pDDSPrimary = NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_pDD-&gt;Release();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g_pDD = NULL;<br>&nbsp;&nbsp;&nbsp; }<br>}</font><font face=Tahoma size=2><br><br>这个程序检测DirectDraw对象的指针g_pDD和DirectDraw平面对象的指针g_pDDSPrimary是否为NULL.然后,调用IDirectDrawSurface7::Release方法令DirectDraw平面对象的reference count(可以认为是创建的对象的数目)数减1,这使得reference count减为0,DirectDraw平面对象就释放了.然后,还需将他的指针值设为NULL.接下来,程序调用IDirectDraw7::Release,同样是令DirectDraw的reference count减1,然后然后....全销毁.</font><font face=Tahoma color=#c0c0c0 size=2><br>　</font></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </div>
                        </td>
                    </tr>
                    </center>
                    <tr>
                        <td width="100%" bgColor=#1d2532><font face=Tahoma size=2>　</font></td>
                    </tr>
                    <tr>
                        <td width="100%"><font face=Tahoma size=2>&nbsp;</font></td>
                    </tr>
                </tbody>
            </table>
            </div>
            </td>
        </tr>
        <tr>
            <td width="100%" colSpan=3></td>
        </tr>
    </tbody>
</table>
</p>
<img src ="http://www.cppblog.com/iniwf/aggbug/85697.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-05-25 14:29 <a href="http://www.cppblog.com/iniwf/archive/2009/05/25/85697.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>提供修复界面的NSIS安装包</title><link>http://www.cppblog.com/iniwf/archive/2009/05/08/82207.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Fri, 08 May 2009 00:59:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/05/08/82207.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/82207.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/05/08/82207.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/82207.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/82207.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: ;&nbsp;该脚本由邦畿千里制作，可自由传播使用&nbsp;QQ:911377!include&nbsp;"LogicLib.nsh";&nbsp;安装背景及文字!define&nbsp;PRODUCT_TEXT_KEY&nbsp;"(邦畿千里)"!define&nbsp;PRODUCT_TEXT_BACK&nbsp;"resource\back.bmp"!define&nbsp;PRODUCT...&nbsp;&nbsp;<a href='http://www.cppblog.com/iniwf/archive/2009/05/08/82207.html'>阅读全文</a><img src ="http://www.cppblog.com/iniwf/aggbug/82207.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-05-08 08:59 <a href="http://www.cppblog.com/iniwf/archive/2009/05/08/82207.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ReactOS v0.3.9 - 开源的FOSS操作系统</title><link>http://www.cppblog.com/iniwf/archive/2009/04/27/81270.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 27 Apr 2009 14:33:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/27/81270.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/81270.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/27/81270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/81270.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/81270.html</trackback:ping><description><![CDATA[<p><strong>ReactOS项目致力于开发一个免费软件，向大家提供完全兼容Microsoft Windows XP的操作系统。ReactOS 旨在实现和NT与XP操作系统二进制下的完全应用程序和驱动设备的兼容性，通过使用类似构架和提供完全公共接口。</strong>自由软件倡导者认同自由软件操作系统现状 的改善促进了竞争，ReactOS也同样获益匪浅ReactOS是最完整的类 Windows&#174;操作系统的工作平台， 因此，参与开发的程序员需要花费许多精力去学习ReactOS源码 和研究ReactOS开发文档。</p>
<p>ReactOS组件也越来越多，兼容性也越来越好，但 ReactOS 不会有任何束缚。 ReactOS已经并将继续吸收最新版本的Win32 API，跟踪甚至发展最新的操作系统技术 。尽我们所能，使用最新的技术，并不断的融合新的特点到最新版本。<br><br>简单地说，ReactOS目标就是用您的硬件设备去运行您的应用程序，最后，诞生一个任何人多可以免费使用的 FOSS 操作系统!<br><br>更新：<a href="http://www.reactos.org/wiki/index.php/ChangeLog-0.3.9" target=_blank><u><font color=#0000ff>http://www.reactos.org/wiki/index.php/ChangeLog-0.3.9</font></u></a><br>官网：<a href="http://www.reactos.org/" target=_blank><u><font color=#0000ff>http://www.reactos.org</font></u></a>；<a href="http://www.reactoschina.com/" target=_blank><u><font color=#0000ff>http://www.reactoschina.com</font></u></a><br><br>Installation CD - 官方下载：<br><a href="http://downloads.sourceforge.net/reactos/ReactOS-0.3.9-REL-iso.zip" target=_blank><u><font color=#0000ff>http://downloads.sourceforge.net/reactos/ReactOS-0.3.9-REL-iso.zip</font></u></a><br>Live CD - 官方下载：<br><a href="http://downloads.sourceforge.net/reactos/ReactOS-0.3.9-REL-live.zip" target=_blank><u><font color=#0000ff>http://downloads.sourceforge.net/reactos/ReactOS-0.3.9-REL-live.zip</font></u></a><br>更多下载：<a href="http://www.reactos.org/zh/download.html" target=_blank><u><font color=#0000ff>http://www.reactos.org/zh/download.html</font></u></a><br><br><a href="http://img.cnbeta.com/newsimg/090206/18360502149070.jpg" target=_blank><img class=insertimage title=点击在新窗口中浏览此图片 alt=点击在新窗口中浏览此图片 src="http://img.cnbeta.com/newsimg/090206/18360502149070.jpg" width=500 onload="if(this.width>500) {this.resized=true; this.width=500;}" border=0 resized="true"></a><br><a href="http://img.cnbeta.com/newsimg/090206/18361011302369243.jpg" target=_blank><img class=insertimage title=点击在新窗口中浏览此图片 alt=点击在新窗口中浏览此图片 src="http://img.cnbeta.com/newsimg/090206/18361011302369243.jpg" width=500 onload="if(this.width>500) {this.resized=true; this.width=500;}" border=0></a><strong><br></strong></p>
<img src ="http://www.cppblog.com/iniwf/aggbug/81270.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-27 22:33 <a href="http://www.cppblog.com/iniwf/archive/2009/04/27/81270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><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></channel></rss>