??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲精品NV久久久久久久久久 ,精品熟女少妇AV免费久久 ,国产午夜福利精品久久http://www.shnenglu.com/Walker/category/15672.html先学会{文章Q在仔细L章,最后自己写点东?.......zh-cnSun, 01 Apr 2012 04:38:49 GMTSun, 01 Apr 2012 04:38:49 GMT60How to: Convert Between Various String Typeshttp://www.shnenglu.com/Walker/articles/147908.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Wed, 01 Jun 2011 22:43:00 GMThttp://www.shnenglu.com/Walker/articles/147908.htmlhttp://www.shnenglu.com/Walker/comments/147908.htmlhttp://www.shnenglu.com/Walker/articles/147908.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/147908.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/147908.htmlhttp://msdn.microsoft.com/en-us/library/ms235631.aspx
// convert_from_char.cpp
// compile with: /clr /link comsuppw.lib

#include <iostream>
#include <stdlib.h>
#include <string>

#include "atlbase.h"
#include "atlstr.h"
#include "comutil.h"

using namespace std;
using namespace System;

int main()
{
    // Create and display a C style string, and then use it 
    // to create different kinds of strings.
    char *orig = "Hello, World!";
    cout << orig << " (char *)" << endl;

    // newsize describes the length of the 
    // wchar_t string called wcstring in terms of the number 
    // of wide characters, not the number of bytes.
    size_t newsize = strlen(orig) + 1;

    // The following creates a buffer large enough to contain 
    // the exact number of characters in the original string
    // in the new format. If you want to add more characters
    // to the end of the string, increase the value of newsize
    // to increase the size of the buffer.
    wchar_t * wcstring = new wchar_t[newsize];

    // Convert char* string to a wchar_t* string.
    size_t convertedChars = 0;
    mbstowcs_s(&convertedChars, wcstring, newsize, orig, _TRUNCATE);
    // Display the result and indicate the type of string that it is.
    wcout << wcstring << _T(" (wchar_t *)") << endl;

    // Convert the C style string to a _bstr_t string.
    _bstr_t bstrt(orig);
    // Append the type of string to the new string
    // and then display the result.
    bstrt += " (_bstr_t)";
    cout << bstrt << endl;

    // Convert the C style string to a CComBSTR string.
    CComBSTR ccombstr(orig);
    if (ccombstr.Append(_T(" (CComBSTR)")) == S_OK)
    {
        CW2A printstr(ccombstr);
        cout << printstr << endl;
    }

    // Convert the C style string to a CstringA and display it.
    CStringA cstringa(orig);
    cstringa += " (CStringA)";
    cout << cstringa << endl;

    // Convert the C style string to a CStringW and display it.
    CStringW cstring(orig);
    cstring += " (CStringW)";
    // To display a CStringW correctly, use wcout and cast cstring
    // to (LPCTSTR).
    wcout << (LPCTSTR)cstring << endl;

    // Convert the C style string to a basic_string and display it.
    string basicstring(orig);
    basicstring += " (basic_string)";
    cout << basicstring << endl;

    // Convert the C style string to a System::String and display it.
    String ^systemstring = gcnew String(orig);
    systemstring += " (System::String)";
    Console::WriteLine("{0}", systemstring);
    delete systemstring;
}

Output

Hello, World! (char *)
Hello, World! (wchar_t *)
Hello, World! (_bstr_t)
Hello, World! (CComBSTR)
Hello, World! (CStringA)
Hello, World! (CStringW)
Hello, World! (basic_string)
Hello, World! (System::String)


]]>
字符~码http://www.shnenglu.com/Walker/articles/147443.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Fri, 27 May 2011 14:54:00 GMThttp://www.shnenglu.com/Walker/articles/147443.htmlhttp://www.shnenglu.com/Walker/comments/147443.htmlhttp://www.shnenglu.com/Walker/articles/147443.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/147443.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/147443.html关于字符~码Q你所需要知道的

 

字符~码的问题看似很,l常被技术h员忽视,但是很容易导致一些莫名其妙的问题。这里ȝ了一下字W编码的一些普及性的知识Q希望对大家有所帮助?/p>

q是得从ASCII码说?/h2>

 

说到字符~码Q不得不说ASCII码的双Ӏ计机一开始发明的时候是用来解决数字计算的问题,后来Z发现Q计机q可以做更多的事Q例如文本处理。但׃计算机只?#8220;?#8221;Q因此h们必d诉计机哪个数字来代表哪个特定字W,例如65代表字母‘A’Q?6代表字母‘B’Q以此类推。但?strong>计算Z间字W?数字的对应关pdd一_否则׃造成同一D|字在不同计算Z昄出来的字W不一?/span>。因此美国国家标准协会ANSI制定了一个标准,规定了常用字W的集合以及每个字符对应的编Pq就是ASCII字符集(Character SetQ,也称ASCII码?/p>

当时的计机普遍使用8比特字节作ؓ最的存储和处理单元,加之当时用到的字W也很少Q?6个大写英文字母q有数字再加上其他常用符P也不?00个,因此使用7个比特位可以高效的存储和处理ASCII码,剩下最高位1比特被用作一些通讯pȝ的奇偶校验?/p>

