关于编写DLL时,DEF文件(模板文件)的输出(EXPORTS)函数出现链接错误:
其中.def文件如下:
LIBRARY "usb_fm.dll"
EXPORTS
Initialize @1 private
链接错误信息如下:
Linking...
fm.def : warning LNK4022: cannot find unique match for symbol 'Initialize'
fm.def : warning LNK4002: "void __cdecl Initialize(struct HWND__ *)" (?Initialize@@YAXPAUHWND__@@@Z) defined in ./debug/main_operation.obj
fm.def : warning LNK4002: "public: virtual long __stdcall CAggDirectDraw::Initialize(struct _GUID *)" (?Initialize@CAggDirectDraw@@UAGJPAU_GUID@@@Z) defined in D:/InstallSoftware/Microsoft SDKs/Windows/v6.0/Samples/Multimedia/DirectShow/BaseClasses/Debug/Strmbasd.lib
fm.def : warning LNK4002: "public: virtual long __stdcall CAggDrawSurface::Initialize(struct IDirectDraw *,struct _DDSURFACEDESC *)" (?Initialize@CAggDrawSurface@@UAGJPAUIDirectDraw@@PAU_DDSURFACEDESC@@@Z) defined in D:/InstallSoftware/Microsoft SDKs/Windows/v6.0/Samples/Multimedia/DirectShow/BaseClasses/Debug/Strmbasd.lib
fm.def : warning LNK4002: "private: long __thiscall CDynamicOutputPin::Initialize(void)" (?Initialize@CDynamicOutputPin@@AAEJXZ) defined in D:/InstallSoftware/Microsoft SDKs/Windows/v6.0/Samples/Multimedia/DirectShow/BaseClasses/Debug/Strmbasd.lib
fm.def : warning LNK4002: "protected: void __thiscall CDHtmlDialog::Initialize(void)" (?Initialize@CDHtmlDialog@@IAEXXZ) defined in D:/InstallSoftware/VS2005/VC/atlmfc/lib/uafxcw.lib
fm.def : warning LNK4002: "public: long __thiscall CDataSourceControl::Initialize(void)" (?Initialize@CDataSourceControl@@QAEJXZ) defined in D:/InstallSoftware/VS2005/VC/atlmfc/lib/uafxcw.lib
fm.def : error LNK2001: unresolved external symbol Initialize
E:/PersonalProject/usb_fm/Debug/usb_fm.lib : fatal error LNK1120: 1 unresolved externals
从上面的错误可以看出提示Initialize函数没有定义,可我明明定义了啊?
找了半天,最后发现输出的警告信息中也是关于Initialize的,仔细一看,推测错误产生的原因应该是:因为在DLL的DEF文件中定义EXPORTS函数时,只需要指明函数名称,而不需要指出函数的返回值及参数(因此这种类型的错误只有编写DLL时才可能出现),在我的程序中输出了一个Initialize函数,但很多库文件(.LIB)也包含和Initialize同名的函数,所以出现链接错误。
于是将Initialize改为InitialFM,问题果然解决~~ 修改后的.def文件如下:
LIBRARY "usb_fm.dll"
EXPORTS
InitialFM @1 private
现在DLL工程可以成功通过编译,并生成了dll文件和lib文件。
接下来我在一个应用程序中显示调用dll中的函数,也可以成功;但当我用隐式的方法调用dll中的函数时,应用程序编译出现链接错误!
找了很久都没有发现问题,最后网上问到的!主要是private关键字引起的,MSDN中关于private的解释如下: The optional keyword PRIVATE prevents entryname from being placed in the import library generated by LINK. It has no effect on the export in the image also generated by LINK.
可选的PRIVATE关键字禁止将entryname(这里指函数名InitialFM)放到由LINK生成的导入库中。它对同样是由LINK生成的映像的导出无效。也就是说private关键字可以阻止应用程序隐式调用dll中的函数,因为private函数不会放到LINK生成的导入库,但不会阻止应用程序显式调用dll中的函数,因为private函数还是会放到LINK生成的导出库。
其实早就怀疑private了,但查以前写的COM组件,发现.def文件中的函数也是private的,也就没有多想了。
COM组件的输出(EXPORT)函数一般是private的原因:
因为DllRegisterServer这些dll中的函数本质上是由COM在运行时调用的,所以要禁止静态的编译后link,只容许被动态加载(也就是显式调用),所以只能定义成PRIVATE的,否则出错。
关于更多.def文件和dll的编写资料可在MSDN中通过索引查询“.def”和“DLLs”获取。