<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>mengxp杂谈</title>
	<atom:link href="http://www.deadc0de.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.deadc0de.com</link>
	<description>a piece world of win32asm</description>
	<pubDate>Tue, 17 Nov 2009 04:29:54 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>war3 1.20e dota反互通图作弊 及 war3对战平台原理</title>
		<link>http://www.deadc0de.com/archives/war3_1_20e_anti_cheat.html</link>
		<comments>http://www.deadc0de.com/archives/war3_1_20e_anti_cheat.html#comments</comments>
		<pubDate>Tue, 17 Nov 2009 04:29:54 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=83</guid>
		<description><![CDATA[大部分互通图在作弊的时候都需要输入作弊指令 -xxx
而且为了掩人耳目有些指令具有迷惑作用比如 -ma空格空格空格
但是如果设置聊天对象为ob那么队友也看不到你输入的指令
但事实上这一切尽在服务器眼中
玩家输入的任何游戏指令(-打头)
都会通过26号游戏协议包(F7 26 xx xx)发送到服务器
假如我们做了一个服务器插件
监控玩家使用的游戏指令
就可以达到一定的监测作弊效果
然后配合踢人功能惩罚作弊者&#8230;那么&#8230;
 
另外在分析魔兽协议的时候又学习了一下iocp模型
war3为每个连接创建了一个线程接收网络数据包
使用WSARecv接收数据
如果返回-1并且WSAGetLastError = ERROR_IO_PENDING
那么就使用GetQueuedCompletionStatus等待IO完成
另外通过lpCompletionKey结构，可以得知被等待的socket句柄和缓冲区
 
至此我们也可以猜测各种游戏平台的工作原理
hook socket把TCP改成UDP
hook connect返回成功
hook AcceptEx或其他。。
hook send通过sendto把数据传到服务器由服务器转发，要确保数据正确到达
hook WSARecv，貌似可以阻塞也可以不阻塞，总之数据来了就返回，如果不阻塞。那么再
hook GetQueuedCompletionStatus，等待数据到来
大体应该就是这样子，全是猜测，下一步可以构想一下自己实现的对战平台的网络模型
另外我想数据可以是由服务器中转的，也可以是用UDP穿墙技术实现的。
区别就是经过服务器中转。还可以多实现一些功能
比如聊天监控，作弊监控……
]]></description>
			<content:encoded><![CDATA[<p>大部分互通图在作弊的时候都需要输入作弊指令 -xxx<br />
而且为了掩人耳目有些指令具有迷惑作用比如 -ma空格空格空格<br />
但是如果设置聊天对象为ob那么队友也看不到你输入的指令</p>
<p>但事实上这一切尽在服务器眼中<br />
玩家输入的任何游戏指令(-打头)<br />
都会通过26号游戏协议包(F7 26 xx xx)发送到服务器</p>
<p>假如我们做了一个服务器插件<br />
监控玩家使用的游戏指令<br />
就可以达到一定的监测作弊效果</p>
<p>然后配合踢人功能惩罚作弊者&#8230;那么&#8230;</p>
<p> </p>
<p>另外在分析魔兽协议的时候又学习了一下iocp模型<br />
war3为每个连接创建了一个线程接收网络数据包<br />
使用WSARecv接收数据<br />
如果返回-1并且WSAGetLastError = ERROR_IO_PENDING<br />
那么就使用GetQueuedCompletionStatus等待IO完成<br />
另外通过lpCompletionKey结构，可以得知被等待的socket句柄和缓冲区</p>
<p> </p>
<p>至此我们也可以猜测各种游戏平台的工作原理<br />
hook socket把TCP改成UDP<br />
hook connect返回成功<br />
hook AcceptEx或其他。。<br />
hook send通过sendto把数据传到服务器由服务器转发，要确保数据正确到达<br />
hook WSARecv，貌似可以阻塞也可以不阻塞，总之数据来了就返回，如果不阻塞。那么再<br />
hook GetQueuedCompletionStatus，等待数据到来<br />
大体应该就是这样子，全是猜测，下一步可以构想一下自己实现的对战平台的网络模型</p>
<p>另外我想数据可以是由服务器中转的，也可以是用UDP穿墙技术实现的。<br />
区别就是经过服务器中转。还可以多实现一些功能<br />
比如聊天监控，作弊监控……</p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/war3_1_20e_anti_cheat.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>ping monitor 监测ping的小工具</title>
		<link>http://www.deadc0de.com/archives/ping-monitor.html</link>
		<comments>http://www.deadc0de.com/archives/ping-monitor.html#comments</comments>
		<pubDate>Sat, 24 Oct 2009 06:03:38 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[Win32编程]]></category>

		<category><![CDATA[ping]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=74</guid>
		<description><![CDATA[点此下载(3.6KB)
PingMonitor是一个专门监测网络线路ping值的小工具
你完全可以在命令行中使用 ping ip -t 代替它
但这个小工具使用起来将更加便捷直观。
这个工具可以同时监控2个ip
在Options.ini里.你可以设置电信、网通、铁通或局域网等任何IP地址
还可以个性化设定界面的颜色。
 
如果你的线路是ADSL，而且是你是个在线游戏爱好者。
我相信这个工具将会对你很有用。

]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.deadc0de.com/wp-content/uploads/2009/10/pingmonitor.rar">点此下载(3.6KB)</a></p>
<p>PingMonitor是一个专门监测网络线路ping值的小工具</p>
<p>你完全可以在命令行中使用 ping ip -t 代替它</p>
<p>但这个小工具使用起来将更加便捷直观。</p>
<p>这个工具可以同时监控2个ip</p>
<p>在Options.ini里.你可以设置电信、网通、铁通或局域网等任何IP地址</p>
<p>还可以个性化设定界面的颜色。</p>
<p> </p>
<p>如果你的线路是ADSL，而且是你是个在线游戏爱好者。</p>
<p>我相信这个工具将会对你很有用。</p>
<p><img class="alignnone" src="http://www.deadc0de.com/wp-content/uploads/2009/10/pingmonitor.png" alt="" width="507" height="272" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/ping-monitor.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>单步跟踪引擎.太慢</title>
		<link>http://www.deadc0de.com/archives/step-trace-bullsh1t.html</link>
		<comments>http://www.deadc0de.com/archives/step-trace-bullsh1t.html#comments</comments>
		<pubDate>Wed, 21 Oct 2009 12:49:13 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[Win32编程]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=68</guid>
		<description><![CDATA[原本想做个注入dll式的单步跟踪软件，记录某个线程的执行记录
现在看来这想法太离谱了，现在引擎做出来了，发现跟踪30000行指令需要10秒，加上记录也是10秒左右
想想我准备调试的那些可怕的程序，运行起来的话。。这。。。太扯了 :&#60;
还是改做调试器吧 :&#62;
 
00401343: push 00402169h
00401348: push 00402162h
0040134D: call 004012C5h
004012C5: push ebp
004012C6: mov ebp , esp
004012C8: add esp , F4h
004012CB: lea eax , dword ptr [ebp-04h]
004012CE: push eax
004012CF: push 04h
004012D1: push 00h
004012D3: push dword ptr [ebp+08h]
004012D6: push 80000002h
004012DB: call RegOpenKeyExA (advapi32.dll)
004013B4: jmp RegOpenKeyExA (advapi32.dll)
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;
004012E0: or eax , eax
004012E2: [...]]]></description>
			<content:encoded><![CDATA[<p>原本想做个注入dll式的单步跟踪软件，记录某个线程的执行记录<br />
现在看来这想法太离谱了，现在引擎做出来了，发现跟踪30000行指令需要10秒，加上记录也是10秒左右<br />
想想我准备调试的那些可怕的程序，运行起来的话。。这。。。太扯了 :&lt;</p>
<p>还是改做调试器吧 :&gt;</p>
<p> </p>
<p>00401343: push 00402169h<br />
00401348: push 00402162h<br />
0040134D: call 004012C5h<br />
004012C5: push ebp<br />
004012C6: mov ebp , esp<br />
004012C8: add esp , F4h<br />
004012CB: lea eax , dword ptr [ebp-04h]<br />
004012CE: push eax<br />
004012CF: push 04h<br />
004012D1: push 00h<br />
004012D3: push dword ptr [ebp+08h]<br />
004012D6: push 80000002h<br />
004012DB: call RegOpenKeyExA (advapi32.dll)<br />
004013B4: jmp RegOpenKeyExA (advapi32.dll)<br />
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;<br />
004012E0: or eax , eax<br />
004012E2: jne 00401322h<br />
004012E4: push dword ptr [ebp+0Ch]<br />
004012E7: push dword ptr [ebp-04h]<br />
004012EA: call RegDeleteKeyA (advapi32.dll)<br />
004013A8: jmp RegDeleteKeyA (advapi32.dll)<br />
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;<br />
004012EF: lea eax , dword ptr [ebp-0Ch]<br />
004012F2: push eax<br />
004012F3: lea eax , dword ptr [ebp-08h]<br />
004012F6: push eax<br />
004012F7: push 00h<br />
004012F9: push 00h<br />
004012FB: push 01h<br />
004012FD: push 00h<br />
004012FF: push 00h<br />
00401301: push dword ptr [ebp+0Ch]<br />
00401304: push dword ptr [ebp-04h]<br />
00401307: call RegCreateKeyExA (advapi32.dll)<br />
004013A2: jmp RegCreateKeyExA (advapi32.dll)<br />
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;<br />
0040130C: push dword ptr [ebp-04h]<br />
0040130F: call RegCloseKey (advapi32.dll)<br />
0040139C: jmp RegCloseKey (advapi32.dll)<br />
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;<br />
00401314: cmp dword ptr [ebp-0Ch] , 02h<br />
00401318: je 004012E4h<br />
0040131A: push dword ptr [ebp-08h]<br />
0040131D: call RegCloseKey (advapi32.dll)<br />
0040139C: jmp RegCloseKey (advapi32.dll)<br />
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;<br />
00401322: leave<br />
00401323: retn 0008h<br />
00401352: push 40h<br />
00401354: push 00402184h<br />
00401359: push 00402171h<br />
0040135E: push 00h<br />
00401360: call MessageBoxA (user32.dll)<br />
00401378: jmp MessageBoxA (user32.dll)<br />
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;<br />
00401365: push 00h<br />
00401367: call ExitProcess (kernel32.dll)<br />
0040137E: jmp ExitProcess (kernel32.dll)<br />
&#8230;&#8230;..: Out of watch limit, wait for ret &#8230;&#8230;&#8230;&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/step-trace-bullsh1t.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Dr.Com客户端的替代 - Free Dr.COM win32/linux</title>
		<link>http://www.deadc0de.com/archives/free-drcom.html</link>
		<comments>http://www.deadc0de.com/archives/free-drcom.html#comments</comments>
		<pubDate>Mon, 19 Oct 2009 12:55:09 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[Win32编程]]></category>

		<category><![CDATA[drcom]]></category>

		<category><![CDATA[free drcom]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=64</guid>
		<description><![CDATA[点此下载&#8230;
 
Dr.COM是北京城市热点开发的一款基于局域网的宽带接入服务器，广泛应用于高校校园网、小区宽带。
邮电学院南校区使用的就是Dr.COM服务器做宽带接入，使用了专用的客户端进行直通方式认证。
该客户端的功能很强大，但我们学校使用的客户端比较简单。主要做了如下2条限制。
 
绑定ARP缓冲中所有IP为非法MAC，阻塞局域网内TCPIP通讯，防止做共享网络。同时也无法玩局域网游戏(TCP/IP)
检测常见的共享网络的软件，并加以提示，停止激活Dr.COM网关。拒绝服务。
 
我们只需模拟Dr.COM客户端激活Dr.COM网关的方法。即可抛弃官方客户端及如上限制。
 
使用方法
拨号宽带连接，运行本软件。提示已经激活即可上网。
最小化后隐藏窗口，再次运行本软件可以调出窗口。
本软件没有设置自动运行，可以在程序-&#62;启动中创建快捷方式。
 
常见问题
1.如果提示无法绑定 UDP 61440 端口，请检查是否已经卸载原来的客户端。(官方客户端为ishare_user.exe进程)
2.如果提示接收数据2超时，请检查程序的连接是否被 Windows阻止。必要时可以关闭 Windows 的防火墙。
   XP环境下服务名为 Windows Firewall/Internet Connection Sharing (ICS)
   Vista/Win7环境下服务名为 Windows Firewall
  禁用这个服务即可。
]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.deadc0de.com/wp-content/uploads/2009/10/freedrcom.rar">点此下载&#8230;</a></p>
<p> </p>
<p>Dr.COM是北京城市热点开发的一款基于局域网的宽带接入服务器，广泛应用于高校校园网、小区宽带。</p>
<p>邮电学院南校区使用的就是Dr.COM服务器做宽带接入，使用了专用的客户端进行直通方式认证。</p>
<p>该客户端的功能很强大，但我们学校使用的客户端比较简单。主要做了如下2条限制。</p>
<p> </p>
<p>绑定ARP缓冲中所有IP为非法MAC，阻塞局域网内TCPIP通讯，防止做共享网络。同时也无法玩局域网游戏(TCP/IP)</p>
<p>检测常见的共享网络的软件，并加以提示，停止激活Dr.COM网关。拒绝服务。</p>
<p> </p>
<p>我们只需模拟Dr.COM客户端激活Dr.COM网关的方法。即可抛弃官方客户端及如上限制。</p>
<p> </p>
<p>使用方法</p>
<p>拨号宽带连接，运行本软件。提示已经激活即可上网。</p>
<p>最小化后隐藏窗口，再次运行本软件可以调出窗口。</p>
<p>本软件没有设置自动运行，可以在程序-&gt;启动中创建快捷方式。</p>
<p> </p>
<p>常见问题</p>
<p>1.如果提示无法绑定 UDP 61440 端口，请检查是否已经卸载原来的客户端。(官方客户端为ishare_user.exe进程)</p>
<p>2.如果提示接收数据2超时，请检查程序的连接是否被 Windows阻止。必要时可以关闭 Windows 的防火墙。</p>
<p>   XP环境下服务名为 Windows Firewall/Internet Connection Sharing (ICS)<br />
   Vista/Win7环境下服务名为 Windows Firewall</p>
<p>  禁用这个服务即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/free-drcom.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>NBP开发手记</title>
		<link>http://www.deadc0de.com/archives/nbpdevelopment.html</link>
		<comments>http://www.deadc0de.com/archives/nbpdevelopment.html#comments</comments>
		<pubDate>Tue, 18 Aug 2009 03:22:20 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[Win32编程]]></category>

		<category><![CDATA[NBP]]></category>

		<category><![CDATA[PXE]]></category>

		<category><![CDATA[无盘]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=58</guid>
		<description><![CDATA[以前对PXE只有一点点了解，最近在研究PXE无盘启动XP，所以学习了PXE的spec文档，对PXE有了初步的认识，同时又在调试时遇到了很多的很挫的问题。现在把它们记录下来。

所谓NBP是来自PXE文档的说法，全称 Network Bootstrap Program，也就是我以前理解的 PXE Boot File。他的作用就是 Hook Int 13h 虚拟一个硬盘，对硬盘的读写，通过 PXE API(通常使用UDP)定向到网络，返回数据给调用者，达到通过网络启动的目的。

 

一、NBP工作流程
]]></description>
			<content:encoded><![CDATA[<p>以前对PXE只有一点点了解，最近在研究PXE无盘启动XP，所以学习了PXE的spec文档，对PXE有了初步的认识，同时又在调试时遇到了很多的很挫的问题。现在把它们记录下来。</p>
<p>所谓NBP是来自PXE文档的说法，全称 Network Bootstrap Program，也就是我以前理解的 PXE Boot File。他的作用就是 Hook Int 13h 虚拟一个硬盘，对硬盘的读写，通过 PXE API(通常使用UDP)定向到网络，返回数据给调用者，达到通过网络启动的目的。</p>
<p> </p>
<p>一、NBP工作流程</p>
<p>1.Int 1Ah 获取PXE接口，这没什么说的。分2.0和2.1或以上版本2种区别，一般只需要兼容2.1就行。<br />
2.PXENV_GET_CACHED_INFO 的3号功能，获取自己的IP和服务器IP。<br />
3.PXENV_UDP_OPEN 初始化 UDP 网络接口。<br />
4.Hook Int13h,I/O PXENV_UDP_READ/PXENV_UDP_WRITE<br />
5.读 MBR 到 0000:7C00。<br />
6.跳转 0000:7C00。</p>
<p>这些流程除了第四步Hook Int13h都不难，只是UDP部分需要注意处理丢包的问题。C/S用会话ID，很好解决。<br />
另外那个UDP接口，收到大于4K的包，容易卡死在UDP_RECV里很久才返回一个错误。所以一次收发包最好小于4KB……<br />
二、Hook Int13h</p>
<p>对于无盘启动XP，需要处理如下的功能号。并尽量按照标准来处理。。</p>
<p>FUN00: IO复位<br />
FUN02: 读扇区<br />
FUN03: 写扇区<br />
FUN08: 读磁盘参数<br />
FUN15: 读磁盘类型<br />
FUN41: 检验扩展功能是否存在<br />
FUN42: 扩展读<br />
FUN43: 扩展写<br />
FUN48: 扩展读磁盘参数</p>
<p>貌似对于无盘启动XP，处理写操作好像并不是必要的。一般情况XP在内核启动前不需要写操作。内核启动后加载磁盘驱动，然后才会有写操作，那时候已经不需要Int13h了。不过NTLDR是设计了写操作接口的，说不准，没准啥时候来个FUN03/43，所以最好还是处理了吧。</p>
<p>而且我在做NBP之前还在想，如果进入保护模式后，还使用Int13h怎么办？因为PXE文档中对保护模式中使用PXE API的说明很模糊。SDK中也没有例子，做起来会很烦，可是真正做的时候发现，在进入保护模式后，根本不需要Int13h了，系统会使用磁盘驱动来完成IO。</p>
<p>而且我发现XP在启动的时候并没有使用扩展Int13h功能，不过调试发现Win7的 Boot Sector/Mgr 会使用Int13x。</p>
<p> <br />
三、万恶的 CHS 寻址</p>
<p>（以下段落引用互联网。。。有更正）<br />
&#8212;&#8211; Quote Start&#8212;&#8211;<br />
到目前为止，人们常说的硬盘参数还是古老的 CHS (Cylinder/Head/Sector)参数。那么为什么要使用这些参数，它们的意义是什么？它们的取值范围是什么？<br />
很久以前(long long ago &#8230;)，硬盘的容量还非常小的时候，人们采用与软盘类似的结构生产硬盘。也就是硬盘盘片的每一条磁道都具有相同的扇区数。由此产生了所谓的3D参数 (Disk Geometry)。即磁头数(Heads)，柱面数(Cylinders)，扇区数(Sectors)，以及相应的寻址方式。</p>
<p>其中:<br />
磁头数(Heads) 表示硬盘总共有几个磁头，也就是有几面盘片，最大为 255 (用 8 个二进制位存储)；<br />
柱面数(Cylinders) 表示硬盘每一面盘片上有几条磁道，最大为 1023(用 10 个二进制位存储)；<br />
扇区数(Sectors) 表示每一条磁道上有几个扇区， 最大为 63 (用 6个二进制位存储)。<br />
每个扇区一般是 512个字节，理论上讲这不是必须的，但好象没有取别的值的。</p>
<p>所以磁盘最大容量为：<br />
256 * 1024 * 63 * 512 / 1048576 = 8064 MB ( 1M = 1048576 Bytes ) 或硬盘厂商常用的单位：<br />
256 * 1024 * 63 * 512 / 1000000 = 8263 MB ( 1M = 1000000 Bytes )</p>
<p>在 CHS 寻址方式中，磁头，柱面，扇区的取值范围分别为 0到 Heads - 1， 0到 Cylinders - 1，1到 Sectors (注意是从1开始)。<br />
&#8212;&#8211; Quote End&#8212;&#8211;</p>
<p>上述段落阐述了什么是 CHS 寻址，按照上文的理解Heads是磁头数单位应该是最大的，但是其实不是酱紫的~<br />
按照寻址高低位排序，Cylinder &gt; Head &gt; Sector。</p>
<p>而且 H 不是表示硬盘总共有几个磁头，是最大磁头号。C 不是有几个柱面，是最大柱面号。要注意！</p>
<p> </p>
<p>要把 CHS 转换成绝对地址，可以使用下面的公式<br />
BlockAddr = ( Cylinder * NumberOfHeads + Head ) * SectorsPerTrack + Sector - 1;</p>
<p>NumberOfHeads 是磁盘的磁头数，假如最大磁头号码为255，那么NumberOfHeads = 256<br />
SectorsPerTrack 是磁盘每磁道的扇区数 值为 63。</p>
<p>而且注意最后有一个-1。因为Sector取值范围是1-63而不是0-63。<br />
Int13h 8号功能会返回 MAX HEAD，也就是最大磁头号。不要和上面公式的 NumberOfHeads 混淆。</p>
<p> <br />
四、万恶的 BootSector 程序</p>
<p>照理来说 XP 的 BootSector 应该使用Int13h的8号功能首先获取磁盘最大磁头号。<br />
而他却使用了 BPB(BIOS Parameter Block) 结构中定义 HeadsPerCylinder。。。<br />
注意这个 HeadsPerCylinder 不是最大磁头号而是磁头数，同上面公式中的 NumberOfHeads。<br />
一般情况这里的值是FF，那么最大磁头号应该是 254！！</p>
<p>;======================================================<br />
; BPB( BIOS Parameter Block )<br />
;======================================================<br />
BytesPerSector  DW ? ; 每个扇区的字节数 (512 1024 2048 4096)<br />
SectorsPerCluster DB ? ; 每个簇的扇区数 ( 1 2 4 8 16 32 64 128 )<br />
    ; 两者相乘不能超过32K(簇最大大小)<br />
ReservedSectors  DW ? ; 从卷的第一个扇区开始的保留扇区数目<br />
    ; 该值不能为0，对于FAT12/FAT16，该值通常为1<br />
    ; 对于FAT32，典型值为32<br />
NumberOfFATs  DB ? ; 卷上FAT数据结构的数目，该值通常应为2<br />
RootEntries  DW ? ; 对于FAT12/FAT16,该值表示32字节目录项的数目<br />
    ; 对于FAT32，该值必须为0<br />
NumberOfSectors16 DW ? ; 该卷上的扇区总数，该字段可以为0，如果该字段<br />
    ; 为0，则NumberOfSectors32不能为0；对于FAT32<br />
    ; 该字段必须为0<br />
MediaDescriptor  DB ? ; 介质类型<br />
SectorsPerFAT16  DW ? ; 该字段标识一个FAT结构占有的扇区数（FAT12/FAT16）<br />
    ; 对于FAT32卷，该字段必须为0<br />
SectorsPerTrack  DW ? ; 用于INT 0&#215;13中断的每个磁道的扇区数<br />
HeadsPerCylinder DW ? ; 用于INT 0&#215;13中断的每个柱面的磁头数<br />
HiddenSectors  DD ? ; 包含该FAT卷的分区之前的隐藏扇区数<br />
NumberOfSectors32 DD ? ; 该字段包含该卷上的所有扇区数目，对于FAT32，该字段<br />
    ; 不为0；FAT12/FAT16可根据实际大小是否超过65536个扇<br />
    ; 区数决定是否采用该字段</p>
<p> </p>
<p>To Be Continued..</p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/nbpdevelopment.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>暑假搞scsi虚拟盘</title>
		<link>http://www.deadc0de.com/archives/vscsidisk.html</link>
		<comments>http://www.deadc0de.com/archives/vscsidisk.html#comments</comments>
		<pubDate>Sat, 25 Jul 2009 03:07:37 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[Win32编程]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=54</guid>
		<description><![CDATA[通过分析学习miniram和minifile的代码，实现了不需要定时器(TimerCall)的scsi驱动程序，效率很高，但当块大小为2K甚至更低的时候。效率依然赶不上DaemonTools。DT的scsi驱动很小，主要的处理都在他的总线驱动里，没有使用ZwReadFile或者ZwWriteFile而直接向文件系统?发送irp读数据。抛开缓存机制不说，也就是说我用纯内存当虚拟盘，1K块大小读取速度只有11MB/s，而DT可以达到50MB/s以上，真是变态啊，都是发irp，差距咋就那么大呢。可恶的ms scsiport啊。。
结合tdi又做出了网络虚拟盘。但无法应用于无盘网络系统，tdi在系统启动时无法使用，只能使用ndis，这样我还需要自己实现一个tcp协议……
]]></description>
			<content:encoded><![CDATA[<p>通过分析学习miniram和minifile的代码，实现了不需要定时器(TimerCall)的scsi驱动程序，效率很高，但当块大小为2K甚至更低的时候。效率依然赶不上DaemonTools。DT的scsi驱动很小，主要的处理都在他的总线驱动里，没有使用ZwReadFile或者ZwWriteFile而直接向文件系统?发送irp读数据。抛开缓存机制不说，也就是说我用纯内存当虚拟盘，1K块大小读取速度只有11MB/s，而DT可以达到50MB/s以上，真是变态啊，都是发irp，差距咋就那么大呢。可恶的ms scsiport啊。。</p>
<p>结合tdi又做出了网络虚拟盘。但无法应用于无盘网络系统，tdi在系统启动时无法使用，只能使用ndis，这样我还需要自己实现一个tcp协议……</p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/vscsidisk.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>也谈向Richedit插入动态Gif的实现</title>
		<link>http://www.deadc0de.com/archives/insert-gif-to-richedit.html</link>
		<comments>http://www.deadc0de.com/archives/insert-gif-to-richedit.html#comments</comments>
		<pubDate>Tue, 26 May 2009 10:12:29 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[Win32编程]]></category>

		<category><![CDATA[DynamicGif]]></category>

		<category><![CDATA[richedit插入gif]]></category>

		<category><![CDATA[richedit插入图片]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=14</guid>
		<description><![CDATA[最近在做IM软件，需要实现向Richedit插入表情，表情是动态的GIF图像。
由于以前没有做过关于richedit的开发，百度了下，需要使用OLE技术。也就是说，插入的图片都是一个OLE对象。而RICHEDIT则是一个OLE容器，相关链接如下。
How to insert a bitmap into an RTF document using the RichEdit control in Visual C++ 6.0
http://support.microsoft.com/default.aspx?scid=kb;en-us;220844
Animated Emoticons like those in MSN Messenger(英文版)
http://www.codeproject.com/KB/edit/AnimatedEmoticon.aspx
Animated Emoticons like those in MSN Messenger(中文版)
http://blog.csdn.net/dTianx/archive/2004/11/17/184949.aspx
DynamicGif作者blog
http://blog.csdn.net/kql01
 
第一个链接是微软提供的部分代码，只能用于插入BMP图片。使用了OleCreateFromFile创建的ole对象。
第二个、第三个链接都是dTianx写的，使用QQ的ImageOle.dll创建了ole对象。提供全部源代码。
第四个链接是一牛自己做的一个控件，导出函数InsertGifToRichedit2A可以方便地插入任意GIF。只提供DLL文件，而且只有1.21版本的，据说作者开发了1.4版本但他没有开放下载。
我对这4个方法逐一试验，结果发现这些方案都不可靠。下面我将详细的介绍，我假设各位读者从前没接触过RICHEDIT嵌入OLE这方面内容，2个星期前我也一无所知……
首先第一个不用说，只能插入BMP图片，不符合我们的要求，但这段代码已经可以让我们了解了插入一个OLE对象的全部步骤。
第三个DynamicGif使用导出函数InsertGifToRichedit2A，方便地插入了任意Gif图片，但是我发现他放出的这个1.21版本有问题，这个问题发生在拖动滚动条的时候，richedit会很不正常……
放弃使用了DynamicGif后我把所有的赌注加在了ImageOle上，可是可是待我把一切实现在汇编上的时候。我发现噩梦到来了……(-__-！夸张一小下)。
首先。上面的例子创建GifAnimator实例，获取IGifAnimator接口，IGifAnimator::LoadFromFile载入图片，然后调用神奇的IGifAnimator::TriggerFrameChange函数，调用IRichEditOle::InsertObject插入ole对象。顺理成章，但是这个例子有一个很明显的bug就是在显示透明gif的时候没有擦除背景。如下图所示。
 
另外当我实现在汇编上的时候我发现刷新有闪动问题，可能是InvalidateRect的第三个参数bErase被设置了TRUE，可是我调试发现ImageOle在调用这个api的时候bErase为False。郁闷地研究了好几天，终于发现这一切的一切居然是一个扩展风格导致的，WS_EX_TRANSPARENT，透明风格，而dTianx和qq的richedit的确有这个扩展风格。-_-!!! 当我给richedit加入这个风格后，刷新的确不闪了，但richedit背景与窗体相同，这就需要子类化richedit响应WM_ERASEBKGND消息，手工画背景为白色。
解决了这个后又开始着手解决显示透明gif的问题。这个问题应该源于GDI+的GdipImageDrawRectI函数，它只绘制了不透明的部分到DC上，我不知道qq是怎么解决的，我觉得应该有一个函数可以关掉GID+的这个特性:只绘制不透明部分到DC。但对GDI+非常不了解，找了好久没有找到。我想出来的解决方法是hook这段代码，加入一个创建白色刷子刷白色背景，然后再调用GdipImageDrawRectI绘制gif帧。这样每次绘制GIF帧背景都会被刷成白色。而且的确实现了~
在实验过程中，我还发现如果使用类名称为RICHEDIT20A版本的richedit会导致内存泄露问题，就是删除了已经插入的图片对象内存依然不释放，使用类名称为RICHEDIT版本的richedit没有此问题。而qq使用的是RICHEDIT20A，我复制了qq目录的riched20.dll仍然存在这个问题，我不知道qq有没有这个问题，以及他是怎么怎么解决的。
然后当我把代码转移到我的IM工程上我发现了更严重的问题。嵌入过多的ole会导致程序崩溃，崩溃点是riched20.dll，查看调用堆栈发现是其内部造成的，我无法查明原因。这个错误so奇怪，在实验工程上无论插入多少个图片都不会崩溃，一转移到IM工程上就有问题，我屏蔽了很多很多代码，最终发觉可能跟线程有关。在WM_INITDIALOG中插入多少个都没问题，在另外的线程插入就有问题，我想，把插入ole的代码放在窗口过程里。用SendMessage触发来添加，结果还是不行，很奇怪很奇怪。。。
还有另外一个很令我崩溃的问题，第一次创建richedit，插入gif图片可以，但当你销毁这个带有richedit的窗口，然后再创建，再次插入gif图片，图片不会动了（打开聊天窗口，插入gif，正常；关闭聊天窗口，打开聊天窗口，插入gif，gif不会动）。又另我百思不得其解。为此我调试了ImageOle，晓得了ImageOle的原理。
ImageOle读取了一个图片后，依赖接口IViewObject::OnDraw实现刷新ole区域显示gif的不同帧。而这个OnDraw是需要InvalidateRect触发的。这就需要一个定时器来定时调用InvalidateRect实现刷新，这个定时器是由ImageOle内部创建的，是在CreateInstance的时候。而且他有个判断，如果已经创建过（句柄不为0），就不需要再创建了。当我销毁我的聊天窗口的时候，这个定时器居然神奇般地不见了……所以再次插入gif就不能自主动态刷新gif了。dTianx的mfc工程，他并未销毁窗口，用spy++可以看到当关闭窗口的时候，窗口只是变为了invisable……所以没有这个bug。
这种种的问题逼迫我去寻找其他的替代DLL。
我发现浩方平台也有个ImageOle，接口名换了，但也有LoadFromFile,TriggerFrameChange，和QQ的ImageOle惊人的相似，好神奇~两者难道有某种关联？ImageOle的开发者究竟是谁呢？不管这些，我实验调用浩方的ImageOle.dll也不成。然后又寻找了飞信、百度Hi……
找到了很多。都用不明白。
这迫使我选择另一条崎岖的路。
另一条最有可能成功的路。。
自己造个ActiveX实现OLE对象嵌入。。
需要编程实现n个COM接口
IDispatch
IOleObject
IOleInPlaceObject
IOleInPlaceActiveObject
IOleControl
IDataObject
IProvideClassInfo
IPersistStorage
IPersistStreamInit
IPersistPropertyBag
IViewObject2
ISpecifyPropertyPages
ICategorizeProperties
IConnectionPointContainer
IRunnableObject
接口太多了，工程量巨大，我肯定自己搞不定的，不过好在印象里masm32包里有个asmctrl的工程，是一个activex控件，可以被vb调用，肯定已经实现了这些东西，找来masm32 v9果然有。尝试把他插入richedit里，发现插入1个以上ole的绘制就有问题。看readme可以知道这个工程的作者。
他就是japheth，一个德国的汇编超人……
作品有牛x闪闪的COMView和牛x闪闪闪的masm兼容编译器JWasm。otz
他对COM的研究可谓是非常的透彻了，不然怎么可能敢用汇编写ActiveX~~
超人的网站 http://www.japheth.de
网站的 COM &#38; Assembly 栏目里有一个ASMCtrl工程。版本是2.5.5，v9里的可能是1.0版本，看历史记录可知后续版本修正了bugs。而且2.0版本后使用了大量的宏，因为这些宏，我读他的代码变得非常非常难。。看了超人japheth的代码我才知道汇编原来是这么这么玩的，太牛x的……太令人无语了。看不懂，给超人写信要来了1.3的版本，1.3代码比2.x好理解，只用了少量的宏，不过编译后插入richedit依然存在bug。无奈只有使用2.5.5版本当模板做我的PicOlePlus了。
然后结合我对调试ImageOle得出的经验和对GDIPlus的学习。很费力地修改了ASMCtrl工程，得到了伟大的PicOlePlus.dll这个东东
下面是我做的一些主要更改。
1.CAsmClass.inc里面添加一些类私有变量，还需要在CAsmClass的MEMBER里申明
  hGdiplus dd ?
  pImage dd ?
  dwWidth dd ?
  dwHeight dd ?
  dwFrameCount dd ?
  pPropertyItem dd ? ;id len type pvalue[]
  dwFrame0Tick dd ?
2.在CAsmClass::Create里面初始化GDI+，CAsmClass::Destroy里面释放图像，释放GDI+。代码略
3.修改IAsmClass的接口，添加了一个LoadFromFile方法，调用LoadImageFile函数
LoadImageFile Proc lpszFile
  Local wszFile[260]:WORD
  
  invoke MultiByteToWideChar,CP_OEMCP,MB_PRECOMPOSED,lpszFile,-1,addr wszFile,255
  invoke GdipLoadImageFromFile,addr wszFile,addr [...]]]></description>
			<content:encoded><![CDATA[<p>最近在做IM软件，需要实现向Richedit插入表情，表情是动态的GIF图像。</p>
<p>由于以前没有做过关于richedit的开发，百度了下，需要使用OLE技术。也就是说，插入的图片都是一个OLE对象。而RICHEDIT则是一个OLE容器，相关链接如下。</p>
<p>How to insert a bitmap into an RTF document using the RichEdit control in Visual C++ 6.0<br />
<a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;220844">http://support.microsoft.com/default.aspx?scid=kb;en-us;220844</a></p>
<p>Animated Emoticons like those in MSN Messenger(英文版)<br />
<a href="http://www.codeproject.com/KB/edit/AnimatedEmoticon.aspx">http://www.codeproject.com/KB/edit/AnimatedEmoticon.aspx</a></p>
<p>Animated Emoticons like those in MSN Messenger(中文版)<br />
<a href="http://blog.csdn.net/dTianx/archive/2004/11/17/184949.aspx">http://blog.csdn.net/dTianx/archive/2004/11/17/184949.aspx</a></p>
<p>DynamicGif作者blog<br />
<a href="http://blog.csdn.net/kql01">http://blog.csdn.net/kql01</a></p>
<p><span id="more-14"></span> </p>
<p>第一个链接是微软提供的部分代码，只能用于插入BMP图片。使用了OleCreateFromFile创建的ole对象。</p>
<p>第二个、第三个链接都是dTianx写的，使用QQ的ImageOle.dll创建了ole对象。提供全部源代码。</p>
<p>第四个链接是一牛自己做的一个控件，导出函数InsertGifToRichedit2A可以方便地插入任意GIF。只提供DLL文件，而且只有1.21版本的，据说作者开发了1.4版本但他没有开放下载。<br />
我对这4个方法逐一试验，结果发现这些方案都不可靠。下面我将详细的介绍，我假设各位读者从前没接触过RICHEDIT嵌入OLE这方面内容，2个星期前我也一无所知……</p>
<p>首先第一个不用说，只能插入BMP图片，不符合我们的要求，但这段代码已经可以让我们了解了插入一个OLE对象的全部步骤。</p>
<p>第三个DynamicGif使用导出函数InsertGifToRichedit2A，方便地插入了任意Gif图片，但是我发现他放出的这个1.21版本有问题，这个问题发生在拖动滚动条的时候，richedit会很不正常……</p>
<p>放弃使用了DynamicGif后我把所有的赌注加在了ImageOle上，可是可是待我把一切实现在汇编上的时候。我发现噩梦到来了……(-__-！夸张一小下)。</p>
<p>首先。上面的例子创建GifAnimator实例，获取IGifAnimator接口，IGifAnimator::LoadFromFile载入图片，然后调用神奇的IGifAnimator::TriggerFrameChange函数，调用IRichEditOle::InsertObject插入ole对象。顺理成章，但是这个例子有一个很明显的bug就是在显示透明gif的时候没有擦除背景。如下图所示。</p>
<p> <img class="alignnone size-full wp-image-15" title="e6b581e6b197" src="http://www.deadc0de.com/wp-content/uploads/2009/05/e6b581e6b197.jpg" alt="e6b581e6b197" width="338" height="303" /></p>
<p>另外当我实现在汇编上的时候我发现刷新有闪动问题，可能是InvalidateRect的第三个参数bErase被设置了TRUE，可是我调试发现ImageOle在调用这个api的时候bErase为False。郁闷地研究了好几天，终于发现这一切的一切居然是一个扩展风格导致的，WS_EX_TRANSPARENT，透明风格，而dTianx和qq的richedit的确有这个扩展风格。-_-!!! 当我给richedit加入这个风格后，刷新的确不闪了，但richedit背景与窗体相同，这就需要子类化richedit响应WM_ERASEBKGND消息，手工画背景为白色。</p>
<p>解决了这个后又开始着手解决显示透明gif的问题。这个问题应该源于GDI+的GdipImageDrawRectI函数，它只绘制了不透明的部分到DC上，我不知道qq是怎么解决的，我觉得应该有一个函数可以关掉GID+的这个特性:只绘制不透明部分到DC。但对GDI+非常不了解，找了好久没有找到。我想出来的解决方法是hook这段代码，加入一个创建白色刷子刷白色背景，然后再调用GdipImageDrawRectI绘制gif帧。这样每次绘制GIF帧背景都会被刷成白色。而且的确实现了~</p>
<p>在实验过程中，我还发现如果使用类名称为RICHEDIT20A版本的richedit会导致内存泄露问题，就是删除了已经插入的图片对象内存依然不释放，使用类名称为RICHEDIT版本的richedit没有此问题。而qq使用的是RICHEDIT20A，我复制了qq目录的riched20.dll仍然存在这个问题，我不知道qq有没有这个问题，以及他是怎么怎么解决的。</p>
<p>然后当我把代码转移到我的IM工程上我发现了更严重的问题。嵌入过多的ole会导致程序崩溃，崩溃点是riched20.dll，查看调用堆栈发现是其内部造成的，我无法查明原因。这个错误so奇怪，在实验工程上无论插入多少个图片都不会崩溃，一转移到IM工程上就有问题，我屏蔽了很多很多代码，最终发觉可能跟线程有关。在WM_INITDIALOG中插入多少个都没问题，在另外的线程插入就有问题，我想，把插入ole的代码放在窗口过程里。用SendMessage触发来添加，结果还是不行，很奇怪很奇怪。。。</p>
<p>还有另外一个很令我崩溃的问题，第一次创建richedit，插入gif图片可以，但当你销毁这个带有richedit的窗口，然后再创建，再次插入gif图片，图片不会动了（打开聊天窗口，插入gif，正常；关闭聊天窗口，打开聊天窗口，插入gif，gif不会动）。又另我百思不得其解。为此我调试了ImageOle，晓得了ImageOle的原理。</p>
<p>ImageOle读取了一个图片后，依赖接口IViewObject::OnDraw实现刷新ole区域显示gif的不同帧。而这个OnDraw是需要InvalidateRect触发的。这就需要一个定时器来定时调用InvalidateRect实现刷新，这个定时器是由ImageOle内部创建的，是在CreateInstance的时候。而且他有个判断，如果已经创建过（句柄不为0），就不需要再创建了。当我销毁我的聊天窗口的时候，这个定时器居然神奇般地不见了……所以再次插入gif就不能自主动态刷新gif了。dTianx的mfc工程，他并未销毁窗口，用spy++可以看到当关闭窗口的时候，窗口只是变为了invisable……所以没有这个bug。</p>
<p>这种种的问题逼迫我去寻找其他的替代DLL。</p>
<p>我发现浩方平台也有个ImageOle，接口名换了，但也有LoadFromFile,TriggerFrameChange，和QQ的ImageOle惊人的相似，好神奇~两者难道有某种关联？ImageOle的开发者究竟是谁呢？不管这些，我实验调用浩方的ImageOle.dll也不成。然后又寻找了飞信、百度Hi……</p>
<p>找到了很多。都用不明白。</p>
<p>这迫使我选择另一条崎岖的路。</p>
<p>另一条最有可能成功的路。。</p>
<p>自己造个ActiveX实现OLE对象嵌入。。</p>
<p>需要编程实现n个COM接口<br />
IDispatch<br />
IOleObject<br />
IOleInPlaceObject<br />
IOleInPlaceActiveObject<br />
IOleControl<br />
IDataObject<br />
IProvideClassInfo<br />
IPersistStorage<br />
IPersistStreamInit<br />
IPersistPropertyBag<br />
IViewObject2<br />
ISpecifyPropertyPages<br />
ICategorizeProperties<br />
IConnectionPointContainer<br />
IRunnableObject</p>
<p>接口太多了，工程量巨大，我肯定自己搞不定的，不过好在印象里masm32包里有个asmctrl的工程，是一个activex控件，可以被vb调用，肯定已经实现了这些东西，找来masm32 v9果然有。尝试把他插入richedit里，发现插入1个以上ole的绘制就有问题。看readme可以知道这个工程的作者。</p>
<p>他就是japheth，一个德国的汇编超人……</p>
<p>作品有牛x闪闪的COMView和牛x闪闪闪的masm兼容编译器JWasm。otz</p>
<p>他对COM的研究可谓是非常的透彻了，不然怎么可能敢用汇编写ActiveX~~</p>
<p>超人的网站 <a href="http://www.japheth.de">http://www.japheth.de</a></p>
<p>网站的 COM &amp; Assembly 栏目里有一个ASMCtrl工程。版本是2.5.5，v9里的可能是1.0版本，看历史记录可知后续版本修正了bugs。而且2.0版本后使用了大量的宏，因为这些宏，我读他的代码变得非常非常难。。看了超人japheth的代码我才知道汇编原来是这么这么玩的，太牛x的……太令人无语了。看不懂，给超人写信要来了1.3的版本，1.3代码比2.x好理解，只用了少量的宏，不过编译后插入richedit依然存在bug。无奈只有使用2.5.5版本当模板做我的PicOlePlus了。</p>
<p>然后结合我对调试ImageOle得出的经验和对GDIPlus的学习。很费力地修改了ASMCtrl工程，得到了伟大的PicOlePlus.dll这个东东</p>
<p>下面是我做的一些主要更改。</p>
<p>1.CAsmClass.inc里面添加一些类私有变量，还需要在CAsmClass的MEMBER里申明</p>
<p>  hGdiplus dd ?<br />
  pImage dd ?<br />
  dwWidth dd ?<br />
  dwHeight dd ?<br />
  dwFrameCount dd ?<br />
  pPropertyItem dd ? ;id len type pvalue[]<br />
  dwFrame0Tick dd ?</p>
<p>2.在CAsmClass::Create里面初始化GDI+，CAsmClass::Destroy里面释放图像，释放GDI+。代码略</p>
<p>3.修改IAsmClass的接口，添加了一个LoadFromFile方法，调用LoadImageFile函数<br />
LoadImageFile Proc lpszFile<br />
  Local wszFile[260]:WORD<br />
  <br />
  invoke MultiByteToWideChar,CP_OEMCP,MB_PRECOMPOSED,lpszFile,-1,addr wszFile,255<br />
  invoke GdipLoadImageFromFile,addr wszFile,addr m_pImage<br />
  .if !eax<br />
   invoke GdipGetImageWidth,m_pImage,addr m_dwWidth<br />
   invoke GdipGetImageHeight,m_pImage,addr m_dwHeight<br />
   invoke SetOleExtent   ;设置Ole尺寸<br />
   invoke SendViewChange@CAsmClass,ebx ;!!! 通知OLE容器OLE大小改变<br />
   invoke IsAnimatedGIF<br />
   xor eax,eax<br />
  .endif<br />
  ret<br />
LoadImageFile EndP</p>
<p>IsAnimatedGIF Proc<br />
  Local dwDimensionCount<br />
  Local pDimensionIDs<br />
  Local nSize<br />
  <br />
  invoke GdipImageGetFrameDimensionsCount,m_pImage,addr dwDimensionCount<br />
  mov eax,dwDimensionCount<br />
  shl eax,4<br />
  invoke LocalAlloc,LPTR,eax<br />
  mov pDimensionIDs,eax<br />
  invoke GdipImageGetFrameDimensionsList,m_pImage,pDimensionIDs,dwDimensionCount<br />
  invoke GdipImageGetFrameCount,m_pImage,pDimensionIDs,addr m_dwFrameCount<br />
  invoke GdipGetPropertyItemSize,m_pImage,PropertyTagFrameDelay,addr nSize<br />
  .if !eax<br />
   invoke LocalAlloc,LPTR,nSize<br />
   mov m_pPropertyItem,eax<br />
   invoke GdipGetPropertyItem,m_pImage,PropertyTagFrameDelay,nSize,m_pPropertyItem<br />
  .endif<br />
  invoke LocalFree,pDimensionIDs<br />
  invoke timeGetTime<br />
  mov m_dwFrame0Tick,eax<br />
  ret<br />
IsAnimatedGIF EndP</p>
<p>SetOleExtent Proc ;计算尺寸<br />
  pushad<br />
  invoke GetDC,0<br />
  mov esi,eax<br />
  invoke GetDeviceCaps, esi, LOGPIXELSX<br />
  push eax<br />
  mov eax,m_dwWidth<br />
  mov m_pixelExtent.cx_, eax<br />
  mov ecx,HIMETRIC_PER_INCH<br />
  mul ecx<br />
  pop ecx<br />
  xor edx,edx<br />
  div ecx<br />
  mov m_himetricExtent.cx_, eax<br />
  <br />
  invoke GetDeviceCaps, esi, LOGPIXELSY<br />
  push eax<br />
  mov eax,m_dwHeight<br />
  mov m_pixelExtent.cy, eax<br />
  mov ecx,HIMETRIC_PER_INCH<br />
  mul ecx<br />
  pop ecx<br />
  xor edx,edx<br />
  div ecx<br />
  mov m_himetricExtent.cy, eax<br />
  invoke DeleteObject,esi<br />
  popad<br />
  ret<br />
SetOleExtent EndP</p>
<p>4.修改IViewObject::OnDraw的代码调用DrawImage</p>
<p>sIID_FrameDimensionTime TEXTEQU &lt;{06aedbd6dH, 03fb5H, 0418aH, {083h,0a6h,07fh,045h,022h,09dh,0c8h,072h}}&gt;<br />
FrameDimensionTime GUID sIID_FrameDimensionTime</p>
<p>DrawImage Proc uses esi edi ebx hDC,X,Y<br />
  Local dwTicks<br />
  Local hGraphics<br />
  Local rt:RECT<br />
  Local hScrDC,hTempDC,hBitmap<br />
  <br />
  .if m_pPropertyItem  ;通过帧延迟数据和已经经过的时间计算当前应该显示的帧<br />
   invoke timeGetTime ;timeGetTime精度是1ms<br />
   sub eax,m_dwFrame0Tick<br />
   xor edx,edx<br />
   mov ecx,10<br />
   div ecx<br />
   mov dwTicks,eax<br />
   <br />
   mov esi,m_pPropertyItem<br />
   mov esi,dword ptr [esi+12]<br />
   xor edi,edi<br />
   .While TRUE<br />
    mov eax,[esi+edi*4]<br />
    .Break .if dwTicks &lt; eax<br />
    sub dwTicks,eax<br />
    inc edi<br />
    .if edi == m_dwFrameCount<br />
     xor edi,edi<br />
    .endif<br />
   .EndW<br />
   <br />
   invoke GdipImageSelectActiveFrame,m_pImage,addr FrameDimensionTime,edi ;选择帧<br />
  .endif<br />
  <br />
  invoke GetDC,0  ;创建缓冲dc<br />
  mov hScrDC,eax<br />
  invoke CreateCompatibleDC,hScrDC<br />
  mov hTempDC,eax<br />
  invoke CreateCompatibleBitmap,hScrDC,m_dwWidth,m_dwHeight<br />
  mov hBitmap,eax<br />
  invoke SelectObject,hTempDC,hBitmap<br />
  invoke DeleteObject,eax<br />
  invoke DeleteDC,hScrDC<br />
  <br />
  invoke SetRect,addr rt,0,0,m_dwWidth,m_dwHeight ;填充背景为白色<br />
  invoke GetStockObject,WHITE_BRUSH<br />
  invoke FillRect,hTempDC,addr rt,eax<br />
  <br />
  invoke GdipCreateFromHDC,hTempDC,addr hGraphics<br />
  invoke GdipDrawImageRectI,hGraphics,m_pImage,0,0,m_dwWidth,m_dwHeight ;画gif帧<br />
  invoke GdipDeleteGraphics,hGraphics<br />
  <br />
  invoke BitBlt,hDC,X,Y,m_dwWidth,m_dwHeight,hTempDC,0,0,SRCCOPY<br />
  invoke DeleteObject,hBitmap<br />
  invoke DeleteDC,hTempDC<br />
  ret<br />
DrawImage EndP<br />
这样，一个支持gif的OLE组件就搞定了，哈哈哈~不过这里没有刷新，刷新我给弄到外面去了。有外面代码控制刷新。</p>
<p>下面是插入GIF的代码</p>
<p>InsertObject Proc uses esi edi ebx hWnd,ID,lpszFile<br />
  Local lpOleInterface<br />
  Local lpLockBytes<br />
  Local lpStorage<br />
  Local lpClientSite<br />
  Local ppv<br />
  Local lpOleObject<br />
  Local lpPicOlePlus<br />
  Local clsid:GUID<br />
  Local reo:REOBJECT<br />
  <br />
  mov lpOleInterface,0<br />
  mov lpLockBytes,0<br />
  mov lpStorage,0<br />
  mov lpClientSite,0<br />
  mov ppv,0<br />
  mov lpOleObject,0<br />
  mov lpPicOlePlus,0<br />
  <br />
  invoke SendDlgItemMessage,hWnd,ID,EM_GETOLEINTERFACE,0,addr lpOleInterface<br />
  cmp lpOleInterface,0<br />
  jz exit<br />
  <br />
  invoke CreateILockBytesOnHGlobal,NULL,TRUE,addr lpLockBytes<br />
  cmp lpLockBytes,0<br />
  jz exit<br />
  <br />
  invoke StgCreateDocfileOnILockBytes,lpLockBytes,STGM_SHARE_EXCLUSIVE or STGM_CREATE or STGM_READWRITE,0,addr lpStorage<br />
  cmp lpStorage,0<br />
  jz exit<br />
  <br />
  mov esi,lpOleInterface<br />
  mov esi,dword ptr [esi]<br />
  assume esi:ptr IRichEditOle<br />
  invoke [esi].GetClientSite,lpOleInterface,addr lpClientSite<br />
  cmp lpClientSite,0<br />
  jz exit<br />
  <br />
  invoke CoInitializeEx,NULL,COINIT_APARTMENTTHREADED<br />
  invoke CoCreateInstance,addr IID_PicOlePlus,0,CLSCTX_INPROC_SERVER or CLSCTX_INPROC_HANDLER or CLSCTX_LOCAL_SERVER,addr IID_IUnknown,addr ppv<br />
  invoke OleRun,ppv<br />
  <br />
  mov ecx,ppv<br />
  mov ecx,[ecx]<br />
  invoke [ecx][IUnknown.QueryInterface],ppv,addr IID_IPicOlePlus,addr lpPicOlePlus<br />
  cmp lpPicOlePlus,0<br />
  jz exit<br />
  <br />
  mov eax,ppv<br />
  mov eax,[eax]<br />
  invoke [eax][IUnknown.Release],ppv<br />
  <br />
  mov edi,lpPicOlePlus<br />
  mov edi,dword ptr [edi]<br />
  assume edi:ptr IPicOlePlus<br />
  <br />
  invoke [edi].LoadFromFile,lpPicOlePlus,lpszFile<br />
  cmp eax,0<br />
  jnz exit<br />
  <br />
  invoke [edi].QueryInterface,lpPicOlePlus,addr IID_IOleObject,addr lpOleObject<br />
  cmp lpOleObject,0<br />
  jz exit<br />
  <br />
  invoke OleSetContainedObject,lpOleObject,TRUE<br />
  .if eax<br />
   ret<br />
  .endif<br />
  <br />
  mov edi,lpOleObject<br />
  mov edi,dword ptr [edi]<br />
  assume edi:ptr IOleObject<br />
  invoke [edi].GetUserClassID,lpOleObject,addr clsid<br />
  <br />
  mov reo.cbStruct,sizeof reo<br />
  mov reo.cp,-1<br />
  invoke RtlMoveMemory,addr reo.clsid,addr clsid,sizeof clsid<br />
  m2m reo.poleobj,lpOleObject<br />
  m2m reo.pstg,lpStorage<br />
  m2m reo.polesite,lpClientSite<br />
  mov reo.sizel.x,0<br />
  mov reo.sizel.y,0<br />
  mov reo.dvaspect,1 ;DVASPECT_CONTENT<br />
  mov reo.dwFlags,2 ;REO_BELOWBASELINE<br />
  mov reo.dwUser,0<br />
  invoke [esi].InsertObject,lpOleInterface,addr reo<br />
  <br />
exit:  .if lpClientSite<br />
   mov eax,lpClientSite<br />
   mov eax,[eax]<br />
   invoke [eax][IUnknown.Release],lpClientSite<br />
  .endif<br />
  <br />
  .if lpOleObject<br />
   mov eax,lpOleObject<br />
   mov eax,[eax]<br />
   invoke [eax][IUnknown.Release],lpOleObject<br />
  .endif<br />
  <br />
  .if lpStorage<br />
   mov eax,lpStorage<br />
   mov eax,[eax]<br />
   invoke [eax][IUnknown.Release],lpStorage<br />
  .endif<br />
  <br />
  .if lpPicOlePlus<br />
   mov eax,lpPicOlePlus<br />
   mov eax,[eax]<br />
   invoke [eax][IUnknown.Release],lpPicOlePlus<br />
  .endif<br />
  <br />
  .if lpLockBytes<br />
   mov eax,lpLockBytes<br />
   mov eax,[eax]<br />
   invoke [eax][IUnknown.Release],lpLockBytes<br />
  .endif<br />
  ret<br />
InsertObject EndP<br />
在窗口过程中注册一个定时器调用RedrawObject来刷新。这个代码和ImageOle的有些不同。</p>
<p>HiMetricX2Pixel Proc dwHiMetricX<br />
  Local hDC<br />
  <br />
  invoke GetDC,0<br />
  mov hDC,eax<br />
  invoke GetDeviceCaps,hDC,LOGPIXELSX<br />
  push eax<br />
  invoke DeleteObject,hDC<br />
  pop eax<br />
  mov ecx,dwHiMetricX<br />
  mul ecx<br />
  xor edx,edx<br />
  mov ecx,2540<br />
  div ecx<br />
  .if edx<br />
   inc eax<br />
  .endif<br />
  ret<br />
HiMetricX2Pixel EndP</p>
<p>RedrawObject Proc uses esi edi ebx hWnd,ID<br />
  Local hRichEdit<br />
  Local lpOleInterface<br />
  Local dwObjectCount<br />
  Local reo:REOBJECT<br />
  Local szTemp[256]:BYTE<br />
  Local pt:POINT<br />
  Local rcClient:RECT<br />
  Local rc:RECT<br />
  Local IsInClient<br />
  <br />
  invoke GetDlgItem,hWnd,ID<br />
  mov hRichEdit,eax<br />
  <br />
  invoke SendMessage,hRichEdit,EM_GETOLEINTERFACE,0,addr lpOleInterface<br />
  cmp lpOleInterface,0<br />
  jz exit<br />
  <br />
  mov esi,lpOleInterface<br />
  mov esi,[esi]<br />
  assume esi:ptr IRichEditOle<br />
  <br />
  invoke [esi].GetObjectCount,lpOleInterface ;取OLE对象数量<br />
  mov dwObjectCount,eax<br />
  <br />
  invoke GetClientRect,hRichEdit,addr rcClient<br />
  <br />
  xor ebx,ebx     ;循环获取每个OLE然后根据需要刷新之<br />
  .While ebx &lt; dwObjectCount<br />
   mov IsInClient,0<br />
   <br />
   invoke RtlZeroMemory,addr reo,sizeof reo<br />
   mov reo.cbStruct,sizeof reo<br />
   invoke [esi]._GetObject,lpOleInterface,ebx,addr reo,1 ;1 = REO_GETOBJ_POLEOBJ<br />
   <br />
   invoke SendMessage,hRichEdit,EM_POSFROMCHAR,addr pt,reo.cp<br />
   m2m rc.left,pt.x<br />
   m2m rc.top,pt.y<br />
   m2m rc.right,pt.x<br />
   invoke HiMetricX2Pixel,reo.sizel.x<br />
   add rc.right,eax<br />
   m2m rc.bottom,rcClient.bottom<br />
   <br />
   mov eax,reo.poleobj<br />
   mov eax,[eax]<br />
   invoke [eax.IOleObject.Release],reo.poleobj<br />
   <br />
   invoke PtInRect,addr rcClient,rc.left,rc.top<br />
   .if eax<br />
    inc IsInClient<br />
    jmp @f<br />
   .endif<br />
   <br />
   invoke PtInRect,addr rcClient,rc.right,rc.top<br />
   .if eax<br />
    inc IsInClient<br />
    jmp @f<br />
   .endif<br />
   <br />
   invoke PtInRect,addr rcClient,rc.left,rc.bottom<br />
   .if eax<br />
    inc IsInClient<br />
    jmp @f<br />
   .endif<br />
   <br />
   invoke PtInRect,addr rcClient,rc.right,rc.bottom<br />
   .if eax<br />
    inc IsInClient<br />
    jmp @f<br />
   .endif<br />
   <br />
@@:   <br />
   .if IsInClient<br />
    invoke InvalidateRect,hRichEdit,addr rc,0<br />
   .endif<br />
   inc ebx<br />
  .EndW<br />
exit:  <br />
  ret<br />
RedrawObject EndP</p>
<p>最终效果如下</p>
<p><img class="alignnone size-full wp-image-16" title="e9a284e8a788" src="http://www.deadc0de.com/wp-content/uploads/2009/05/e9a284e8a788.jpg" alt="e9a284e8a788" width="459" height="301" /></p>
<p>测试工程打包下载 <a href="http://www.deadc0de.com/wp-content/uploads/2009/05/richole2.rar">http://www.deadc0de.com/wp-content/uploads/2009/05/richole2.rar</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/insert-gif-to-richedit.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>探究 LoadImage 内部的图像缩放函数</title>
		<link>http://www.deadc0de.com/archives/stretch-func-in-loadimage.html</link>
		<comments>http://www.deadc0de.com/archives/stretch-func-in-loadimage.html#comments</comments>
		<pubDate>Tue, 26 May 2009 09:37:30 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[Win32编程]]></category>

		<category><![CDATA[图像缩放，StretchDIBits]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=7</guid>
		<description><![CDATA[最近研究界面编程涉及到了位图缩放。但发现系统提供的 StretchDIBits 缩放效果很不理想。但又研究发现 LoadImage 这个 API 内部实现了缩放功能。下面是 LoadImage 原型。
HANDLE LoadImage(
    HINSTANCE hinst,  // handle of the instance that contains the image
    LPCTSTR lpszName, // name or identifier of image
    UINT uType, // type of image
    int cxDesired, // desired width
    int cyDesired, // desired height
    UINT fuLoad // load flags
   );
设置此函数的 cxDesired cyDesired 参数实现缩放，效果较好。但是这个函数只能从文件或资源中获取位图。不方便用于已读入内存的位图。
而 StretchDIBits 可以实现但是效果又不理想。这使我有了研究 LoadImage 内部的冲动…… -_-!

首先看了 ReactOS [...]]]></description>
			<content:encoded><![CDATA[<p>最近研究界面编程涉及到了位图缩放。但发现系统提供的 StretchDIBits 缩放效果很不理想。但又研究发现 LoadImage 这个 API 内部实现了缩放功能。下面是 LoadImage 原型。</p>
<p>HANDLE LoadImage(<br />
    HINSTANCE hinst,  // handle of the instance that contains the image<br />
    LPCTSTR lpszName, // name or identifier of image<br />
    UINT uType, // type of image<br />
    int cxDesired, // desired width<br />
    int cyDesired, // desired height<br />
    UINT fuLoad // load flags<br />
   );<br />
设置此函数的 cxDesired cyDesired 参数实现缩放，效果较好。但是这个函数只能从文件或资源中获取位图。不方便用于已读入内存的位图。</p>
<p>而 StretchDIBits 可以实现但是效果又不理想。这使我有了研究 LoadImage 内部的冲动…… -_-!<br />
<span id="more-7"></span><br />
首先看了 ReactOS 中的 LoadImage，他对 BITMAP 类型的文件使用 LoadBitmapImage 处理。</p>
<p>      case IMAGE_BITMAP:<br />
         return LoadBitmapImage(hinst, lpszName, fuLoad);</p>
<p>忽略了 cxDesired 和 cyDesired。显然这一点和 Windows 不同。我只好使用OD调试 Windows 的 user32.dll。</p>
<p>我假设 LoadImage 内部使用 StretchDIBits 进行缩放，对 StretchDIBits 下断。果然断了下来。但发觉函数的参数不对。</p>
<p>（原图像宽高均40，cxDesired,cyDesired设置为30）</p>
<p>0012F318   77D1CAB6  /CALL 到 StretchDIBits 来自 user32.77D1CAB0<br />
0012F31C   E8011199  |hDC = E8011199<br />
0012F320   00000000  |XDest = 0<br />
0012F324   00000000  |YDest = 0<br />
0012F328   0000001E  |WidthDest = 1E (30.)<br />
0012F32C   0000001E  |HeightDest = 1E (30.)<br />
0012F330   00000000  |XSrc = 0<br />
0012F334   00000000  |YSrc = 0<br />
0012F338   0000001E  |WidthSrc = 1E (30.)<br />
0012F33C   0000001E  |HeightSrc = 1E (30.)<br />
0012F340   001493C8  |pBits = 001493C8<br />
0012F344   00148FA0  |pBitmapinfo = 00148FA0<br />
0012F348   00000000  |Usage = DIB_RGB_COLORS<br />
0012F34C   00CC0020  \ROP = SRCCOPY</p>
<p>参数中源宽高和目标宽高是相同的，都是缩放后的30，明显在调用这个函数之前就已经处理好了位图数据。</p>
<p>位图缓冲区是 001493C8，重新调试了1次后发现这个地址不变。然后我对这个地址下了硬件访问断点以得到缩放位图数据的函数。运行，断在了下面的位置</p>
<p>77D21C35    52              push    edx<br />
77D21C36    03C8            add     ecx, eax<br />
77D21C38    FF71 03         push    dword ptr [ecx+3]<br />
77D21C3B    FF31            push    dword ptr [ecx]<br />
77D21C3D    FF70 03         push    dword ptr [eax+3]<br />
77D21C40    FF30            push    dword ptr [eax]<br />
77D21C42    E8 EFB7FFFF     call    77D1D436    &lt;&lt;&lt;&lt; 插值算法，根据4个像素点求1个像素点<br />
77D21C47    8BC8            mov     ecx, eax<br />
77D21C49    C1E9 10         shr     ecx, 10<br />
77D21C4C    880F            mov     byte ptr [edi], cl   &lt;&lt;&lt;&lt;&lt; EIP 断在这里<br />
77D21C4E    47              inc     edi<br />
77D21C4F    8827            mov     byte ptr [edi], ah<br />
77D21C51    47              inc     edi<br />
77D21C52    8807            mov     byte ptr [edi], al</p>
<p>用IDA分析这个函数，发现这个函数有很多参数 -_-! SO MANY&#8230;</p>
<p>int __stdcall sub_77D21BD0(int a1, int BufferSrc, int LineBytes, int a4, int a5, int a6, int a7, int BufferDst, int a9, int DestWidth, int DestHeight)</p>
<p>有一些参数懒得猜测其意，继续向上分析</p>
<p>交叉参考发现这个函数在一个函数表里……用OD返回到调用他的函数。发现是这样的</p>
<p>77D1D71B    FF10            call    dword ptr [eax]    &lt;&lt;&lt;&lt; 是从这里进入 sub_77D21BD0<br />
77D1D71D    834D FC FF      or      dword ptr [ebp-4], FFFFFFFF<br />
77D1D721    33C0            xor     eax, eax<br />
77D1D723    40              inc     eax<br />
77D1D724    E8 D7AEFFFF     call    77D18600<br />
77D1D729    C2 1000         retn    10</p>
<p>retn 10 可知此函数有 4 个参数。执行到返回。来到了下面的位置</p>
<p>77D1D5DF    56              push    esi<br />
77D1D5E0    53              push    ebx<br />
77D1D5E1    FF75 2C         push    dword ptr [ebp+2C]<br />
77D1D5E4    8943 04         mov     dword ptr [ebx+4], eax<br />
77D1D5E7    8B45 18         mov     eax, dword ptr [ebp+18]<br />
77D1D5EA    FF75 30         push    dword ptr [ebp+30]<br />
77D1D5ED    8943 08         mov     dword ptr [ebx+8], eax<br />
77D1D5F0    66:8B45 FC      mov     ax, word ptr [ebp-4]<br />
77D1D5F4    66:8943 0E      mov     word ptr [ebx+E], ax<br />
77D1D5F8    E8 24000000     call    77D1D621<br />
77D1D5FD    85C0            test    eax, eax  &lt;&lt;&lt;&lt; 返回到这里</p>
<p>对他的上一句 call 下断点重新运行程序。得到4个参数</p>
<p>0012F33C   00146C20<br />
0012F340   00AD0036<br />
0012F344   00146C70<br />
0012F348   00147098</p>
<p>查看内存后我发现了这个函数的原型</p>
<p>int __stdcall sub_77D1D621(BITMAPINFO *SrcHdr, PVOID *lpSrcBits, BITMAPINFO *DstHdr, PVOID *lpDstBits)</p>
<p>这样。我们就可以使用 sub_77D1D621 这个内部函数缩放位图数据啦。</p>
<p>可是怎么使用呢。由于是user32内部函数，没有导出。在不同的操作系统这个函数的位置是不同的。我当初想过把这个函数抠出来独立实现，可是发现这个代码写的实在是有点搞……里面利用了一些函数表，而且一些代码乱乱的。不好移植，所以干脆就使用特征码的方法搜索吧！ -_-!</p>
<p>查看了 win2000/xp/2003/vista 的 user32.dll<br />
，发现除win2000的函数开头有些不一致，其他版本的win大体还是相同的！</p>
<p>77D1D621    6A 28           push    28<br />
77D1D623    68 30D7D177     push    77D1D730<br />
77D1D628    E8 93AFFFFF     call    77D185C0<br />
77D1D62D    33FF            xor     edi, edi<br />
77D1D62F    8B4D 08         mov     ecx, dword ptr [ebp+8]<br />
77D1D632    3979 10         cmp     dword ptr [ecx+10], edi</p>
<p>这是winxp sp3开头的6行代码。由于2、3行涉及到了变量我们使用后三行作为特征码</p>
<p>特征码为 33 FF 8B 4D 08 39 79 10 共8个字节。</p>
<p>下面是搜索函数</p>
<p>GetStretchBits Proc uses esi edi ebx<br />
Local hUser32</p>
<p>invoke GetModuleHandle,CTEXT(&#8221;user32.dll&#8221;)<br />
mov edi,eax</p>
<p>@@: .if dword ptr [edi] == 4d8bff33h &amp;&amp; dword ptr [edi+4] == 10793908h<br />
.if word ptr [edi-12] == 286Ah ;winxp/2003/vista<br />
lea eax,[edi-12]<br />
ret<br />
.endif<br />
.if dword ptr [edi-28h] == 6AEC8B55h ;win2000<br />
lea eax,[edi-28h]<br />
ret<br />
.endif<br />
mov eax,0 ;无法识别<br />
ret<br />
.endif<br />
inc edi<br />
jmp @b</p>
<p>ret<br />
GetStretchBits EndP</p>
<p>对于 40&#215;40 的一个QQ头像，缩放至20&#215;20，与 Photoshop(两次立方平滑)/StretchDIBits 对比效果如下</p>
<p> <img class="alignnone size-full wp-image-12" title="result" src="http://www.deadc0de.com/wp-content/uploads/2009/05/result.bmp" alt="result" /></p>
<p>从左到右依次为</p>
<p>1.源图像<br />
2.user32.LoadImage 内部函数处理<br />
3.Photoshop(两次立方平滑)<br />
4.StretchDIBits</p>
<p> <br />
下面我给出大致的测试代码片段<br />
测试平台 LENOVO_XP_PRO_SP3_CHS_090416<br />
.586<br />
.Model Flat,StdCall<br />
Option CaseMap :None</p>
<p>Include Windows.inc<br />
Include User32.inc<br />
Include Kernel32.inc<br />
Include Gdi32.inc</p>
<p>IncludeLib User32.lib<br />
IncludeLib Kernel32.lib<br />
IncludeLib Gdi32.lib</p>
<p>.Code<br />
GetStretchBits Proc uses esi edi ebx<br />
  Local hUser32<br />
  <br />
  invoke GetModuleHandle,CTEXT(&#8221;user32.dll&#8221;)<br />
  mov edi,eax<br />
  <br />
@@:  .if dword ptr [edi] == 4d8bff33h &amp;&amp; dword ptr [edi+4] == 10793908h<br />
   .if word ptr [edi-12] == 286Ah<br />
    lea eax,[edi-12]<br />
    ret<br />
   .endif<br />
   .if dword ptr [edi-28h] == 6AEC8B55h<br />
    lea eax,[edi-28h]<br />
    ret<br />
   .endif<br />
   mov eax,0<br />
   ret<br />
  .endif<br />
  inc edi<br />
  jmp @b<br />
  <br />
  ret<br />
GetStretchBits EndP</p>
<p>Start Proc<br />
 Local hFile,dwFileSize,dwRead<br />
 Local lpBI,lpBits<br />
 Local lpSrcBuffer,lpDstBuffer<br />
 Local bi:BITMAPINFOHEADER<br />
 Local hDC<br />
 <br />
 invoke CreateFile,CTEXT(&#8221;NEWFACE\24.bmp&#8221;),GENERIC_ALL,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 ;40&#215;40 24Bit Bitmap<br />
 mov hFile,eax<br />
 invoke GetFileSize,hFile,0<br />
 mov dwFileSize,eax<br />
 invoke LocalAlloc,LPTR,65536<br />
 mov lpSrcBuffer,eax<br />
 invoke ReadFile,hFile,lpSrcBuffer,dwFileSize,addr dwRead,0 ;读入lpSrcBuffer<br />
 invoke CloseHandle,hFile<br />
 <br />
 mov esi,lpSrcBuffer<br />
 add esi,[esi][BITMAPFILEHEADER.bfOffBits]<br />
 mov lpBits,esi  ;定位位图数据区<br />
 <br />
 mov esi,lpSrcBuffer<br />
 add esi,sizeof BITMAPFILEHEADER<br />
 mov lpBI,esi  ;定位 源 BITMAPFILEHEADER<br />
 <br />
 invoke RtlMoveMemory,addr bi,lpBI,sizeof BITMAPINFOHEADER ;复制一份 BITMAPFILEHEADER 当作 目标 BITMAPFILEHEADER<br />
 mov bi.biWidth,20  ;设置缩放宽<br />
 mov bi.biHeight,20  ;设置缩放高<br />
 <br />
 invoke LocalAlloc,LPTR,65536 ;申请目标缓冲区<br />
 mov lpDstBuffer,eax<br />
 <br />
 invoke GetStretchBits  ;获取函数地址<br />
 .if eax<br />
  push lpDstBuffer<br />
  lea ecx,bi<br />
  push ecx<br />
  push lpBits<br />
  push lpBI<br />
  call eax  ;调用之<br />
 .endif<br />
 <br />
 invoke GetDC,0   ;获取屏幕 DC<br />
 mov hDC,eax<br />
 <br />
 invoke StretchDIBits,hDC,0,0,20,20,0,0,20,20,lpDstBuffer,addr bi,DIB_RGB_COLORS,SRCCOPY ;画在屏幕 0,0 处<br />
 invoke ExitProcess,0<br />
Start EndP<br />
End Start</p>
<p>(完)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/stretch-func-in-loadimage.html/feed</wfw:commentRss>
		</item>
		<item>
		<title>Hello world！</title>
		<link>http://www.deadc0de.com/archives/hello-world.html</link>
		<comments>http://www.deadc0de.com/archives/hello-world.html#comments</comments>
		<pubDate>Tue, 26 May 2009 05:45:51 +0000</pubDate>
		<dc:creator>mengxp</dc:creator>
		
		<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://www.deadc0de.com/?p=1</guid>
		<description><![CDATA[从今天开始我也加入blog阵营啦
使用WordPress程序
欢迎大家常来玩
嘿嘿~
]]></description>
			<content:encoded><![CDATA[<p>从今天开始我也加入blog阵营啦</p>
<p>使用WordPress程序</p>
<p>欢迎大家常来玩</p>
<p>嘿嘿~</p>
]]></content:encoded>
			<wfw:commentRss>http://www.deadc0de.com/archives/hello-world.html/feed</wfw:commentRss>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.494 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2010-03-10 12:45:05 -->