注意Q字节代表系l能够处理的最单位,不一定是8比特。只是现代计机的事实标准就是用8比特来代表一个字节。在很多技术规格文献中Qؓ了避免生歧义,更們֐于?位组QOctetQ而不是字节(ByteQ这个术语来8个比特的二进制流。下文中Z便于理解Q我会g用大家熟悉的“字节”q个概念?/p>

ASCII table

ASCII字符集由95个可打印字符Q?x20-0x7EQ和33个控制字W(0x00-0x19Q?x7FQ组成。可打印字符用于昄在输备上Q例如荧屏或者打印纸上,控制字符用于向计机发出一些特D指令,例如0x07会让计算机发出哔的一壎ͼ0x00通常用于指示字符串的l束Q?x0D?x0A用于指示打印机的打印针头退到行首(回RQƈUd下一行(换行Q?/p>

那时候的字符~解码系l非常简单,是单的查表q程。例如将字符序列~码Zq制写入存储设备,只需要在ASCII字符集中依次扑ֈ字符对应的字节,然后直接该字节写入存储讑֤卛_。解码二q制的q程也是cM?/p>

OEM字符集的衍生

当计机开始发展v来的时候,Z逐渐发现QASCII字符集里那可怜的128个字W已l不能再满他们的需求了。h们就在想Q一个字节能够表C的数字Q编P?56个,而ASCII字符只用C0x00~0x7FQ也是占用了前128个,后面128个数字不用白不用Q因此很多h打v了后面这128个数字的L。可是问题在于,很多人同时有q样的想法,但是大家对于0x80-0xFFq后面的128个数字分别对应什么样的字W,却有各自的想法。这导致了当时销往世界各地的机器上出现了大量各式各LOEM字符集?/p>

下面q张表是IBM-PC机推出的其中一个OEM字符集,字符集的?28个字W和ASCII字符集的基本一_Z么说基本一致呢Q是因ؓ?2个控制字W在某些情况下会被IBM-PC机当作可打印字符解释Q,后面128个字W空间加入了一些欧z国家用到的重音字符Q以及一些用于画U条ȝ字符?/p>

IBM-PC OEM字符? src=

事实上,大部分OEM字符集是兼容ASCII字符集的Q也是_大家对于0x00~0x7Fq个范围的解释基本是相同的,而对于后半部?x80~0xFF的解释却不一定相同。甚x时候同L字符在不同OEM字符集中对应的字节也是不同的?/p>

不同的OEM字符集导致h们无法跨机器交流各种文档。例如职员甲发了一简历résumésl职员乙Q结果职员乙看到的却是rגsumגsQ因?#233;字符在职员甲机器上的OEM字符集中对应的字节是0x82Q而在职员乙的机器上,׃使用的OEM字符集不同,?x82字节解码后得到的字符却是ג?/p>

多字节字W集QMBCSQ和中文字符?/h2>

上面我们提到的字W集都是Z单字节编码,也就是说Q一个字节翻译成一个字W。这对于拉丁语系国家来说可能没有什么问题,因ؓ他们通过扩展W?个比特,可以得?56个字W了Q够用了。但是对于亚z国家来_256个字W是q远不够用的。因此这些国家的Zؓ了用上电脑,又要保持和ASCII字符集的兼容Q就发明了多字节~码方式Q相应的字符集就UCؓ多字节字W集。例如中国用的是双字节字W集~码QDBCSQDouble Byte Character SetQ?/p>

对于单字节字W集来说Q代码页中只需要有一张码表即可,上面记录着256个数字代表的字符。程序只需要做单的查表操作可以完成编解码的过E?/p>

代码|字符集编码的具体实现Q你可以把他理解Z?#8220;字符-字节”映射表,通过查表实现“字符-字节”的翻译。下面会有更详细的描q?/p>

而对于多字节字符集,代码中通常会有很多码表。那么程序怎么知道该用哪张码表去解码二进制流呢?{案是,ҎW一个字节来选择不同的码表进行解?/span>?/p>

例如目前最常用的中文字W集GB2312Q涵盖了所有简体字W以及一部分其他字符QGBKQK代表扩展的意思)则在GB2312的基上加入了对繁体字W等其他非简体字W(GB18030字符集不是双字节字符集,我们在讲Unicode的时候会提到Q。这两个字符集的字符都是使用1-2个字节来表示。Windowspȝ采用936代码|实现对GBK字符集的~解码。在解析字节的时候,如果遇到字节的最高位?的话Q那么就使用936代码中的第1张码表进行解码,q就和单字节字符集的~解码方式一致了?/p>

image

当字节的高位?的时候,切的说Q当W一个字节位?x81–0xFE之间ӞҎW一个字节不同找C码页中的相应的码表,例如当第一个字节是0x81Q那么对?36中的下面q张码表Q?/code>

image

Q关?36代码中完整的码表信息,参见MSDNQ?a >http://msdn.microsoft.com/en-us/library/cc194913%28v=MSDN.10%29.aspx.Q?/p>

按照936代码늚码表Q当E序遇到q箋字节?x81 0x40的时候,׃解码?#8220;?#8221;字符?

ANSI标准、国家标准、ISO标准

不同ASCII衍生字符集的出现Q让文档交流变得非常困难Q因此各U组l都陆箋q行了标准化程。例如美国ANSIl织制定了ANSI标准字符~码Q注意,我们现在通常说到ANSI~码Q通常指的是^台的默认~码Q例如英文操作系l中是ISO-8859-1Q中文系l是GBKQ,ISOl织制定的各UISO标准字符~码Q还有各国也会制定一些国家标准字W集Q例如中国的GBKQGB2312和GB18030?/p>

操作pȝ在发布的时候,通常会往机器里预装这些标准的字符集还有^C用的字符集,q样只要你的文档是用标准字W集~写的,通用性就比较高了。例如你用GB2312字符集编写的文档Q在中国大陆内的M机器上都能正显C。同Ӟ我们也可以在一台机器上阅读多个国家不同语言的文档了Q前提是本机必须安装该文档用的字符集?/p>

Unicode的出?/h2>

虽然通过使用不同字符集,我们可以在一台机器上查阅不同语言的文档,但是我们仍然无法解决一个问题:在一份文档中昄所有字W?/span>。ؓ了解册个问题,我们需要一个全人类达成p的巨大的字符集,q就是Unicode字符集?/p>

Unicode字符集概q?/h3>

Unicode字符集涵盖了目前人类使用的所有字W,qؓ每个字符q行l一~号Q分配唯一的字W码QCode PointQ。Unicode字符集将所有字W按照用上的频J度划分?7个层面(PlaneQ,每个层面上有216=65536个字W码I间?/p>

image

其中W?个层面BMPQ基本涵盖了当今世界用到的所有字W。其他的层面要么是用来表CZ些远古时期的文字Q要么是留作扩展。我们^常用到的Unicode字符Q一般都是位于BMP层面上的。目前Unicode字符集中有大量字符I间未用?

~码pȝ的变?/h3>

在Unicode出现之前Q所有的字符集都是和具体~码Ҏl定在一LQ都是直接将字符和最l字节流l定MQ例如ASCII~码pȝ规定使用7比特来编码ASCII字符集;GB2312以及GBK字符集,限定了用最?个字节来~码所有字W,q且规定了字节序。这L~码pȝ通常用简单的查表Q也是通过代码就可以直接字W映ؓ存储讑֤上的字节了。例如下面这个例子:

image

q种方式的缺点在于,字符和字节流之间耦合得太紧密了,从而限定了字符集的扩展能力。假设以后火星h入住地球了,要往现有字符集中加入火星文就变得很难甚至不可能了Q而且很容易破坏现有的~码规则?/p>

因此Unicode在设计上考虑Cq一点,字W集和字W编码方案分d?/p>

字符~码pȝ

也就是说Q?strong>虽然每个字符在Unicode字符集中都能扑ֈ唯一定的编P字符码,又称Unicode码)Q但是决定最l字节流的却是具体的字符~码。例如同h对Unicode字符“A”q行~码QUTF-8字符~码得到的字节流?x41Q而UTF-16Q大端模式)得到的是0x00 0x41?/p>

常见的Unicode~码

UCS-2/UTF-16

如果要我们来实现Unicode字符集中BMP字符的编码方案,我们会怎么实现Q由于BMP层面上有216=65536个字W码Q因此我们只需要两个字节就可以完全表示q所有的字符了?/p>

举个例子Q?#8220;?#8221;的Unicode字符码是0x4E2D(01001110 00101101)Q那么我们可以编码ؓ01001110 00101101Q大端)或?0101101 01001110 Q小端)?/p>

UCS-2和UTF-16对于BMP层面的字W均是?个字节来表示Qƈ且编码得到的l果完全一致。不同之处在于,UCS-2最初设计的时候只考虑到BMP字符Q因此用固?个字节长度,也就是说Q他无法表示Unicode其他层面上的字符Q而UTF-16Z解除q个限制Q支持Unicode全字W集的编解码Q采用了变长~码Q最?个字节,如果要编码BMP以外的字W,则需?个字节结?/span>Q这里就不讨论那么远Q有兴趣可以参考维基百U:UTF-16/UCS-2?/p>

Windows从NT时代开始就采用了UTF-16~码Q很多流行的~程q_Q例?NetQJavaQQtq有Mac下的Cocoa{都是用UTF-16作ؓ基础的字W编码。例如代码中的字W串Q在内存中相应的字节就是用UTF-16~码q的?/p>

UTF-8

UTF-8应该是目前应用最q泛的一UUnicode~码Ҏ。由于UCS-2/UTF-16对于ASCII字符使用两个字节q行~码Q存储和处理效率相对低下Qƈ且由于ASCII字符l过UTF-16~码后得到的两个字节Q高字节始终?x00Q很多C语言的函数都此字节视ؓ字符串末从而导致无法正解析文本。因此一开始推出的时候遭到很多西方国家的抵触Q大大媄响了Unicode的推行。后来聪明的Z发明了UTF-8~码Q解决了q个问题?/p>

UTF-8~码Ҏ采用1-4个字节来~码字符Q方法其实也非常单?/p>

image

Q上图中的x代表Unicode码的?位,y代表?位)

对于ASCII字符的编码用单字节Q和ASCII~码一怸Pq样所有原先用ASCII~解码的文档可以直接{到UTF-8~码了。对于其他字W,则?-4个字节来表示Q其中,首字节前|?的数目代表正解析所需要的字节敎ͼ剩余字节的高2位始l是10。例如首字节?110yyyyQ前|有3?Q说明正解析d需?个字节,需要和后面2个以10开头的字节l合才能正确解析得到字符?/p>

关于UTF-8的更多信息,参考维基百U:UTF-8?/p>

GB18030

M能够Unicode字符映射为字节流的编码都属于Unicode~码。中国的GB18030~码Q覆盖了Unicode所有的字符Q因此也是一UUnicode~码。只不过他的~码方式q不像UTF-8或者UTF-16一PUnicode字符的编号通过一定的规则q行转换Q而只能通过查表的手D进行编码?/p>

关于GB18030的更多信息,参考:GB18030?/p>

Unicode相关的常见问?/h3>

Unicode是两个字节吗Q?/strong>

Unicode只是定义了一个庞大的、全球通用的字W集Qƈ为每个字W规定了唯一定的编P具体存储Z么样的字节流Q取决于字符~码Ҏ。推荐的Unicode~码是UTF-16和UTF-8?/p>

带签名的UTF-8指的是什么意思?

带签名指的是字节以BOM标记开始。很多Y件会“”的探当前字节流使用的字W编码,q种探测q程Z效率考虑Q通常会提取字节流前面若干个字节,看看是否W合某些常见字符~码的编码规则。由于UTF-8和ASCII~码对于U英文的~码是一LQ无法区分开来,因此通过在字节流最前面dBOM标记可以告诉软gQ当前用的是Unicode~码Q判别成功率十分准了。但是需要注意,不是所有Y件或者程序都能正处理BOM标记Q例如PHP׃会检BOM标记Q直接把它当普通字节流解析了。因此如果你的PHP文g是采用带BOM标记的UTF-8q行~码的,那么有可能会出现问题?/p>

Unicode~码和以前的字符集编码有什么区别?

早期字符~码、字W集和代码页{概念都是表辑֐一个意思。例如GB2312字符集、GB2312~码Q?36代码,实际上说的是同个东西。但是对于Unicode则不同,Unicode字符集只是定义了字符的集合和唯一~号QUnicode~码Q则是对UTF-8、UCS-2/UTF-16{具体编码方案的l称而已Qƈ不是具体的编码方案。所以当需要用到字W编码的时候,你可以写gb2312Qcodepage936Qutf-8Qutf-16Q但请不要写unicodeQ看q别人在|页的meta标签里头写charset=unicodeQ有感而发Q?/p>

 

q问题

q指的是程序显C出来的字符文本无法用Q何语a去解诅R一般情况下会包含大?或者�。ؕ码问题是所有计机用户或多或少会遇到的问题?span style="color: #008000">造成q的原因就是因Z用了错误的字W编码去解码字节?/strong>Q?strong>因此当我们在思考Q何跟文本昄有关的问题时Q请时刻保持清醒Q当前用的字符~码是什?/span>。只有这P我们才能正确分析和处理ؕ码问题?/span>

例如最常见的网ؕ码问题。如果你是网站技术h员,遇到q样的问题,需?span style="color: #000000">查以下原因:

  • 服务器返回的响应头Content-Type没有指明字符~码
  • |页内是否用META HTTP-EQUIV标签指定了字W编?
  • |页文g本n存储时用的字符~码和网声明的字符~码是否一? 

image image

注意Q网解析的q程如果使用的字W编码不正确Q还可能会导致脚本或者样式表出错。具体细节可以参考我以前写过的文章:文档字符集导致的脚本错误?a >Asp.Net面的编码问?/font>?/p>

不久前看到某技术论坛有人反馈,WinFormE序使用ClipboardcȝGetDataҎ去访问剪切板中的HTML内容时会出现q的问题,我估计也是由于WinForm在获取HTML文本的时候没有用Ҏ的字符~码D的。Windows剪脓板只支持UTF-8~码Q也是说你传入的文本都会被UTF-8~解码。这样一来,只要两个E序都是调用Windows剪切板API~程的话Q那么复制粘贴的q程中不会出Cؕ码。除非一方在获取到剪贴板数据之后使用了错误的字符~码q行解码Q才会得Cؕ码(我做了简单的WinForm剪切板编E实验,发现GetData使用的是pȝ默认~码Q而不是UTF-8~码Q?/p>

关于q中出?或者�Q这里需要额外提一下,当程序用特定字W编码解析字节流的时候,一旦遇到无法解析的字节时Q就会用?或者�来替代。因此,一旦你最l解析得到的文本包含q样的字W,而你又无法得到原始字节流的时候,说明正确的信息已l彻底丢׃Q尝试Q何字W编码都无法从这L字符文本中还原出正确的信息来?/p>

必要的术语解?/h2>

字符集(Character SetQ?/strong>Q字面上的理解就是字W的集合Q例如ASCII字符集,定义?28个字W;GB2312定义?445个字W。?strong>计算机系l中提到的字W集准确来说Q指的是已编L字符的有序集合(不一定是q箋Q?/span>?/p>

字符码(Code PointQ?/strong>指的是字符集中每个字符的数字编受例如ASCII字符集用0-127q连l的128个数字分别表C?28个字W;GBK字符集用区位码的方式ؓ每个字符~号Q首先定义一?4X94的矩阵,行称?#8220;?#8221;Q列UCؓ“?#8221;Q然后将所有国标汉字放入矩阵当中,q样每个汉字可以用唯一?#8220;Z”码来标识了。例?#8220;?#8221;字被攑ֈ54区第48位,因此字符码就?448。而Unicode中将字符集按照一定的cd划分?~16q?7个层面(PlanesQ中Q每个层面中拥有216=65536个字W码Q因此Unicoded拥有的字W码Q也xUnicode的字W空间d?7*65536=1114112?/p>

image

~码的过E是字W{换成字节?/p>

解码的过E是字节流解析为字W?/p>

字符~码QCharacter EncodingQ?/strong>是将字符集中的字W码映射为字节流的一U具体实现方案。例如ASCII字符~码规定使用单字节中低位?个比特去~码所有的字符。例?#8216;A’的编h65Q用单字节表C就?x41Q因此写入存储设备的时候就是b’01000001’。GBK~码则是区位码QGBK的字W码Q中的区码和位码的分别加?xA0Q?60Q的偏移Q之所以要加上q样的偏U,主要是ؓ了和ASCII码兼容)Q例如刚刚提到的“?#8221;字,Z码是5448Q十六进制是0x3630Q区码和位码分别加上0xA0的偏UM后就得到0xD6D0Q这是“?#8221;字的GBK~码l果?/p>

代码(Code PageQ?/strong>一U字W编码具体Ş式。早期字W相对少Q因此通常会用类D格的形式字W直接映ؓ字节,然后通过查表的方式来实现字符的编解码。现代操作系l沿用了q种方式。例如Windows使用936代码cMacpȝ使用EUC-CN代码实现GBK字符集的~码Q名字虽然不一P但对于同一汉字的编码肯定是一L?/p>

大小?/strong>的说法源自《格列佛游记》。我们知道,鸡蛋通常一端大一端小Q小人国的h们对于剥蛋壳时应从哪一端开始剥h着不一L看法。同P计算机界对于传输多字节字Q由多个字节来共同表CZ个数据类型)Ӟ是先传高位字节(大端Q还是先传低位字节(端Q也有着不一L看法Q这是计算机里头大端模式的由来了。无论是写文件还是网l传输,实际上都是往设备进行写操作的过E,而且q个写操作是从流的低地址向高地址开始写Q这很符合h的习惯)Q对于多字节字来_如果先写入高位字节,则称作大端模式。反之则UC端模式。也是_大端模式下,字节序和设备的地址序是相反的Q而小端模式则是相同的。一般网l协议都采用大端模式q行传输?/p>

——Kevin Yang

参考链接:



]]>
dllhttp://www.shnenglu.com/Walker/articles/147052.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Tue, 24 May 2011 12:39:00 GMThttp://www.shnenglu.com/Walker/articles/147052.htmlhttp://www.shnenglu.com/Walker/comments/147052.htmlhttp://www.shnenglu.com/Walker/articles/147052.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/147052.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/147052.html 

Def?/span>__declspec(dllexport)

其实def的功能相当于extern “C” __declspec(dllexport)Q所以它也仅能处?/span>C函数Q而不能处理重载函数。?/span>__declspec(dllexport)?/span>__declspec(dllimport)配合使用能够适应M情况Q因?/span>__declspec(dllexport)是更为先q的Ҏ。所以,目前普遍的看法是不?/span>def文gQ我也同意这个看法?br />

如何在dll中定义输出函?/h1>

M来说有两U方法,
一U是d一个def定义文gQ在此文件中定义dll中要输出的函敎ͼ
W二U是在源代码中待输出的函数前加上__declspec(dllexport)关键字?/font>

2)名字修饰U定

1、修饰名(Decoration   name)

“C”或?#8220;C++”函数在内部(~译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字W串。有些情况下使用函数的修饰名是必要的Q如在模块定义文仉头指定输?#8220;C++”重蝲函数、构造函数、析构函敎ͼ又如在汇~代码里调用“C””?#8220;C++”函数{?

修饰名由函数名、类名、调用约定、返回类型、参数等共同军_?

2、名字修饰约定随调用U定和编译种c?C或C++)的不同而变化。函数名修饰U定随编译种cd调用U定的不同而不同,下面分别说明?

a、C~译时函数名修饰U定规则Q?

__stdcall调用U定在输出函数名前加上一个下划线前缀Q后面加上一?#8220;@”W号和其参数的字节数Q格式ؓ_functionname@number?

__cdecl调用U定仅在输出函数名前加上一个下划线前缀Q格式ؓ_functionname?

__fastcall调用U定在输出函数名前加上一?#8220;@”W号Q后面也是一?#8220;@”W号和其参数的字节数Q格式ؓ@functionname@number?

它们均不改变输出函数名中的字W大写Q这和PASCAL调用U定不同QPASCALU定输出的函数名无Q何修C全部大写?

b、C++~译时函数名修饰U定规则Q?

__stdcall调用U定Q?
1、以“?”标识函数名的开始,后跟函数名;
2、函数名后面?#8220;@@YG”标识参数表的开始,后跟参数表;
3、参数表以代可C:
X--void   Q?
D--charQ?
E--unsigned   charQ?
F--shortQ?
H--intQ?
I--unsigned   intQ?
J--longQ?
K--unsigned   longQ?
M--floatQ?
N--doubleQ?
_N--boolQ?
....
PA--表示指针Q后面的代号表明指针cdQ如果相同类型的指针q箋出现Q以“0”代替Q一?#8220;0”代表一ơ重复;
4、参数表的第一ؓ该函数的q回值类型,其后依次为参数的数据cd,指针标识在其所指数据类型前Q? 
5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数Q则?#8220;Z”标识l束?

其格式ؓ“?functionname@@YG*****@Z”?#8220;?functionname@@YG*XZ”Q例?
int   Test1Qchar   *var1,unsigned   longQ?----“?Test1@@YGHPADK@Z”
void   Test2Q)   -----“?Test2@@YGXXZ”

__cdecl调用U定Q?
规则同上面的_stdcall调用U定Q只是参数表的开始标识由上面?#8220;@@YG”变ؓ“@@YA”?

__fastcall调用U定Q?
规则同上面的_stdcall调用U定Q只是参数表的开始标识由上面?#8220;@@YG”变ؓ“@@YI”?

VC++对函数的省缺声明?"__cedcl ",只能被C/C++调用.


大家都应该知道,函数调用都有修饰W,有__cdecl,__stdcall,__fastcall{等Q不同修饰符意味着不同的参数出栈方式以及不同的函数名称变化?
对于C语言Q默认是__cdeclQ但如果我们要做DLL需要其它语a例如Delphi、VB{调用,通常都徏议声明ؓ__stdcall。win32的api基本都是__stdcall方式Q就是那个WINAPI宏)?

问题出在这个stdcall了。对于VC来说Q__cdecl修饰的函敎ͼ在DLL的导出名字一般是没有变化的,但对于__stdcall׃做一些改变,在前面加一个下划线Q后面再补上一?#8220;@”Q再加上参数的字节长度d?

例如Q对于函?int add(int a, int b)Q如果int __cdecl add(int a, int b)Q在DLL的导出名字还?#8220;add”Q但如果是int __stdcall add(int a, int b)Q在DLL的导出名字就?#8220;_add@8”Q查看导出名字可以用VC自带的dumpbin命oQ方法是“dumpbin /exports aaa.dll”Q?

好了Q说了一大堆废话Q开始进主题啦?

开始的时候,我做了一个test.dllQ例如包含一个导出的函数 int __stdcall build(int n)?
在delphi的时候,如果?

Java代码 复制代码 收藏代码
  1. function build(n:Integer): integer; stdcall; external 'test.dll' name 'build';  

 

q种声明方式的话Q导入会出错Q因为找不到buildq个函数Q这里的build已经Ҏ名字是_build@4。当Ӟ如果我们直接?#8220;external 'test.dll' name '_build@4';”应该是没有问题的。不q不爽。ؓ什么win api的函数就没有q些׃八糟的东西?

后来在网上找了一遍,写了一个def文g?

Java代码 复制代码 收藏代码
  1. LIBRARY   test.dll   
  2.   
  3. EXPORTS   
  4. build  

 



q样生成的dll的导出名字就没有那些׃八糟的修饰符了。不q这个时候又轮到Cq边有问题了。原因是Cq边在链接的时候找不到_build@4q个函数。真是晕倒,两边不讨好?

我就觉得奇怪,怎么win api的函敎ͼx有那些修饰符Q但又可以让delphi、C、VB{正调用呢Q奇怪啊?br />
今天写线E函数时Q发现msdn中对ThreadProc的定义有要求QDWORD WINAPI ThreadProc(LPVOID lpParameter);

不解Z么要用WINAPI宏定义,查了后发C面的定义。于是乎需要区别__stdcall和__cdecl两者的区别Q?#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
#define cdecl _cdecl
#ifndef CDECL
#define CDECL _cdecl
#endif

几乎我们写的每一个WINDOWS API函数都是__stdcallcd的,首先Q需要了解两者之间的区别Q?WINDOWS的函数调用时需要用到栈QSTACKQ一U先入后出的存储l构Q。当函数调用完成后,栈需要清楚,q里是问题的关键,如何清除Q?如果我们的函C用了_cdeclQ那么栈的清除工作是p用者,用COM的术语来讲就是客h完成的。这样带来了一个棘手的问题Q不同的~译器生栈的方式不相同,那么调用者能否正常的完成清除工作呢?{案是不能。如果用__stdcallQ上面的问题p决了Q函数自px除工作。所以,在跨Q开发)q_的调用中Q我们都使用__stdcallQ虽然有时是?WINAPI的样子出玎ͼ。那么ؓ什么还需要_cdecl呢?当我们遇到这L函数如fprintf()它的参数是可变的Q不定长的,被调用者事先无法知道参数的长度Q事后的清除工作也无法正常的q行Q因此,q种情况我们只能使用_cdecl。到q里我们有一个结论,如果你的E序中没有涉及可变参敎ͼ最好用__stdcall关键字?

2.

__cdecl,__stdcall是声明的函数调用协议.主要是传参和Ҏ斚w的不?一般c++用的是__cdecl,windows里大都用的是__stdcall(API)

__cdecl 是C/C++和MFCE序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdeclU定Ӟ函数参数按照从右到左的顺序入栈,q且p用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能用该调用U定。由于每一个用__cdeclU定的函数都要包含清理堆栈的代码Q所以生的可执行文件大会比较大。__cdecl可以写成_cdecl?
__stdcall调用U定用于调用Win32 API函数。采用__stdcallU定Ӟ函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函C本n知道传进来的参数个数Q因此被调用的函数可以在q回前用一条ret n指o直接清理传递参数的堆栈。__stdcall可以写成_stdcall?
__fastcall U定用于Ҏ能要求非常高的场合。__fastcallU定函数的从左边开始的两个大小不大?个字节(DWORDQ的参数分别攑֜ECX和EDX寄存器,其余的参C旧自叛_左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall可以写成_fastcall

3.

__stdcall:

_stdcall 调用U定相当?6位动态库中经怋用的PASCAL调用U定?

 
?2位的VC++5.0中PASCAL调用U定不再被支持(实际上它已被定义?__stdcall。除了__pascal外,__fortran和__syscall也不被支持)Q取而代之的是__stdcall调用U定。两者实质上是一致的Q即函数的参数自叛_左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明Q?

_stdcall是PascalE序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC函数编译后会在函数名前面加上下划线前缀Q在函数名后加上"@"和参数的字节数?

_cdecl:

_cdecl c调用U定, 按从双左的序压参数入栈,p用者把参数弹出栈。对于传送参数的内存栈是p用者来l护的(正因为如此,实现可变参数的函数只能用该调用U定Q。另外,在函数名修饰U定斚w也有所不同?

_cdecl是C和CQ+E序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以生的可执行文件大会比调用_stdcall函数的大。函数采用从叛_左的压栈方式。VC函数编译后会在函数名前面加上下划线前缀。是MFC~省调用U定?

__fastcall:

__fastcall调用U定??如其名,它的主要特点是快,因ؓ它是通过寄存器来传送参数的Q实际上Q它用ECX和EDX传送前两个双字QDWORDQ或更小的参敎ͼ剩下的参C旧自叛_左压栈传送,被调用的函数在返回前清理传送参数的内存栈)Q在函数名修饰约定方面,它和前两者均不同?

_fastcall方式的函数采用寄存器传递参敎ͼVC函数编译后会在函数名前面加?@"前缀Q在函数名后加上"@"和参数的字节数?

thiscall:

thiscall仅仅应用?C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定?

naked call:

采用1-4的调用约定时Q如果必要的话,q入函数时编译器会生代码来保存ESIQEDIQEBXQEBP寄存器,退出函数时则生代码恢复这些寄存器的内宏V?

naked call不生这L代码。naked call不是cd修饰W,故必d_declspec共同使用?

另附:

关键?__stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在~译环境的Setting...\C/C++ \Code Generationw择。当加在输出函数前的关键字与~译环境中的选择不同Ӟ直接加在输出函数前的关键字有效。它们对应的命o行参数分别ؓ/Gz?/Gd?Gr。缺省状态ؓ/GdQ即__cdecl?

要完全模仿PASCAL调用U定首先必须使用__stdcall调用U定Q至于函数名修饰U定Q可以通过其它Ҏ模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏Q它可以出函数译成适当的调用约定,在WIN32中,它被定义为__stdcall。用WINAPI宏可以创qAPIs?

名字修饰U定

1、修饰名(Decoration name)
“C” 或?#8220;C++”函数在内部(~译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字W串。有些情况下使用函数的修饰名是必要的Q如在模块定义文仉头指定输?#8220;C++”重蝲函数、构造函数、析构函敎ͼ又如在汇~代码里调用“C””?#8220;C++”函数{?

修饰名由函数名、类名、调用约定、返回类型、参数等共同军_?

2、名字修饰约定随调用U定和编译种c?C或C++)的不同而变化。函数名修饰U定随编译种cd调用U定的不同而不同,下面分别说明?

a、C~译时函数名修饰U定规则Q?

__stdcall调用U定在输出函数名前加上一个下划线前缀Q后面加上一?#8220;@”W号和其参数的字节数Q格式ؓ_functionname@number?

__cdecl调用U定仅在输出函数名前加上一个下划线前缀Q格式ؓ_functionname?

__fastcall调用U定在输出函数名前加上一?#8220;@”W号Q后面也是一?#8220;@”W号和其参数的字节数Q格式ؓ@functionname@number?

它们均不改变输出函数名中的字W大写Q这和PASCAL调用U定不同QPASCALU定输出的函数名无Q何修C全部大写?

b、C++~译时函数名修饰U定规则Q?

__stdcall调用U定Q?
1、以“?”标识函数名的开始,后跟函数名;
2、函数名后面?#8220;@@YG”标识参数表的开始,后跟参数表;
3、参数表以代可C:
X--void Q?
D--charQ?
E--unsigned charQ?
F--shortQ?
H--intQ?
I--unsigned intQ?
J--longQ?
K--unsigned longQ?
M--floatQ?
N--doubleQ?
_N--boolQ?
....
PA--表示指针Q后面的代号表明指针cdQ如果相同类型的指针q箋出现Q以“0”代替Q一?#8220;0”代表一ơ重复;
4、参数表的第一ؓ该函数的q回值类型,其后依次为参数的数据cd,指针标识在其所指数据类型前Q?
5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数Q则?#8220;Z”标识l束?

其格式ؓ“?functionname@@YG*****@Z”?#8220;?functionname@@YG*XZ”Q例?
int Test1Qchar *var1,unsigned longQ?----“?Test1@@YGHPADK@Z”
void Test2Q) -----“?Test2@@YGXXZ”

__cdecl调用U定Q?
规则同上面的_stdcall调用U定Q只是参数表的开始标识由上面?#8220;@@YG”变ؓ“@@YA”?

__fastcall调用U定Q?
规则同上面的_stdcall调用U定Q只是参数表的开始标识由上面?#8220;@@YG”变ؓ“@@YI”?
VC++对函数的省缺声明?#8220;__cedcl“,只能被C/C++调用.

CB在输出函数声明时使用4U修饰符?
//__cdecl
cb的默认|它会在输出函数名前加_Qƈ保留此函数名不变Q参数按照从叛_左的序依次传递给栈,也可以写成_cdecl和cdecl形式?
//__fastcall
她修饰的函数的参数将肯呢感C用寄存器来处理,其函数名前加@Q参数按照从左到右的序压栈Q?
//__pascal
它说明的函数名用Pascal格式的命名约定。这时函数名全部大写。参数按照从左到右的序压栈Q?
//__stdcall
使用标准U定的函数名。函数名不会改变。用__stdcall修饰时。参数按照由叛_左的序压栈Q也可以是_stdcallQ?

VC++对函数的省缺声明?__cedcl",只能被C/C++调用.

注意Q?

1、_beginthread需要__cdecl的线E函数地址Q_beginthreadex和CreateThread需要__stdcall的线E函数地址?

2、一般WIN32的函数都是__stdcall。而且在Windef.h中有如下的定义:

#define CALLBACK __stdcall

#define WINAPI  __stdcall

3、extern "C" _declspec(dllexport) int __cdecl Add(int a, int b);

   typedef int (__cdecl*FunPointer)(int a, int b);

   修饰W的书写序如上?

4、extern "C"的作用:如果Add(int a, int b)是在c语言~译器编译,而在c++文g使用Q则需要在c++文g中声明:extern "C" Add(int a, int b)Q因为c~译器和c++~译器对函数名的解释不一Pc++~译器解释函数名的时候要考虑函数参数Q这h了方便函数重载,而在c语言中不存在函数重蝲的问题)Q用extern "C"Q实质就是告诉c++~译器,该函数是c库里面的函数。如果不使用extern "C"则会出现链接错误?

一般象如下使用Q?

#ifdef _cplusplus

#define EXTERN_C extern "C"

#else

#define EXTERN_C extern

#endif

#ifdef _cplusplus

extern "C"{

#endif

EXTERN_C int func(int a, int b);

#ifdef _cplusplus

}

#endif

5、MFC提供了一些宏Q可以用AFX_EXT_CLASS来代替__declspec(DLLexport)Qƈ修饰cdQ从而导出类QAFX_API_EXPORT来修饰函敎ͼAFX_DATA_EXPORT来修饰变?

AFX_CLASS_IMPORTQ__declspec(DLLexport)

AFX_API_IMPORTQ__declspec(DLLexport)

AFX_DATA_IMPORTQ__declspec(DLLexport)

AFX_CLASS_EXPORTQ__declspec(DLLexport)

AFX_API_EXPORTQ__declspec(DLLexport)

AFX_DATA_EXPORTQ__declspec(DLLexport)

AFX_EXT_CLASSQ?ifdef _AFXEXT

   AFX_CLASS_EXPORT

        #else

   AFX_CLASS_IMPORT

6、DLLMain负责初始?Initialization)和结?(Termination)工作Q每当一个新的进E或者该q程的新的线E访问DLLӞ或者访问DLL的每一个进E或者线E不再用DLL或者结束时Q都会调用DLLMain。但是,使用TerminateProcess或TerminateThreadl束q程或者线E,不会调用DLLMain?

7、一个DLL在内存中只有一个实?

DLLE序和调用其输出函数的程序的关系Q?

1)、DLL与进E、线E之间的关系

DLL模块被映到调用它的q程的虚拟地址I间?

DLL使用的内存从调用q程的虚拟地址I间分配Q只能被该进E的U程所讉K?

DLL的句柄可以被调用q程使用Q调用进E的句柄可以被DLL使用?

DLLDLL可以有自q数据D,但没有自q堆栈Q用调用进E的栈,与调用它的应用程序相同的堆栈模式?

2)、关于共享数据段

DLL定义的全局变量可以被调用进E访问;DLL可以讉K调用q程的全局数据。用同一 DLL的每一个进E都有自qDLL全局变量实例。如果多个线Eƈ发访问同一变量Q则需要用同步机Ӟ对一个DLL的变量,如果希望每个使用DLL的线E都有自q|则应该用线E局部存?TLSQThread Local Strorage)?



]]>C与C++怺调用http://www.shnenglu.com/Walker/articles/146992.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Mon, 23 May 2011 12:53:00 GMThttp://www.shnenglu.com/Walker/articles/146992.htmlhttp://www.shnenglu.com/Walker/comments/146992.htmlhttp://www.shnenglu.com/Walker/articles/146992.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/146992.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/146992.html 在实际工作中可能l常要进行C和C++的؜合编E,C++调用C语言的代码通常都比较容易,但也有一些细节需要注意。C要调用C++的代码就略ؓȝ一些,因ؓC不支持面向对象的特征?br />首先我们来看一下C++调用C语言的代码。要让你的C代码既能被C代码又能被C++调用虽说ҎQ但是还是有需要注意的地方。现有三个文件分别如下:
/* file TestC.h */
#ifndef TESTC_H
#define TESTC_H
 
#ifdef __cplusplus
extern "C" {
#endif
 
int add(int a, int b);
      
#ifdef __cplusplus
}
#endif
 
#endif /* TESTC_H */
 
 
/* file TestC.c */
 
#include "TestC.h"
 
int add(int a, int b)
{
    return (a + b);
}
 
 
/* file TestCpp.cpp */
#include "stdio.h"
#include "TestC.h"
 
int main()
{
       printf("add = %d\n", add(2, 5));
      
       return 0;
}
说明Q?br />file TestC.h是C的头文gQfile TestC.c是其实现文gQfile TestCpp.cpp是调用C函数的C++文g?br />文gTestC.h中的TESTC_H定义是ؓ了头文g保护Q?#8221; #ifdef __cplusplus”q个不能~少Q你可以L看C的标准库头文件中都有q个Q如”stdio.h”。有了这个宏~译器就知道现在是Cq是C++在调用它?br />Z么要区分C与C++调用呢?其深层次原因是因为C和C++~译器在~译和链接时对于函数的处理不一栗C++Z支持函数重蝲在编译时会加入函数参数及cd信息。如上面的addҎQC~译器编译后在符号库中的名字为_addQ而C++~译器则会生像_add_int_int之类的名字。C++正是依靠q种机制实现了函数的重蝲?br />extern关键字表C将函数或变量声明ؓ全局cdQ与之相对应的是static。static限定函数或变量的作用域ؓ本文件。externq有一个作用就是与”C”q在一起用,即extern “C”通知~译器将extern “C”所包含的代码按照C的方式编译和链接?br /> 
下面我们来看看如何在C语言中用C++的代码(包括C++cȝҎQ。ؓ了简单v见,我将cȝ定义和实现放在一个文件中(通常应该是将分别攑֜.h?cpp文g?。自定义cL?q里省略了头文g保护{其它细?如下Q?br />//* file TestClass.h */
 
class HJH
{
public:
    int add(int a, int b)
       {
              return (a + b);
       }
};
 
C++cd装ؓC函数的文ӞZ略也声明和实现攑֜了同一个文件中Q如下:
/* file TestCpp.cpp */
 
#include "TestClass.h"
 
extern "C" int add_cpp(int a, int b);
 
int add_cpp(int a, int b)
{
       HJH hjh;
 
       return hjh.add(a, b);      
}
 
实际调用C++代码的C文g如下Q?br />/*file TestC.c */
#include "stdio.h"
 
extern int add_cpp(int a, int b);
 
int main()
{
       printf("add_cpp = %d\n", add_cpp(2, 5));
      
       return 0;
}
上面的过E很清晰Q就是用一个函数将C++cȝ使用装hQ然后将它外部声明ؓC函数可以了?br />文gTestClass.h定义q实C一个类Q该cd有一个addҎ。文件TestCpp.cpp定义q实C一个函数add_cppQ函C定义了一个HJHcd象ƈ调用了该对象的addҎ。然后将add_cpp函数q行外部声明为C?br />TestC.c文g中ؓ了用add_cpp函数Q也需要进行外部声明。这是ؓ了通知~译器说明这个函数是在其他文件中实现Q注意在C文g中的extern后面不可?#8221;C”Q。当q三个文件一L译链接时Q编译器可以找到add_cpp的具体实现?/p>

本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/gobitan/archive/2007/03/18/1532769.aspx



]]>
转GetUserDefaultLangID,GetSystemDefaultLangID,GetUserDefaultUILanguage比较http://www.shnenglu.com/Walker/articles/146720.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Wed, 18 May 2011 23:24:00 GMThttp://www.shnenglu.com/Walker/articles/146720.htmlhttp://www.shnenglu.com/Walker/comments/146720.htmlhttp://www.shnenglu.com/Walker/articles/146720.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/146720.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/146720.html 

q三个函数都是得到语aIDQ但是用的场合上却是有所不同Q下面先l出三个函数的原型和解释Q?br />GetUserDefaultLangID
Returns the language identifier for the current user locale.

LANGID GetUserDefaultLangID(void);
Parameters
This function has no parameters.

Return Values
Returns the language identifier for the current user locale.

GetSystemDefaultLangID
Returns the language identifier for the system locale.

LANGID GetSystemDefaultLangID(void);
Parameters
This function has no parameters.

Return Values
Returns the language identifier for the system locale.

GetUserDefaultUILanguage
Retrieves the user UI language for the current user. If the current user has not set a language, GetUserDefaultUILanguage returns the language identifier for the system default UI language.

LANGID GetUserDefaultUILanguage(void);
Parameters
This function has no parameters.

Return Value
Returns the language identifier for the user UI language for the current user.

要理解这三个函数的用先要看看控刉?>区域和语a选项Q?br />W一代表数字货币时_W二|pȝUI和Y件界面,W三高U是非unicodeE序界面语言?br />下面是这三个面讄Ӟ不同的函数的l果Q?br />                          区域选项       语言         高
GetUserDefaultLangID        改变          不变        不变     
GetSystemDefaultLangID       不变          不变       改变
GetUserDefaultUILanguage      改变          改变       不变
׃可见QGetUserDefaultUILanguage会和pȝUI的语a一_所以我们徏议在写UNICODEE序界面时用q个
函数会好些,和OS的UI一_不会让客得很奇怪?/p>

 

本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/nanjian011/archive/2009/10/23/4716903.aspx

 



]]>
调试Release发布版程序的Crash错误 Q{Q?/title><link>http://www.shnenglu.com/Walker/articles/146153.html</link><dc:creator>漫步者?amp;…?K?/dc:creator><author>漫步者?amp;…?K?/author><pubDate>Tue, 10 May 2011 23:53:00 GMT</pubDate><guid>http://www.shnenglu.com/Walker/articles/146153.html</guid><wfw:comment>http://www.shnenglu.com/Walker/comments/146153.html</wfw:comment><comments>http://www.shnenglu.com/Walker/articles/146153.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/Walker/comments/commentRss/146153.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Walker/services/trackbacks/146153.html</trackback:ping><description><![CDATA[<h2><a id=viewpost1_TitleUrl href="http://www.shnenglu.com/woaidongmao/archive/2011/05/10/146092.html">调试Release发布版程序的Crash错误</a> </h2> <div> <p><a title=http://blog.sina.com.cn/s/blog_48f93b530100fsln.html >http://blog.sina.com.cn/s/blog_48f93b530100fsln.html</a></p> <p> </p> <p><span>?span>Windows</span>q_下用<span>C++</span>开发应用程序,最不想见到的情冉|怕就是程序崩溃,而要惌军_起问题的<span>bug</span>Q最困难的应该就是调?span>release</span>版本了。因?span>release</span>版本来就了很多调试信息Q更何况一般都是发布出ȝ用户使用Q?span>crash</span>的现场很难保留和重现。本文将l出几个解决ҎQ完成对<span>release</span>版应用程?span>crash</span>错误的调试。(本文只讨?span>Windows</span>q_<span>MSVC</span>环境下的调试Q对于其他^台和开发环境没有关注,请大家自己借鉴和尝试。)</span></span></span></p> <p> <wbr></p> <p><span> <wbr> <wbr>  </span><wbr><strong><span>Ҏ一Q崩溃地址</span></strong><span> <span xml:lang="EN-US"><strong><span>+ MAP</span></span>文g</strong></span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span>q种Ҏ只能?span xml:lang="EN-US"><span>VC7</span></span>以前的版本开发的E序使用?span xml:lang="EN-US"><span> <wbr></span></span></p> </span> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> <strong>1</strong></span></span><strong><span>、崩溃地址</span></strong></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> <wbr> <wbr></span><span>所谓崩溃地址是引vE序崩溃的内存地址Q在</span><span><span xml:lang="EN-US">WinXP</span></span><span>下应用程?/span><span><span xml:lang="EN-US">crash</span></span><span>的对话框如下图:</span></span></p> <p align=center><span><a target=_blank><span><span><img title=clip_image001 border=0 alt=clip_image001 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image001_0ad42579-f1c9-44ae-b2f8-b50b8a737e0d.jpg" width=424 height=181 v:shapes="_x0000_i1025"></span></span></a></span></p> <p align=center><span><a target=_blank><span><span><img title=clip_image002 border=0 alt=clip_image002 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image002_cb3f00fa-aa22-4301-a42c-92c12dff2ea4.jpg" width=494 height=120 v:shapes="_x0000_i1026" real_src="http://s7.sinaimg.cn/bmiddle/48f93b53g79aad2c60546&690"></span></span></a></span></p> <p align=center><span><a target=_blank><span><span><img title=clip_image003 border=0 alt=clip_image003 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image003_fea81ac9-41ea-42be-a24f-d0fe99dafa80.jpg" width=494 height=380 v:shapes="_x0000_i1027" real_src="http://s2.sinaimg.cn/bmiddle/48f93b53g79aad34a5521&690"></span></span></a></span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span><span>上面W?/span><span><span xml:lang="EN-US">2</span></span><span>张图中画U线的gؓ</span><span><span xml:lang="EN-US">crash</span></span><span>的代码偏Ud址Q第</span><span><span xml:lang="EN-US">3</span></span><span>张图为即</span><span><span xml:lang="EN-US">crash</span></span><span>l对地址Q一般引?/span><span><span xml:lang="EN-US">crash</span></span><span>的原因多为内存操作错误,我们用这两个地址?/span><span><span xml:lang="EN-US">MAP</span></span><span>文gp定位出错的代码行?/span></span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> <strong>2</strong></span></span><strong><span>?span xml:lang="EN-US"><span>MAP</span></span>文g</span></strong></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> MAP</span></span><span>文g是记录应用程序信息的文gQ文本文ӞQ里面大概包含了E序的全局W号、源码模块名、源码文件和行号{信息,而这些信息能够帮助我们定位出错的代码行?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span>怎样生成<span xml:lang="EN-US"><span>MAP</span></span>文g呢??span xml:lang="EN-US"><span>VC6</span></span>ZQ在 <span xml:lang="EN-US"><span>Project Settings -> C/C++ <wbr>-> Debug info</span></span>中,选择 <span xml:lang="EN-US"><span>Line Numbers Only</span> </span>Q在 <span xml:lang="EN-US"><span>Project Settings -> Link</span> </span>中,选择 <span xml:lang="EN-US"><span>Generate mapfile</span></span>,q在<span xml:lang="EN-US"><span>Project Options</span> </span>里面输入 </span><span xml:lang="EN-US"><span>/MAPINFO:LINES</span></span><span> </span><span>?/span><span> <span><span xml:lang="EN-US">/MAPINFO:EXPORTS</span></span>Q重新编译程序就会生?span xml:lang="EN-US"><span>.map</span></span>文g?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span>以上讄对应的编译链接选项分别分:</span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><strong><span xml:lang="EN-US"><span>/Zi</span></span></strong><span> </span><span>?表示生成<span xml:lang="EN-US"><span>pdb</span></span>调试信息Q?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><strong><span xml:lang="EN-US"><span>/MAP[:filename]</span></span></strong><span> </span><span>?表示生成<span xml:lang="EN-US"><span>map</span></span>文g名;</span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><strong><span xml:lang="EN-US"><span>/MAPINFO:EXPORTS <wbr></span></strong></span><span>?表示生成?span xml:lang="EN-US"><span>map</span></span>文g中加?span xml:lang="EN-US"><span>exported functions</span></span>Q生?span xml:lang="EN-US"><span>DLL</span></span>文gӞQ?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><strong><span xml:lang="EN-US"><span>/MAPINFO:LINES <wbr></span></strong></span><span>?表示生成?span xml:lang="EN-US"><span>map</span></span>文g中加入代码行信息?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span>׃<span xml:lang="EN-US"><span>/MAPINFO:LINES</span></span>选项?span xml:lang="EN-US"><span>VC8</span></span>以后的版本中不再支持Q因此通过<span xml:lang="EN-US"><span>MAP</span></span>文g中的信息?span xml:lang="EN-US"><span>crash</span></span>地址定位出错代码行就比较困难了,所以这U方案只能在<span xml:lang="EN-US"><span>VC7</span></span>及以前的版本中用?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> <wbr></span><span>一?/span><span><span xml:lang="EN-US">MAP</span></span><span>文g片段CZ如下Q?/span><span><span xml:lang="EN-US"> <wbr></span></span></p> </span> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> <a target=_blank><span><span><img title=clip_image004 border=0 alt=clip_image004 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image004_fbf07de5-1b63-4473-917f-f6695904c9be.jpg" width=494 height=229 v:shapes="_x0000_i1028" real_src="http://s4.sinaimg.cn/bmiddle/48f93b53g79aae50a0e23&690"></span></span> <wbr></a> <wbr></span></p> </span> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> <a target=_blank><span><span><img title=clip_image005 border=0 alt=clip_image005 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image005_93f97fcb-0900-4f57-baf2-29a04f821fa2.jpg" width=494 height=227 v:shapes="_x0000_i1029" real_src="http://s1.sinaimg.cn/bmiddle/48f93b53g79aae6db2060&690"></span></span></a></span></p> </span> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span><span>图中</span><span><span xml:lang="EN-US">Rva+Base</span></span><span>列的地址行函数对应的函数l对地址Q?/span><span><span xml:lang="EN-US">Address</span></span><span>列中冒号后面的地址为函数相对偏Ud址?/span><span><span xml:lang="EN-US"> <wbr> <wbr> <wbr></span></span></p> </span> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> <strong>3</strong></span></span><strong><span>、定?span xml:lang="EN-US"><span>crash</span></span>代码</span></strong></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span>有了上面的介l,定位<span xml:lang="EN-US"><span>crash</span></span>代码很单了。用下面的公式来q行定位Q?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><strong><span>崩溃行偏U?<span xml:lang="EN-US"><span>=</span> </span>崩溃地址 <span xml:lang="EN-US"><span>-</span> </span>崩溃函数l对地址 <span xml:lang="EN-US"><span>+</span> </span>函数相对偏移</span></strong></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr></span> </span><span>我们首先Ҏ崩溃地址Q绝对地址Q,按照扑ֈW?span xml:lang="EN-US"><span>2</span></span>张图?span xml:lang="EN-US"><span>Rva+Base</span></span>列的地址扑ֈ发生崩溃的函敎ͼ卛_溃地址大于该函数行?span xml:lang="EN-US"><span>Rva+Base</span></span>地址且小于下个函数的地址Q,然后扑ֈ该行对应的函数相对偏Ud址Q带入公式中Q就得到了崩溃行偏移Q该DC崩溃行的代码相对于代码所在函数的偏移量。用该值去与第<span xml:lang="EN-US"><span>3</span></span>张图中对应函数冒号后面的偏移量去比较Q最接近的值前面的那个十进制数即ؓ代码所在函C的行受?/span></p> <p><span xml:lang="EN-US"><span> <wbr> <wbr> <wbr> ok</span></span><span>Q到此我们已l成功找C崩溃的代码行Q只不过q种Ҏq是比较费力Qƈ且限制比较多Q我们看看下面的Ҏ?/span></p> <p><span>上篇l出的方案一q要补充几句。通过<span>“crash</span>地址<span> + MAP</span>文g<span>”</span>来定位出错代码位|虽焉要经q比较复杂的地址计算Q但却是最单实现的方式。如果仅仅想通过崩溃地址定位出错的函敎ͼ更加方便了。我在网上找C个解?span>MAP</span>文g的小工具Q可以非常清晰的列出每个函数的地址Qƈ且可以将分析表格导出?span>Excel</span>文g。工具下载地址Q?span><a ><span>http://e.ys168.com/?tinyfun</span></a></span>Q工L录下<span>VCMapper.exe</span>?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>另外上篇主要参考两文章:</span></p> <p><span> <wbr> <wbr> <wbr> <a ><span>http://www.vckbase.com/document/viewdoc/?id=908</span></a></span></p> <p><span> <wbr> <wbr> <wbr> <a ><span>http://www.vckbase.com/document/viewdoc/?id=1473</span></a></span></p> <p> <wbr></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>Ҏ二:崩溃地址<span> + MAP</span>文g<span> + COD</span>文g</span></strong></p> <p><span> <wbr> <wbr> <wbr> </span><span>׃<span>VC8</span>以后的版本都不再支持<span>MAP</span>文g中生代码行信息Q因此我们寻扑֏一U定位方式:<span>COD</span>文g?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>1</span></strong></span><strong><span>?span>COD</span>文g</span></strong></p> <p><span> <wbr> <wbr> <wbr> COD</span><span>文g是一个包含了汇编码、二q制机器码和源代码对应信息的文gQ每一?span>cpp</span>都对应一?span>COD</span>文g。通过q个文gQ我们可以非常方便地q行定位?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>?span>VC6</span>中生?span>COD</span>文g的设|方式ؓQ?span>Project Settings -> C/C++</span>Q在<span> Category </span>中?span> Listing Files</span>Q在<span> Listing file type </span>l合框中?span> Assembly</span>Q?span>Machine code</span>Q?span>and source</span>。在<span>VC8</span>中生?span>COD</span>文g的设|方式ؓQ?span>Project Properties -> C/C++ -> Output Files -> Assembler Output </span>,选择<span> Assembly</span>Q?span>Machine code</span>Q?span>and Source(/Facs)</span>?/span></p> <p><span> <wbr> <wbr> </span><wbr></p> <p><span> <wbr> <wbr> <wbr> <strong><span>2</span></strong></span><strong><span>、定位崩溃行</span></strong></p> <p><span> <wbr> <wbr> <wbr> </span><span>下面通过举例q行说明。现在我有一个基于对话框?span>MFC</span>应用E序<span>CrashTest</span>Q在<span>CCrashTestDlg::OnInitDialog</span>函数中写入导?span>crash</span>的代码语句(W?span>99</span>行)Q源文g如下Q?/span></p> <p><span> <wbr> <wbr> <wbr> <a target=_blank><span><span><img title=clip_image006 border=0 alt=clip_image006 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image006_887560ee-8b72-42ea-a623-220dcb47ac1f.jpg" width=360 height=169 v:shapes="_x0000_i1030"></span></span></a></span></p> <p><span> <wbr> <wbr> <wbr> </span><span>Ҏ崩溃地址Q?span>0x<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="4012" unitname="a">004012A</st1:chmetcnv>3</span>Q以?span>MAP</span>文gQ定位片D图片如下)Q定?span>crash</span>函数?span>OnInitDialog</span>Qƈ且我们可以很Ҏ地计出崩溃地址相对于崩溃函数的偏移量ؓ<span> 0x<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="4012" unitname="a">004012A</st1:chmetcnv>3 - 0x004011E0 = 0xC3</span>?/span></p> <p><span> <wbr> <wbr> <wbr> <a target=_blank><span><span><img title=clip_image007 border=0 alt=clip_image007 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image007_dc9962ce-f969-4c7f-aa0b-e973cac321ce.jpg" width=494 height=36 v:shapes="_x0000_i1031" real_src="http://s11.sinaimg.cn/bmiddle/48f93b53g79bbc5f052da&690"></span></span></a></span></p> <p><span> <wbr> <wbr> <wbr> </span><span>再来看看<span>CrashTestDlg.cod</span>文gQ我们根据文件中源码信息扑ֈ<span>OnInitDialog</span>函数信息片段Q?/span></p> <p><span> <wbr> <wbr> <wbr> <a target=_blank><span><span><img title=clip_image008 border=0 alt=clip_image008 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image008_00ac91bc-99ca-4d4f-86cd-b0d1d23bb10a.jpg" width=494 height=231 v:shapes="_x0000_i1032" real_src="http://s4.sinaimg.cn/bmiddle/48f93b53g79bc3c02d3f3&690"></span></span></a></span></p> <p><span> <wbr> <wbr> <wbr> </span><span>可以看到囄中第一行ؓ<span>OnInitDialog</span>函数汇编代码的v始行Q找?span>“int * p = NULL;”</span>q一句源码,其前面的<span>98</span>表示q行代码在源文g中的行号Q下面的<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="0" unitname="C"><span>000c</span></st1:chmetcnv><span>1</span>表示相对于函数开始位|的偏移量,后面?span>“<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="33" unitname="C">33 c</st1:chmetcnv><st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="0" unitname="”">0”</st1:chmetcnv></span>为机器码Q?span>“xor eax</span>Q?span>eax”</span>为汇~码。那么我们根据前面算出来的偏U量<span>0xC3</span>Q找到对应出错的语句?span>99</span>行:<span>“*p = 5;”</span>?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>ȝ一下定位步骤:</span></p> <p><span> <wbr> <wbr> <wbr> 1) </span><span>Ҏ公式 <strong><span>崩溃语句在函C偏移地址<span> = </span>崩溃地址<span> - </span>崩溃函数地址</span></strong> 计算出偏U量<span>X</span>Q?/span></p> <p><span> <wbr> <wbr> <wbr> 2) </span><span>Ҏ公式 <strong><span>崩溃语句?span>COD</span>文g中地址<span> = </span>崩溃函数?span>COD</span>文g中地址<span> + <wbr>X</span></span></strong><span> </span>计算出地址<span>Y</span>。其中崩溃函数在<span>COD</span>文g中地址?span>COD</span>文g中函数v始括?span>“{”</span>后面表明的地址Q一般情况下?span>0x0000</span>Q?/span></p> <p><span> <wbr> <wbr> <wbr> 3) </span><span>Ҏ<span>Y</span>?span>COD</span>文g中找到对应代码行?/span></p> <p><span> <wbr> <wbr> </span><wbr></p> <p><span> <wbr> <wbr> <wbr> ok</span><span>Q方案二介绍完了。这U方法最大的好处是没?span>VC</span>开发环境版本限Ӟ而且<span>COD</span>文g里面包含的信息更加丰富,不但可以帮助我们定位<span>crash</span>Q还能帮我们分析很多东西。当Ӟq也D~译生成了很多信息文件?/span></p> <p><span>Ҏ前面两篇博文Q我们要定位崩溃行代码,必须要自己根据相关信息文件进行计。如果需要处理的量比较大Q恐怕会很费力气。有没有更简单快速的办法呢?</span></p> <p><span> <wbr> <wbr> <wbr> </span><span>最直接的想法就是写一个小工具Q根据规则和信息q行自动定位Q不q开发v来也是要费一番功夫的。o人开心的是,我们可以扑ֈcM的工P而且是开源免费的Q程序员的世界也许很多时候都是这么单U而乐于分享!</span></p> <p><span> <wbr> <wbr> </span><wbr></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>Ҏ三:崩溃地址<span> + PDB</span>文g<span> + CrashFinder</span></span></strong></p> <p><span> <wbr><wbr> <wbr><wbr> <wbr><wbr> CrashFinder</span><span>是一个开源工P作者是<span>John Robbin</span>Q大家可以去他的<span>blog</span>上去扑օ?span>CrashFinder</span>的信息。我们这里以<span>CrashFinder2.5</span>版本Z介绍Q相x章链接ؓQ?span><a ><span>http://www.wintellect.com/CS/blogs/jrobbins/archive/2006/04/19/crashfinder-returns.aspx</span></a></span></span></p> <p><span> <wbr><wbr> <wbr><wbr> <wbr> <strong><span>1</span></strong></span><strong><span>?span>PDB</span>文g</span></strong></p> <p><span> <wbr> <wbr> <wbr> PDB</span><span>Q?span>Program Database</span>Q文件中包含?span>exe</span>E序所有的调试相关信息Q具体可以查?span>MSDN</span>。当~译选项讄?span>/Zi</span>Q链接选项讄?span>/DEBUG</span>Q?span>/OPT:REF</span>Ӟ׃生成工程?span>.pdb</span>文g。具体到<span>VC2005</span>中,是<span> Project Propertise -> C/C++ -> General -> Debug Information Format </span>设|ؓ<span> Program Database</span>Q?span>/Zi</span>Q,<span>Linker -> Debugging -> Generate Debug Info </span>设|ؓ<span> Yes</span>Q?span>/Debug</span>Q,<span>Linker -> Optimization -> References </span><wbr>设|ؓ<span> Eliminate <wbr>Unreferenced Data</span>Q?span>/OPT:REF</span>Q?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>只要讄以上选项Q?span>release</span>版本也能生成<span>PDB</span>文g。当Ӟ对应的应用程序也会稍大?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>2</span></strong></span><strong><span>?span>CrashFinder</span></span></strong></p> <p><span> <wbr> <wbr> <wbr> CrashFinder</span><span>能够q行需要两个条Ӟ一是系l必要?span>dbghelp.dll</span>文gQ二?span>PDB</span>文g必须?span>exe</span>文g在一个\径下。对?span>dbghelp.dll</span>Q一般在pȝ<span>system32</span>路径下都有,如果没有下蝲一个放到这个目录下可以了?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>先看一?span>CrashFinder</span>的界面?/span></p> <p><span> <wbr> <wbr> </span><wbr></p> <p align=center><span><a target=_blank><span><span><img title=clip_image009 border=0 alt=clip_image009 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image009_60e666ee-6f31-4132-929c-3ef20c5a9fde.jpg" width=494 height=327 v:shapes="_x0000_i1033"></span></span></a></span></p> <p><span> <wbr> <wbr> <wbr> </span><span>用v来也非常单。首先选择<span>File->New</span>或点dh新徏按钮Q选择要调试的<span>exe</span>文g打开Q会发现<span>exe</span>及所依赖?span>dll</span>文g信息都已l加载进来。在下半部分的编辑框中输入崩溃地址Q?span>16</span>q制Q,点右边的<span>“Find”</span>按钮Q就会在下面昄崩溃的源文g路径、名UC及崩溃所在行号了Q如下图所C?/span></p> <p align=center><span><a target=_blank><span><span><img title=clip_image010 border=0 alt=clip_image010 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image010_4b25ce3c-1bea-4818-be58-230d6cba8a6a.jpg" width=494 height=370 v:shapes="_x0000_i1034" real_src="http://s10.sinaimg.cn/bmiddle/48f93b53g7a37da3b3c69&690"></span></span></a></span></p> <p><span> <wbr> <wbr> <wbr> </span><span>?span>CrashFinder</span>q行<span>crash</span>定位真的非常方便。但是我在用过E中发现了一?span>bug</span>Q每ơ启动程序后Q直接新建的话加载进来的<span>exe</span>模块都显C叉Q提C找不到<span>debug symbols</span>。但是用打开按钮随便打开一个文件失败后Q再新徏p成功。猜可能是直接新徏Q定?span>PDB</span>文g时的路径不对引v的。有源码Q但是懒的看了呵呵,大家感兴可以试一下?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>好了Q方案三׃l到q里Q后面还有更加强大的Ҏ<span> : )</span></span></p> <p><span>前面几个Ҏ都是直接定位<span>crash</span>的代码位|,但是在比较大型的E序中,只知道这个信息还是远q不够的Q我们希望知道更多关于调用函数顺序及变量值等信息Q也是<span>crash</span>时调用堆栈信息?/span></p> <p> <wbr></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>Ҏ四:<span>SetUnhandledExceptionFil<wbr>ter + StackWalker</span></span></strong></p> <p><span> <wbr> <wbr> <wbr> </span><span>q个Ҏ需要自己动手往工程里添加代码了。要实现上面的想法,需要做两g事情Q?span>1</span>、需要在<span>crash</span>时有Z对程序堆栈进行处理;<span>2</span>、对堆栈信息q行攉?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>1</span></strong></span><strong><span>?span>SetUnhandleExceptionFilt<wbr>er</span>函数</span></strong></p> <p><span> <wbr> <wbr> <wbr> Windows</span><span>q_下的<span>C++</span>E序异常通常可分ZU:l构化异常(<span>Structured Exception</span>Q可以理解ؓ与操作系l相关的异常Q和<span>C++</span>异常。对于结构化异常处理Q?span>SEH</span>Q,可以扑ֈ很多资料Q在此不l说。对?span>crash</span>错误Q一般由未被正常捕获的异常引P<span>Windows</span>操作pȝ提供了一?span>API</span>函数可以在程?span>crash</span>之前有机会处理这些异常,是<span>SetUnhandleExceptionFilt<wbr>er</span>函数。(<span>C++</span>也有一个类似函?span>set_terminate</span>可以处理未被捕获?span>C++</span>异常。)</span></p> <p><span> <wbr> <wbr> <wbr> SetUnhandleExceptionFilt<wbr>er</span><span>函数声明如下Q?/span></p> <p><span> <wbr> <wbr> <wbr> LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFil<wbr>ter(<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> LPTOP_LEVEL_EXCEPTION_FILTER <em>lpTopLevelExceptionFilte<wbr>r</em><br> <wbr> <wbr> <wbr> );</span></p> <p><span> <wbr> <wbr> <wbr> </span><span>其中<span> LPTOP_LEVEL_EXCEPTION_FILTER </span>定义如下Q?/span></p> <p><span> <wbr> <wbr> <wbr> typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(<br> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> __in struct _EXCEPTION_POINTERS *ExceptionInfo<br> <wbr> <wbr> <wbr> );<br> <wbr> <wbr> <wbr> typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;</span></p> <p><span> <wbr> <wbr> <wbr> </span><span>单来_<span>SetUnhandleExceptionFilt<wbr>er</span>允许我们讄一个自q函数作ؓ全局<span>SEH</span>qo函数Q当E序<span>crash</span>前会调用我们的函数进行处理。我们可以利用的?span> _EXCEPTION_POINTERS </span>l构cd的变?span>ExceptionInfo</span>Q它包含了对异常的描qC及发生异常的U程状态,qo函数可以通过q回不同的值来让系ll运行或退出应用程序?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>关于<span> SetUnhandleExceptionFilt<wbr>er </span>函数的具体用法和CZ请参?span>MSDN</span>?/span></p> <p> <wbr></p> <p><span> <wbr> <wbr> <wbr> <strong><span>2</span></strong></span><strong><span>?span>StackWalker</span></span></strong><span><br> <wbr> <wbr> <wbr> </span><span>现在我们已经有机会可以在<span>crash</span>之前对程序状态信息进行处理了Q只需要生成ƈ保存堆栈信息大功告成了?span>Windows</span>?span>dbghelp.dll</span>库提供了一个函数可以得到当前堆栈信息:<span>StackWalk64</span>Q在<span>Win2K</span>以前版本中ؓ<span>StackWalk</span>Q。该函数声明如下Q?/span></p> <p><span> <wbr> <wbr> <wbr> BOOL WINAPI StackWalk64(<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> DWORD <em>MachineType</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hProcess</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hThread</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in_out <wbr> <wbr> <wbr> <wbr> <wbr> LPSTACKFRAME64 <em>StackFrame</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in_out <wbr> <wbr> <wbr> <wbr> <wbr> PVOID <em>ContextRecord</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PREAD_PROCESS_MEMORY_ROUTINE64 <em>ReadMemoryRoutine</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PFUNCTION_TABLE_ACCESS_ROUTINE64 <em>FunctionTableAccessRouti<wbr>ne</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PGET_MODULE_BASE_ROUTINE64 <em>GetModuleBaseRoutine</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PTRANSLATE_ADDRESS_ROUTINE64 <em>TranslateAddress</em><br> <wbr> <wbr> <wbr> );<br> <wbr> <wbr> <wbr> </span><span>该函数的具体用法可以参?span>MSDN</span>。在q里推荐一个牛人写好的<span>StackWalker</span>Q可以直接拿来用Q开源的?span>StackWalker</span>提供了一个基c,l出了几个简单的接口Q可以方便地生成堆栈信息Qƈ且支持一pd<span>VC</span>版本Q非常好用。我们可以自己写一个子c,q载虚函数<span>OnOutput</span>Q就可以堆栈信息输Zؓ特定格式了?span>StackWalker</span>的地址为:<span><a ><span>http://www.codeproject.com/KB/threads/StackWalker.aspx</span></a></span>?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>不过对于<span>Release</span>版本来说Q?span>StackWalk64</span>函数获得的堆栈信息有可能不完整。如果异常是?span>MFC</span>的模块抛出,那么获得的堆栈可能缺前面调用模块信息。另外,<span>StackWalk64</span>需要最新的<span>dbghelp.dll</span>文g支持才能工作Q要正确输出<span>crash</span>的函数名和行P需要要<span>pdb</span>文g支持。以上不x可能影响输出信息的完整性和效果Q而对于发布在外的E序Q要带上<span>pdb</span>文g几乎不可能,因此q个Ҏq是有缺憄Q比较适用于本地的<span>release</span>版本调试?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>下一我们将介绍一个更加完善的解决Ҏ</span></p> <p><span>当我们把自己?span>release</span>版本E序发布出去以后Q一般都是在用户的机器上q行。这U情况下Q对于第四种ҎQ因为需?span>pdb</span>文g才能够正生成堆栈调用的函数行号及代码行P因此Ҏ四只适用于本?span>release</span>版的调试Q否则只能生成不完整的堆栈信息。对于前三种ҎQ其实只需要用户告知崩溃地址Q然后在本地查找<span>crash</span>地址可以了Q但是定?span>crash</span>的过E非怸方便Q如?span>crash</span>的情冉|较多Q前三种Ҏ都不合适。而且Q前三种Ҏ均不能生成堆栈调用信息,对于<span>debug</span>的作用有限?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>下面我们来看一个更加完善的解决Ҏ?/span></p> <p> <wbr></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>Ҏ五:<span>SetUnhandledExceptionFil<wbr><wbr>ter + Minidump</span></span></strong></p> <p><span> <wbr> <wbr> <wbr> SetUnhandleExceptionFilt<wbr>er</span><span>函数我们已经介绍q了Q本Ҏ的思\q是要利用我们自q异常处理函数Q来生成<span>minidump</span>文g?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>1</span></strong></span><strong><span>?span>Minidump</span>概念</span></strong></p> <p><span> <wbr> <wbr> <wbr> minidump</span><span>Q小存储器{储)可以理解Z?span>dump</span>文gQ里面记录了能够帮助调试<span>crash</span>的最有用信息。实际上Q如果你?pȝ属?span> -> </span>高<span> -> </span>启动和故障恢?span> -> </span>讄<span> -> </span>写入调试信息 中选择<span>“</span>内存{?span>(64 KB)”</span>的话Q当pȝ意外停止旉会在<span>C:\Windows\Minidump\</span>路径下生成一?span>.dmp</span>后缀的文Ӟq个文g是<span>minidump</span>文gQ只不过q个是内核态的<span>minidump</span>?/span></p> <p><span> <wbr> <wbr> </span><wbr><span>我们要生成的是用h的<span>minidump</span>Q文件中包含了程序运行的模块信息、线E信息、堆栈调用信息等。而且ZW合?span>mini</span>的特性,<span>dump</span>文g是压~过的?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>2</span></strong></span><strong><span>、生?span>minidump</span>文g</span></strong></p> <p><span> <wbr> <wbr> <wbr> </span><span>生成<span>minidump</span>文g?span>API</span>函数?span>MiniDumpWriteDump</span>Q该函数需?span>dbghelp.lib</span>支持Q其原型如下<span>:</span></span></p> <p><span> <wbr> <wbr> <wbr> BOOL WINAPI MiniDumpWriteDump(<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hProcess</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> DWORD <em>ProcessId</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hFile</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> MINIDUMP_TYPE <em>DumpType</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PMINIDUMP_EXCEPTION_INFORMATION <em>ExceptionParam</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PMINIDUMP_USER_STREAM_INFORMATION <em>UserStreamParam</em>,<br> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PMINIDUMP_CALLBACK_INFORMATION <em>CallbackParam</em><br> <wbr> <wbr> <wbr> );</span></p> <p><span> <wbr> <wbr> <wbr> </span><span>在我们的异常处理函数中加入以下代码:</span></p> <p><span> <wbr> <wbr> <wbr> HANDLE hFile = ::CreateFile( _T("E:\\dumpfile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);<br> <wbr> <wbr> <wbr> <wbr> if( hFile != INVALID_HANDLE_VALUE)<br> <wbr> <wbr> <wbr> <wbr> {<br> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> MINIDUMP_EXCEPTION_INFORMATION einfo;<br> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> einfo.ThreadId = ::GetCurrentThreadId();<br> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> einfo.ExceptionPointers = pExInfo;<br> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> einfo.ClientPointers = FALSE;</span></p> <p><span> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &einfo, NULL, NULL);<br> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> ::CloseHandle(hFile);<br> <wbr> <wbr> <wbr> <wbr> }</span></p> <p><span> <wbr> <wbr> <wbr> </span><span>其中Q?span>pExInfo</span>变量为异常处理函?span>PEXCEPTION_POINTERS</span>cd的参数。具体请参?span>MSDN</span>?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>3</span></strong></span><strong><span>、调?span>minidump</span></span></strong></p> <p><span> <wbr> <wbr> <wbr> </span><wbr><span>调试<span>dump</span>文g首先需?span>pdb</span>文gQ因此我?span>build</span>E序旉要设|?span> Debug Infomation Format </span>?span> “Program Database</span>Q?span>/Zi</span>Q?span>”</span>。其ơ,我们q要保所用的<span>dump</span>文g与源代码?span>exe</span>?span>pdb</span>文g版本是一致的Q这要求我们必须l护好程序版本信息?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>调试<span>minidump</span>最方便的环境就?span>VS</span>了,我们只要?span>.dmp</span>?span>.exe</span>?span>.pdb</span>文g攑֜一个\径下Q保证源代码文g的\径与~译时的路径一致就可以了,剩下的就?span>VS</span>帮我们完成。双?span>.dmp</span>文g或者在文g打开工程中选择<span>“dump files”</span>Q加?span>dump</span>文gQ然后按<span>F5</span>q行p直接恢复<span>crash</span>时的现场了,你可以定?span>crash</span>的代码,可以查看调用堆栈Q可以查看线E和模块信息<span>...</span>一切都跟你讄断点调试一P太强大了Q看个截囑֐?/span></p> <p><span><a target=_blank><span><span><img title=clip_image012 border=0 alt=clip_image012 src="http://www.shnenglu.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image012_3df8e4ef-d0e8-49d3-a9af-47f58cc713c6.jpg" width=616 height=469 v:shapes="_x0000_i1035"></span></span></a><a target=_blank></a></span></p> <p><span> <wbr> <wbr> <wbr> </span><span>需要注意的是,对于<span>release</span>版的E序来说Q很多代码是l过~译器优化过的,因此定位的时候可能会有所偏差Q大家可以考虑讄选项L代码优化?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>其他可以调试<span>minidump</span>的工兯?span>WinDbg</span>{,大家可以查阅相关资料?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>本文主要参考了q篇文章Q?span><a ><span>http://vicchina.51.net/research/other/seh/minidumps/intro.htm</span></a></span>?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>下一,我们给Z个调?span>release</span>发布E序的完解x案,适合用户量较大的应用发布E序的调试?/span></p> <p><span>上一我们已l给ZҎQ能够非常方便的通过<span>dump</span>文g?span>crash</span>错误q行调试和定位;从整个流E上看还差最后一步,x样拿到<span>crash</span>时生的<span>dump</span>文g。如果可以让用户把文件发送过来自然不错,但对于类似免费共享Y件等在互联网上发布的E序呢?我们的用h不确定的Q而且用户量有可能非常大,即我们能想办法联系到用PM能挨个去攉<span>crash</span>信息吧?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>我们需要一U方案,能够提供<span>crash</span>信息汇报功能?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>我们可以架设一台服务器专门q行信息攉Q只要客L?span>crash</span>时正汇报即可,但是相应的维护成本和开发难度也不可忽视。有没有更简单的Ҏ呢?q记得我的博?span>“<a target=_blank><span><span>为程序添加自动发送Email</span></span><span><span>功能</span></span></a>”</span>吗?q就是简单有效的ҎQ?/span></p> <p> <wbr></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>Ҏ六:<span>minidump + email</span></span></strong></p> <p><span> <wbr> <wbr> <wbr> </span><span>我们只需要在异常处理Ӟ先生?span>minidump</span>信息文gQ再?span>email</span>方式文件发送到指定邮箱p了。剩下的是我们每天查看邮箱Q提?span>dump</span>文gq行调试了?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>1</span></strong></span><strong><span>?span>Email</span>功能</span></strong></p> <p><span> <wbr> <wbr> <wbr> </span><span>首先我们来看一?span>email</span>发送都需要哪些相关信息?/span></p> <p><span> <wbr> <wbr> <wbr> a</span><span>、发送端邮箱帐户Q?/span></p> <p><span> <wbr> <wbr> <wbr> b</span><span>、接收端邮箱帐户Q?/span></p> <p><span> <wbr> <wbr> <wbr> c</span><span>?span>email</span>标题Q一般应有Y件名U及版本信息Q?/span></p> <p><span> <wbr> <wbr> <wbr> d</span><span>?span>email</span>正文Q一般应有简单的<span>crash</span>信息提示Q以区别不同原因造成?span>crash</span>Q?/span></p> <p><span> <wbr> <wbr> <wbr> <wbr>e</span><span>?span>email</span>附gQ当然就是我们的<span>dump</span>文g了,q可以加上Y件生成的<span>log</span>文g{?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>当然Q对于标题应该尽量多加一些信息区别引?span>crash</span>的原因,比如?span>crash</span>的地址信息加到标题中;因ؓ当每天有成百上千?span>crash</span>汇报上来Q重复的<span>crash</span>占大多数Q把旉都花在区分它们n上有点太费。由此看来,前面Ҏ中提到的<span>StackWalker</span>q是有些用处的,我们可以用它来生成一?span>crash</span>的文字描qC息,写到标题或正文中厅R?/span></p> <p><span> <wbr> <wbr> <wbr> dump</span><span>文g的大是否适合作ؓ邮g的附件呢Q实际上<span>minidump</span>产生的文件一般在?span>K</span>到几?span>K</span>之间Q作?span>email</span>的附件没有Q何问题?/span></p> <p><span> <wbr> <wbr> <wbr> </span><span>关于发?span>email</span>相关技术细节,已经?span>“<a target=_blank><span><span>为程序添加自动发送Email</span></span><span><span>功能</span></span></a>”</span>文中介绍了,大家可以参考。其实,Ҏ受邮׃邮g的处理还是很Ҏ费力的,大家可以考虑写一些脚本将处理程自动化,提高效率?/span></p> <p><span> <wbr> <wbr> <wbr> <strong><span>2</span></strong></span><strong><span>?span>google breakpad</span></span></strong></p> <p><span> <wbr> <wbr> <wbr> google breakpad</span><span>是一个开源的跨^?span>crash report</span>pȝQ光从开源和跨^台这两个特点上来看,它就以U的上是一个完善而有效的工具了。其实,<span>breakpad</span>在整?span>crash report</span>层次上给Z一个系l的解x案,也就是说它几乎能适应各种软g、各U^台的应用要求?/span></p> <p><span> <wbr> <wbr> <wbr> breakpad</span><span>的整体思\跟上面介l的Ҏ是相似的Q只不过最后提?span>dump</span>文g的方式更加完善。大家有兴趣可以d的官方网址查阅相关资料Q?span><a ><span>http://code.google.com/p/google-breakpad/</span></a></span>?/span></p> <p> <wbr></p> <p><span> <wbr> <wbr> <wbr> ok</span><span>Q关于调?span>release</span>发布E序?span>crash</span>错误pd文章写完了。这几篇文章l出的方案由单到复杂Q由陋到完善Q对<span>crash</span>调试有了一个比较全面的ȝ。当Ӟ其中涉及到的概念和技术还很多Q需要我们去不断学习和领悟,也希望大家能够互怺?/span></p> </div> <img src ="http://www.shnenglu.com/Walker/aggbug/146153.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Walker/" target="_blank">漫步者?…?K?/a> 2011-05-11 07:53 <a href="http://www.shnenglu.com/Walker/articles/146153.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dll pe 文g?/title><link>http://www.shnenglu.com/Walker/articles/144121.html</link><dc:creator>漫步者?amp;…?K?/dc:creator><author>漫步者?amp;…?K?/author><pubDate>Wed, 13 Apr 2011 06:41:00 GMT</pubDate><guid>http://www.shnenglu.com/Walker/articles/144121.html</guid><wfw:comment>http://www.shnenglu.com/Walker/comments/144121.html</wfw:comment><comments>http://www.shnenglu.com/Walker/articles/144121.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/Walker/comments/commentRss/144121.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Walker/services/trackbacks/144121.html</trackback:ping><description><![CDATA[问:1.1<br>我知道程序中最重要的段是textD,请告诉我text D在哪?<br>{:1.1<br>text D在文g偏移0x400处,大小0x200字节Q该区可q行Q可dQ包含代码?br>该区在内存中RVA 0x1000处,大小0x1000.<br><br>问:1.2<br>我用urtraedit 打开hello.exe 看了Q在0x400?0x600处,大部分都?Qؓ什么这样呢?br>{:1.2<br><span id="4sayquo" class=highlight><strong><font color=#ff0000>pe</font></strong></span> 格式大部分文仉是这Pq是寚w所要求的,文g寚w?x200, 内存寚w?x1000<br>你可以在NT_Option_Header 的Section_Alignment, File_Alignment 域中看到q两个数据?br><br>//<br>// Optional header format.<br>//<br><br>ty<span id="yuygyyk" class=highlight><strong><font color=#ff0000>pe</font></strong></span>def struct _IMAGE_OPTIONAL_HEADER {<br>//<br>// Standard fields.<br>//<br><br>WORD Magic;<br>BYTE MajorLinkerVersion;<br>BYTE MinorLinkerVersion;<br>DWORD SizeOfCode;<br>DWORD SizeOfInitializedData;<br>DWORD SizeOfUninitializedData;<br>DWORD AddressOfEntryPoint;<br>DWORD BaseOfCode;<br>DWORD BaseOfData;<br><br>//<br>// NT additional fields.<br>//<br><br>DWORD ImageBase;<br>DWORD SectionAlignment;<br>DWORD FileAlignment;<br>WORD MajorO<span id="ksukmuc" class=highlight><strong><font color=#ff0000>pe</font></strong></span>ratingSystemVersion;<br>WORD MinorO<span id="eukcce2" class=highlight><strong><font color=#ff0000>pe</font></strong></span>ratingSystemVersion;<br>WORD MajorImageVersion;<br>WORD MinorImageVersion;<br>WORD MajorSubsystemVersion;<br>WORD MinorSubsystemVersion;<br>DWORD Win32VersionValue;<br>DWORD SizeOfImage;<br>DWORD SizeOfHeaders;<br>DWORD CheckSum;<br>WORD Subsystem;<br>WORD DllCharacteristics;<br>DWORD SizeOfStackReserve;<br>DWORD SizeOfStackCommit;<br>DWORD SizeOfHeapReserve;<br>DWORD SizeOfHeapCommit;<br>DWORD LoaderFlags;z<br>DWORD NumberOfRvaAndSizes;<br>IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];<br>} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;<br><br>问:1.3<br>慢点Q别一下子贴那么多东西Q我q没有找?_IMAGE_OPTIONAL_HEADER 的位|呢Q?br>告诉我怎样?br>{:1.3<br>贴上那个_IMAGE_OPTIONAL_HEADERl构好说话,它的位置紧跟?MAGE_FILE_HEADER 之后<br>告诉你个技巧,那个Magic对NT x86来讲L010B,在头文g扑ֈ那个010b,是IMAGE_OPTIONAL_HEADER32 l构的地址?br><br>问:1.4<br>问题来多了?br>_IMAGE_OPTIONAL_HEADER q没有说清呢Q又出来一个IMAGE_FILE_HEADER。先不管IMAGE_FILE_HEADER<br>先按你的技巧,在头部找?10b, 因ؓ是little endial, 在ultraedit 中要?b 01.<br>好,扑ֈ了,那?50 45 00 00 (ascii PE)相距不远Q在偏移D8处,按你所说SectionAlignment和FileAlignment 应该在结构第9个,W?0个DWORD 处?br>好,扑ֈ了,在f8处有00001000Q?FC处ؓ00 00 02 00 Q我已经考虑了endian,以后不用提醒了)?br>{:1.4<br>呀Q进步不吗Q这样一下子你就把IMAGE_OPTIONAL_HEADER32 中所有的东西都找出来了?br><br>问:1.5<br>是的Q我可以把Optional header中所有东襉K扑և来,但我现在除了刚才介绍的第9个DWORD为内存对齐大,W?0个DWORD为文件对齐大,其它我都不知道是q什么的Q?br>{:1.5<br>别着急,其实q是很容易理解的Q从字面意义p猜大概。不q我们现在还不是通读Optional header的时候,q是拣我们最兛_的问题插手吧?br><br>问:1.6 <br>q是回到text D上来吧Q刚才你对textD大,位置Q属性分析的头头是到<br>你是从那看出来的Q?br>{:1.6<br>是从section header 中看出来的,每一个section, 都有一个section header 描述其位|,大小Q属性?br>section header 的结构是q样定义?br>#define IMAGE_SIZEOF_SHORT_NAME 8<br><br>ty<span id="qy2mgiy" class=highlight><strong><font color=#ff0000>pe</font></strong></span>def struct _IMAGE_SECTION_HEADER {<br>BYTE Name[IMAGE_SIZEOF_SHORT_NAME];<br>union {<br>DWORD PhysicalAddress;<br>DWORD VirtualSize;<br>} Misc;<br>DWORD VirtualAddress;<br>DWORD SizeOfRawData;<br>DWORD PointerToRawData;<br>DWORD PointerToRelocations;<br>DWORD PointerToLinenumbers;<br>WORD NumberOfRelocations;<br>WORD NumberOfLinenumbers;<br>DWORD Characteristics;<br>} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;<br><br>问:1.7<br>呦,慢点Q怎么又往外甩l构Q我很菜Q?哦,不太多,q行吧?br>不过你还是告诉我具体位置在哪吧,我好拿结构和数据对对受?br>{:1.7<br>好,正是q种<span id="2cowmo4" class=highlight><strong><font color=#ff0000>学习</font></strong></span>Ҏ。你一定能学会的?br>节表头是一个数l,它把所有节的位|,长度Q属性放在了一?br>紧跟在option header 之后Q所以你从文件头部往下找可以了?br>看到IMAGE_SECTION_HEADERl构的第一个成员了吗,它是<br>BYTE Name??br>q是节名Uͼ你要扄text D名字就?.text, 你看ultraedit<br>ascii 码区L件开始不q的地方Q有一?text, 对应的二q制<br>数据?E 74 65 78 74, q就是text 端IMAGE_SECTION_HEADER?br><br>问:1.8<br>原来玄机在这里呀。我试试看。哦Q看见了Q在1B8处???br>字节是节名称。后面的00 00 00 28 到底是物理地址q是虚拟大小Q?br>Q偷LQ虚拟大,表示内存中只?x28个字节有效,其它全是0Q,在后?0 00 10 00 是虚拟相对地址 俗称RVA, 是在内存中相对与v始地址的偏UR再后面00 00 02 00 为SizeOfRawDataQ?是文g中大,再后?00 00 04 00 ?br>PointerToRawDataQ是文g的偏U?后面有三个DWORD 全是0Q他?br>是重定位信息和行P很好QEXE文g可以不用这些。最后一?br>60 00 00 20 代表属性可读,可写Q是代码。好Q我l于理解你的W一句话了?br>不解释一下,我怎么能一下子听的懂呢Q?谢谢你?br>那么我又有问题了。那E序针真是搜索这?text字符串找到Text 节表头吗Q?br>{:1.8<br>不是。前面说q,节表头紧随Optional header 之后?br><br>问:1.9<br>Optional header l构变量太多Q我C一下都没数清,到底占多个字节呢?<br>{:1.9<br>正等着你这一问呢Q是啊,数都C清,U|现在C了将来也Ҏ忘?br>估计微Y也想Cq一点,他把OPTION header 的大放C _IMAGE_FILE_HEADER 的一个变量中Q?br>下面是_IMAGE_FILE_HEADER 的定?br>ty<span id="w2kauce" class=highlight><strong><font color=#ff0000>pe</font></strong></span>def struct _IMAGE_FILE_HEADER {<br>WORD Machine;<br>WORD NumberOfSections;<br>DWORD TimeDateStamp;<br>DWORD PointerToSymbolTable;<br>DWORD NumberOfSymbols;<br>WORD SizeOfOptionalHeader;<br>WORD Characteristics;<br>} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;<br>SizeOfOptionalHeader 一般L0xE0<br><br>问:1.10<br>我今天已l学了不东西了Q看样子后面q很多的样子。再问最后一个问题?br>FILE_HEADER 在文件什么位|呢?br>{:1.10<br>q个单,在PE标识W后面。看C吗,在C0处,ascii 是PE. 二进制是50 45 00 00<br><br>代学生:<br>哦,看到了,今天10个问题已l满了,我还惛_Q可是有点篏了。。?br>代老师Q?br>今天到q里吧,好好休息一下?br><br><br>引言Q?上一ơ以hello.exe ZQ介l了<span id="ksmmms2" class=highlight><strong><font color=#ff0000>pe</font></strong></span> 文g_节表和导入表?br>q一ơ我们以 count.dll ZQ介l导和重定位?br>count.dll 是罗云斌win32汇编~程中的例子E序Q因其短,故被选中?br><br>?.1Qdll Z么叫动态连接库Q与q_的静态连接有什么不同?br>{?.1Q静态连接库在编译连接时由link E序把库文g直接d到运行程序中?br>动态连接库在编译连接时只是把插桩加C码里。运行时由加载器载入<br>内存Q修Ҏ桩代码指向正确的地址。这个过E在上一讲中已经说过了?br><br>?.2Q既然是库函敎ͼ׃有一堆函数构成,那么是否每个函数都可以被外边调用呢?<br>{?.2Q库函数可分Zc,一个是库入口函数?br>一cMؓ可被外部调用Q叫导出库函数?br>一cM能被外部调用Q我们叫它私有函数吧。因为它没有向外提供接口?br><br>?.3Q导出函数是怎样向外提供接口的呢Q?或者说我们怎样才能使用导出函数呢?<br>{?.3Q?q个问题是我们的重点Q我们结合实例来吧,慢慢把它讲清楚?br>用ultraedit 打开q个dll 文g?br>ultraedit看到的是最原始的文Ӟ其它众多?span class=highlight><strong><font color=#ff0000>pe</font></strong></span> 分析软g都是从原始文件分析得到的。哦Q当Ӟq话说的多余了?br>大概览一下这个文件?br>区分一下dos_ PE _ 节表Q?有几个块l成。这些都是很明显的。上一讲中已经说过了?br>哦,上一讲讲的是exe, q里是dll, 不过它们都是PE 文gQ?格式是一L?br><br>Z复习一下上ơ内容,q可以说是上?讲的_֍了,却是以count.dll ZQ同样通俗易懂Q?br>dos _ 标记 "MZ"<br>00000000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ?........ ..<br>00000010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ?......@.......<br>00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................<br>00000030 00 00 00 00 00 00 00 00 00 00 00 00 C0 00 00 00 ............?..<br>PE _ 标记 "PE"<br>000000C0 50 45 00 00 4C 01 04 00 F6 34 EB 3C 00 00 00 00 PE..L...??....<br>000000D0 00 00 00 00 E0 00 0E 21 0B 01 05 0C 00 02 00 00 ....?.!........ <br>从dos?0x3c 处也能看出PE 头位|?br>节表Q?有明昄字符串标讎ͼ此处?.text"<br>000001B0 2E 74 65 78 74 00 00 00 .text...<br>000001C0 70 00 00 00 00 10 00 00 00 02 00 00 00 04 00 00 p...............<br>000001D0 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 ............ ..`<br>000001E0 2E 72 64 61 74 61 00 00 BC 00 00 00 00 20 00 00 .rdata..?... ..<br>000001F0 00 02 00 00 00 06 00 00 00 00 00 00 00 00 00 00 ................<br>00000200 00 00 00 00 40 00 00 40 2E 64 61 74 61 00 00 00 ....@..@.data...<br>00000210 04 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 .....0..........<br>00000220 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 C0 ............@..?<br>00000230 2E 72 65 6C 6F 63 00 00 2C 00 00 00 00 40 00 00 .reloc..,....@..<br>00000240 00 02 00 00 00 08 00 00 00 00 00 00 00 00 00 00 ................<br>00000250 00 00 00 00 40 00 00 42 ....@..B........ <br>C数节表有4?br>从PE ?0xc7处也能看出来?br>大致览一下后面的数据块划分,块与块之间很Ҏ识别Q因为每一块之间都有很?Q?br>它们是以512字节寚w填充的?br>咦! 怎么只看C3块, 节表头中不是?块吗Q?br>再仔l对照一下节表头Q跟我一块找找?br>00000400 55 8B EC B8 01 00 00 00 C9 C2 0C 00 55 8B EC 6A U?...?.U嬱j<br>00000410 01 FF 75 10 FF 75 0C FF 75 08 E8 4B 00 00 00 C9 . . . .?..?<br>00000420 C2 0C 00 55 8B EC FF 05 00 30 00 10 FF 35 00 30 ?.U?.0.. .0<br>00000430 00 10 FF 75 0C FF 75 08 E8 CF FF FF FF A1 00 30 .. . .?0<br>00000440 00 10 C9 C2 08 00 55 8B EC FF 0D 00 30 00 10 FF ..?.U?.0.. br /> 00000450 35 00 30 00 10 FF 75 0C FF 75 08 E8 AC FF FF FF 5.0.. . .?br /> 00000460 A1 00 30 00 10 C9 C2 08 00 CC FF 25 00 20 00 10 ?0..?.?%. ..<br>00000470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ <br>q段?text D,因ؓ节表已经说了Qtext D内存地址0x1000,大小0x70<br>在文件中处于偏移0x400, 占用文g大小0x200 字节?br><br>00000600 38 20 00 00 00 00 00 00 30 20 00 00 00 00 00 00 8 ......0 ......<br>00000610 00 00 00 00 48 20 00 00 00 20 00 00 00 00 00 00 ....H ... ......<br>00000620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................<br>00000630 38 20 00 00 00 00 00 00 27 02 53 65 74 44 6C 67 8 ......'.SetDlg<br>00000640 49 74 65 6D 49 6E 74 00 55 53 45 52 33 32 2E 64 ItemInt.USER32.d<br>00000650 6C 6C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ll..............<br>00000660 00 00 00 00 F6 34 EB 3C 00 00 00 00 9C 20 00 00 ....??....?..<br>00000670 01 00 00 00 02 00 00 00 02 00 00 00 88 20 00 00 ............?..<br>00000680 90 20 00 00 98 20 00 00 46 10 00 00 23 10 00 00 ?..?..F...#...<br>00000690 A8 20 00 00 B2 20 00 00 00 00 01 00 43 6F 75 6E ?..?......Coun<br>000006A0 74 65 72 2E 64 6C 6C 00 5F 44 65 63 43 6F 75 6E ter.dll._DecCoun<br>000006B0 74 00 5F 49 6E 63 43 6F 75 6E 74 00 00 00 00 00 t._IncCount.....<br>000006C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ <br>q段?rdata D,因ؓ节表已经说了Qrdata D内存地址0x2000,大小0xbc<br>在文件中处于偏移0x600, 占用文g大小0x200 字节?br><br><br>00000800 00 10 00 00 18 00 00 00 28 30 2E 30 3E 30 4B 30 ........(0.0>0K0<br>00000810 51 30 61 30 6C 30 00 00 00 00 00 00 00 00 00 00 Q0a0l0..........<br>00000820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................<br>q段?reloc D,因ؓ节表已经说了Qreloc D内存地址0x4000,大小0x20<br>在文件中处于偏移0x800, 占用文g大小0x200 字节?br><br>哦!也!文g分析完了?br>呦,不是说看看丢了哪一块吗Q?是data D丢了。看看节表怎么_<br>节表已经说了Qdata D内存地址0x4000,大小0x4<br>在文件中处于偏移0x0, 占用文g大小0x0 字节?br>怪不得文件中找不到它的踪影,原来它不存在。但内存中还是给它留了位|?br>不过q里是个特例Q一般文仉会有data D늚存在?br><br>Q小声说Q别高兴太早了,q只是划分了各个块,把每块的具体内容分析完才完<br>哦,?<br>下面我们再详l分析一下各个段功能?br>text Dؓ核心Q其它段都是为它服务的?br>text D?br>即代码段Q由指o集构成。你可以反汇~出q部分内容,q道它们的功能了?br>Z本帖的完整性,我把它脓q来Qƈ不长?br>10001000 EntryPoint:<br>10001000 55 push ebp<br>10001001 8BEC mov ebp,esp<br>10001003 B801000000 mov eax,00000001h<br>10001008 C9 leave<br>10001009 C20C00 retn 000Ch<br>;----------------------------------------------------------------------------------------------------<br>1000100C SUB_L1000100C:<br>1000100C 55 push ebp<br>1000100D 8BEC mov ebp,esp<br>1000100F 6A01 push 00000001h<br>10001011 FF7510 push [ebp+10h]<br>10001014 FF750C push [ebp+0Ch]<br>10001017 FF7508 push [ebp+08h]<br>1000101A E84B000000 call jmp_USER32.dll!SetDlgItemInt<br>1000101F C9 leave<br>10001020 C20C00 retn 000Ch<br>;----------------------------------------------------------------------------------------------------<br>10001023 _IncCount:<br>10001023 55 push ebp<br>10001024 8BEC mov ebp,esp<br>10001026 FF0500300010 inc [L10003000]<br>1000102C FF3500300010 push [L10003000]<br>10001032 FF750C push [ebp+0Ch]<br>10001035 FF7508 push [ebp+08h]<br>10001038 E8CFFFFFFF call SUB_L1000100C<br>1000103D A100300010 mov eax,[L10003000]<br>10001042 C9 leave<br>10001043 C20800 retn 0008h<br>;----------------------------------------------------------------------------------------------------<br>10001046 _DecCount:<br>10001046 55 push ebp<br>10001047 8BEC mov ebp,esp<br>10001049 FF0D00300010 dec [L10003000]<br>1000104F FF3500300010 push [L10003000]<br>10001055 FF750C push [ebp+0Ch]<br>10001058 FF7508 push [ebp+08h]<br>1000105B E8ACFFFFFF call SUB_L1000100C<br>10001060 A100300010 mov eax,[L10003000]<br>10001065 C9 leave<br>10001066 C20800 retn 0008h<br>;----------------------------------------------------------------------------------------------------<br>10001069 CC Align 2<br>1000106A jmp_USER32.dll!SetDlgItemInt:<br>1000106A FF2500200010 jmp [********] //故意把名字隐含了<br>;----------------------------------------------------------------------------------------------------<br>代码分三c:<br>W一cM地址无关Q它们二q制代码已经定下来了<br>W二cM地址有关Q它们二q制代码也定下来了,如果dll 加蝲到它的默认地址Q代码不用修?br>W三cL代码q没有确定,用插桩来表示。如Q?br>jmp [********], 它的插桩?10002000 地址<br><br>先来解决插桩问题吧,q就是hello.exe 讲中的导入表问题?br>1. 内存地址 10002000-10000000(image_base) = 2000(RVA)<br>说实话,image_base 我记不清位置Q也没有明显标记Q每ơ要查结构偏UR?br>好在dll 通常?0000000,exe 通常?00000Q不查也没有问题?br>我又查了一边,偏移是PE 标识后(不包括PE00标识Q第13 个DWORD 偏移处。加q印象Q跟IAT在目录项偏移一?br><br>2. RVA -> offset: 从节表知 RVA 0x2000== offset 0x600Q?br>3. [0x600] == 2038, RVA 2038==offset 638, [0x638]== "0207 setDlgItemInt", 前面是导出序P后面是导出名U?br>; ---------------------------------------------------------------------------<br>loader 解决插桩的问题是从导入表开始的。导入表是目录项的第二项Q?br>00000140 08 20 00 00 28 00 00 00 . ..(...<br><br>导入表指向导入函数库数组?br>RVA 2008 == offset 608, length=0x28 (一个导入表l构?个DWORD-0x14, ?x28Z,一个有效项Q一个全0标识)<br>W一:<br>originFirstThunk == 2030, RVA 2030==offset 630 (IAT 的备份,供你看的Q?br>Name = 2048, RVA 2048 = offset 648 == "USER32.dll"<br>FirstThunk == 2000Q?RVA 2000 == offset 600 (IAT loader 加蝲时会更改q一部分Q以完成插桩Q?br>导入表给Z"USER32.dll"Q插桩处2038 l出了函数名"setDlgItemInt", loader Ҏq些信息完成插桩?br>; ---------------------------------------------------------------------------<br>x我们已经复习了hello.exe 中讲q的东西了?br><br>?.4Q其实话说的多不清楚Q越越ҎDQ把导入表部分再概括一下把?br>{?.4Q导入表在rdata 域?br>W一部分为导入地址表,q部分loader 在加载时会修改其数值完成插桩?br>W二部分为导入表。导入表是ؓIAT 服务的,loader 要修改IAT, 必须要知道导入函数的名称?br>q由导入表提供。导入表同时q提供,该函数名负责IAT 表中哪一个区间?br>所以,导入表是导入函数库数l。每个结构有5个DWORD 变量Q?个没用?br>3个变量全是RVA, 一个指向函数名Q一个指向IAT, 另一个也指向IATQ在l构最前面,但这个IAT loader 不会更改它?br>W三部分按地理位|划分是IAT 的备份表Q就是导入表中Origin_FirstThunk指向的部分?br>W四部分是导入函数名表,前面是序号导出,后面紧跟名称导出。修Ҏ桩当然要用这些信?br>W五部分为函数库名称区域。因为导入表只提供RVA, RVA指向的是q个区域?br><br>?.5Q好Q这样一看rdata 的上半部分意义就明白了,那下面还有一部分内容呢?<br>{?.5Q这是q没开始讲的导了。导比导入表单。因为导入表可能从好几个库中导入Q?br>而导只是自q相关函数导出?br><br>?.6Qؓ什么要把导和导入表攑ֈ一起呢?br>{?.6Q因为它们的属性相同。看节表rdata. 0x40000040,两个属性,我查了一下文档,前面那个1代表可读Q?br>后面那个1代表代码包含初始化数据?br><br>?.7Q?具体导出表要包含那些内容才算把函数导Z呢?<br>{:呦,拖课了! 今天主要是复习了一下前边讲的内容,下一课我们再讲导Q? <div style="LINE-HEIGHT: 18px" class=bword>?.7Q?dll ?exe 文g都是PE 文gQ?在PE 文g中是怎样区分的呢Q?br>{?.7Q有一些小差别?br>例如Q?exe 通常被加载到0x400000, 而dll 默认加蝲?x10000000<br>exe 通常不含reloc D,而dll 包含?br>exe 通常不含export D, 而dll 包含?br>exe 属?10f, 最?bit 说它没有重定位信?<br>dll 属?10e. 最高位? 说它是dll, q个好像是最关键差别了吧?br><br>?.8Qexe ?entrypoint 是程序执行的起始点,dll ?entrypoint 是什么呢Q?br>{?.8Q这个地Ҏdll 的入口函数地址Q它是不可以省略的?br>dll 在加载,卸蝲以及U程加蝲Q卸载时都会<br>到这里执行程序?它的通用l构是这L?br>BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)<br>{<br>switch(fswReason)<br>case DLL_PROCESS_ATTACH<br>......<br>case DLL_THREAD_ATTACH<br>.....<br>case DLL_THREAD_DETACH<br>.....<br>case DLL_PROCESS_DETACH<br>.... <br>return (TRUE or False) // true , 成功Qfalse p|Q?loader 会把它从内存卸掉?br>} <br>count.dll 中没有按q种l构Q它只是单的q回一个TRUE,因ؓ它不需要申请内存和释放内存{初始化操作?br>count.dll entrypoint ?0001000Q?PE 标识后第10 DWORD 地址Q记不住用工h?br><br>?.9Q前面说qdll 有入口函敎ͼ 导出函数和非导出函数。又回到上次未讲的问?br>导出表是怎样把函数导出的?br>{?.9Q我们先猜一猜导出函数的关键要素吧?br>1. 导出库名U?br>2. 函数导出序号。(提供序号导出Q?br>3. 导出函数?Q提供函数名导出Q?br>4. 序号或函数名对应的地址?br>它向pȝ报告q些信息已经_了?br><br>?.10Q?l合例子和结构定义具体说一下吧?br>{?.10Q?看count.dll 目录第一?br>00000130 60 20 00 00 5C 00 00 00 ` ..\... <br>位置 RVA 2060 == offset 660<br>大小 0x5c<br>00000660 00 00 00 00 F6 34 EB 3C 00 00 00 00 9C 20 00 00 ....??....?..<br>00000670 01 00 00 00 02 00 00 00 02 00 00 00 88 20 00 00 ............?..<br>00000680 90 20 00 00 98 20 00 00 46 10 00 00 23 10 00 00 ?..?..F...#...<br>00000690 A8 20 00 00 B2 20 00 00 00 00 01 00 43 6F 75 6E ?..?......Coun<br>000006A0 74 65 72 2E 64 6C 6C 00 5F 44 65 63 43 6F 75 6E ter.dll._DecCoun<br>000006B0 74 00 5F 49 6E 63 43 6F 75 6E 74 00 t._IncCount. <br><br>那么q是一个什么样的数据结构呢Q我们先猜猜看,q里也叫逆向<span id="qqiiqia" class=highlight><strong><font color=#ff0000>学习</font></strong></span>Ҏ吧?br>首先函数名称Qcount.dllQoffset 69c), 函数名称_DecCount(offset 6a8), _IncCount(6b2) 都已l看C?br>转换为RVA. offset 69c =RVA 209c<br>offset 6a8 =RVA 20a8<br>offset 6b2 =RVA 20b2<br>很高兴在数据中找C9c 20, a8 20, b2 20. x一下这些地址?br>?60 在o一下,感觉后面?0000002 可能是个数吧?br>后边?088Q?2090Q?098 <br>RVA 2088 == offset 688 [688] == 46 10 00 00 23 10 00 00 // 像函数地址,赶紧认一下(在上一)Q正是?br>RVA 2090 == offset 690 [690] == A8 20 00 00 B2 20 00 00 // 函数名称地址<br>RVA 2098 == offset 690 [690] == 00 00 01 00 // 像hint<br>q样划分后,看不懂的׃多了Q?span class=highlight><strong><font color=#ff0000>学习</font></strong></span>数据l构是时候了?br>ty<span id="2is2oy4" class=highlight><strong><font color=#ff0000>pe</font></strong></span>def struct _IMAGE_EXPORT_DIRECTORY {<br>DWORD Characteristics;<br>DWORD TimeDateStamp;<br>WORD MajorVersion;<br>WORD MinorVersion;<br>DWORD Name;<br>DWORD Base;<br>DWORD NumberOfFunctions;<br>DWORD NumberOfNames;<br>DWORD AddressOfFunctions; // RVA from base of image<br>DWORD AddressOfNames; // RVA from base of image<br>DWORD AddressOfNameOrdinals; // RVA from base of image<br>} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;<br>我们分析的是对的Q那个base 是什么东?Q是导出函数的基序号。所以导出函数序号不?Q?Q而是1Q? ?br>很明显,1指的?046地址Q?指的?023地址<br>?.11 再ȝ一下导吧?br>{?.11 导出表有两种导出ҎQ一U是按名U导出,一U是按序号导出。其中序号导出的个数L大于{于名称导出的个数?br>导出的函数地址?字节一字排开。每一个函数烦引号是+Base 值就是导出序受?br>序号不直观,所以有些函数用名称导出Q名U导出最l还是要扑ֈ函数序号。所以把名称所在的名称数组的位|ؓ索引<br>M名称序号数组中拿到序P此ؓ索引PQ由索引号取到函数地址?br><br>?.12 假如loader 要插桩本函数 _IncCount 地址Q它是怎样操作的?br>{?.12<br>1. 它首先要加蝲我们的dll. 用loadlibrary<br>2. 扑ֈ我们的导?br>3. 再找到导中AddressOfNames?br>4. 遍历该表扑ֈ_IncCount 函数Q记下它的烦?br>5. 从AddressOfNameOrdinals数组中,取到该烦引对应的函数地址序号<br>6. 从导中找到AddressOfFunctionsQ?用得到的序号d到它的地址?br>7. 该地址d充到IAT 的对应位|上?br><br>q样插桩完成了。哇Q这个小插桩要经q这么多步骤哇,有没有办法简化一下啦... {着你去实现呢!<br><br>?.13 count.dll 中还有一个reloc 节,讲讲它是怎样构成的?br>{?.13 reloc 也是为text D|务的Q前面说q,若dll 加蝲到它默认位置Q可以不用reloc Dc?br>当不能加载到默认地址Ӟ 某些于地址有关的指令需要重新定位。就是说要修Ҏ令中地址<br>使其指向正确的地址?br>看目录项中reloc 表,W?个表Q烦引号?Q:<br>00000160 00 40 00 00 18 00 00 00 .@......<br>RVA 4000 = offset 800, size=0x18<br>哦,U不用reloc 目录,直接目视也看见它了。这个程序很,是这L?br>00000800 00 10 00 00 18 00 00 00 28 30 2E 30 3E 30 4B 30 ........(0.0>0K0<br>00000810 51 30 61 30 6C 30 00 00 00 00 00 00 00 00 00 00 Q0a0l0..........<br>我们也像export 表一P先猜猜reloc l构应该有那些重要元素?br>1. 内存地址Q?4byte, 我们要知道对哪的指oq行重定位。即where 问题<br>2. 替换Ҏ?是替换一字节Q?字节q是4字节Q?是how 的问题,估计有几个bit 够了?br>3. 用什么替换?是一个what 的问题?q个问题׃用考虑了,q个what,是加蝲地址与默认地址偏移?br>q样看v来一个reloc 至也?5 bytes 了。如果有很多,那这很多就构成一个数l?br>我这里介l的Ҏ是一U逆向?span class=highlight><strong><font color=#ff0000>学习</font></strong></span>ҎQ或者是原始的思考方法。因为我x初设计这个PE l构的h也会<br>q么惟?br>现在使用的PE l构在这个想法的基础上进行了优化Q得reloc表占用较的字节Q?br>具体说是它让一个reloc 占?byte,下面看它的方法:<br><br>1.<br>ty<span id="iaaas2o" class=highlight><strong><font color=#ff0000>pe</font></strong></span>def struct _IMAGE_BASE_RELOCATION {<br>DWORD VirtualAddress;<br>DWORD SizeOfBlock;<br>} IMAGE_BASE_RELOCATION;<br><br>它的意思是_它要重定位VirtualAddress=1000q块区域Q?该区域所占的重定位信息大ؓSizeOfBlock=0x18<br>后面紧跟的每2 个bytes 构成一个重定位, 其中?2 bit为重定位地址Q??bits 为重定位cd?br>我这里把重定位类型copy q来Q其中有的在x86 上是用不到的?br>//<br>// Based relocation ty<span id="geyissc" class=highlight><strong><font color=#ff0000>pe</font></strong></span>s.<br>//<br><br>#define IMAGE_REL_BASED_ABSOLUTE 0<br>#define IMAGE_REL_BASED_HIGH 1<br>#define IMAGE_REL_BASED_LOW 2<br>#define IMAGE_REL_BASED_HIGHLOW 3<br>#define IMAGE_REL_BASED_HIGHADJ 4<br>#define IMAGE_REL_BASED_MIPS_JMPADDR 5<br>#define IMAGE_REL_BASED_SECTION 6<br>#define IMAGE_REL_BASED_REL32 7<br><br>#define IMAGE_REL_BASED_MIPS_JMPADDR16 9<br>#define IMAGE_REL_BASED_IA64_IMM64 9<br>#define IMAGE_REL_BASED_DIR64 10<br>#define IMAGE_REL_BASED_HIGH3ADJ 11<br><br>地址只有12为意味着你只能管?K 范围Q是的,如果过?K范围Q我们重新定义一个IMAGE_BASE_RELOCATION<br>变量可以了Q它又能理下一?K的范围。后l没16bit Z个reloc , 最N?000 l尾。当Ӟ׃<br>重定位块大小有定义,U不用0000标识l尾也没有问题,把这里的冗余姑且叫双保险吧,是费?bytes<br>count.dll 中,我们先算有几个重定位项。哦Q不用算Q有7个,一查就查出来了?br>但当E序大的时候还是要计算的?0x18-8)/2-1=7 个?br>计算公式Q?SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))/2-1<br><br>问:5.14 那这7个重定位到底是怎样重定位的呢?q是l出具体l果更彻底?br>{:5.14 好,做事做到底,现在我们开始吧?br>W一:28 30 其内容ؓ 0x3028 重定位类型ؓ3Q?地址?x28<br>其它重定位类型都?Q?地址不同而已?br>#define IMAGE_REL_BASED_HIGHLOW 3<br>那么q个BASE_HIGHLOW 是什么意思呢Q我们先看看0x28处的指o?br>10001026 FF0500300010 inc [L10003000]<br>假如我们的dll 不是被加载到0x10000000Q而是加蝲?x30000000, 卛_上提高了0x20000000<br>我们只要?x28地址处也加上0x20000000,可以了。这栯条指令就变成?br>10001026 FF0500300030 inc [L30003000]<br>看明白了吧! H户U就是这栯捅破?br><br><br>好了Q本帖就到这里结束吧Q?/div> <img src ="http://www.shnenglu.com/Walker/aggbug/144121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Walker/" target="_blank">漫步者?…?K?/a> 2011-04-13 14:41 <a href="http://www.shnenglu.com/Walker/articles/144121.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows Serviceshttp://www.shnenglu.com/Walker/articles/144118.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Wed, 13 Apr 2011 05:53:00 GMThttp://www.shnenglu.com/Walker/articles/144118.htmlhttp://www.shnenglu.com/Walker/comments/144118.htmlhttp://www.shnenglu.com/Walker/articles/144118.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/144118.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/144118.htmlServices

1 概述

1服务E序遵@Service Control Manager(SCM)的接口规则?/span> 启动方式?/span>3U:1 pȝ启动时自动启动;2 通过服务控制面板;3 通过使用服务函数?/span>2服务可以在没有用L录到pȝ的情况下q行?/span>

2 驱动服务Q?/span>驱动服务遵@讑֤驱动协议Q它与服务程序类|但是不与SCM交互?/span>

3 The SCM processes service control notifications in a serial fashion

4 The default security descriptor allows the LocalSystem account, and members of the Administrators and Power Users groups to stop and start services.

 

2 服务?/span>Win7中的新特?/span>

1 可以注册一个服务,在一个特定事件发生时启动或者停止服务?/span>

更新的函?/span>

ChangeServiceConfig

Changes the configuration parameters of a service. This function supports managed service accounts and virtual accounts. For more information, see Service Accounts Step-by-Step Guide.

ChangeServiceConfig2

Changes the optional configuration parameters of a service. This function supports new configuration information levels for processor groups and service trigger events.

CreateService

Creates a service object and adds it to the specified service control manager database. This function supports managed service accounts and virtual accounts. For more information, see Service Accounts Step-by-Step Guide.

HandlerEx

An application-defined callback function used with the RegisterServiceCtrlHandlerEx function. This callback function supports new extended control codes for system time changes and service trigger events.

QueryServiceConfig2

Retrieves the optional configuration parameters of a service. This function supports new configuration information levels for processor groups and service trigger events.

SetServiceStatus

Updates the service control manager's status information for the calling service. This function supports new extended control codes for system time changes and service trigger events.

 

新的l构

SERVICE_TIMECHANGE_INFO

Contains system time change settings.

SERVICE_TRIGGER

Represents a service trigger event.

SERVICE_TRIGGER_INFO

Contains trigger event information for a service.

SERVICE_TRIGGER_SPECIFIC_DATA_ITEM

Contains trigger-specific data for a service trigger event.

Service Changes for Windows Vista

 

Z提高性能、可靠性、安全性、可理性等Q在Vista之后Q服务提供了很多增强的特性?/span>

 

Session 0 Isolation

服务Lq行?/span>Session 0Q在Vista之前Q第一个用户也是运行在Session0Q在Vista之后Q第一个用戯行在Session1Q第二个用户q行?/span>Session2Q?/span> {等Q这LL序和服务始l运行在不同?/span>Session上?/span>Session 0 不支持与用户交互的进E。这意味着Service不能向应用程序发送消息,应用E序也不能像Service发送消息。除此之外,Serviceq不能显C?/span>GUIQ如对话框,但是Service可以使用WTSSendMessage 函数在另一?/span>Session中显C对话框?/span>

Delayed AutoStart 在系l启动的一段旉Q?/span>shortly afterQ内启动服务?/span>

Failure Detection and RecoveryQ如果服务失败,SCM可以执行p|actionQ如重启服务?/span>

Preshutdown NotificationsQ是服务有够的旉来优雅的关闭?/span>

Restricted Network AcessQ?/span>

Running with Least PrivilageQ?/span>

 

关于服务

SCMl护者已安装的服务和驱动服务的数据库Qƈ且提供了l一的安全的控制它们的方法。数据库信息包括每个服务如何被启动?/span>

下面cd的程序?/span>SCM提供的函敎ͼ

服务E序Q?/span>Service ProgramQ:一个ؓ一个或者多个服务提供可执行代码的程序。服务程序用连接到SCM的函C及发送状态信息给SCM的函数?/span>

服务配置E序Q?/span>Service configuration programQ:一个查询和修改服务数据库的E序。服务配|程序用打开数据库的函数Q在数据库中安装和删除服务,对安装的服务查询和修攚w|参敎ͼ安全参数。服务配|程序管理服务以及驱动服务?/span>

服务控制E序Q?/span>Service Control ProgramQ:一个启动ƈ控制服务以及驱动服务的程序。服务控制程序用发送请求到SCM的函数?/span>

Service Control Manager(服务控制理?/span>)

1 pȝ启动时启动;它是一?/span>RPC (Remote procedure call) 服务器。所以服务配|和服务控制E序可以在远E机器上理服务?/span>

提供接口功能Q?/span>

理安装服务的数据库Q?/span>

启动服务Q系l启动时Q或者需要时Q?/span>

枚D安装的服?/span>

行的服务l护状态信?/span>

传输控制hQ?/span>control requestQ给正在q行的服?/span>

锁定和解锁服务数据库?/span>

Database of installed service

在注册表中维护着已安装的服务列表。注册表如下

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.

q个键gؓ每一个安装的服务包含一个子键,子键的名字就是服务名?/span>

Database又叫?/span>ServiceActive databse或?/span>SCM database。你必须使用SCM提供的函敎ͼ而不能直接修?/span>databseQ即不能直接修改注册表?/span>

自动启动服务

在系l启动的时?/span>,SCM启动所有自启动的服务以及它们依赖的服务?/span>

启动序Q?/span>

1 按加载启动组列表中的序Q这个信息保存在ServiceGroupOrdergQ?/span>

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control

2 GroupOrderList

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control

3 每个服务的依赖列?/span>

当启动完成后Q系l执行启动认证程序(boot verification programQ根?/span>BootVerificationProgram

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control.

默认情况下,q个值是没有讄的。你可以提供E序来检系l,调用NotifyBootConfigStatus通知SCMQ报?/span>boot 状态?/span>

在成功重启后Q系l会组册表复制一份到下面目录?/span>last-known-good (LKG) configuration

HKEY_LOCAL_MACHINE\SYSTEM\ControlSetXXX\Services

按需启动服务

当服务启动之后,SCM执行下列步骤Q?/span>

获取存储在数据库中的帐户信息

d到服务帐?/span>

加蝲用户配置

在暂停状态下创徏服务

l进E分配一?/span>Logon?/span>token

允许q程执行

Service Record List

׃每个服务实体是从注册表数据库中读取的Q因?/span>SCM为每个服务创Z一?/span>Service RecordQ?/span>

一?/span>Service Record包括Q?/span>

服务?/span>

启动cd

服务状态(SERVICE_STATUSl构Q?/span>

指向依赖服务列表的指?/span>

服务在安装的时候用户名和密码是指定好的?/span>SCM在注册表中存攄户名Q在Local Security AuthorityQ?/span>LSAQ中存放密码?/span>

SCM保存两䆾用户密码。一个当前密码和一个备份密码。在服务W一ơ安装的时候用当前密码,备䆾密码是未初始化的。当用当前密码运行服务成功后Q才会将当前密码写入到备份密码?/span>SCM在收到服务状态通知后更新服务状态?/span>

驱动服务的状态是通过查询IOpȝ获得的,而不是通过状态通知Q这Ҏ?/span>Service不同的?/span>

SCM Handles

SCM支持句柄Q?/span>HandleQ来讉K下面的对象?/span>

已安装的服务的数据库Q用SCManager object来表C?/span>

服务Q由一个安装的服务代表服务对象?/span>

数据库锁

Service Programs服务E序

Main函数Q?/span>

调用StartServiceCtrlDispatcher函数q接?/span>SCMq启?/span>control dispatcherU程?/span>Control dispatcherU程循环loopQ等待在dispatch table中指定的服务的请求?/span>

例子Q?/span>

void __cdecl _tmain(int argc, TCHAR *argv[])

{

    // If command-line parameter is "install", install the service.

    // Otherwise, the service is probably being started by the SCM.

 

    if( lstrcmpi( argv[1], TEXT("install")) == 0 )

    {

        SvcInstall();

        return;

    }

 

    // TO_DO: Add any additional services for the process to this table.

    SERVICE_TABLE_ENTRY DispatchTable[] =

    {

        { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },

        { NULL, NULL }

    };

 

    // This call returns when the service has stopped.

    // The process should simply terminate when the call returns.

 

    if (!StartServiceCtrlDispatcher( DispatchTable ))

    {

        SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));

    }

}

ServiceMain函数Q?/span>

首先调用RegisterServiceCtrlHandler函数来注?/span>SvcCtrHandler函数作ؓ服务的处理函敎ͼ然后开始初始化?/span>

VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )

{

    // Register the handler function for the service

 

    gSvcStatusHandle = RegisterServiceCtrlHandler(

        SVCNAME,

        SvcCtrlHandler);

 

    if( !gSvcStatusHandle )

    {

        SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));

        return;

    }

 

    // These SERVICE_STATUS members remain as set here

 

    gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;

    gSvcStatus.dwServiceSpecificExitCode = 0;   

 

    // Report initial status to the SCM

 

    ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );

 

    // Perform service-specific initialization and work.

 

    SvcInit( dwArgc, lpszArgv );

}

 

