不知道这个问题是不是有人碰到过。最近做一组试验,使用的数据,操作都有很多相似的地方,因此做了简单的封装,以前做的时候一直没有把WinMain这个函数(或者说符号)也编译到库里面,而这次尝试着这么做了一下,具体的说,建立一个lib项目,除所需要的库和类之外还将_tWinMain这个函数也编译进去,这样将这个库链接到某个项目之中(比如就一个main.cpp的项目),WinMain就不必再写了。但这样不行。
在VS2005下,链接结果是“无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用”,这说明要么lib.lib中的WinMain没有被链接上,要么这个WinMain的符号。
而如果你在main.cpp里再定义一个WinMain,链接结果是符号冲突。但WinMain显然是可以被编译入lib的,MFC的代码就是如此。所以我想问问各位高手,有谁知道这里应该怎么做才好,是否应该调整编译和链接的参数?
在VS2005下,链接结果是“无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用”,这说明要么lib.lib中的WinMain没有被链接上,要么这个WinMain的符号。
而如果你在main.cpp里再定义一个WinMain,链接结果是符号冲突。但WinMain显然是可以被编译入lib的,MFC的代码就是如此。所以我想问问各位高手,有谁知道这里应该怎么做才好,是否应该调整编译和链接的参数?
------------
MFC不是这样,有WinMain源代码,跟踪调试即可找到。
extern "C" int __stdcall
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
.....
}
extern "C" int __stdcall
wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
.....
}
// baseclass.h文件
class BaseClass
{
virtual void Create() = 0;
virtual int Run() = 0;
BaseClass();
};//baseclass.cpp文件#include "baseclass.h"
BaseClass* g_GlobalInstance;BaseClass::BaseClass();
{
assert( (g_GlobalInstance==0) );
g_GlobalInstance = this;
}extern "C" int WINAPI _tWinMain( HINSTANCE, HINSTANCE, LPTSTR, INT )
{
g_GlobalInstance->Create();
return g_GlobalInstance->Run()
}上面这段代码为一个工程,编译成lib文件。派生类的工程依赖于上面的工程
// derive class main.cpp#include "BaseClass.h"
class MyClass : public BaseClass
{
// 重载虚函数.....
// .....
};MyClass TheApp;两个工程都使用_UNICODE,编译通过,链接出现错误:“error LNK2019: 无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用”。如果把WinMain函数copy一份到派生类的cpp文件中,得到的结果是:“error LNK2005: _wWinMain@16 已经在 main.obj 中定义”,这两个符号是不一样的。这里的问题我推测是由于unicode版的依然以WinMain为入口而不是以wWinMain,这就比较奇怪,为何使用_UNICODE之后___tmainCRTStartup还是引用_WinMain@16呢。而如果将WinMain移入派生类工程而不是lib工程,则没问题,所以我的问题是:为什么lib文件中的_wWinMain@16不能被当作入口呢,在编译参数方面是否需要什么调整。如果用多字节的编码,改用_MBCS编译,编译通过,这个我昨天确实没测试,因为2005默认是unicode,一直已经非常习惯使用unicode。因为实际的代码对输入参数进行了一定的处理以支持拖放文件至图标的操作,因此我还是希望这个代码能在_UNICODE下完成。其实这东西换点“丑陋”的方法也能实现,并不影响使用,刨根问底只不过是好奇心使然罢了。to 1楼:MFC的函数确实是编译入lib的,这个文件你在生成exe文件的时候并不会被编译,能跟踪到代码是因为.pdb文件提供的调试信息的缘故,这跟上面我那个例子里面函数被编译入lib,然后用_DEBUG链接,能够跟踪代码运行是一个道理的。
因为在MFC中实际上tWinMain为WinMain或wWinMain。以下是MFC中的宏定义
#define _tWinMain WinMain
#define _tWinMain wWinMain
C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc\appmodul.cpp摘录:
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc\winmain.cpp
C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\crt\src\crt0.c想办法满足编译宏。
lib.cpp
--------------------
#include<stdio.h>extern int func(int x, int y);
int main()
{
printf("main for lib\n");
printf("结果 = %d\n",func(12,2323) );
return 0;
}
**************************
主程序工程,在链接的时候需要将 lib工程编译的lib文件 加入
main.cpp
-------------------
int func(int x, int y)
{
return x+y;
}
编译即可运行。
因为在MFC中实际上tWinMain为WinMain或wWinMain。以下是MFC中的宏定义
#define _tWinMain WinMain
#define _tWinMain wWinMain
-------------------------------------------我知道_tWinMain本身就是一个宏,写成_tWinMain这种形式是为了配合不同的编译参数方便(因为vc的stl对unicode字符文本文件的支持不好,有可能会使用不同的编码类型,这样做是为了方便随时调整),在_UNICODE条件下_tWinMain会被裁决为wWinMain,反之则是WinMain。但上面那个例子,用WinMain可以通过,反之如果用wWinMain就不能链接通过(无论在_UNICODE还是_MBCS条件下都是这样,即便显式地写wWinMain也一样)。参数方面我不知道要修改什么参数才合适。无聊中我特地做试验:最初的入口都是从___tmainCRTStartup开始的,___tmainCRTStartup是这么处理的:#ifdef WPRFLAG
mainret = wWinMain(
#else /* WPRFLAG */
mainret = WinMain(
#endif /* WPRFLAG */
(HINSTANCE)&__ImageBase,
NULL,
lpszCommandLine,
StartupInfo.dwFlags & STARTF_USESHOWWINDOW
? StartupInfo.wShowWindow
: SW_SHOWDEFAULT )表面上看这是很确定的编译分支,不是WinMain就是wWinMain,但事实上这里是相当有趣的,因为你的代码里,无论是_UNICODE还是_MBCS,你都可以任意地使用这两个入口,编译一样通过,但如果你要把这个函数编译到lib文件里,则只有WinMain能够奏效。甚至这两个函数都写都可以,在同一个文件里靠前的那个会被当作入口,不同文件中是什么结果我没有测试。所以这个WPRFLAG标志是怎么处理的就很有意思了。至于为何要使用wWinMain,那是为了接受命令行参数的时候可以接收到wchar_t的字符串,通常用GetCommandLine函数获得的命令行处理起来比直接从第三个参数上获得的字符串要稍微麻烦一些,仅此而已。main函数和wmain函数也是一样的,使用wmain函数才能接受wchar_t*类型的字符串参数。同样也有_tmain这么一个宏,会自动根据_UNICODE和_MBCS来裁决main或者wmain。这么点类型的问题说小也小,但有时候处理起来也很麻烦。回到上面那个问题,我在lib工程里定义WinMain,则无论在_UNICODE还是_MBCS条件下都能链接成功,反之如果是wWinMain,无论在哪种条件下都会出现错误。main和wmain我没测试过,再弄个新的工程太麻烦了。
---------------------------------------------------
楼上我想你没明白我的意思,上文的那个函数_tWinMain不是定义在lib项目里,而是生成exe文件的那个项目里的话,链接肯定通过。但MFC显然不是这样的,你编译MFC代码的时候,编译器不会编译appmodul.cpp这个文件,用生成的obj来链接你的exe文件,而是直接链接MFC的库文件。这和我链接包含了wWinMain符号的库却不能通过的行为是一致的。而相同的行为导致不同的结果,这才是我的疑问。如果MFC代码每次都要编译这个文件的话,那也就没什么疑问了。能看到代码不代表是用它编译的,这是调试库和调试器的功劳,你删掉源文件照样能编译通过,只不过不能调试到那里而已。