﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-喜++&amp;&amp;方块糖-文章分类-VC</title><link>http://www.cppblog.com/WangYu/category/6133.html</link><description>海阔天空</description><language>zh-cn</language><lastBuildDate>Fri, 23 May 2008 14:30:43 GMT</lastBuildDate><pubDate>Fri, 23 May 2008 14:30:43 GMT</pubDate><ttl>60</ttl><item><title>WinAPI编程基础 之 消息分流器</title><link>http://www.cppblog.com/WangYu/articles/42501.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Sun, 03 Feb 2008 18:53:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42501.html</guid><description><![CDATA[<table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
    <tbody>
        <tr>
            <td align="center" height="25"><font style="font-size: 14pt;" color="#02368d"><strong>WinAPI编程基础 之 消息分流器[z]</strong></font><br>
            </td>
        </tr>
        <tr>
            <td bgcolor="#d2dee2" height="1"><br></td>
        </tr>
        <tr>
            <td bgcolor="#ffffff" height="1"><br></td>
        </tr>
        <tr>
            <td align="center">
            <table style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0" width="100%">
                <tbody>
                    <tr>
                        <td width="100%">
                        <div id="art" width="100%" style="margin: 15px;">
                        文章作者：Bideyore[E.S.T]<br>信息来源：邪恶八进制 中国（<a href="http://www.eviloctal.com/" target="_blank">www.EvilOctal.com</a>）<br>
                        <p>对于熟悉Win API编程的同志们来说，windowsx.h这个头文件应该不会太陌生吧，这次要讲的内容就来自这个windowsx.h头文件。<br><br>经常能在msdn上查到这样一些函数，明明是个函数，而且模样长得和一般的api函数也一样一样的，可却叫做macro，为什么呢？留意一下函数使用的requirement，你会发现，它的声明正是在windowsx.h这个头文件里。<br><br>Windowsx.h包含了这样一些内容：<br style="color: #ff0000;">宏API，窗口消息分流器，控件API；<br style="color: #ff0000;"><br>所
                        有的这些宏定义，可以使你的程序更加安全，简洁，结构更清晰，大大提高程序的可读性；其中窗口消息分流器（message
                        cracker）是我们今天要讨论的话题，它可以使我们的API程序变得更简洁。下面就进入我们的主题：（有关windowsx.h的更多内容，可以参考
                        MS Knowledge Base Article #83456.）<br><br>消息分流器是Windows提供的一组宏定义，它的两个最大的作用，用MS的话来说，就是：<br><br>● 安全的数据类型，因为消息分流器完成了大量的类型转换的工作；<br>● 使程序向32位windows的转化更简单；<br><br>当然，使用消息分流器会大大改变程序的面貌，你也可以选择不使用它。<br><br>下面我们就以一个对话框窗口的消息处理过程为例，看看消息分流器到底是怎么运作的。<br><br><br>1．消息分流器的基本使用<br>先看一个普通的窗口消息处理函数，它可能需要处理一些窗口的初始化，无效客户区重绘等消息：<br><br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, <br>  WPARAM wParam, LPARAM lParam)<br>{<br>  switch(msg)<br>  {<br>    case WM_CREATE:<br>    // ...<br>    return 0;<br>        <br>    case WM_PAINT:<br>    // ...<br>    return 0;<br>                <br>    case WM_DESTROY:<br>    //...<br>    return 0;<br>}<br>return DefWindowProc(hwnd, msg, wParam, lParam);<br>}<br>  <br>而通过使用消息分流器，我们可以把每个case都写到相应的消息处理函数中，就像下面这样：<br><br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, <br>  WPARAM wParam, LPARAM lParam)<br>{<br>  switch(msg)<br>  {<br>    case WM_CREATE:<br>           return HANDLE_WM_CREATE(hwnd, wParam, lParam, Cls_OnCreate);<br>        <br>    case WM_PAINT:<br>           return HANDLE_WM_PAINT(hwnd, wParam, lParam, Cls_OnPaint);<br>                <br>    case WM_DESTROY:<br>    return HANDLE_WM_DESTROY(hwnd, wParam, lParam, Cls_OnDestroy);<br>}<br>return DefWindowProc(hwnd, msg, wParam, lParam);<br>}<br><br>这里用到了三个宏定义：HANDLE_WM_CREATE, HANDLE_WM_PAINT, HANDLE_WM_DESTROY；这三个宏定义就是我们的三个消息分流器（别看叫什么分流器，说穿了也不值几个钱，呵呵），它们在windowsx.h中的定义如下：<br><br>#define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) <br>((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L)<br>#define HANDLE_WM_PAINT(hwnd, wParam, lParam, fn) <br>((fn)(hwnd), 0L)<br>#define HANDLE_WM_DESTROYCLIPBOARD(hwnd, wParam, lParam, fn) <br>    ((fn)(hwnd), 0L)<br><br>把这三个宏定义替换回去，就变成：<br><br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, <br>  WPARAM wParam, LPARAM lParam)<br>{<br>  switch(msg)<br>  {<br>    case WM_CREATE:<br>           return Cls_OnCreate(hwnd, (LPCREATESTRUCT)(lParam) ? 0L : (LRESULT)-1L;<br>        // 如果处理了消息，则Cls_OnCreate应返回TRUE，导致WndProc返回0，否则Cls_OnCreate返回FALSE，导致WndProc返回-1；<br>    case WM_PAINT:<br>           return Cls_OnPaint(hwnd), 0L;<br>        // 逗号表达式；Cls_OnPaint是void类型，这里返回0；        <br>    case WM_DESTROY:<br>    return Cls_OnDestroy(hwnd), 0L;              // 同Cls_OnPaint<br>}       <br>return DefWindowProc(hwnd, msg, wParam, lParam);<br>}<br><br>之后我们就可以按照消息分流器的定义编写相应的消息处理函数了：<br><br>BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct){&#8230;};<br>void Cls_OnPaint(HWND hwnd){&#8230;};<br>void Cls_OnDestroyClipboard(HWND hwnd){&#8230;};<br><br>windowsx.h还提供了一个更加简化的方法：使用HANDLE_MSG宏，这个宏是这样定义的：<br><br>#define HANDLE_MSG(hwnd, message, fn)    <br style="color: #ff0000;">    case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))<br style="color: #ff0000;"><br style="color: #ff0000;">这个宏要做的就是根据不同的message（##用来连接前后的字符串），把自己&#8220;变成&#8221;相应的HANDLE_XXXXMESSAGE形式的宏，再通过相应的宏来执行消息处理代码；<br>比如实际代码中写入：<br><br>HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate)<br><br>则经过转换就变成：<br><br>case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate))<br><br>这样，我们就可以直接把程序写为：<br>LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, <br>  WPARAM wParam, LPARAM lParam)<br>{<br>  switch(msg)<br>{<br>HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate);<br>HANDLE_MSG(hwnd, WM_PAINT, Cls_OnPaint);<br>HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy);<br>}<br>return DefWindowProc(hwnd, msg, wParam, lParam);<br>}<br><br>之
                        后直接编写相应的消息处理过程就可以了。是不是简洁多了？而且把消息处理封装到函数里面，就可以使用VS直接跳转到这个函数，再也不用费劲去找那个
                        case了。要注意的一点是，虽然windowsx.h里包括了所有消息对应的分流器，但它们的参数是宏定义显式说明的，在编写消息处理函数时，必须遵循
                        宏定义中的参数类型，否则会导致错误；这么多消息分流器，我们每次新写一个消息处理函数时就得看看是否把参数设置正确了，整个过程繁琐冗长。好在已经有一
                        个工具叫Message Cracker Wizard，可以帮助我们生成消息分流器和相关的处理过程，具体见：<a href="http://www.codeproject.com/win32/msgcrackwizard.asp" target="_blank"></a><a href="http://www.codeproject.com/win32/msgcrackwizard.asp" target="_blank">http://www.codeproject.com/win32/msgcrackwizard.asp</a>。<br><br><br>2．在对话框中使用消息分流器<br>在对话框消息处理中，窗口子类化是我们经常使用的手段，这也可以通过消息分流器实现，但是有点小问题 :&gt;<br>下面是一个使用了windowsx.h消息分流器的对话框及其处理过程：<br>&#8230;&#8230;<br>int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) <br>{<br>DialogBoxParam(<br>              hinstExe, MAKEINTRESOURCE(IDD_PASSTHRU), NULL, (DLGPROC)Dlg_Proc, 0);<br><br>   return(0);<br>}<br>&#8230;&#8230;<br><br>LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, <br>  WPARAM wParam, LPARAM lParam)<br>{<br>  switch(msg)<br>{<br>HANDLE_MSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog);  // 不能直接使用HANDLE_MSG宏<br>HANDLE_MSG(hwnd, WM_COMMAND, Cls_OnCommand);       // 不能直接使用HANDLE_MSG宏<br>}<br><br>return false;<br>}<br><br>以上程序中直接使用HANDLE_MSG可能导致错误；为什么呢？问题出在子类化的消息处理过程的返回值上，msdn中对于对话框消息处理过程的返回值有如下说明：<br><br>一般情况下，对话框过程函数应该在处理了消息的情况下返回TRUE，如果没有处理，则返回FALSE。如果对话框过程返回了FALSE，那么对话框管理器为这条消息准备默认的对话操作。<br><br>如
                        果对话框处理了一个需要特定返回值的消息，则对话框的返回值应该被设置为调用SetWindowLong(The <strong style="color: #0000ff;">SetWindowLong</strong> function changes an attribute of the specified
                        window. The function also sets a 32-bit (long) value at the specified offset
                        into the extra window memory of a window. )后的返回值，并在返回TRUE之前立即返回这个
                        值。注意你必须立即调用SetWindowLong（这个函数用于调用窗口子类化的过程），这会导致DWL_MSGRESULT值被一个嵌套的对话框消息
                        改写。返回值为特定值的消息有：<br>&#8226;       WM_CHARTOITEM <br>&#8226;       WM_COMPAREITEM <br>&#8226;       WM_CTLCOLORBTN <br>&#8226;       WM_CTLCOLORDLG <br>&#8226;       WM_CTLCOLOREDIT <br>&#8226;       WM_CTLCOLORLISTBOX <br>&#8226;       WM_CTLCOLORSCROLLBAR <br>&#8226;       WM_CTLCOLORSTATIC <br>&#8226;       WM_INITDIALOG <br>&#8226;       WM_QUERYDRAGICON <br>&#8226;       WM_VKEYTOITEM<br>看到没有？ 我们的消息WM_INITDIALOG也在其中，对这个消息进行处理的过程不能简单的返回TRUE表示对消息进行了处理，而是另有其意；它将转化为：<br><br>case (WM_INITDIALOG): return HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, Cls_OnInitDialog);<br><br>宏HANDLE_WM_INITDIALOG定义如下：<br><br>#define HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, fn) <br>(LRESULT)(DWORD)(UINT)(BOOL)(fn)((hwnd), (HWND)(wParam), lParam)<br><br>对WM_INITDIALOG的处理，如果返回TRUE，则表示设置键盘焦点到对话框的默认控件，否则返回FALSE；这时好像还看不出什么问题，而对于我们的另外一个消息WM_COMMAND，HANDLE_MSG简单的把它变成：<br><br>case (WM_COMMAND): return  HANDLE_WM_COMMAND(hwnd, wParam, lParam, Cls_OnCommand);<br><br>宏HANDLE_WM_COMMAND定义如下：<br><br>#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) <br>    ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)<br><br>问
                        题出来了，我们的Cls_OnCommand由于是个void型的函数，是没有返回值的，因此windows默认这种消息处理过程必须返回一个0值，而返
                        回0值不就表示我们的消息过程不处理这个消息么？这个矛盾是HANDLE_MSG无法解决的。怎么办才能使消息过程在处理完WM_COMMAND消息之后
                        正确的返回一个TRUE呢？ 答案是使用另一个windowsx.h中的宏：SetDlgMsgResult(hwnd, msg, result) <br><br>这个宏定义如下：<br><br>#define     SetDlgMsgResult(hwnd, msg, result) (( <br>        (msg) == WM_CTLCOLORMSGBOX      || <br>        (msg) == WM_CTLCOLOREDIT        || <br>        (msg) == WM_CTLCOLORLISTBOX     || <br>        (msg) == WM_CTLCOLORBTN         || <br>        (msg) == WM_CTLCOLORDLG         || <br>        (msg) == WM_CTLCOLORSCROLLBAR   || <br>        (msg) == WM_CTLCOLORSTATIC      || <br>        (msg) == WM_COMPAREITEM         || <br>        (msg) == WM_VKEYTOITEM          || <br>        (msg) == WM_CHARTOITEM          || <br>        (msg) == WM_QUERYDRAGICON       || <br>        (msg) == WM_INITDIALOG             <br>    ) ? (BOOL)(result) : (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE))<br><br>（有没有注意到，里面多了一个WM_CTLCOLORMSGBOX ？ 这个消息是16位WinAPI中的消息，一度被转换为Win32 API的一个消息；现在在最新的32位API中已经被删除了；保留它可能考虑到兼容性的问题，这里不做进一步讨论）<br>现
                        在看到了，如果对话框过程处理的消息恰巧为返回特定值中的一个，则如实返回result；不要被前面的BOOL蒙蔽，BOOL在头文件中的定义实际上是一
                        个int型，一旦需要返回非TRUE或FALSE的其他值，照样可以；这样，我们的Cls_OnInitDialog就能够正确的返回它的BOOL值了，
                        而Cls_OnCommand在处理之后，也可以由后面的逗号表达式正确的返回一个TRUE表示消息已处理。<br><br>在《Windows核心编程》一书中，大牛Jeffrey自己定义了一个宏，使SetDlgMsgResult宏的使用更加方便：<br><br>#define chHANDLE_DLGMSG(hwnd, message, fn)                 <br>   case (message): return (SetDlgMsgResult(hwnd, uMsg,     <br>      HANDLE_##message((hwnd), (wParam), (lParam), (fn)))) <br><br>可见这个宏只是简单的对SetDlgMsgRseult宏进行了封装。<br><br>这样，我们最终的代码可以写成：<br><br>LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, <br>  WPARAM wParam, LPARAM lParam)<br>{<br>  switch(msg)<br>{<br>chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog);  // 使用大牛的chHANDLE_DLGMSG宏<br>chHANDLE_DLGMSG(hwnd, WM_COMMAND, Cls_OnCommand);       <br>}<br><br>return false;<br>}<br><br><br><br>下面把原来程序整个框架列出来：<br><br>LRESULT CALLBACK Dlg_Proc(HWND hwnd, UNIT umsg, WPARAM wparam, LPARAM lparam)<br>{<br>       switch(msg)       <br>       {<br>       case WM_COMMAND:               // 每个case都被一个message cracker代替，这里使用大牛同志的<br>              // do something;       // chHANDLE_DLGMSG宏；这个宏负责对消息筛选，处理并返回相应的值<br>              return true;<br><br>       case WM_INITDIALOG: <br>              // do something;<br>              return xxxx;<br>}<br><br>return false;       // 如果消息不在我们的DlgProc过程中被处理，则告诉调用这个DlgProc的消息，<br>}                                   //告诉系统的对话框管理器，这个消息我们不处理，交给你了<br><br>对比一下，消息分流器的作用不言自明。<br><br>以上只是介绍了消息分流器的部分应用，更多创造性的用法还等你自己在实践中发掘。<br><br>下面列出一些有用的参考资料：<br><br><a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;83456" target="_blank">http://support.microsoft.com/default.aspx?scid=kb;en-us;83456</a> 介绍了STRICT宏定义以及windowsx.h<br><a href="http://www.codeproject.com/win32/msgcrackwizard.asp" target="_blank">http://www.codeproject.com/win32/msgcrackwizard.asp</a>       提供message cracker wizard的下载，而且附有源代码<br>《windows核心编程》windows系统编程，就跟定大牛了 :&gt; 他在自己的sample中大量使用了message cracker</p>
                        </div>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br> <img src ="http://www.cppblog.com/WangYu/aggbug/42501.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-04 02:53 <a href="http://www.cppblog.com/WangYu/articles/42501.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>链接错误LNK2001</title><link>http://www.cppblog.com/WangYu/articles/42499.html</link><dc:creator>喜++</dc:creator><author>喜++</author><pubDate>Sun, 03 Feb 2008 16:51:00 GMT</pubDate><guid>http://www.cppblog.com/WangYu/articles/42499.html</guid><description><![CDATA[学习VC＋＋时经常会遇到链接错误LNK2001，该错误非常讨厌，因为对于 &nbsp; <br> &nbsp; 编程者来说，最好改的错误莫过于编译错误，而一般说来发生连接错误时， &nbsp; <br> &nbsp; 编译都已通过。产生连接错误的原因非常多，尤其LNK2001错误，常常使人不 &nbsp; <br> &nbsp; 明其所以然。如果不深入地学习和理解VC＋＋，要想改正连接错误LNK2001非 &nbsp; <br> &nbsp; 常困难。 &nbsp; <br> &nbsp; 　　初学者在学习VC＋＋的过程中，遇到的LNK2001错误的错误消息主要为： &nbsp; <br> &nbsp; 　　unresolved &nbsp; external &nbsp; symbol &nbsp; &#8220;symbol&#8221;（不确定的外部&#8220;符号&#8221;）。 &nbsp; <br> &nbsp; 　　如果连接程序不能在所有的库和目标文件内找到所引用的函数、变量或 &nbsp; <br> &nbsp; 标签，将产生此错误消息。一般来说，发生错误的原因有两个：一是所引用 &nbsp; <br> &nbsp; 的函数、变量不存在、拼写不正确或者使用错误；其次可能使用了不同版本 &nbsp; <br> &nbsp; 的连接库。 &nbsp; <br> &nbsp; 　　以下是可能产生LNK2001错误的原因： &nbsp; <br> &nbsp; 　　一．由于编码错误导致的LNK2001。 &nbsp; <br> &nbsp; 　　1．不相匹配的程序代码或模块定义(.DEF)文件能导致LNK2001。例如, &nbsp;  &nbsp; <br> &nbsp; 如果在C＋＋ &nbsp; 源文件内声明了一变量&#8220;var1&#8221;，却试图在另一文件内以变量 &nbsp; <br> &nbsp; &#8220;VAR1&#8221;访问该变量,将发生该错误。 &nbsp; <br> &nbsp; 　　2．如果使用的内联函数是在.CPP文件内定义的，而不是在头文件内定 &nbsp; <br> &nbsp; 义将导致LNK2001错误。 &nbsp; <br> &nbsp; 　　3．调用函数时如果所用的参数类型同函数声明时的类型不符将会产生 &nbsp; <br> &nbsp; LNK2001。 &nbsp; <br> &nbsp; 　　4．试图从基类的构造函数或析构函数中调用虚拟函数时将会导致LNK2001。 &nbsp; <br> &nbsp; 　　5．要注意函数和变量的可公用性，只有全局变量、函数是可公用的。 &nbsp; <br> &nbsp; 　　静态函数和静态变量具有相同的使用范围限制。当试图从文件外部访问 &nbsp; <br> &nbsp; 任何没有在该文件内声明的静态变量时将导致编译错误或LNK2001。 &nbsp; <br> &nbsp; 　　函数内声明的变量(局部变量) &nbsp; 只能在该函数的范围内使用。 &nbsp; <br> &nbsp; 　　C＋＋ &nbsp; 的全局常量只有静态连接性能。这不同于C，如果试图在C＋＋的 &nbsp; <br> &nbsp; 多个文件内使用全局变量也会产生LNK2001错误。一种解决的方法是需要时在 &nbsp; <br> &nbsp; 头文件中加入该常量的初始化代码，并在.CPP文件中包含该头文件；另一种 &nbsp; <br> &nbsp; 方法是使用时给该变量赋以常数。 &nbsp; <br> &nbsp; 　　二．由于编译和链接的设置而造成的LNK2001 &nbsp; <br> &nbsp; 　　1．如果编译时使用的是/NOD(/NODEFAULTLIB)选项，程序所需要的运行 &nbsp; <br> &nbsp; 库和MFC库在连接时由编译器写入目标文件模块， &nbsp; 但除非在文件中明确包含 &nbsp; <br> &nbsp; 这些库名，否则这些库不会被链接进工程文件。在这种情况下使用/NOD将导 &nbsp; <br> &nbsp; 致错误LNK2001。 &nbsp; <br> &nbsp; 　　2．如果没有为wWinMainCRTStartup设定程序入口，在使用Unicode和MFC &nbsp; <br> &nbsp; 时将得到&#8220;unresolved &nbsp; external &nbsp; on &nbsp; _WinMain@16&#8221;的LNK2001错误信息。 &nbsp; <br> &nbsp; 　　3．使用/MD选项编译时,既然所有的运行库都被保留在动态链接库之内， &nbsp; <br> &nbsp; 源文件中对&#8220;func&#8221;的引用，在目标文件里即对&#8220;__imp__func&#8221; &nbsp; 的引用。 &nbsp; <br> &nbsp; 如果试图使用静态库LIBC.LIB或LIBCMT.LIB进行连接，将在__imp__func上发 &nbsp; <br> &nbsp; 生LNK2001；如果不使用/MD选项编译，在使用MSVCxx.LIB连接时也会发生LNK2001。 &nbsp; <br> &nbsp; 　　4．使用/ML选项编译时，如用LIBCMT.LIB链接会在_errno上发生LNK2001。 &nbsp; <br> &nbsp; 　　5．当编译调试版的应用程序时，如果采用发行版模态库进行连接也会产 &nbsp; <br> &nbsp; 生LNK2001；同样，使用调试版模态库连接发行版应用程序时也会产生相同的 &nbsp; <br> &nbsp; 问题。 &nbsp; <br> &nbsp; 　　6．不同版本的库和编译器的混合使用也能产生问题，因为新版的库里可 &nbsp; <br> &nbsp; 能包含早先的版本没有的符号和说明。 &nbsp; <br> &nbsp; 　　7．在不同的模块使用内联和非内联的编译选项能够导致LNK2001。如果 &nbsp; <br> &nbsp; 创建C＋＋库时打开了函数内联（/Ob1或/Ob2)，但是在描述该函数的相应头 &nbsp; <br> &nbsp; 文件里却关闭了函数内联（没有inline关键字），这时将得到该错误信息。 &nbsp; <br> &nbsp; 为避免该问题的发生，应该在相应的头文件中用inline关键字标志内联函数。 &nbsp; <br> &nbsp; 　　8．不正确的/SUBSYSTEM或/ENTRY设置也能导致LNK2001。 &nbsp; <br> &nbsp; 　　其实，产生LNK2001的原因还有很多，以上的原因只是一部分而已，对初 &nbsp; <br> &nbsp; 学者来说这些就够理解一阵子了。但是，分析错误原因的目的是为了避免错 &nbsp; <br> &nbsp; 误的发生。LNK2001错误虽然比较困难，但是只要注意到了上述问题，还是能 &nbsp; <br> &nbsp; 够避免和予以解决的。　　 &nbsp; <br> &nbsp; ----------------------------------- &nbsp; <br> &nbsp; 转
<br> <img src ="http://www.cppblog.com/WangYu/aggbug/42499.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/WangYu/" target="_blank">喜++</a> 2008-02-04 00:51 <a href="http://www.cppblog.com/WangYu/articles/42499.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>