//

// Purpose:

//   The service code

//

// Parameters:

//   dwArgc - Number of arguments in the lpszArgv array

//   lpszArgv - Array of strings. The first string is the name of

//     the service and subsequent strings are passed by the process

//     that called the StartService function to start the service.

//

// Return value:

//   None

//

VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)

{

    // TO_DO: Declare and set any required variables.

    //   Be sure to periodically call ReportSvcStatus() with

    //   SERVICE_START_PENDING. If initialization fails, call

    //   ReportSvcStatus with SERVICE_STOPPED.

 

    // Create an event. The control handler function, SvcCtrlHandler,

    // signals this event when it receives the stop control code.

 

    ghSvcStopEvent = CreateEvent(

                         NULL,    // default security attributes

                         TRUE,    // manual reset event

                         FALSE,   // not signaled

                         NULL);   // no name

 

    if ( ghSvcStopEvent == NULL)

    {

        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );

        return;

    }

 

    // Report running status when initialization is complete.

 

    ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );

 

    // TO_DO: Perform work until service stops.

 

    while(1)

    {

        // Check whether to stop the service.

 

        WaitForSingleObject(ghSvcStopEvent, INFINITE);

 

        ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );

        return;

    }

}

 

//

// Purpose:

//   Sets the current service status and reports it to the SCM.

