<?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/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>不敢流泪 &#187; VC</title>
	<atom:link href="http://www.boluor.com/tag/vc/feed" rel="self" type="application/rss+xml" />
	<link>http://www.boluor.com</link>
	<description></description>
	<lastBuildDate>Fri, 07 Oct 2011 07:28:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>向记事本发送键盘消息</title>
		<link>http://www.boluor.com/send-keyboard-message-to-notepad-in-windows.html</link>
		<comments>http://www.boluor.com/send-keyboard-message-to-notepad-in-windows.html#comments</comments>
		<pubDate>Fri, 10 Jul 2009 16:30:10 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=185</guid>
		<description><![CDATA[<p>    上午还在实验室的时候，龙哥短信说我们的门锁坏了，开不了，他们出不来，我只好回来。回来后，试了很久，还是打不开，于是一脚踹开了。然后发现门傍貌似裂了，锁的弹簧断了&#8230;打电话找了木工，修了很久终于可以用了，由于时间比较晚，也就没去实验室了。<br />
    打电话让FM帮我带回来本子。突然想起来前天和Sandy的关于魔兽改键工具的讨论，我需要确定war3到底是怎么接受键盘信息的。于是迫不及待地想写个给其他程序发送键盘消息的程序。我选择了记事本做测试。<br />
    思路很简单，FindWindow获得句柄，然后SendMessage或者PostMessage。至于其中遇到的问题，还需要仔细探究消息队列，SendMessa&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>    上午还在实验室的时候，龙哥短信说我们的门锁坏了，开不了，他们出不来，我只好回来。回来后，试了很久，还是打不开，于是一脚踹开了。然后发现门傍貌似裂了，锁的弹簧断了&#8230;打电话找了木工，修了很久终于可以用了，由于时间比较晚，也就没去实验室了。<br />
    打电话让FM帮我带回来本子。突然想起来前天和Sandy的关于魔兽改键工具的讨论，我需要确定war3到底是怎么接受键盘信息的。于是迫不及待地想写个给其他程序发送键盘消息的程序。我选择了记事本做测试。<br />
    思路很简单，FindWindow获得句柄，然后SendMessage或者PostMessage。至于其中遇到的问题，还需要仔细探究消息队列，SendMessage和PostMessage的区别，下次再讨论。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #0000ff;">void</span> CSendMessageTestDlg<span style="color: #008080;">::</span><span style="color: #007788;">OnBnClickedButton1</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #666666;">// TODO: 在此添加控件通知处理程序代码</span>
	HWND hwnd <span style="color: #000080;">=</span> <span style="color: #008080;">::</span><span style="color: #007788;">FindWindow</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;无标题 - 记事本&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
&nbsp;
	HWND hedit <span style="color: #000080;">=</span> <span style="color: #008080;">::</span><span style="color: #007788;">GetDlgItem</span><span style="color: #008000;">&#40;</span>hwnd,<span style="color: #208080;">0x0000000F</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span><span style="color: #666666;">//编辑框控件ID。用spy++可以看到。</span>
&nbsp;
	<span style="color: #666666;">//HWND hedit = ::FindWindowEx(hwnd,NULL,&quot;edit&quot;,NULL);</span>
	<span style="color: #666666;">//::SendMessage(hedit,WM_KEYDOWN,'1',0);//SendMessage时只有用WM_CHAR才有用。</span>
	<span style="color: #008080;">::</span><span style="color: #007788;">PostMessage</span><span style="color: #008000;">&#40;</span>hedit,WM_CHAR,<span style="color: #FF0000;">'1'</span>,<span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #666666;">//PostMessage用WM_KEYUP,WM_KEYDOWN或者WM_CHAR都可以。</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>    要了解在MFC工程中，用::FindWindow和不使用::的区别。当不使用时，调用的是当前类的成员函数。<br />
    发张用Spy++查看窗口句柄的的截图.Spy++还可以查看进程的消息队列，功能很强大。<br />
<div id="attachment_188" class="wp-caption alignnone" style="width: 310px"><a href="http://www.boluor.com/wp-content/uploads/2009/07/spynotepad.JPG"><img src="http://www.boluor.com/wp-content/uploads/2009/07/spynotepad-300x141.jpg" alt="Spy++查看记事本比编辑控件窗口" title="spy&amp;notepad" width="300" height="141" class="size-medium wp-image-188" /></a><p class="wp-caption-text">Spy++查看记事本比编辑控件窗口</p></div></p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/send-keyboard-message-to-notepad-in-windows.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>VC++创建目录</title>
		<link>http://www.boluor.com/vc-create-directory.html</link>
		<comments>http://www.boluor.com/vc-create-directory.html#comments</comments>
		<pubDate>Wed, 08 Jul 2009 13:53:52 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=181</guid>
		<description><![CDATA[<p>        这几天暑期集训带队，每天会将各个队的进度报告和自己的带队笔记放到相应的目录下。我习惯每天的记录单独存放，比如今天会创建个名为”090708”的目录，明天会创建一个名为“090709”的。而在这个目录下，会将各队的信息单独存放，就是要创建三个目录：“MFC队”，“ASP队”，“改键工具”。本来想手工一个个建的，后来想不如编程实现。查了些资料，用VC++实现了。<br />
	参照DirectoryInfo类的例子写成的。其实用到的托管到底是怎么回事我现在也没搞清楚。忙完实验室了回头看看吧。<br />
	创建控制台程序，选择空项目。<br />
<span id="more-181"></span></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #0000ff;">u</span></pre></td></tr></table></div><p>&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>        这几天暑期集训带队，每天会将各个队的进度报告和自己的带队笔记放到相应的目录下。我习惯每天的记录单独存放，比如今天会创建个名为”090708”的目录，明天会创建一个名为“090709”的。而在这个目录下，会将各队的信息单独存放，就是要创建三个目录：“MFC队”，“ASP队”，“改键工具”。本来想手工一个个建的，后来想不如编程实现。查了些资料，用VC++实现了。<br />
	参照DirectoryInfo类的例子写成的。其实用到的托管到底是怎么回事我现在也没搞清楚。忙完实验室了回头看看吧。<br />
	创建控制台程序，选择空项目。<br />
<span id="more-181"></span></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> System<span style="color: #008080;">;</span>
<span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> System<span style="color: #008080;">::</span><span style="color: #007788;">IO</span><span style="color: #008080;">;</span>
&nbsp;
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	DirectoryInfo<span style="color: #000040;">^</span> di<span style="color: #008080;">;</span>
	String<span style="color: #000040;">^</span> fatherDir<span style="color: #008080;">;</span>
	String<span style="color: #000040;">^</span> fatherDir_prefix <span style="color: #000080;">=</span> gcnew String <span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;I: 9暑期集训mstc&amp;&amp;cc监督进度 907&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">8</span><span style="color: #008080;">;</span>i<span style="color: #000080;">&lt;=</span><span style="color: #0000dd;">20</span><span style="color: #008080;">;</span>i<span style="color: #000040;">++</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		fatherDir <span style="color: #000080;">=</span> gcnew String <span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>i<span style="color: #000080;">&lt;</span><span style="color: #0000dd;">10</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
			fatherDir <span style="color: #000080;">=</span> fatherDir_prefix <span style="color: #000040;">+</span> <span style="color: #FF0000;">&quot;0&quot;</span><span style="color: #000040;">+</span> i<span style="color: #008080;">;</span>
		<span style="color: #008000;">&#125;</span><span style="color: #0000ff;">else</span><span style="color: #008000;">&#123;</span>
			fatherDir <span style="color: #000080;">=</span> fatherDir_prefix <span style="color: #000040;">+</span> i<span style="color: #008080;">;</span>
		<span style="color: #008000;">&#125;</span>
&nbsp;
		di<span style="color: #000080;">=</span> gcnew DirectoryInfo<span style="color: #008000;">&#40;</span>fatherDir<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
&nbsp;
		<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>di<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>Exists <span style="color: #000080;">==</span> <span style="color: #0000ff;">false</span><span style="color: #008000;">&#41;</span>
			di<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>Create<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
&nbsp;
		<span style="color: #666666;">// Create three subdirectory in the directory just created.</span>
		di<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>CreateSubdirectory<span style="color: #008000;">&#40;</span> <span style="color: #FF0000;">&quot;MFC队&quot;</span> <span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		di<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>CreateSubdirectory<span style="color: #008000;">&#40;</span> <span style="color: #FF0000;">&quot;ASP队&quot;</span> <span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		di<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>CreateSubdirectory<span style="color: #008000;">&#40;</span> <span style="color: #FF0000;">&quot;改键工具&quot;</span> <span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>	在编译时，会遇到编译错误。需要设置下：</p>
<p>1.	项目属性—配置属性—常规—字符集，选择多字节字符集。VC2005默认是Unicode。<br />
2.	提示错误error C2871: “System”: 具有该名称的命名空间不存在时，是因为程序是托管代码，在项目中选择公共语言运行库支持/clr就好了。设置路径就在设置字符集的下面一栏。<br />
3.	提示错误error LNK2001: unresolved external symbol _WinMain@16。参照：<a href="http://blog.csai.cn/user1/16781/archives/2006/6412.html ">vc中error LNK2001:unresolved external symbol _WinMain@16的解决方法</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/vc-create-directory.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>键盘钩子函数执行两次解决方法</title>
		<link>http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html</link>
		<comments>http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html#comments</comments>
		<pubDate>Mon, 06 Jul 2009 09:31:03 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=169</guid>
		<description><![CDATA[<p>考虑下面的键盘钩子函数。(MFC工程)</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;">LRESULT CALLBACK KeyProc<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nCode,WPARAM w,LPARAM l<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">'A'</span><span style="color: #000080;">==</span>w<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		MessageBox<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&#34;yes&#34;</span>,<span style="color: #FF0000;">&#34;test&#34;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
&#160;
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>    也就是想在按下A的时候，弹出一个对话框。</p>
<p>     然后在一个事件中添加：<br />
     SetWindowsHookEx(WH_KEYBOARD,KeyProc,NULL,GetCurrentThread&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>考虑下面的键盘钩子函数。(MFC工程)</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;">LRESULT CALLBACK KeyProc<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nCode,WPARAM w,LPARAM l<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">'A'</span><span style="color: #000080;">==</span>w<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		MessageBox<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;yes&quot;</span>,<span style="color: #FF0000;">&quot;test&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
&nbsp;
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>    也就是想在按下A的时候，弹出一个对话框。</p>
<p>     然后在一个事件中添加：<br />
     SetWindowsHookEx(WH_KEYBOARD,KeyProc,NULL,GetCurrentThreadId());<br />
    安装钩子。事件被触发后，就可以调用上面的键盘钩子函数了。</p>
<p>    但是在执行时会发现，当按下A时，提示框弹出了两遍。是什么原因呢？<br />
<span id="more-169"></span><br />
     想到了一个解释，那就是当按键时，按下和弹起分别是一个事件，所以调用了两次钩子函数。那么如何解决呢？看MSDN的帮助时发现，KeyboardProc Function的第三个参数，也就是LPARAM类型的参数，有很多的作用。LAPRAM占4个字节，32位。每一位都是一个特殊的标志。比如说如果Alt键被按下的话，第29位就是1，否则为0。那么键盘按下和弹起是哪个呢？是第30位。这一位标记了上次按键的状态。在消息发送之前，如果按键是按下的，那么值为1，否则为0。(经Sandy提醒,添加下面一句)第31位标记当前按键的状态，取值和第30位的规则相同。我们可以根据这一位来过滤掉按下和弹起中的一个。<br />
     添加一层过滤后，代码如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;">LRESULT CALLBACK KeyProc<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nCode,WPARAM w,LPARAM l<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>l  <span style="color: #000080;">&gt;&gt;</span> <span style="color: #0000dd;">31</span> <span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>	<span style="color: #666666;">//修改前为第30位，应该是第31位，判断当前按键状态。</span>
		<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">'A'</span><span style="color: #000080;">==</span>w<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
			MessageBox<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;yes&quot;</span>,<span style="color: #FF0000;">&quot;test&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
		<span style="color: #008000;">&#125;</span>
	<span style="color: #008000;">&#125;</span>
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>	 那么为什么我之前用第30位来判断也是可以的呢? 原因在于，当前按键时，第30位也就是保存了上次按键的状态位必定为弹起状态，那么上面的语句也就可以正常执行。不过，每次判断的都不是当前按键的状态，而是上次的状态。这在其他应用中会造成很难察觉的错误。(谢谢Sandy的提醒，以后多点细心^.^)。</p>
<p>     其实系统提供了一个预定义KF_UP，在winuser.h中，来标记按键是否是弹起状态。不过，使用时需要注意，如果这样判断：<br />
     if ( l &#038; KF_UP){<br />
             //key up<br />
     }<br />
     是不正确的。注意在KF_UP定义的地方有一句：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="cpp" style="font-family:Courier new,verdana;"> <span style="color: #ff0000; font-style: italic;">/*
 * WM_KEYUP/DOWN/CHAR HIWORD(lParam) flags
 */</span>
<span style="color: #339900;">#define KF_UP             0x8000</span></pre></td></tr></table></div>

<p>      这表明，KF_UP是高位标志。使用时需要移位。所以可以这样比较：<br />
      if ( KF_UP &#038; ( l >> 16 ) ){<br />
      }<br />
      或者利用宏HIWORD取得参数l的高16位。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/solution-to-the-keyboard-hook-function-is-executed-twice.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>detours 笔记</title>
		<link>http://www.boluor.com/detours-notes.html</link>
		<comments>http://www.boluor.com/detours-notes.html#comments</comments>
		<pubDate>Fri, 12 Jun 2009 18:15:26 +0000</pubDate>
		<dc:creator>boluor</dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[detours]]></category>
		<category><![CDATA[DLL]]></category>
		<category><![CDATA[hook]]></category>
		<category><![CDATA[VC]]></category>

		<guid isPermaLink="false">http://www.boluor.com/?p=125</guid>
		<description><![CDATA[<p>　　花了一周的时间来了解detours，现在终于有了进展。一周前下载了detours的时候，茫然了好一阵子才找到了头绪。现在总结下方便像我一样的初学者了。</p>
<p>　　我不再介绍Detours到底是什么了，因为已经有很好的帮助文档了，从detours官网上可以下载detours包。我下载的版本是detours2.1 express版本。包中有帮助文档，有detours的源码，还有很多例子。编译的时候，CMD下在detours的目录输入nmake，就可以编译了。需要注意的是，detours2.1平台要求是NT内核的，所以98下是不能用的。</p>
<p>　　编译好后，下面就说使用之前几点需要知道的，看帮助文档是可以获得这些&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>　　花了一周的时间来了解detours，现在终于有了进展。一周前下载了detours的时候，茫然了好一阵子才找到了头绪。现在总结下方便像我一样的初学者了。</p>
<p>　　我不再介绍Detours到底是什么了，因为已经有很好的帮助文档了，从detours官网上可以下载detours包。我下载的版本是detours2.1 express版本。包中有帮助文档，有detours的源码，还有很多例子。编译的时候，CMD下在detours的目录输入nmake，就可以编译了。需要注意的是，detours2.1平台要求是NT内核的，所以98下是不能用的。</p>
<p>　　编译好后，下面就说使用之前几点需要知道的，看帮助文档是可以获得这些信息的。了解了这几点，使用detours会更加明了。detours是通过在目标进程中”打桩“来完成拦截目标函数的(语出Sandy)。这个概念需要理清。介绍文档中有示意图，可以仔细看。detours修改的是目标进程在内存中的二进制映像，并不修改硬盘上的文件，所以可以恢复。</p>
<p>　　detours编译好后，有几个文件需要注意。detours.h包含了detours提供的API的声明，如果需要使用这些函数，需要包含此头文件。detours.lib就是这些API的实现，是静态链接库，跟DLL生成的lib文件是不同的，如果不是很清楚的话可以看我之前的一篇”Dll的两种链接方式与LIB”。当调用了detours的API的时候，需要在项目中声明，在程序连接时包含此lib，这样编译器就会将detours.lib链接到目标程序中生成最后的可执行程序，这个程序运行时就不再需要detours.lib了。还有一个是detoured.dll，使用了detours的话是总是需要在项目文件夹下添加此文件的，按照文档说明，detoured.dll 是一个标志，帮助微软的开发人员和工具判断某个进程是否已经被detours拦截。</p>
<p>　　下面创建一个非常简单的例子，VC2005拦截Sleep API。detours包中的例子中的simple也是拦截的Sleep API。</p>
<p><strong>1.</strong>	首先创建一个Non-MFC DLL工程HookSleepDLL。将detours.h，detoured.dll，detours.lib三个文件拷贝这个工程目录中。在HookSleepDLL.cpp中添加三行声明：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;"><span style="color: #339900;">#include &quot;detours.h&quot;</span>
<span style="color: #339900;">#pragma comment(lib,&quot;detoured.lib&quot;)</span>
<span style="color: #339900;">#pragma comment(lib,&quot;detours.lib&quot;)</span></pre></div></div>

<p><strong>2.	</strong>接着需要定义Sleep函数的指针：<br />
static VOID (WINAPI * TrueSleep)(DWORD dwMilliseconds) = Sleep;<br />
detours通过这个函数指针来找到Sleep在内存中的映像，当然作为常识，参数和返回值和Sleep需要一致，不然怎么叫这个函数的指针^.^。<br />
<strong>3.</strong>定义替换Sleep函数的函数，这个函数的参数类型和返回值需要和Sleep函数一摸一样。<br />
VOID WINAPI DelaySleep(DWORD dwMilliseconds){<br />
	TrueSleep(dwMilliseconds+15000);<br />
}<br />
我这里为了简单，就在原来Sleep的时间上加15秒。这就是说，如果Sleep被Hook住了，那么Sleep(1000)也就是睡一秒会变为睡16秒。</p>
<p><strong>4.</strong>下面说明在DLL被加载的时候开始拦截Sleep API。</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:Courier new,verdana;">BOOL APIENTRY DllMain<span style="color: #008000;">&#40;</span> HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 <span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> error<span style="color: #008080;">;</span>
	<span style="color: #0000ff;">switch</span><span style="color: #008000;">&#40;</span>ul_reason_for_call<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
		<span style="color: #0000ff;">case</span> DLL_PROCESS_ATTACH<span style="color: #008080;">:</span>
			DetourTransactionBegin<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourUpdateThread<span style="color: #008000;">&#40;</span><span style="color: #008080;">::</span><span style="color: #007788;">GetCurrentThread</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourAttach<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span>PVOID<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span>TrueSleep, DelaySleep<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			error <span style="color: #000080;">=</span> DetourTransactionCommit<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>NO_ERROR<span style="color: #000040;">!</span><span style="color: #000080;">=</span>error<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span>
				<span style="color: #008080;">::</span><span style="color: #007788;">MessageBox</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;Error!&quot;</span>,<span style="color: #FF0000;">&quot;Error in Detours!&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #008000;">&#125;</span>
			<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">case</span> DLL_PROCESS_DETACH<span style="color: #008080;">:</span>
			DetourTransactionBegin<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourUpdateThread<span style="color: #008000;">&#40;</span>GetCurrentThread<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			DetourDetach<span style="color: #008000;">&#40;</span><span style="color: #000040;">&amp;</span><span style="color: #008000;">&#40;</span>PVOID<span style="color: #000040;">&amp;</span><span style="color: #008000;">&#41;</span>TrueSleep, DelaySleep<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			error <span style="color: #000080;">=</span> DetourTransactionCommit<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #008080;">::</span><span style="color: #007788;">MessageBox</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,<span style="color: #FF0000;">&quot;Detour ends&quot;</span>,<span style="color: #FF0000;">&quot;Prompt!&quot;</span>,MB_OK<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
			<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">return</span> TRUE<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>　　用到的几个API简要说明下：<br />
DetourTransactionBegin：	开始一个新的detours事务。<br />
DetourUpdateThread：讲一个线程入队等待更新。（不知道理解的有没有偏差）<br />
DetourAttach：将目标进程(Sleep)和替换它的函数DelaySleep绑定。第一个参数是一个指针，这个指针指向目标函数的指针(有点绕&#8230;)。<br />
DetourTransactionCommit:	事务提交，如果不提交之前的更改是不会生效的。这个函数会返回一个值，如果是NO_ERROR 表明是成功，如果是其他的表示失败。<br />
DetourDetach：	解除目标进程和替换函数的绑定，参数和DetourDetach一摸一样。</p>
<p><strong>5.</strong>生成DLL。如果提示诸如无法将const char *转换到LPCSTR之类的错误，这是字符集的问题，将项目的属性设置为多字节的。或者使用_T，L之类的标记，具体可google。<br />
测试程序。创建一个MFC对话框程序。将前一步生成的HookSleepDLL.dll和detoured.dll拷贝到当前项目目录下。在界面上画两个Button控件BtnTest和BtnLoadLibrary。在BtnTest的事件中写：<br />
Sleep(1000);<br />
MessageBox(NULL,”hello,test”,”test”,MB_OK);<br />
说明在停一秒后弹出对话框。<br />
然后在BtnLoadLibrary的事件中写：<br />
HINSTANCE g_dll;<br />
g_dll=LoadLibrary(“SleepHOOKDll.dll”);</p>
<p><strong>6.</strong>编译运行，首先点击BtnLoadLibrary，将DLL加载到当前进程空间中。这个时候可以用IceSword查看模块信息，是否已经被加载。然后点击BtnTest，如果没有问题的话，效果会如预期，在16秒后才弹出对话框。</p>
<p>　　至此，一个简单的利用detours拦截API的例子已经完成。想要完成更复杂的工程是需要更多的知识储备和耐心的。在了解detours的时候，我顺便加深了对DLL的了解程度。需要特别注意的是，假如现在有一个DLL工程并没有输出任何的符号，在编译后有lib文件，当然这个DLL可以在被加载的时候干点事情。然后使用这个DLL的工程如果采用了静态链接的方式，那么编译器会对其进行优化，在进程运行时，并不会加载这个DLL。或者是你有输出函数，但是工程并没有调用任何函数，那么也会被优化掉。所以不错的选择是选择DLL的动态链接方式。</p>
<p>　　还有一点，作为DLL的补充吧，因为之后要完成一个进程保护程序，提前说下准备的知识吧，关于DLL的。windows为DLL等系统资源预留了2g-4g的虚拟内存空间地址，所以加载DLL的时候，系统会将DLL加载到这个位置，这样多个进程可以共享。所以虽然DLL可以被多个进程加载多次，但是系统只会保留一份在内存，也就是说系统检测到内存中已经有这个DLL，就不会再加载了，而是简单的映射下就可以了。这种共享在大家都只是读DLL的时候是没问题的，但是如果DLL中的某个变量可写，那么就会很危险。所以系统采取了COW（copy on write）机制。在某个进程需要写的时候，就单独申请空间，然后将这个新的页面映射到进程的写数据页面。这样，某个进程写数据并不会干扰其他的进程，因为改的只是一个拷贝。回到我们今天的主题detours上，拿上面的例子，拦截Sleep，也只是拦截本进程中调用Sleep的操作，并不会拦截其他进程调用Sleep操作，因为拦截时发生了写操作，所以修改的时候修改的是一份针对本进程的拷贝，并不是所有的进程。如果想达到拦截所有进程调用Sleep的操作，我目前想到的方法就是对所有的进程注入DLL实现拦截了。</p>
<p>　　终于写完了，两点多了，睡哦。　　</p>
]]></content:encoded>
			<wfw:commentRss>http://www.boluor.com/detours-notes.html/feed</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
	</channel>
</rss>

