﻿<?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++博客-微尘--KeepMoving-随笔分类-Windows编程</title><link>http://www.cppblog.com/tyt2008cn/category/6166.html</link><description>为了忘却的记忆</description><language>zh-cn</language><lastBuildDate>Wed, 21 May 2008 04:42:19 GMT</lastBuildDate><pubDate>Wed, 21 May 2008 04:42:19 GMT</pubDate><ttl>60</ttl><item><title>（转）Win32应用程序的加载与启动分析</title><link>http://www.cppblog.com/tyt2008cn/archive/2008/03/07/43898.html</link><dc:creator>微尘</dc:creator><author>微尘</author><pubDate>Fri, 07 Mar 2008 08:38:00 GMT</pubDate><guid>http://www.cppblog.com/tyt2008cn/archive/2008/03/07/43898.html</guid><wfw:comment>http://www.cppblog.com/tyt2008cn/comments/43898.html</wfw:comment><comments>http://www.cppblog.com/tyt2008cn/archive/2008/03/07/43898.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tyt2008cn/comments/commentRss/43898.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tyt2008cn/services/trackbacks/43898.html</trackback:ping><description><![CDATA[&nbsp;转自 <a id=ArticleTitle1_ArticleTitle1_AuthorLink href="http://dev.csdn.net/user/chenxixia"><u><font color=#0000ff>chenxixia</font></u></a> 的 Blog <br>
<p id=TBPingURL>Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=455591</p>
<br><br>设有一个Win32下的可执行文件MyApp.exe，这是一个Win32应用程序，符合标准的PE格式。MyApp.exe的主要执行代码都集中在其源文件MyApp.cpp中，该文件第一个被执行的函数是WinMain。初学者会认为程序就是首先从这个WinMain函数开始执行，其实不然。
<p>&nbsp;&nbsp;&nbsp; 在WinMain函数被执行之前，有一系列复杂的加载动作，还要执行一大段启动代码。运行程序MyApp.exe时，操作系统的加载程序首先为进程分配一个4GB的虚拟地址空间，然后把程序MyApp.exe所占用的磁盘空间作为虚拟内存映射到这个4GB的虚拟地址空间中。一般情况下，会映射到虚拟地址空间中0X00400000的位置。加载一个应用程序的时间比一般人所设想的要少，因为加载一个PE文件并不是把这个文件整个一次性的从磁盘读到内存中，而是简单的做一个内存映射，映射一个大文件和映射一个小文件所花费的时间相差无几。当然，真正执行文件中的代码时，操作系统还是要把存在于磁盘上的虚拟内存中的代码交换到物理内存(RAM)中。但是，这种交换也不是把整个文件所占用的虚拟地址空间一次性的全部从磁盘交换到物理内存中，操作系统会根据需要和内存占用情况交换一页或多页。当然，这种交换是双向的，即存在于物理内存中的一部分当前没有被使用的页也可能被交换到磁盘中。</p>
<p>&nbsp;&nbsp;&nbsp; 接着，系统在内核中创建进程对象和主线程对象以及其它内容。</p>
<p>&nbsp;&nbsp;&nbsp; 然后操作系统的加载程序搜索PE文件中的引入表，加载所有应用程序所使用的动态链接库。对动态链接库的加载与对应用程序的加载完全类似。</p>
<p>&nbsp;&nbsp;&nbsp; 再接着，操作系统执行PE文件首部所指定地址处的代码，开始应用程序主线程的执行。首先被执行的代码并不是MyApp中的WinMain函数，而是被称为C Runtime startup code的WinMainCRTStartup函数，该函数是连接时由连接程序附加到文件MyApp.exe中的。该函数得到新进程的全部命令行指针和环境变量的指针，完成一些C运行时全局变量以及C运行时内存分配函数的初始化工作。如果使用C++编程，还要执行全局类对象的构造函数。最后，WinMainCRTStartup函数调用WinMain函数。</p>
<p>&nbsp;&nbsp; WinMainCRTStartup函数传给WinMain函数的4个参数分别为：hInstance、hPrevInstance、lpCmdline、nCmdShow。</p>
<p>&nbsp;&nbsp;&nbsp; hInstance：该进程所对应的应用程序当前实例的句柄。WinMainCRTStartup函数通过调用GetStartupInfo函数获得该参数的值。该参数实际上是应用程序被加载到进程虚拟地址空间的地址，通常情况下，对于大多数进程，该参数总是0X00400000。</p>
<p>&nbsp;&nbsp;&nbsp; hPrevInstance：应用程序前一实例的句柄。由于Win32应用程序的每一个实例总是运行在自己的独立的进程地址空间中，因此，对于Win32应用程序，WinMainCRTStartup函数传给该参数的值总是NULL。如果应用程序希望知道是否有另一个实例在运行，可以通过线程同步技术，创建一个具有唯一名称的互斥量，通过检测这个互斥量是否存在可以知道是否有另一个实例在运行。</p>
<p>&nbsp;&nbsp;&nbsp; lpCmdline：命令行参数的指针。该指针指向一个以0结尾的字符串，该字符串不包括应用程序名。</p>
<p>&nbsp;&nbsp;&nbsp; nCmdShow：指定如何显示应用程序窗口。如果该程序通过在资源管理器中双击图标运行，WinMainCRTStartup函数传给该参数的值为SW_SHOWNORMAL。如果通过在另一个应用程序中调用CreatProcess函数运行，该参数由CreatProcess函数的参数lpStartupInfo(STARTUPINFO.wShowWindow)指定。</p>
<br><br>
<p id=TBPingURL>&nbsp;</p>
<img src ="http://www.cppblog.com/tyt2008cn/aggbug/43898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tyt2008cn/" target="_blank">微尘</a> 2008-03-07 16:38 <a href="http://www.cppblog.com/tyt2008cn/archive/2008/03/07/43898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>也谈 extern "C" 的用法</title><link>http://www.cppblog.com/tyt2008cn/archive/2008/02/25/43231.html</link><dc:creator>微尘</dc:creator><author>微尘</author><pubDate>Mon, 25 Feb 2008 12:46:00 GMT</pubDate><guid>http://www.cppblog.com/tyt2008cn/archive/2008/02/25/43231.html</guid><wfw:comment>http://www.cppblog.com/tyt2008cn/comments/43231.html</wfw:comment><comments>http://www.cppblog.com/tyt2008cn/archive/2008/02/25/43231.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tyt2008cn/comments/commentRss/43231.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tyt2008cn/services/trackbacks/43231.html</trackback:ping><description><![CDATA[&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;相信大家在编码中，有时会见到某某函数前面加了 extern "C" 的关键字修饰，尤其在模块(Dll)提供的头文件中。但是否很清楚的了解它的作用， 我当时第一次碰到时，其实是懂非懂的^_^。查了些资料，才明白其用法，详细如下。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1. 首先说下编译器编译函数时，对函数名的处理。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在C语言中，对函数如: IRoleView* RoleViewCreate(int nType); 编译后生成的函数名是RoleViewCreate<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但是在C++中，由于存在函数重载的特性，所以编译时C++编译器会根据参数、返回值等信息对函数名改编，如上面函数在C++编译器中生成的函数名是 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#82;&#111;&#108;&#101;&#86;&#105;&#101;&#119;&#67;&#114;&#101;&#97;&#116;&#101;&#64;&#64;&#89;&#65;&#80;&#65;&#86;&#73;&#82;&#111;&#108;&#101;&#86;&#105;&#101;&#119;&#64;&#64;&#72;&#64;&#122;">?RoleViewCreate@@YAPAVIRoleView@@H@z</a><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; 2. extern "C"的含义。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extern "C" 有两重含义：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其一，被修饰的变量或函数是extern的存储类型，它告诉编译器，其声明的变量或函数可以被本模块和外部某块使用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其二，被其修饰的函数或变量是按照C语言的方式来编译和链接的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意，extern "C"写法 只在C++中被支持，C语言不支持该写法。<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 3. extern "C"的两种惯用用法：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a) 在C++中使用C语言的函数或变量，在包含C语言提供的头文件是，需要用 extern "C" { } 来包含头文件。如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; extern "C"<br>&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;#include "lua.h"&nbsp;&nbsp;&nbsp;&nbsp;//lua.h是C编写，并提供的头文件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这里引用下第1点的函数来说明上述写法的用途：假设lua.h中包含函数 IRoleView* RoleViewCreate(int nType), 那么C语言的编译时生成的函数名是RoleViewCreate，而当C++客户程序去使用它时，默认是按C++的链接方式（即不加以上的 extern "C"时），所以C++客户程序会去外部模块中查找函数名为<br><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#63;&#82;&#111;&#108;&#101;&#86;&#105;&#101;&#119;&#67;&#114;&#101;&#97;&#116;&#101;&#64;&#64;&#89;&#65;&#80;&#65;&#86;&#73;&#82;&#111;&#108;&#101;&#86;&#105;&#101;&#119;&#64;&#64;&#72;&#64;&#122;">?RoleViewCreate@@YAPAVIRoleView@@H@z</a>的函数，这样C++编译器会找不到该函数，报出"无法解析的外部标识符"的错误； 而当加上 <br>extern "C" { } 时，它就告诉编译器头文件中包含的函数或变量要按照C语言的编译链接方式进行，所以C++编译器会去外部模块查找函数名为<br>RoleViewCreate的函数，从而得到正确结果。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b) 在C中调用一个C++语言中的函数或变量时，C++的头文件需要添加 extern "C"，这是为了让编译器对函数或变量按C语言的方式进行编译，已供C语言调用； 但在C语言中，不能直接包含声明了extern "C"的头文件，而应该在C文件中把在C++头文件中定义的函数，声明为 extern类型，因为在C语言中，并不支持extern "C"的写法；<br><br>参考资料：《C++中extern "C" 含义深层探索》作者：宋宝华<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 《C++ Primer》<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<img src ="http://www.cppblog.com/tyt2008cn/aggbug/43231.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tyt2008cn/" target="_blank">微尘</a> 2008-02-25 20:46 <a href="http://www.cppblog.com/tyt2008cn/archive/2008/02/25/43231.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>