//

// Parameters:

//   dwCurrentState - The current state (see SERVICE_STATUS)

//   dwWin32ExitCode - The system error code

//   dwWaitHint - Estimated time for pending operation,

//     in milliseconds

//

// Return value:

//   None

//

VOID ReportSvcStatus( DWORD dwCurrentState,

                      DWORD dwWin32ExitCode,

                      DWORD dwWaitHint)

{

    static DWORD dwCheckPoint = 1;

 

    // Fill in the SERVICE_STATUS structure.

 

    gSvcStatus.dwCurrentState = dwCurrentState;

    gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;

    gSvcStatus.dwWaitHint = dwWaitHint;

 

    if (dwCurrentState == SERVICE_START_PENDING)

        gSvcStatus.dwControlsAccepted = 0;

    else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

 

    if ( (dwCurrentState == SERVICE_RUNNING) ||

           (dwCurrentState == SERVICE_STOPPED) )

        gSvcStatus.dwCheckPoint = 0;

    else gSvcStatus.dwCheckPoint = dwCheckPoint++;

 

    // Report the status of the service to the SCM.

    SetServiceStatus( gSvcStatusHandle, &gSvcStatus );

}

 

Control Handler函数Q?/span>

该函数是?/span>dispatcher thread调用的,它处理在OpCode参数中传q来的控制码Q然后调?/span>ReportSvcStatus函数来更新服务状态。当Handler收到控制码时Q只有当收到的控制码引v服务状态变化时才报告服务状态?/span>

VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )

{

   // Handle the requested control code.

 

   switch(dwCtrl)

  

      case SERVICE_CONTROL_STOP:

         ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

 

         // Signal the service to stop.

 

         SetEvent(ghSvcStopEvent);

         ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);

        

         return;

 

      case SERVICE_CONTROL_INTERROGATE:

         break;

 

      default:

         break;

   }

  

}

Service Configuration Program Tasks服务配置E序的Q?/span>

安装服务

删除服务

改变服务配置

查询服务配置

Service Control Program Tasks服务控制E序d

启动服务

停止服务

改变一个服务的DACL

 

Service Functions

The following functions are used or implemented by services.

Function

Description

Handler

An application-defined callback function used with the RegisterServiceCtrlHandler function.

HandlerEx

An application-defined callback function used with the RegisterServiceCtrlHandlerEx function.

RegisterServiceCtrlHandler

Registers a function to handle service control requests.

RegisterServiceCtrlHandlerEx

Registers a function to handle extended service control requests.

ServiceMain

An application-defined function that serves as the starting point for a service.

SetServiceBits

Registers a service type with the service control manager and the Server service.

SetServiceStatus

Updates the service control manager's status information for the calling service.

StartServiceCtrlDispatcher

Connects the main thread of a service process to the service control manager.

 

