如何在DLL中使用一个类?

24

我能把一个类放进DLL文件里吗? 我写的这个类是这样的:

    class SDLConsole
{
      public:
             SDLConsole();
             ~SDLConsole(){};
             void getInfo(int,int);
             void initConsole(char*, char*, SDL_Surface*, int, int, int);
             void sendMsg(char*,int, SDL_Surface*);
             void cls(SDL_Surface*);

      private:
              TTF_Font *font;
              SDL_Surface *consoleImg;
              int width, pos, height, line, size, ctLine;
              SDL_Surface* render(char*,int);

};

我知道如何加载 DLL 并使用其中的函数,但是如何将一个类放到 DLL 中呢?非常感谢。


我相信你需要的一切都在这里 - icecrime
4个回答

29

如果您使用运行时动态链接(使用LoadLibrary来加载dll),则无法直接访问类,您需要为您的类声明一个接口并创建一个返回此类实例的函数,例如:

class ISDLConsole
{
  public:             
         virtual void getInfo(int,int) = 0;
         virtual void initConsole(char*, char*, SDL_Surface*, int, int, int) = 0;
         virtual void sendMsg(char*,int, SDL_Surface*) = 0;
         virtual void cls(SDL_Surface*) = 0;
 };

 class SDLConsole: public ISDLConsole
 {
    //rest of the code
 };

 __declspec(dllexport) ISDLConsole *Create()
 {
    return new SDLConsole();
 }
否则,如果您在加载时链接dll,只需使用icecrime提供的信息:http://msdn.microsoft.com/en-us/library/a90k134d.aspx

5
因为只有虚函数表格的布局需要在库和客户端之间匹配,这相对容易实现,即使在不同的语言之间也是如此。另一方面,使用 __declspec(dllexport),一切都必须匹配:编译器供应商、编译器版本、编译选项,否则你将遭遇名称修饰不匹配(连接错误)或一个定义规则违反及相关崩溃。 - Ben Voigt
3
最终您将面临这样一种情况:必须使用X版本的编译器来编译您的应用程序,因为它需要使用A库。然后,您想要使用B库,但是无法使用,因为它需要使用Y版本的编译器。 - Ben Voigt
1
作为一名 Windows C++ 开发者,在我多年的开发经验中,我从未接触过这个技巧 - 很棒的答案,我肯定会自己使用。 - Rob
@spt025,你的dll将被进程加载,并且需要一个入口点,以便进程可以“init”或在那里调用某些内容。当它这样做时,它可以提供指向类或函数指针的指针,以便你的dll可以调用实例化类。 - bcsanches
2
或许在这个答案中应该提到COM(组件对象模型),因为它的工作方式几乎相同:入口点函数被称为DllGetClassObject,你只会收到接口指针。 - stakx - no longer contributing
显示剩余5条评论

13

解决方案bcsanches提出,

 __declspec(dllexport) ISDLConsole *Create()
 {
    return new SDLConsole();
 }

如果您要使用bcsanches建议的这种方法,则请确保使用以下函数来delete您的对象,
 __declspec(dllexport) void Destroy(ISDLConsole *instance)
 {
       delete instance;
 }

请始终成对定义此类函数,因为这可以确保您从创建它们的同一堆、内存池等中删除对象。请参见pair-functions


我想将我的C++类创建为dll,并在我的C#应用程序中使用它。您能否也指导我如何做?还需要什么?当我以这种方式创建它时,如何在C#中调用我的dll? - Hossein
1
@Hossein:在C#中搜索PInvoke,你会找到很多相关主题。 - Nawaz
你的意思是我需要像普通的dll(比如user32.dll)一样使用传统的DIIIMport吗?好的,我会试一试。 - Hossein
好的,这里是问题!我应该如何指定我的工厂类的返回类型?public static extern object?! Create_xGramManilpulator(wchar_t* filename);在C#中,我应该使用什么来处理vector<string>和wchar_t*类型? - Hossein
@Nawaz:如果你想将一个C++类导出到C#,你需要做两件事情:首先,像COM类一样布局你的类(即实现IUnknown并遵循COM协议),以便你可以在.NET端使用COM互操作。其次,从你的DLL中导出一个工厂函数,返回你的类的一个实例,如bcsanches的答案所建议的那样,然后你可以在你的.NET代码中使用[DllImport] - stakx - no longer contributing

5

您可以在此页面此页面找到所有相关信息:

#ifdef _EXPORTING
   #define CLASS_DECLSPEC __declspec(dllexport)
#else
   #define CLASS_DECLSPEC __declspec(dllimport)
#endif

class CLASS_DECLSPEC SDLConsole
{
    /* ... */
};

现在只需要在构建 DLL 时定义预处理器符号 _EXPORTING 即可。


2
这不是“剩下的全部”。您还需要确保使用完全相同的编译器来构建DLL和所有客户端,编译器选项也要匹配。以这种方式做事情会付出巨大的可维护性代价,如bcsanches所建议的pure virtual interface更好。 - Ben Voigt
@Ben:你可能是对的,但我必须承认我一直都是这样做事情的,而且我无法想象在一个大项目中使用“纯虚拟接口”方法。 - icecrime
如果您有模块因为它们都是本地单个项目而紧密耦合,为什么不使用静态库呢? - Ben Voigt

2
如果你想要公开一个类中的数据,以上解决方案并不足够。你需要在DLL编译中在类本身上加上__declspec(dllexport),在连接到DLL的模块上加上__declspec(dllimport)
一种常见的技术是这样做(Microsoft向导生成的代码就是这样)。
#ifdef EXPORT_API
#define MY_API __declspec(dllexport)
#else
#define MY_API __declspec(dllimport)
#endif

class MY_API MyClass {
   ...
};

请确保在DLL项目中定义了EXPORT_API,并确保在链接到DLL的模块中未定义。
如果您从头开始在Visual C++中创建一个新的DLL项目,并勾选“导出符号”复选框,则会使用此技术生成一些示例代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接