我正在使用VS2010,我正在设计一个MVC应用程序。
说我在解决方案中有“Project 1”和“Project 2”。需要按顺序编译,P1编译为DLL,P2编译为动态使用DLL的Exe文件。 P2声明了一个视图接口。两个项目都有一个实现接口的视图类(一个具有纯虚方法的类)。
现在的问题是,我不能在P1中包含接口的头文件,因为链接器会说他无法解析这个外部符号。这当然是正确的,因为它稍后在P2中编译。
所以我做的是,我将P2的include文件夹添加到P1,并在P1中包含了interface.cpp而不是头文件。
它有效,但我不认为这是我应该做的。或者不是吗?界面显然是编译两次,每个项目一次。
我不想将界面移动到P1,这将解决问题。假设,我不希望这样。
感谢您提供意见。
编辑:代码段:
Project1:
View1.hpp // nothing special
View1.cpp:
#include ViewInterface.cpp
View1::View1(int x) : ViewInterface(int x)
Project2:
ViewInterface.hpp:
#ifdef EXP
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif
class ViewInterface : SomeOtherClass, AnotherClass
{
virtual void DECLDIR show(void) const = 0;
virtual void DECLDIR hide(void) const = 0;
}
ViewInterface.cpp:
ViewInterface::ViewInterface(int x) : SomeOtherClass(int x), AnotherClass(int x)
View2.hpp // nothing special
View2.cpp:
#define EXP
#include ViewInterface.h
View2::View2(int x) : ViewInterface(int x)
答案 0 :(得分:1)
为了使可执行文件能够使用DLL中的类,您需要确保它们已正确导出。编译DLL时,使用__declspec(dllexport)
标记该类,并在编译可执行文件时,使用__declspec(dllimport)
标记该类。通常,这是通过如下宏来完成的:
// In your DLL's header file
#ifdef COMPILING_MY_DLL
#define MY_EXPORT __declspec(dllexport)
#else
#define MY_EXPORT __declspec(dllimport)
#endif
class MY_EXPORT MyClass
{
...
};
然后,在仅您的DLL项目中,您定义宏COMPILING_MY_DLL
。
答案 1 :(得分:0)
我不想将界面移动到P1,会解决什么问题 问题。假设,我不希望这样。
然后将其移至第三个实体。
你必须决定什么对你更重要。如果你想要一个干净的解决方案,那么要么将接口定义移动到DLL(P1),要么移动到DLL和EXE都可以使用的东西 - 我们称之为“P0”。 P0甚至不必是被编译的东西 - 一个简单的头文件在它自己的目录中,所有内联定义的内容都可以。我首选的选择是让P0成为一个DLL。
这是唯一清洁解决方案。以下“解决方案”实际上只是我为了完整而描述的黑客。
如果你真的想要“脏”的解决方案,那么就这样做吧,唯一不同的是你将定义接口的头文件放在application-source-folder中。
如果你想要它非常脏,那么在构建你建议的DLL时包含.cpp文件。它也会起作用......它真的很糟糕。
当然你必须要注意一些事情。例如。 “接口”不应该有任何静态数据成员,并且“接口”中的任何功能都不应具有本地静态功能。因为如果它们这样做,那么这些静态变量将被实例化两次 - 一次在DLL中,一次在EXE中。此外,由于所有代码都将编译到两个项目中,因此如果要更改任何内容,则必须重新编译这两个项目。 (代码在DLL和EXE中重复,只要它们是从相同的代码编译就不是问题。)
如果您选择“P0 =只是头文件”解决方案,那么这些限制当然也适用。
最后真的是非常糟糕的超级解决方案:在EXE中实现接口,从EXE实现dllexport并在DLL中进行dllimport(是的,可以做到!)。缺点是您必须进行特殊的EXE构建,只构建EXE的导入库(可以在没有DLL的导入库的情况下完成)。使用EXE的导入库,您可以构建DLL,然后使用DLL的导入库,您可以构建EXE本身。 这样“接口”甚至可以拥有静态数据成员,如果只更改代码(即头文件没有变化),那么你只需要重新编译EXE。