The following functions are used by programs that control or configure services.

Function

Description

ChangeServiceConfig

Changes the configuration parameters of a service.

ChangeServiceConfig2

Changes the optional configuration parameters of a service.

CloseServiceHandle

Closes the specified handle to a service control manager object or a service object.

ControlService

Sends a control code to a service.

ControlServiceEx

Sends a control code to a service.

CreateService

Creates a service object and adds it to the specified service control manager database.

DeleteService

Marks the specified service for deletion from the service control manager database.

EnumDependentServices

Retrieves the name and status of each service that depends on the specified service.

EnumServicesStatusEx

Enumerates services in the specified service control manager database based on the specified information level.

GetServiceDisplayName

Retrieves the display name of the specified service.

GetServiceKeyName

Retrieves the service name of the specified service.

NotifyBootConfigStatus

Reports the boot status to the service control manager.

NotifyServiceStatusChange

Enables an application to receive notification when the specified service is created or deleted or when its status changes.

OpenSCManager

Establishes a connection to the service control manager on the specified computer and opens the specified service control manager database.

OpenService

Opens an existing service.

QueryServiceConfig

Retrieves the configuration parameters of the specified service.

QueryServiceConfig2

Retrieves the optional configuration parameters of the specified service.

QueryServiceObjectSecurity

Retrieves a copy of the security descriptor associated with a service object.

QueryServiceStatusEx

Retrieves the current status of the specified service based on the specified information level.

SetServiceObjectSecurity

Sets the security descriptor of a service object.

StartService

Starts a service.

Service Structures

The following structures are used with services:



]]>
64-bit C/C++ applications http://www.shnenglu.com/Walker/articles/144117.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Wed, 13 Apr 2011 05:14:00 GMThttp://www.shnenglu.com/Walker/articles/144117.htmlhttp://www.shnenglu.com/Walker/comments/144117.htmlhttp://www.shnenglu.com/Walker/articles/144117.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/144117.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/144117.html阅读全文

]]>
OnEraseBkGnd与OnPaint的联pL什么?http://www.shnenglu.com/Walker/articles/144098.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Wed, 13 Apr 2011 02:42:00 GMThttp://www.shnenglu.com/Walker/articles/144098.htmlhttp://www.shnenglu.com/Walker/comments/144098.htmlhttp://www.shnenglu.com/Walker/articles/144098.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/144098.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/144098.html的OnEraseBkGnd只是重画背景则不会有闪烁.而在OnPaint里面,
׃它隐含的调用了OnEraseBkGnd,而你又没有处理OnEraseBkGnd
函数,q时和H口~省的背景刷相关?~省?
OnEraseBkGnd操作使用H口的缺省背景刷h背景(一般情?
下是白刷),而随后你又自己重画背景造成屏幕闪动.
另外一个问题是OnEraseBkGnd不是每次都会被调用的.如果?
调用Invalidate的时候参CؓTRUE,那么在OnPaint里面隐含
调用BeginPaint的时候就产生WM_ERASEBKGND消息,如果参数
是FALSE,则不会重刯?

所以解x法有三个?
1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.
2.用OnPaint实现,同时重蝲OnEraseBkGnd,其中直接q回.
3.用OnPaint实现,创徏H口时设|背景刷为空
4.用OnPaint实现,但是要求h时用Invalidate(FALSE)q样
的函?(不过q种情况?H口覆盖{造成的刷新还是要闪一
?所以不是彻底的解决Ҏ)
都挺单的.
------------------------------------------------------
在MFC?M一个windowlg的绘?都是攑֜q两个member function?br>在设定上 OnEraseBkgnd()是用来画底图?而OnPaint()是用来画主要对象?br>举例说明 一个按钮是灰色?上面q有文字
则OnEraseBkgnd()所做的事就是把按钮L灰色
而OnPaint()所做的?是M文字

既然q两个member function都是用来dlg?br>那ؓ何还要分OnPaint() ?OnEraseBkgnd() ?br>其实OnPaint() ?OnEraseBkgnd() Ҏ是有差?br>1. OnEraseBkgnd()的要求是快?在里面的l图E序最好是不要太耗时?br>因ؓ 每当windowlg有Q何小变动 都会马上呼叫OnEraseBkgnd()
2. OnPaint() 是只有在E序有空闲的时候才会被呼叫
3. OnEraseBkgnd() 是在 OnPaint() 之前呼叫?br>所?OnPaint()被呼叫一ơ之?可能会呼叫OnEraseBkgnd()好几?br>

如果我们是一个在做图形化使用者接口的?br>怼需要把一张美的囄设ؓ我们dialog的底?br>把绘囄E序代码攑֜OnPaint() 之中 可能会常到一些问?br>比方说拖曳一个窗口在我们做的dialog上面一直移?br>则dialog会变成灰?直到动作停止才恢?br>q是因ؓ每次需要重l的时?E序都会马上呼叫OnEraseBkgnd()
OnEraseBkgnd()把dialogL灰色
而只有动作停止之?E序才会呼叫OnPaint() q时才会把我们要ȝ底图贴上?br>

q个问题的解?比较差点的方法是把OnEraseBkgnd() 改写成不做事的function
如下所C?br>BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
以上本来是会呼叫CDialog::OnEraseBkgnd() 但是如果我们不呼叫的?br>E序便不会画上灰色的底色?br>

比较好的做法是直接将l图的程序从OnPaint()UdOnEraseBkgnd()来做
如下所C?br>
// m_bmpBKGND ZCBitmap对象 且事先早已加载我们的底图
// 底图的大与我们的窗口client大小一?br>

BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)
{
CRect rc;
GetUpdateRect(&rc);
CDC srcDC;
srcDC.CreateCompatibleDC(pDC);
srcDC.SelectObject(m_bmpBKGND);

pDC->BitBlt(rc.left,rc.top,rc.GetWidth(),
rc.GetHeight(),&srcDC,rc.left,rc.top,SRCCOPY);
return TRUE;
}

特别要注意的?取得重画大小是用GetUpdateRect() 而不是GetClientRect()
如果使用GetClientRect() 会把不该重画的地斚w?br>
另外不推荐用GetUpdateRect()Q很多情况下q个函数在OnEraseBkGnd里调用会有问题,使用GetClipBox()

首先Qtr0j4n是好心hQ写得这么长。但好像不对哦?br>
应该是这L吧,当Windows定客户区需要重l时Q它首先发送WM_ERASEBKGND消息l窗口过E,由WM_ERASEBKGND消息的默认处理用白色d刷除背景Q然后再发送WM_PAINT消息l窗口过E,由WM_PAINT消息的响应程序负责绘d户区内容。或者说Q当Windows定客户区需要重l时Q它分别发送WM_ERASEBKGND和WM_PAINT消息Q由q两个消息的响应E序分别负责刷除背景和重d户内宏V?br>
至闪烁的问题Q是׃刷除背景以后Q在WM_PAINT未执行完成之前,windows已把视频卡的~存输出到屏q上了?br> 

如何擦除之前的背景图片?

