如何在 C++ 中设置多项目解决方案?

How do I set up a multi-project solution in C++?

我是 C++ 的新手,我很难让我的 dll 引用正常工作。几天来我一直在努力让它工作,但我发现的一些解释经常提到做 x 或 y,但不要告诉我 如何做 x 或 y。由于我不是 C++ 老手,我需要有人指导我完成它。我想要做的是:

MySolution
    MyExe (Win32 .exe)
        Main.h
        Main.cpp
            (constructs ImplementationB calls the methods as defined by InterfaceA, then deletes the instances)
            (calls/fills HelperC.Foobar)
    MyInterfaces (dll)
        InterfaceA.h
            ~InterfaceA();
            virtual void DoSomething();
    MyUtils (dll)
        HelperC.h
            static float Foobar;
        HelperD.cpp
            float HelperC::Foobar = 1.0f;
    MyImplementations (dll)
        ImplementationB : InterfaceA
            (uses the value from HelperC.Foobar)

MyExeMyImplementations 项目包含大部分执行代码。但是,我需要一个界面,所以我需要一个界面项目(MyInterfaces)。我需要一些助手 classes 需要可以从 MyExeMyImplementations 访问,因此 MyUtils。我希望这个助手 class 是静态可用的,尽管它不是强制性的。

在开始添加 MyUtilsHelperC class 之前,我有一个编译版本。 我不得不用__declspec(dllexport),以及 DoSomething 方法。我还必须标记 ImplementationB 的构造函数以便从 MyExe 实例化它,这是有道理的。但是,当我尝试用 __declspec(dllexport) 标记整个 class(实现和接口)时,该示例无法编译(not 使感)。

根据我的阅读,在 dll 中包含静态字段并从外部代码中使用它们并不能很好地工作。因此,作为替代方案,我将 Foobar 设为非静态,并将 HelperC 实例传递给 InterfaceA 所描述的方法。因为我已经得到了简单的 classes 来工作,所以我认为它也应该可以工作。但是,现在编译器在 ImplementationB (LNK2019).

的构造函数上抛出错误

简而言之:我在与我的更改无关的部分到处都遇到 link 错误,并且几乎没有文档描述我需要执行的具体步骤才能获得简单的 dll 参考工作。
有人可以指出我需要添加什么以及需要添加到哪里才能编译吗?此外,一些关于 C++ dll 引用的注意事项会有很大帮助(例如,不要跨项目使用静态)。

经过多方挖掘,我发现罪魁祸首是一个神奇的项目设置。它被称为Ignore Import Library,位于Project Properties->Linker->General,默认设置为Yes,但在大多数情况下应设置为No。该设置告诉可执行项目在编译期间使用 dll 的 lib 文件。这对我来说仍然听起来很奇怪(听起来像是重复的构建输出),但据我了解,lib 文件描述了 how 到 link 到 dll。如果您的 dll 在构建期间生成一个库,您可能希望将设置设置为 No.

我还了解到,为了能够将 HelperC class 用作静态可访问的助手,我需要将 dllimportmacro trick 结合使用,如@drescherjm 所述。 dllimport 声明只需要能够跨库使用数据成员(静态 class 字段或全局定义的变量)。它也可以应用于函数,但不是必需的,在这种情况下,它会在库 linking.

期间提供轻微的性能提升

为了完整起见,我的项目结构在运行后:

MySolution
    MyExe (Win32 .exe, Debugger Type=Mixed)
        Main.h
        Main.cpp
            (constructs ImplementationB calls the methods as defined by InterfaceA, then deletes the instances)
            (calls/fills HelperC::Foobar)
    MyInterfaces (dll, Ignore Import Library=Yes, because there is no .lib after building)
        InterfaceA.h
            class __declspec(dllexport) InterfaceA
                ~InterfaceA() {};
                virtual void DoSomething() = 0;
    MyUtils (dll, Ignore Import Library=No)
        HelperC.h
            class __declspec(dllimport/dllexport) HelperC // (see macro trick)
                static float Foobar;
        HelperD.cpp
            float HelperC::Foobar = 1.0f;
    MyImplementations (dll, Ignore Import Library=No)
        ImplementationB.h
            class __declspec(dllexport) ImplementationB : public InterfaceA
                ImplementationB();
                ~ImplementationB();
                void DoSomething();
        ImplementationB.cpp
            ImplementationB::ImplementationB() {};
            ImplementationB::~ImplementationB() {};
            ImplementationB::DoSomething() { /* Omitted */ };
                (uses HelperC::Foobar in implementation)

附带说明:如果您在 Visual Studio 中添加了默认的 C++ class 库项目,您可能需要先将 Project Properties->Debugging->Debugger Type 设置翻转为 Mixed就能在dll代码中set/use断点。参见 this

我希望这能帮助那些正在使用 C++(和 Visual Studio)处理 dll 的其他人。