我明白问题所在了Q你的问题是当窗口大变化时Q原来的背景没有被清除,造成囄重叠昄Q你上面的代码没有问题,写在OnEraseBkgnd()中还是写在OnPaint()中的l果都是一LQ最主要的原因是Q当我们改变H口大小Ӟ触发WM_SIZE消息Q而默认的OnSize在内部调用Invalidate时用的参数是QInvalidate(FLASE);也就是不h背景的。所以原来的背景刷不?br>解决的方法是处理WM_SIZEQ在OnSize()中,调用QInvalidate(TRUE);强制h背景p了?br>

]]>
转log4cplushttp://www.shnenglu.com/Walker/articles/143404.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Mon, 04 Apr 2011 07:57:00 GMThttp://www.shnenglu.com/Walker/articles/143404.htmlhttp://www.shnenglu.com/Walker/comments/143404.htmlhttp://www.shnenglu.com/Walker/articles/143404.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/143404.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/143404.html阅读全文

]]>
软g工程配置规范QVC2005Q?W二?Q{Q?/title><link>http://www.shnenglu.com/Walker/articles/143403.html</link><dc:creator>漫步者?amp;…?K?/dc:creator><author>漫步者?amp;…?K?/author><pubDate>Mon, 04 Apr 2011 07:46:00 GMT</pubDate><guid>http://www.shnenglu.com/Walker/articles/143403.html</guid><wfw:comment>http://www.shnenglu.com/Walker/comments/143403.html</wfw:comment><comments>http://www.shnenglu.com/Walker/articles/143403.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/Walker/comments/commentRss/143403.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/Walker/services/trackbacks/143403.html</trackback:ping><description><![CDATA[<div id="ywmwqg2" class=post> <h2><a id=viewpost1_TitleUrl href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html"><font color=#223355>软g工程配置规范QVC2005Q?W二?/font></a> </h2> <div id="4g4u4yq" class=postbody><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html">转自http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html</a> <p><br><strong></strong></p> <p><strong></strong></p> <p><strong>?/strong><strong> </strong><strong>?/strong><strong> </strong><strong>?/strong><strong> </strong><strong>?/strong><strong></strong> <table border=1 cellSpacing=0 cellPadding=0> <tbody> <tr> <td vAlign=top width=121> <p><strong>版本?/strong><strong></strong></p> </td> <td vAlign=top width=122> <p><strong>日期</strong><strong></strong></p> </td> <td vAlign=top width=121> <p><strong>修改?/strong><strong></strong></p> </td> <td vAlign=top width=122> <p><strong>说明</strong><strong></strong></p> </td> <td vAlign=top width=122> <p><strong>备注</strong><strong></strong></p> </td> </tr> <tr> <td vAlign=top width=121> <p>0.1</p> </td> <td vAlign=top width=122> <p>2010.07.13</p> </td> <td vAlign=top width=121> <p>phoenix</p> </td> <td vAlign=top width=122> </td> <td vAlign=top width=122> </td> </tr> <tr> <td vAlign=top width=121> <p>0.2</p> </td> <td vAlign=top width=122> <p>2011.01.12</p> </td> <td vAlign=top width=121> <p>phoenix</p> </td> <td vAlign=top width=122> </td> <td vAlign=top width=122> </td> </tr> <tr> <td vAlign=top width=121> </td> <td vAlign=top width=122> </td> <td vAlign=top width=121> </td> <td vAlign=top width=122> </td> <td vAlign=top width=122> </td> </tr> <tr> <td vAlign=top width=121> </td> <td vAlign=top width=122> </td> <td vAlign=top width=121> </td> <td vAlign=top width=122> </td> <td vAlign=top width=122> </td> </tr> <tr> <td vAlign=top width=121> </td> <td vAlign=top width=122> </td> <td vAlign=top width=121> </td> <td vAlign=top width=122> </td> <td vAlign=top width=122> </td> </tr> </tbody> </table> </p> <p><strong><br></strong></p> <p>??/p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612780"><font color=#1d58d1>1. 引言... 1</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612781"><font color=#1d58d1>1.1. ~写目的.. 1</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612782"><font color=#1d58d1>1.2. 参考资?. 1</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612783"><font color=#1d58d1>2. 目录l构... 1</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612784"><font color=#1d58d1>3. 工程配置... 2</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612785"><font color=#1d58d1>4. 属性配|?.. 2</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612786"><font color=#1d58d1>4.1. “常规”配置.. 2</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612787"><font color=#1d58d1>4.2. “调试”配置.. 2</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612788"><font color=#1d58d1>4.3. “C/C++”配置.. 3</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612789"><font color=#1d58d1>4.4. “链接?#8221;配置.. 3</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612790"><font color=#1d58d1>4.5. “生成事g”配置.. 3</font></a></p> <p><a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#_Toc282612791"><font color=#1d58d1>5. 附录QVC2005中可以用的?.. 3</font></a></p> <p><strong><font color=#1d58d1></font></strong></p> <h3><a name=_Toc282612780><font color=#1d58d1>1.引言</font></a></h3> <h4><a name=_Toc282612781><font color=#1d58d1>1.1.~写目的</font></a></h4> <p>当开发h员开始进行编码工作以及编码工作的q程中,都需要根据工E的需要配|各U工作\径,如引入第三方库、输出生成的头文件和库文件等。以前部分开发h员对工程路径的定义比较随意,相当一部分使用的是l对路径。如果只是单个开发h员完成一个单一的功能,q种做法看v来没有什么明昄~点。但是,如果是数个开发h员来合作共同完成一个功能和l构都很复杂的工E时Q这U做法的弊端凸昑և来。一个非常明昄问题是Q开发h员A他的工E交l开发h员B~译Ӟ如工E定义用了l对路径Q除非开发h员B的计机与A的计机h完全相同的文件组l结构,否则一ơ性编译通过是不可能。另外,当开发h员在一长串?#8220;Can’t find …”或?#8220;XXX undefined”的错误与警告中焦头烂额时Q也许只是因Z个不L的地方用了l对路径Q或者是大小写的差异?/p> <p>因此Q规范开发h员的工程配置是很有必要的。通过规范的工E配|,所有开发h员之间的工程是无~衔接的Q即M一个开发h员的工程拿到其他人的工程Ӟ无须修改M一个配|便可以一ơ性的~译成功。这L规范操作无异可以大大减少团队成员通过代码交流时的不必要成本。另外,规范的工E配|也便于SubVersion版本控制pȝ的引入,通过引入SubVersion版本控制pȝQ可以协调整个研发团队的工程q度Q控制品的里程发布。这一切,都从规范的工E配|开始?/p> <h4><a name=_Toc282612782><font color=#1d58d1>1.2.参考资?/font></a></h4> <p>1. 《OpenSource SubVersion规范?/p> <p>2. 《Visual Studio 2005~程指南?/p> <h3><a name=_Toc282612783><font color=#1d58d1>2.目录l构</font></a></h3> <p>MyDevelopeFolder</p> <p>├─Bin</p> <p>?├─Debug</p> <p>?├─Program</p> <p>?├─Release</p> <p>?├─UnicodeDebug</p> <p>?├─UnicodeProgram</p> <p>?└─UnicodeRelease</p> <p>├─MySDK</p> <p>?├─Include</p> <p>??├─BCG</p> <p>??└─Boost</p> <p>?└─Lib</p> <p>?├─Debug</p> <p>??├─BCG</p> <p>??└─Boost</p> <p>?├─Program</p> <p>??├─BCG</p> <p>??└─Boost</p> <p>?├─Release</p> <p>??├─BCG</p> <p>??└─Boost</p> <p>?├─UnicodeDebug</p> <p>??├─BCG</p> <p>??└─Boost</p> <p>?├─UnicodeProgram</p> <p>??├─BCG</p> <p>??└─Boost</p> <p>?└─UnicodeRelease</p> <p>?├─BCG</p> <p>?└─Boost</p> <p>├─Project</p> <p>?├─LibMyExample</p> <p>??└─Document</p> <p>?└─MyExampleApp</p> <p>?└─Document</p> <p>├─Solution</p> <p>└─Temp</p> <p>├─Compile</p> <p>└─Link</p> <p>1. MyDevelopeFolder是开发工E的根目?/p> <p>2. Bin目录存放所有动态链接库和可执行E序Q包括自q产出和第三方库,按编译配|名U包括对应的子目录,如Debug、Release。另外,E序q行q程中需要外部的数据文g和启动时需要的配置文g{等都可放于该目?/p> <p>3. MySDK存放<strong>产品目依赖、?/strong>?h文g?lib文g。其中Lib目录下根据配|名U和W三方库名称对lib文gq行理。如某项目用BCG作ؓ界面库,则BCG的头文g攄?#8220;Include\BCG”下,不同版本的库文g|于“Lib\配置名\BCG”下;目输出的lib文g直接位于“Lib\配置?#8221;下。这样品依赖的不同开发库所使用头文件与库文件和输出的头文g与库文g怺之间是独立、彼此不q涉的?/p> <p>4. Project是工E目录,用于存放代码Q按模块名组l次U目录。功能库工程一般的?#8220;Lib”开_以便与其它动态库区别。每个工E模块目录应包含“Document”子目录,该目录下按需要增?#8220;DBM”?#8220;DOC”?#8220;UML”三个子目录?#8220;DBM”用于存放与该模块相关的数据库设计文档Q通常是PowerDesigner文档Q?#8220;DOC”用于存放与该模块相关的一般性说明文档;“UML”用于存放与该模块相关的UML设计文档Q通常是Rational Rose文档?/p> <p>5. Solution是解x案目录,用于存放产品的完整解x案。通常使用解决Ҏ可以生成该品的所有版本?/p> <p>6. Temp是用于编译生成的中间目录Q主要存攄译过E中生成的各U中间文件?/p> <h3><a name=_Toc282612784><font color=#1d58d1>3.工程配置</font></a></h3> <p>Ҏ调试信息与字W集的不同,一般的工程配置有六c,如下表所C? <table border=1 cellSpacing=0 cellPadding=0> <tbody> <tr> <td vAlign=top width=145> <p>名称</p> </td> <td vAlign=top width=145> <p>字符?/p> </td> <td vAlign=top width=145> <p>是否包含调试信息</p> </td> <td vAlign=top width=145> <p>是否包含代码优化</p> </td> </tr> <tr> <td vAlign=top width=145> <p>Debug</p> </td> <td vAlign=top width=145> <p>ANSI</p> </td> <td vAlign=top width=145> <p>YES</p> </td> <td vAlign=top width=145> <p>NO</p> </td> </tr> <tr> <td vAlign=top width=145> <p>Release</p> </td> <td vAlign=top width=145> <p>ANSI</p> </td> <td vAlign=top width=145> <p>YES</p> </td> <td vAlign=top width=145> <p>YES</p> </td> </tr> <tr> <td vAlign=top width=145> <p>Program</p> </td> <td vAlign=top width=145> <p>ANSI</p> </td> <td vAlign=top width=145> <p>NO</p> </td> <td vAlign=top width=145> <p>YES</p> </td> </tr> <tr> <td vAlign=top width=145> <p>UnicodeDebug</p> </td> <td vAlign=top width=145> <p>UNICODE</p> </td> <td vAlign=top width=145> <p>YES</p> </td> <td vAlign=top width=145> <p>NO </p> </td> </tr> <tr> <td vAlign=top width=145> <p>UnicodeRelease</p> </td> <td vAlign=top width=145> <p>UNICODE</p> </td> <td vAlign=top width=145> <p>YES</p> </td> <td vAlign=top width=145> <p>YES</p> </td> </tr> <tr> <td vAlign=top width=145> <p>UnicodeProgram</p> </td> <td vAlign=top width=145> <p>UNICODE</p> </td> <td vAlign=top width=145> <p>NO</p> </td> <td vAlign=top width=145> <p>YES</p> </td> </tr> </tbody> </table> </p> <h3><a name=_Toc282612785><font color=#1d58d1>4.属性配|?/font></a></h3> <h4><a name=_Toc282612786><font color=#1d58d1>4.1.“常规”配置</font></a></h4> <p>输出目录Q?..\..\Temp\Link\$(ProjectName)\$(ConfigurationName)</p> <p>中间目录Q?.\..\Temp\Compile\$(ProjectName)\$(ConfigurationName)</p> <h4><a name=_Toc282612787><font color=#1d58d1>4.2.“调试”配置</font></a></h4> <p>如果需要启动本模块q行调试Q则“命o”为:..\..\Bin\$(ConfigurationName)\$(TargetFileName)</p> <h4><a name=_Toc282612788><font color=#1d58d1>4.3.“C/C++”配置</font></a></h4> <p>1) “附加包含目录”Q?.\..\MineSDK\Include Q如有其它目录请?#8220;;”间隔Q注意用相对\径,<strong><font color=#ff0000>严禁使用l对路径</font>?/strong></p> <p>2) 动态库使用导出宏导?导入Ӟ应在“预处理器”中定义导出宏Q不得在代码文g中定义。导出宏的一般格式ؓ“LIB_XXXXX”?/p> <p>3) 一般情况下不推荐用预~译头?/p> <h4><a name=_Toc282612789><font color=#1d58d1>4.4.“链接?#8221;配置</font></a></h4> <p>1) “输出文g”Q?/p> <p>a) 若ؓDebug版时Q?(OutDir)\$(ProjectName)D.dllQ?/p> <p>b) 若ؓRelease版时Q?(OutDir)\$(ProjectName).dll?/p> <p>2) “附加库目?#8221;Q?.\..\Bin\$(ConfigurationName);..\..\MySDK\LibQ如有其它目录请?#8220;;”间隔Q注意用相对\径,严格止使用本地l对路径?/p> <p>3) “模块定义文g”Q一般不使用模块定义.def文gQ本可选择“从默认配|?#8230;”?/p> <h4><a name=_Toc282612790><font color=#1d58d1>4.5.“生成事g”配置</font></a></h4> <p>一般需要配|的?#8220;生成后事?#8221;。Q何一个工E模块的生成后事仉应包括以下内容:</p> <p>1) copy $(TargetPath) ..\..\Bin\$(ConfigurationName)</p> <p>2) 若有导出的Lib库文Ӟ需要增加:copy $(TargetDir)$(TargetName).lib ..\..\MySDK\Lib</p> <p>3) 若有导出的头文gQ需要增加: copy myhead.h ..\..\MySDK\Include </p> <p>具体文g名与路径视情况而定Q但是严用绝对\径?/p> <h3><a name=_Toc282612791><font color=#1d58d1>5.附录QVC2005</font></a>中可以用的?/h3> <table border=1 cellSpacing=0 cellPadding=0> <tbody> <tr> <td vAlign=top width=163> <p>ConfigurationName</p> </td> <td vAlign=top width=405> <p>配置名字Q通常是Debug或者Release</p> </td> </tr> <tr> <td vAlign=top width=163> <p>IntDir</p> </td> <td vAlign=top width=405> <p>~译器用的中间目录Q出obj文g</p> </td> </tr> <tr> <td vAlign=top width=163> <p>OutDir</p> </td> <td vAlign=top width=405> <p>链接器用的输出目录</p> </td> </tr> <tr> <td vAlign=top width=163> <p>ProjectDir</p> </td> <td vAlign=top width=405> <p>目目录</p> </td> </tr> <tr> <td vAlign=top width=163> <p>ProjectName</p> </td> <td vAlign=top width=405> <p>目名字</p> </td> </tr> <tr> <td vAlign=top width=163> <p>SolutionDir</p> </td> <td vAlign=top width=405> <p>解决Ҏ目录</p> </td> </tr> <tr> <td vAlign=top width=163> <p>TargetDir</p> </td> <td vAlign=top width=405> <p>目标输出文g所在的目录</p> </td> </tr> <tr> <td vAlign=top width=163> <p>TargetExt</p> </td> <td vAlign=top width=405> <p>目标输出的扩展名</p> </td> </tr> <tr> <td vAlign=top width=163> <p>TargetFileName</p> </td> <td vAlign=top width=405> <p>目标输出文g名,包括扩展?/p> </td> </tr> <tr> <td vAlign=top width=163> <p>TargetName</p> </td> <td vAlign=top width=405> <p>目标输出名,不包括扩展名</p> </td> </tr> <tr> <td vAlign=top width=163> <p>TargetPath</p> </td> <td vAlign=top width=405> <p>目标输出文g的全路径?/p> </td> </tr> </tbody> </table> </div> <p class=postfoot>posted on 2011-04-03 18:58 <a href="http://www.shnenglu.com/phoenix8848cn/"><font color=#223355>襉K有悔</font></a> 阅读(352) <a href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#Post"><font color=#223355>评论(4)</font></a>  <a href="http://www.shnenglu.com/phoenix8848cn/admin/EditPosts.aspx?postid=143354"><font color=#223355>~辑</font></a> <a href="http://www.shnenglu.com/phoenix8848cn/AddToFavorite.aspx?id=143354"><font color=#223355>收藏</font></a> <a href="http://www.shnenglu.com/phoenix8848cn/services/trackbacks/143354.aspx"><font color=#223355>引用</font></a> </p> </div> <img src="http://www.shnenglu.com/phoenix8848cn/aggbug/143354.html?webview=1" width=1 height=1> <!-- <rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"> <rdf:description rdf:about="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html" dc:identifier="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html" dc:title="软g工程配置规范QVC2005Q?W二? trackback:ping="http://www.shnenglu.com/phoenix8848cn/services/trackbacks/143354.aspx" /> </rdf:rdf> --><script type=text/javascript> //<![cdata[ Sys.WebForms.PageRequestManager._initialize('AjaxHolder$scriptmanager1', document.getElementById('Form1')); Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tAjaxHolder$UpdatePanel1'], [], [], 90); //]]> </script> <div class="eyiaaq2" id=AjaxHolder_UpdatePanel1><a name=pagedcomment></a><a name=评论> <div class="g42gggq" id=comments> <h3>评论</h3> <div id="uewgg44" class=post> <h2><a title="permalink: re: 软g工程配置规范QVC2005Q?W二? href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#143360">#</a><font color=#000000> </font><a name=143360></a><font color=#000000>re: 软g工程配置规范QVC2005Q?W二?nbsp; </font><a onclick='return SetReplyAuhor("御用软g")' href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#post">回复</a><font color=#000000>  </font><a title=查看该作者发表过的评?href="http://www.shnenglu.com/comment?author=%e5%be%a1%e7%94%a8%e8%bd%af%e4%bb%b6" target=_blank>更多评论</a><font color=#000000> </font><a id=AjaxHolder_Comments_CommentList_ctl00_DeleteLink href="javascript:__doPostBack('AjaxHolder$Comments$CommentList$ctl00$DeleteLink','')"></a><font color=#000000>  </font><a id=AjaxHolder_Comments_CommentList_ctl00_EditLink></a><font color=#000000> </font></h2> 希望可以攑ևpdf版,q样方便使用…… <div id="mwgaias" class=postfoot>2011-04-03 20:19 | <a id=AjaxHolder_Comments_CommentList_ctl00_NameLink target=_blank><font color=#223355>御用软g</font></a> </div> </div> <div id="uemgqow" class=post> <h2><a title="permalink: re: 软g工程配置规范QVC2005Q?W二? href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#143370"><font color=#223355>#</font></a> <a name=143370></a>re: 软g工程配置规范QVC2005Q?W二?nbsp; <a onclick='return SetReplyAuhor("溪流")' href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#post"><font color=#223355>回复</font></a>  <a title=查看该作者发表过的评?href="http://www.shnenglu.com/comment?author=%e6%ba%aa%e6%b5%81" target=_blank><font color=#223355>更多评论</font></a> <a id=AjaxHolder_Comments_CommentList_ctl01_DeleteLink href="javascript:__doPostBack('AjaxHolder$Comments$CommentList$ctl01$DeleteLink','')"></a>  <a id=AjaxHolder_Comments_CommentList_ctl01_EditLink></a> </h2> 个h非常讨厌什?binQsourceQsolutionQ。。?<br>你要么就Ua自己搞,bin、source 都可以,别 solution、project 了,最后自己写脚本 <br>要么q solution、projectQ目录就大体上按默认的,一个project一个目录, <br>既用 solution、projectQ又独立搞一套目录体p,然后修改一大堆Ua关于目录的配|参敎ͼ何必? <div id="sg24eqw" class=postfoot>2011-04-03 21:52 | <a id=AjaxHolder_Comments_CommentList_ctl01_NameLink href="http://www.shnenglu.com/Streamlet/" target=_blank><font color=#223355>溪流</font></a> </div> </div> <div id="icmkckc" class=post> <h2><a title="permalink: re: 软g工程配置规范QVC2005Q?W二? href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#143376"><font color=#223355>#</font></a> <a name=143376></a>re: 软g工程配置规范QVC2005Q?W二?nbsp; <a onclick='return SetReplyAuhor("襉K有悔")' href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#post"><font color=#223355>回复</font></a>  <a title=查看该作者发表过的评?href="http://www.shnenglu.com/comment?author=%e8%a5%bf%e9%97%a8%e6%9c%89%e6%82%94" target=_blank><font color=#223355>更多评论</font></a> <a id=AjaxHolder_Comments_CommentList_ctl02_DeleteLink href="javascript:__doPostBack('AjaxHolder$Comments$CommentList$ctl02$DeleteLink','')"></a>  <a id=AjaxHolder_Comments_CommentList_ctl02_EditLink></a> </h2> @溪流<br>1.Bin里是所有生成的文gQ包含了E序可以q行的最资源,产品发布人员只需要将Bin里的文g打包可以生成安装文件。而默认的配置会在bin目录里生成程序调试数据库{一些非q行旉要的文g。Bin是面向品测试与发布人员的,开发h员只是将dll和exe输出到bin中进行调试。这样产品开发与产品试、发布分开?br>2.Project与Solution分开是因为每个成员都是独立地开发一个或几个Project的,他把Bin与SDK从SVN上checkout出来Q就可以q行自己的代码编写,而不必关心与其他开发h员所同时q行的project的依赖关pRSolution里包含的是整个品的所有project以及project之间的依赖关pR打开solution可以生成一个完整的产品到bin里,而且bin里没有Q何多余的文g?br><br>效果Q采用了q套工程配置ҎQ整个团队代码与工程层面的交明N畅多了,再也没有出现拿到别h的工E半天build不过的问题。而且开发与试、发布之间的卸接也很利。开发h员每天都build后commit到svn。每周一开发部产品理员用solution生成一个完成的binq整理出track后发布到Svn上,试人员用本周一的binq行试Q到了Tag的时间点试部品管理员bin打包成安装程序发布到svn上ƈ通知实施部门有更新版本。Ş成一个完整的程。再用bugzilla与dotproject对品的bug和h员进行管理?br><br>ȝQ这套工E配|应该算是不依赖于第三方工具Q进行基于Svn的代码管理以及多个开发h员之间的合作开发。如果是一个hQ或者project不多的时候就没必要如此复杂。而且修改工程配置是一ơ的Q不需要每ơ都修改。可以说一x逸?br><br>谢谢你的评论?br> <div id="2ummuck" class=postfoot>2011-04-04 00:43 | <a id=AjaxHolder_Comments_CommentList_ctl02_NameLink href="http://www.shnenglu.com/phoenix8848cn/" target=_blank><font color=#223355>襉K有悔</font></a> </div> </div> <div id="4cc4qqy" class=post> <h2><a title="permalink: re: 软g工程配置规范QVC2005Q?W二? href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#143387"><font color=#223355>#</font></a> <a name=143387></a>re: 软g工程配置规范QVC2005Q?W二?a name=Post></a>  <a onclick='return SetReplyAuhor("溪流")' href="http://www.shnenglu.com/phoenix8848cn/archive/2011/04/03/143354.html#post"><font color=#223355>回复</font></a>  <a title=查看该作者发表过的评?href="http://www.shnenglu.com/comment?author=%e6%ba%aa%e6%b5%81" target=_blank><font color=#223355>更多评论</font></a> <a id=AjaxHolder_Comments_CommentList_ctl03_DeleteLink href="javascript:__doPostBack('AjaxHolder$Comments$CommentList$ctl03$DeleteLink','')"></a>  <a id=AjaxHolder_Comments_CommentList_ctl03_EditLink></a> </h2> 1、打包h员不该偷懒,他们应该知道完整的精的文g清单Q而不仅仅?#8220;某个目录下的所有文?#8221; <br>2、还是没有看出来把solution单独藏在一个目录的用意?</div> </div> </div> <img src ="http://www.shnenglu.com/Walker/aggbug/143403.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Walker/" target="_blank">漫步者?…?K?/a> 2011-04-04 15:46 <a href="http://www.shnenglu.com/Walker/articles/143403.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Q{QWin7 UAC的安全、兼容及权限 http://www.shnenglu.com/Walker/articles/141814.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Mon, 14 Mar 2011 12:58:00 GMThttp://www.shnenglu.com/Walker/articles/141814.html

1、Win7 UAC的安全、兼容及权限

Posted on 2010-11-24 22:42 edwardlewiswe 阅读(401) 评论(0) ~辑 收藏

|上关于q个问题讨论较多Q但也不外乎几种Ҏ。ȝ一下,如附中。顺便了解一个UAC?/span> 

UACQ全UUser Account ControlQ用户帐hӞ 

System Safe MonitorQ主机入侵防御系l)

UAC是如何工作的[3]

我们可以单的把UAC当作权限临时重分配的工具。在默认情况下,所有的非系l核心进E都只拥有标准权限,q一权限不能对系l关键区域进行修攏V对于一个程序,如果它当中含有提权申P则在q行时会弹出UACH口要求提权。如果用户允许,则程序暂时性的获得了最高权限,可以对系l关键区域进行更改;如果用户拒绝Q则E序被拒l执行。而如果程序中没有提权甌Q则pȝ会让E序q行于标准权限下。同Ӟ对于所有程序,都可以用“以管理员w䆾q行”的方式手动提权。而即便病毒感染了pȝQ它也处于UAC的监视之下,q得病毒的反清除行Z受到很大ȝ。正是凭借这一机制QUAC成ؓ了一道重要的pȝ防火墙?/span>

?Windows7QNT6.xpȝQ中Q系l取消了对移动设备Autorun.inf的支?/span>?/span>

用户界面Ҏ隔离[5]

在早期的Windows操作pȝ中,在同一用户下运行的所有进E有着相同的安全等U,拥有相同的权限。例如,一个进E可以自由地发送一个Windows消息到另外一个进E的H口。从Windows Vista开始,当然也包括Windows 7Q对于某些Windows消息Q这一方式再也行不通了。进E?或者其他的对象)开始拥有一个新的属性——特权等U?Privilege Level)。一个特权等U较低的q程不再可以向一个特权等U较高的q程发送消息,虽然他们在相同的用户权限下运行。这是所谓的用户界面Ҏ隔离 (User Interface Privilege IsolationQUIPI)?/span>

UIPI的引入,最大的目的是防止恶意代码发送消息给那些拥有较高权限的窗口以对其q行dQ从而获取较高的权限{等?/span>

UIPI的运行机?/span>

在Windows 7中,当UAC(User Account Control)启用的时候,UIPI的运行可以得到最明显的体现。在UAC中,当一个管理员用户dpȝ后,操作pȝ会创Z个o牌对?Token Object)Q第一个是理员o牌,拥有大多数特?cM于Windows Vista之前的System中的用户)Q而第二个是一个经q过滤后的简化版本,只拥有普通用L权限?/span>

默认情况下,以普通用h限启动的q程拥有普通特权等U?UIPI的等U划分ؓ低等U?low)Q普?normal)Q高{(high)Q系l?(system))。同LQ以理员权限运行的q程Q例如,用户右键单击选择“以管理员w䆾q行”或者是通过d“runas”参数调用 ShellExecuteq行的进E,q样的进E就相应地拥有一个较?high)的特权等U?/span>

q将Dpȝ会运行两U不同类型,不同Ҏ{的进E?当然Q从技术上讲这两个q程都是在同一用户?。我们可以用Windows Sysinternals工具集中的进E浏览器(Process Explorer)查看各个q程的特权等U。[6]

所以,当你发现你的q程之间Windows消息通信发生问题Ӟ不妨使用q程览器查看一下两个进E之间是否有合适的Ҏ{?/span>

UIPI所带来的限?/span>

正如我们前文所_{的划分,是ؓ了防止以下犯上。所以,有了用户界面Ҏ隔离Q一个运行在较低Ҏ{的应用程序的行ؓ受C诸多限制Q它不可以:

?/span>验证p高特权等U进E创建的H口句柄

?/span>通过调用SendMessage和PostMessage向由较高Ҏ{q程创徏的窗?/span>?/span>发送Windows消息

?/span>使用U程钩子处理较高Ҏ{q程

?/span>使用普通钩?SetWindowsHookEx)监视较高Ҏ{q程

?/span>向一个较高特权等U进E执行DLL注入

但是Q一些特DWindows消息是容许的。因些消息对q程的安全性没有太大媄响。这些Windows消息包括Q?/span>

0x000 - WM_NULL

0x003 - WM_MOVE

0x005 - WM_SIZE

0x00D - WM_GETTEXT

0x00E - WM_GETTEXTLENGTH

0x033 - WM_GETHOTKEY

0x07F - WM_GETICON

0x305 - WM_RENDERFORMAT

0x308 - WM_DRAWCLIPBOARD

0x30D - WM_CHANGECBCHAIN

0x31A - WM_THEMECHANGED

0x313, 0x31B (WM_???)

修复UIPI问题

ZWindows Vista之前的操作系l行为所设计的应用程序,可能希望Windows消息能够在进E之间自q传递,以完成一些特D的工作。当q些应用E序?Windows 7上运行时Q因为UIPI机制Q这U消息传递被L了,应用E序׃遇到兼容性问题。ؓ了解册个问题,Windows Vista引入了一个新的API函数ChangeWindowMessageFilter[7]。利用这个函敎ͼ我们可以d或者删除能够通过Ҏ{隔离?Windows消息。这像拥有较高Ҏ{的进E,讄了一个过滤器Q允讔R过的Windows消息都被d到这个过滤器的白名单Q只有在q个白名单上的消息才允许传递进来?/span>

如果我们惛_怸个消息可以发送给较高Ҏ{的进E,我们可以在较高特权等U的q程中调用ChangeWindowMessageFilter函数Q以 MSGFLT_ADD作ؓ参数消息添加进消息qo器的白名单。同LQ我们也可以以MSGFLT_REMOVE作ؓ参数这个消息从白名单中删除?/span>

一个示?/span>

对于pȝ消息的处理,接受消息的进E需要将该消息加入到白名单中Q可以通过下面的代码实玎ͼ

需要在高权限程序开始的地方加入以下代码,指定什么消息可以接?/span>

 

代码
1 typedef BOOL (WINAPI *_ChangeWindowMessageFilter)( UINT , DWORD);
2
3 BOOL CVistaMsgRecvApp::AllowMeesageForVista(UINT uMessageID, BOOL bAllow)//注册Vista全局消息
4  {
5 BOOL bResult = FALSE;
6 HMODULE hUserMod = NULL;
7  //vista and later
8 hUserMod = LoadLibrary( L"user32.dll" );
9 if( NULL == hUserMod )
10 {
11 return FALSE;
12 }
13 _ChangeWindowMessageFilter pChangeWindowMessageFilter = (_ChangeWindowMessageFilter)GetProcAddress( hUserMod, "ChangeWindowMessageFilter" );
14 if( NULL == pChangeWindowMessageFilter )
15 {
16 AfxMessageBox(_T("create windowmessage filter failed"));
17 return FALSE;
18 }
19 bResult = pChangeWindowMessageFilter( uMessageID, bAllow ? 1 : 2 );//MSGFLT_ADD: 1, MSGFLT_REMOVE: 2
20 if( NULL != hUserMod )
21 {
22 FreeLibrary( hUserMod );
23 }
24 return bResult;
25 }
对于自定义消息,通常是指大于WM_USER的消息,我们首先必须在系l中注册该消息,然后在调用上面的代码:

 

1 #define WM_MYNEWMESSAGE (WM_USER + 999)<BR>UINT uMsgBall=::RegisterWindowMessage (WM_MYNEWMESSAGE )<BR>if(!uMsgBall)<BR>return FALSE;<BR>
注册消息通过RegisterWindowMessage实现Q函数的参数是你需要注册的消息倹{?/span>

 

此时Q低{的进E就可以像高{的进E发送消息了?/span>

?Win7下如何让应用E序以管理员w䆾q行安装执行

1、法一Q?/span>

runas /profile /env /user:mydomain\admin "mmc %windir%\system32\dsa.msc"

我感觉这U方法不靠谱?/span>

2、法二:

通过manifest文g使VC应用E序获得理员权?/span>

q种Ҏq不错[1]?/span>

<security>

<requestedPrivileges>

<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>

</requestedPrivileges>

</security>

3、法三:

起名为setupQwin7会自己提升权限,增加manifest文g。还有一U方法是Ҏ册表。HKEY_CURRENT_USER\Software \Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers

d一个字W串?名称是你的E序的\径和名字QgؓRUNASADMIN?/span>

也可以做成服务?/span>

4、其它方法:

q有的网友说Q有个开源的目叫做RunAsQ就是用来以指定用户来运行程序的目Q可以参?/span>

参考网址和更多阅?/span>

[1] http://hi.baidu.com/crowreturns/blog/item/f5e7cefd7546a284b801a07e.html

[2] http://www.cnblogs.com/sun8134/archive/2009/10/30/1593025.html

[3] 360 论坛

[4] http://topic.csdn.net/u/20100203/14/d98d8310-4971-47d1-94b1-9cdfbf159b4f.html

[5] http://blog.csdn.net/jinhill/archive/2010/07/21/5752870.aspx

[6] Sysinternals Utilities Index

http://technet.microsoft.com/en-us/sysinternals/bb545027.aspx

[7] ChangeWindowMessageFilter

http://msdn.microsoft.com/en-us/library/ms632675%28VS.85%29.aspx

[ Using the ChangeWindowMessageFilter function is not recommended, as it has process-wide scope. Instead, use the ChangeWindowMessageFilterEx function to control access to specific windows as needed. ChangeWindowMessageFilter may not be supported in future versions of Windows.]

Q?Qhttp://it.chinawin.net/softwaredev/article-b6f9.html



]]>
VC调用外部E序接口 (?http://www.shnenglu.com/Walker/articles/136660.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Thu, 16 Dec 2010 11:48:00 GMThttp://www.shnenglu.com/Walker/articles/136660.htmlhttp://www.shnenglu.com/Walker/comments/136660.htmlhttp://www.shnenglu.com/Walker/articles/136660.html#Feedback0http://www.shnenglu.com/Walker/comments/commentRss/136660.htmlhttp://www.shnenglu.com/Walker/services/trackbacks/136660.html(1) system()

函数? system 
?nbsp;? 发出一个DOS命o 
?nbsp;? 
int system(char *command); 
?nbsp;? system函数已经被收录在标准c库中Q可以直接调?nbsp;
q回?br>  
=-1:出现错误 
  
=0:调用成功但是没有出现子进E?nbsp;
  
>0:成功退出的子进E的id
样例: 
system(
"D:\\game.exe");

int system(
   
const char *command 
);
int _wsystem(
   
const wchar_t *command 
);



(
2) WinExec()
函数原型:
UINT WINAPI WinExec(
  __in          LPCSTR lpCmdLine,
  __in          UINT uCmdShow
);


参数说明Q?br>  lpCmdLineQ  
// 命o路径
  uCmdShowQ  // 昄方式Q共?1U,具体可以查阅MSDN的ShowWindow函数

q回|
  成功Q返回值大?1
  q回0表示内存或者资源溢?br>  q回ERROR_BAD_FORMAT表示exe文g非法或者已损坏?br>  q回ERROR_FILE_NOT_FOUND指定的文件没有找到?br>  q回ERROR_PATH_NOT_FOUND找不到指定\?br>样例:
  WinExec(
"Notepad.exe", SW_SHOW); // 打开C?/span>
  WinExec("D:\\Program Files\\Test\\Test.exe",SW_SHOWMAXIMIZED); // 以最大化的方式打开Test.exeQ注意文件名的大写也必d全一P


(
3) ShellExecute()
函数原型:   

HINSTANCE ShellExecute(          
    HWND hwnd,
    LPCTSTR lpOperation,
    LPCTSTR lpFile,
    LPCTSTR lpParameters,
    LPCTSTR lpDirectory,
    INT nShowCmd
);

用例: ShellExecute(NULL,
"open","C:\\Test.txt",NULL,NULL,SW_SHOWNORMAL); //打开C:\Test.txt 文g        



(
4) CreateProcess() 

函数原型:

BOOL CreateProcess(
    LPCTSTR lpApplicationName, 
//执行E序?/span>
    LPTSTR lpCommandLine, // 参数?br>    //下面两个参数描述了所创徏的进E和U程的安全属性,如果为NULL则用默认的安全属?/span>
    LPSECURITY_ATTRIBUTES lpProcessAttributes, //q程安全属?/span>
    LPSECURITY_ATTRIBUTES lpThreadAttributes,    // U程安全属?/span>
    BOOL bInheritHandles, // l承标志
    DWORD dwCreationFlags, // 创徏标志
    LPVOID lpEnvironment, // 环境变量
    LPCTSTR lpCurrentDirectory,   // q行该进E的初始目录
    LPSTARTUPINFO lpStartupInfo, // 用于在创建子q程时设|各U属?/span>
    LPPROCESS_INFORMATION lpProcessInformation //用于在进E创建后接受相关信息
  ); 

评论:
system()Q主要用在DOS环境?
WinExec(),单实用,方便打开执行q程Q但不能操作控制q程?br>ShellExecute(),增强了操作能力,但对q程的控制还是不够用?br>CreateProcess(),
目前最强劲的进E函敎ͼ通过讄StartupInfol构体参敎ͼ来设|子q程的属性,子进E创建后的信息也保存在ProcessInformationl构体中Q便于操作,功能强大Q但参数q多.


]]>
GDI+学习之线性渐变画?/title><link>http://www.shnenglu.com/Walker/articles/136411.html</link><dc:creator>漫步者?amp;…?K?/dc:creator><author>漫步者?amp;…?K?/author><pubDate>Tue, 14 Dec 2010 12:52:00 GMT</pubDate><guid>http://www.shnenglu.com/Walker/articles/136411.html</guid><description><![CDATA[<p><br>  GDI+学习W记之GDI+环境初始?收藏 <br>作者:朱金?br>来源Q?a >http://blog.csdn.net/clever101/</a> </p> <p> </p> <p><br>      管以前接触q一下GDI+Q但军_从现在开始系l学习GDI+Q所用的教材为《精通GDI~程》。在VS 2010以下版本的VS ~译器用GDI+都需要对GDI+环境q行初始化操作(VS 2010中的MFC 10依赖GDI+Q故不用初始化)?</p> <p>VS 2003、VS 2005和VS 2008的GDI+环境初始化操作步骤一栗?</p> <p><br>      在应用程序类d一个保护权限的数据成员Q?</p> <p>view plaincopy to clipboardprint?<br>ULONG_PTR m_gdiplusToken;  <br>ULONG_PTR m_gdiplusToken; </p> <p> </p> <p><br>在应用程序类的实现文件包含gdi+的头文gQ?</p> <p> </p> <p><br>view plaincopy to clipboardprint?<br>#include <GdiPlus.h>  <br>#include <GdiPlus.h> </p> <p><br>在工E附加库加上QGdiPlus.lib </p> <p> </p> <p><br>然后在应用程序类的InitInstance加上下面初始化代码: </p> <p> </p> <p><br>view plaincopy to clipboardprint?<br>BOOL C***App::InitInstance()   <br>{   <br>    Gdiplus::GdiplusStartupInput StartupInput;   <br>    GdiplusStartup(&m_gdiplusToken,&StartupInput,NULL);   <br>}  <br>BOOL C***App::InitInstance()<br>{<br> Gdiplus::GdiplusStartupInput StartupInput;<br> GdiplusStartup(&m_gdiplusToken,&StartupInput,NULL);<br>}<br> </p> <p> </p> <p>上面代码的作用是初始化GDI+资源?</p> <p> </p> <p><br>  在应用程序类的InitInstance加上下面代码Q?</p> <p> </p> <p><br>view plaincopy to clipboardprint?<br>int C***App::ExitInstance()   <br>{   <br>    // TODO: 在此d专用代码?或调用基c?nbsp;  <br>       <br>    Gdiplus::GdiplusShutdown(m_gdiplusToken);   <br>    return __super::ExitInstance();   <br>}  <br>int C***App::ExitInstance()<br>{<br> // TODO: 在此d专用代码?或调用基c?br>    <br> Gdiplus::GdiplusShutdown(m_gdiplusToken);<br> return __super::ExitInstance();<br>}<br> </p> <p> </p> <p>上面代码的作用是销毁GDI+资源?</p> <p><br>VC 6.0中用GDI+库,请参考这文章:在VC6.0中用GDI+的两U办?</p> <p> </p> <p><br>     现在试一下我们初始化GDI+环境是否成功。我们用GDI+的类接口在视囑֮户区l制一个字W串Q具体代码如下: </p> <p> </p> <p><br>view plaincopy to clipboardprint?<br>CDC *pDC = pView->GetDC();   <br>Gdiplus::Graphics graphics(pDC->m_hDC);   <br>Gdiplus::Pen pen(Gdiplus::Color(255,0,0,255));   <br>Gdiplus::SolidBrush brush(Gdiplus::Color(255,0,0,255));   <br>Gdiplus::FontFamily fontfm(L"宋体");   <br>Gdiplus::Font font(&fontfm,24,Gdiplus::FontStyleRegular,Gdiplus::UnitPixel);   <br>CRect rt;   <br>pView->GetClientRect(&rt);   <br>Gdiplus::PointF pointF(rt.Width()/2,rt.Height()/2);   <br>   graphics.DrawString(L"GDI+E序C意",-1,&font,pointF,&brush);   <br>   graphics.ReleaseHDC(pDC->m_hDC);   <br>   pView->ReleaseDC(pDC);  <br> CDC *pDC = pView->GetDC();<br> Gdiplus::Graphics graphics(pDC->m_hDC);<br> Gdiplus::Pen pen(Gdiplus::Color(255,0,0,255));<br> Gdiplus::SolidBrush brush(Gdiplus::Color(255,0,0,255));<br> Gdiplus::FontFamily fontfm(L"宋体");<br> Gdiplus::Font font(&fontfm,24,Gdiplus::FontStyleRegular,Gdiplus::UnitPixel);<br> CRect rt;<br> pView->GetClientRect(&rt);<br> Gdiplus::PointF pointF(rt.Width()/2,rt.Height()/2);<br>    graphics.DrawString(L"GDI+E序C意",-1,&font,pointF,&brush);<br>    graphics.ReleaseHDC(pDC->m_hDC);<br>    pView->ReleaseDC(pDC);<br> </p> <p> </p> <p> </p> <p> </p> <p>效果囑֦下: </p> <p> </p> <p><br> </p> <p> </p> <p> </p> <p> </p> <p><br>使用GDI+一些注意事: </p> <p> </p> <p><br>1.在DLL中用GDI+库,只需要包含GdiPlus.h和GdiPlus.libQ初始化GDI+环境的工作只需要在主调用程序做Q否则在DLL初始化代码中初始化GDI+环境Ҏ发生DLL重入的错误(以前我犯q这L错误Q?</p> <p> </p> <p><br>2.GDI+接口参数使用的是unicode字符集,因ؓ调用MGDI+cL口时其字W串参数都必ȝ保是unicode字符。在多字节字W集环境下开发常量字W串可以通过L宏{?变量多字节字W{unicode字符可以使用Windows API函数MultiByteToWideChar或ATL的A2W宏?</p> <p> </p> <p><br>3. GDI+的对象和GDI句柄一P同样会占用资源,一ơ用过多的GDI+的对象甚至会发生E序崩溃的现象。因此必随时将不必要的GDI+的对象占用的资源释放掉,如上例的Qgraphics.ReleaseHDC(pDC->m_hDC)?</p> <p> </p> <p>本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/clever101/archive/2010/12/08/6063670.aspx</a><br>作者:朱金?br>来源Q?a >http://blog.csdn.net/clever101/</a> </p> <p> </p> <p><br>      我发C用GDI+来制作画囑ַL调色板极为方便(q个工作如果让GDI来做不知要写多少代码Q。下面我们学习一下GDI+的线性渐变画PLinearGradientBrushcȝ用法Q具体代码如下: </p> <p> </p> <p><br>      view plaincopy to clipboardprint?<br>    CDC *pDC = pView->GetDC();   <br>     // 定义一个画囑֯?nbsp;  <br>    Gdiplus::Graphics graphics(pDC->m_hDC);   <br>       <br>     // 获取视图客户区大?nbsp;  <br>     CRect rt;   <br>     pView->GetClientRect(&rt);   <br>// 定义一个线性渐变画P按红黄蓝l的序四种颜色渐变   <br>     LinearGradientBrush linGrBrush(Point(100,0),Point(100,rt.Height()/2),Color(255,255,0,0),Color(255,0,0,255));   <br>     Color colors[] = {   <br>         Color(255, 255, 0, 0),   // red   <br>         Color(255, 255, 255, 0), //yellow   <br>         Color(255, 0, 0, 255),   // blue   <br>         Color(255, 0, 255, 0)};  // green   <br>         REAL positions[] = {   <br>             0.0f,      <br>             0.33f,      <br>             0.66f,   <br>             1.0f};     <br>    linGrBrush.SetInterpolationColors(colors, positions,4);   <br>    // 填充指定区域矩Ş   <br>    graphics.FillRectangle(&linGrBrush,rt.Width()/2,0,80,rt.Height()/2);   <br> CDC *pDC = pView->GetDC();<br>    // 定义一个画囑֯?br> Gdiplus::Graphics graphics(pDC->m_hDC);<br>    <br>  // 获取视图客户区大?br>  CRect rt;<br>  pView->GetClientRect(&rt);<br>// 定义一个线性渐变画P按红黄蓝l的序四种颜色渐变<br>     LinearGradientBrush linGrBrush(Point(100,0),Point(100,rt.Height()/2),Color(255,255,0,0),Color(255,0,0,255));<br>  Color colors[] = {<br>   Color(255, 255, 0, 0),   // red<br>         Color(255, 255, 255, 0), //yellow<br>   Color(255, 0, 0, 255),   // blue<br>   Color(255, 0, 255, 0)};  // green<br>   REAL positions[] = {<br>    0.0f,   <br>    0.33f,   <br>    0.66f,<br>    1.0f};  <br> linGrBrush.SetInterpolationColors(colors, positions,4);<br> // 填充指定区域矩Ş<br> graphics.FillRectangle(&linGrBrush,rt.Width()/2,0,80,rt.Height()/2);  </p> <p> </p> <p><br>     效果如下Q?</p> <p> </p> <p><br>         </p> <p> </p> <p><br>          q个U性渐变画刷很单,是按垂直方向(即y轴方向)渐变的。我感兴的是画L两个参数Point(100,0),Point(100,rt.Height()/2)Q书上介l的L的颜色和l点颜色的位|和要填充的矩Ş之间的关pL怎样的?我们看到上面的画LL和终点的高度和要填充的矩形的高度是一LQ都是rt.Height()/2。我们把d的高度羃ؓ原来的一般,看看有什么效果,卛_义ؓQ?</p> <p> </p> <p><br>       view plaincopy to clipboardprint?<br>LinearGradientBrush linGrBrush(Point(100,0),Point(100,rt.Height()/4),Color(255,255,0,0),Color(255,0,0,255));  <br>LinearGradientBrush linGrBrush(Point(100,0),Point(100,rt.Height()/4),Color(255,255,0,0),Color(255,0,0,255)); </p> <p>效果囑֦下: </p> <p> </p> <p><br> </p> <p> </p> <p><br>     我们发现上图{于q箋用两个画刷填充这个矩形。假如填充的目标矩Ş的高度小于画L高度Q又会是怎样的效果呢Q代码改为: </p> <p> </p> <p><br>view plaincopy to clipboardprint?<br>LinearGradientBrush linGrBrush(Point(100,0),Point(100,rt.Height()/2),Color(255,255,0,0),Color(255,0,255,0));   <br>     Color colors[] = {   <br>         Color(255, 255, 0, 0),   // red   <br>         Color(255, 255, 255, 0), //yellow   <br>         Color(255, 0, 0, 255),   // blue   <br>         Color(255, 0, 255, 0)};  // green   <br>         REAL positions[] = {   <br>             0.0f,      <br>             0.33f,      <br>             0.66f,   <br>             1.0f};     <br>    linGrBrush.SetInterpolationColors(colors, positions,4);   <br>    // 填充指定区域矩Ş   <br>    graphics.FillRectangle(&linGrBrush,rt.Width()/2,0,80,rt.Height()/4);  <br>LinearGradientBrush linGrBrush(Point(100,0),Point(100,rt.Height()/2),Color(255,255,0,0),Color(255,0,255,0));<br>  Color colors[] = {<br>   Color(255, 255, 0, 0),   // red<br>         Color(255, 255, 255, 0), //yellow<br>   Color(255, 0, 0, 255),   // blue<br>   Color(255, 0, 255, 0)};  // green<br>   REAL positions[] = {<br>    0.0f,   <br>    0.33f,   <br>    0.66f,<br>    1.0f};  <br> linGrBrush.SetInterpolationColors(colors, positions,4);<br> // 填充指定区域矩Ş<br> graphics.FillRectangle(&linGrBrush,rt.Width()/2,0,80,rt.Height()/4);<br> </p> <p> </p> <p><br>效果囑֦下: </p> <p> </p> <p><br> </p> <p> </p> <p> </p> <p>    我们看到q时矩Ş区域的填充只使用了画L一部分。这时或许我们可以得Z个简单的l论Q用d填充多边形区域,有点cM于铺地砖Q地砖好比画PI地好比要填充的区域区域?</p> <p> </p> <p><br>思考题Q?</p> <p> </p> <p><br>      U性渐变画LL和终点的坐标值和要填充的矩Ş之间是什么关p? </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p>本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/clever101/archive/2010/12/08/6063697.aspx</a></p> <img src ="http://www.shnenglu.com/Walker/aggbug/136411.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Walker/" target="_blank">漫步者?…?K?/a> 2010-12-14 20:52 <a href="http://www.shnenglu.com/Walker/articles/136411.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>控g通知消息http://www.shnenglu.com/Walker/articles/136328.html漫步者?amp;…?K?/dc:creator>漫步者?amp;…?K?/author>Mon, 13 Dec 2010 12:30:00 GMThttp://www.shnenglu.com/Walker/articles/136328.html控g通知消息
    
在《深度解?span>VC中的消息Q上Q》中Q我们提C消息的分cL3U:H口消息、命令消息和控g通知消息Q我们这里要谈的是最后一U:控g通知消息?span>
     控g通知消息Q是指这样一U消息,一个窗口内的子控g发生了一些事情,需要通知父窗口。通知消息只适用于标准的H口控g如按钮、列表框、组合框、编辑框Q以?span>Windows公共控g如树状视图、列表视囄。例如,单击或双M个控件、在控g中选择部分文本、操作控件的滚动条都会生通知消息。她cM于命令消息,当用户与控gH口交互Ӟ那么控g通知消息׃从控件窗口发送到它的ȝ口。但是这U消息的存在q不是ؓ了处理用户命令,而是Z让主H口能够改变控gQ例如加载、显C数据。例如按下一个按钮,他向父窗口发送的消息也可以看作是一个控仉知消息Q单击鼠标所产生的消息可以由ȝ口直接处理,然后交给控gH口处理?span>
     控g通知消息主要q口类即直接或间接?span>CWNDcL生类处理?/span>

    控g通知格式
    
控g通知l历了一个演变过E,因?span>SendMessage( )的变?span>Message?span>wParam?span>lParam有三U格式?span>
     W一控g通知格式
    
W一控g通知格式只是H口消息的子集。它的特征格式如下:WM_XXXX。它主要来自下面?span>3U消息类型:
     (1)
表示一个控件窗口要么已l被创徏或销毁,要么已经被鼠标单ȝ消息Q?span>WM_PARENTNOTIFYQ?span>
     (2)发送到父窗口,用来l制自nH口的消息,例如Q?span> WM_CTLCOLOR?span>WM_DRAWITEM?span>WM_MEASUREITEM?span>WM_DELETEITEM?span>WM_CHARTOITEM?span>WM_VKTOITEM?span>WM_COMMAND?span>WM_COMPAREITEM
     (3)有滚动调控g发送,通知父窗口滚动窗口的消息Q?span>WM_VSCROLL?span>WM_HSCROLL
     W二控g通知格式
    
W二控g通知格式与命令消息共享,它的特征格式如下Q?span>WM_COMMAND?span>
     ?span>WM_COMMAND中,lParam用来区分是命令消息还是控仉知消息Q如?span>lParam?span>NULLQ则q是个命令消息,否则lParam里面攄必然是控g的句柄,是一个控仉知消息?span>对于wParam则是低位攄是控?span>IDQ高位放的是相应的消息事件?span>
     W三控g通知格式
    
q个才真正涉及到我们要讲的内容,同时他也是最为灵zȝ一U格式。它的特征格式如下:WM_NOTIFY?span>
     ?span>WM_NOTIFY中,lParam中放的是一个称?a name=baidusnap0>NMHDRl构的指针。在wParam中放的则是控件的ID?/span>

    NMHDRl构的由?/span>
    NMHDR
l构是很值得一提的Q该l构包括有关制作该通知的控件的M内容Q而不受空间和cd的限Ӟ他的来历也是很有意思的?span>
    
在最初的windows3.x中,Ҏ׃存在什?span>WM_NOTIFY
Q控仉知它们父窗口,如鼠标点?span>,控g背景l制事gQ通过发送一个消息到父窗口。简单的通知仅发送一?span>WM_COMMAND消息Q包含一个通知码和一个在wParam中的控gID及一个在lPraram中的控g句柄。这样一来,wParam?span>lParam都被填充了Q没有额外的I间来传递一些其它的消息Q例如鼠标按下的位置和时间?span>
     Z克服q个困难Q?span>windows3.x提Z一个比较低U的解决{略Q那是l一些消息添加一些附加消息,最为明昄是控g自画用到?span>DRAWITEMSTRUCT。不知道大家对这个结构熟悉不Q不q,如果你是老手Q你应该非常清楚q个l构Q这个结构包含了9个内容,几乎你需要控制的信息都给你提供了。ؓ什么说它比较低U呢Q因Z同的消息附加的内容不同,l果是一盘散沙,非常混ؕ?span>
     ?span>win32中,MS又提Z一个更好的解决ҎQ引q?strong>NMHDR
l构。这个结构的引进是消息l一hQ利用它可以传递复杂的信息。这个结构的布局如下Q?span>
    NMHDR
     {
         HWnd hWndFrom ; 相当于原WM_COMMAND传递方式的lParam
         UINT idFrom ;   
相当于原WM_COMMAND传递方式的wParamQ?span>low-orderQ?span>
         UINT code ;      相当于原WM_COMMAND传递方式的Notify Code(wParam"s high-order)
     }
Q?span>
     对于q个l构的应用于WM_NOTIFY信息l构Q结?span>WM_NOTIFY变成了Q?span>
     A、无附加信息。结构变得很单,是一?strong>NMHDR
l构?span>
     B、有附加信息。定义一个大的结构,它的W一个元素就?strong>NMHDRl构Q它的后面放|附加信息?span>
    
    WM_NOTIFYl构的好?/span>
    
除了上面我们所说的好处外,WN_NOTIFYq有自己的独特的好处Q?span>
    
׃在大l构中,W一个成员ؓNMHDR,q样一来,我们可以利用指?strong>NMHDR的指针来传递结构地址Q根据指针的Ҏ,无论消息有没有附加信息,q个指针都适用Q也能够很方便的q行强制转换?/span>

    分析ON_NOTIFY
   
cd导可以创?span>ON_NOTIFY消息映射入口q提供一个处理函数的框架Q来处理 WM_NOTIFYcd的消息?span>ON_NOTIFY消息映射宏有如下语法.
   ON_NOTIFY(wNotifyCode,id,memberFxn)
  其中Q?span>wNotifyCode:要处理的通知消息通知码。比如上面我们提到的LVN_KEYDOWNQ?span>Id:控g标识IDQ?span>MemberFxn:处理此消息的成员函数?span>
  此成员函数有如下的原型声?span>:
   afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result);
  比如:假设你想成员函数OnKeydownList1处理ClistCtrl(标识ID=IDC_LIST1Q的 LVN_KEYDOWN消息,你可以用类向导d如下的消息映?span>:
   ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )
  在上面的例子?span>,cd导提供如下函?span>:
   void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
    {
     LV_KEYDOWN* pLVKey= (LV_KEYDOWN*)pNMHDR;
     *pResult = 0;
     }
  q时cd导提供了一个适当cd的指针,你既可以通过pNMHDRQ也可以通过 pLVKey来访问这个通知l构?/span>

    ON_NOTIFY_RANGE
  有时我们可能需要ؓ一l控件处理相同的WM_NOTIFY消息。这旉要?span>ON_NOTIFY_RANGE而不?span>ON_NOTIFY。不q,很不q的是,VC6?span>ClassWizardq不支持q个消息Q所以我们必L工添加。方法和一般的手工d的消息一P不过需要注意的是:
     (1)
当你使用 ON_NOTIFY_RANGE?span>,你需要指定控件的ID范围.其消息映入口及函数原型如下:
      ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )
    其中Q?span>wNotifyCode:消息通知?span>.比如:LVN_KEYDOWN?span>id: W一控g的标?span>ID?span>
     idLast:最后一个控件的标识ID。(标识g定要q箋Q?span>memberFxn: 消息处理函数?span>
  (2)成员函数必须有如下原型申明:afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );

    l束语:
     WM_NOTIFY
是一个很值得思考的l构Q另外他?span>Reflect Message联系非常紧密Q我们接下来p讨论一下反消息?/span>



]]>Windows消息Q{Q?/title><link>http://www.shnenglu.com/Walker/articles/136326.html</link><dc:creator>漫步者?amp;…?K?/dc:creator><author>漫步者?amp;…?K?/author><pubDate>Mon, 13 Dec 2010 11:47:00 GMT</pubDate><guid>http://www.shnenglu.com/Walker/articles/136326.html</guid><description><![CDATA[     摘要:   转脓:深度解析VC中的消息(? 收藏 hustliQ原作)转自csdn.net摘要QWindows~程和Dos~程Q一个很大的区别是Qwindows~程是事仉动,消息传递的。所以,要做好windows~程Q必d消息机制有一个清楚的认识Q本文希望能够对消息的传递做一个全面的Q由于小生初学VCQ里面可能有一些错误的地方Q还往各位大虾批评、指正? 注意Q有?..  <a href='http://www.shnenglu.com/Walker/articles/136326.html'>阅读全文</a><img src ="http://www.shnenglu.com/Walker/aggbug/136326.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/Walker/" target="_blank">漫步者?…?K?/a> 2010-12-13 19:47 <a href="http://www.shnenglu.com/Walker/articles/136326.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.cqqianmei.cn" target="_blank">ƷþëƬ</a>| <a href="http://www.szjjls.cn" target="_blank">þۺ97ɫֱ</a>| <a href="http://www.siteni.cn" target="_blank">ŮþþƷ㽶69</a>| <a href="http://www.changchun8.cn" target="_blank">žžþþƷר</a>| <a href="http://www.fanggumen.cn" target="_blank">þۺϸϾþúݺݺ97ɫ69</a>| <a href="http://www.ahozs.cn" target="_blank">þ97þ97Ʒӿϼ</a>| <a href="http://www.gjqa.cn" target="_blank">Ʒþþþһ</a>| <a href="http://www.maishuhua.cn" target="_blank">þˬˬAV</a>| <a href="http://www.kxtravel.com.cn" target="_blank">Ʒþˬ</a>| <a href="http://www.sz-kwei.cn" target="_blank">þþþþþþþ</a>| <a href="http://www.51index.cn" target="_blank">˾þ</a>| <a href="http://www.td16yf8g.cn" target="_blank">ŷ龫Ʒþþþþ</a>| <a href="http://www.fanerge.cn" target="_blank">Ʒþþþþþapp</a>| <a href="http://www.z42195.cn" target="_blank">þþþƵ</a>| <a href="http://www.club-biz.cn" target="_blank">Ʒþþ99</a>| <a href="http://www.uyfw.cn" target="_blank">þۺϳDž</a>| <a href="http://www.pp4f.cn" target="_blank">99þ99þ</a>| <a href="http://www.vfqt.cn" target="_blank">yy6080þ</a>| <a href="http://www.bolezi333.cn" target="_blank">þֻоƷþ</a>| <a href="http://www.kuruishanghai.cn" target="_blank">99Ʒþ</a>| <a href="http://www.vfmg.cn" target="_blank">뾫ƷþɪӰ</a>| <a href="http://www.zfrnhbv.com.cn" target="_blank">þþƷһӰ</a>| <a href="http://www.tianyacity.cn" target="_blank">ھƷ˾þþþ777</a>| <a href="http://www.30ww.cn" target="_blank">99REþþƷﶼǾƷ </a>| <a href="http://www.65it.cn" target="_blank">þþ뾫ƷպĦ</a>| <a href="http://www.sxmkw.cn" target="_blank">ŷպƷþþþ</a>| <a href="http://www.icaew.com.cn" target="_blank">69þþƷһ</a>| <a href="http://www.dashu0.com.cn" target="_blank">ŷպĻþþò</a>| <a href="http://www.hebeijiushan.cn" target="_blank">ɫAVԾþþþþ</a>| <a href="http://www.dianwowang.cn" target="_blank">þۺϾþ߾Ʒ </a>| <a href="http://www.jj123.com.cn" target="_blank">þþþþùaѹۿɫƬ</a>| <a href="http://www.yunqichaoyang.cn" target="_blank">޵һƷƷþ</a>| <a href="http://www.coldcha.cn" target="_blank">þeֻйľƷ99</a>| <a href="http://www.yrjiameng.cn" target="_blank">ݺݾƷþþĻ</a>| <a href="http://www.dlygbx.cn" target="_blank">þþƷ99þ˿</a>| <a href="http://www.91368.com.cn" target="_blank">ھƷþþþþ99</a>| <a href="http://www.c118c.cn" target="_blank">þɫһ</a>| <a href="http://www.su26.cn" target="_blank">þþƷĻ23ҳ</a>| <a href="http://www.bbs020.cn" target="_blank">þþƷ}Ů</a>| <a href="http://www.bai41.cn" target="_blank">þۺϹapp</a>| <a href="http://www.kzwn.net.cn" target="_blank">ŮƷþþ </a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>