65.9K
CodeProject 正在变化。 阅读更多。
Home

如何导出 DLL 中的复杂类

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.44/5 (9投票s)

2008 年 1 月 31 日

CPOL

2分钟阅读

viewsIcon

43147

downloadIcon

1077

导出派生类、设计模式等。

引言

由于我花了过去几天的时间来寻找如何从 DLL 导出类的方法,并且找到很多关于 DLL 的资料,但几乎没有帮助我的内容,所以我决定将有用的信息总结在这篇简短的教程中。

背景

对于我们大多数参与面向对象编程的人来说,期望能够从 DLL 导出整个对象是相当自然的。好消息是这是可行的。坏消息是:这并不明显。而且,更糟糕的是:尝试导出继承类或返回类指针的函数。

解决方案

这不是解决此问题的唯一方法,我也不知道它是否是最好的解决方案。这更像是一种提供灵活性和鲁棒性的方法。

从 DLL 导出类的问题是由于 C++ 风格导致函数名称被破坏(mangled names)。对于函数,这很容易 - 只需要将它们定义为 extern "C",就完成了。由于 extern "C" 对类没有影响,你必须想出其他方法。一种方法是从一个带有 纯虚成员函数DLL 包装类 派生你的类,每个你想导出类的函数都对应一个纯虚函数。你的类真正需要继承的是包装类函数的 虚表。包装类可能看起来像一个接口类,在某些情况下你可以将其用作一个接口类,但你必须将其视为一个仅用于一个真实类的包装器。这在简单的继承方案中很明显,你有一个真实的基类和一个真实的派生类。

classical_inheritence_scheme.JPG

当你想在 DLL 中拥有相同的结构并导出 Derived1Derived2 时,你必须重新设计你的方案为

dll_inheritence_scheme.JPG

在 C++ 中,这看起来像

class DllWrapperBase  
{
public:
    DllWrapperBase();
    virtual ~DllWrapperBase();

    virtual long __stdcall        test1()=0;
    virtual float __stdcall        test2()=0;
};          

 class Base : public DllWrapperBase  
{
public:
    Base();
    virtual ~Base();

    virtual long __stdcall        test1(){return 1;}
    virtual float __stdcall        test2(){return 1.8f;};
}; 
class DllWrapperDerived : public Base  
{
public:
    DllWrapperDerived();
    virtual ~DllWrapperDerived();

    virtual long __stdcall    test3()=0;
    virtual float __stdcall    test4()=0;
};
class Derived : public DllWrapperDerived  
{
public:
    Derived();
    virtual ~Derived();
    virtual long __stdcall    test3(){return 3;}
    virtual float __stdcall    test4(){return 3.9f;}
};

为了使它更复杂,我们引入一个 工厂类,它生成 派生类 的对象。这是一种常见的设计模式,看起来如下

enum eProducts
{
    PRODUCT1
};

class Base;
class Derived;

class DllWrapperFactory  
{
public:
    DllWrapperFactory();
    virtual ~DllWrapperFactory();
    virtual Base * __stdcall    CreateProduct(eProducts iValue)=0;
protected:
    virtual Derived * __stdcall CreateDerived()=0;
};

class Factory : public DllWrapperFactory  
{
public:
    Factory();
    virtual ~Factory();
    Base * __stdcall    CreateProduct(eProducts iValue);        
protected:
    Derived * __stdcall CreateDerived();
};

然后,我们添加主要的 DLL 文件

// dll_export.h
#include "Factory.h"

extern "C" __declspec(dllexport) DllWrapperFactory * returnFactory()
{
    DllWrapperFactory * pObj = 
      static_cast<dllwrapperfactory*>(&FactorySingleton::Instance());
    return pObj;
}
// dll_export.cpp
#include "stdafx.h"
#include "dll_export.h"


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

你像这样使用新创建的 DLL

// dll_tester.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>
#include "Factory.h"
#include "Derived.h"

typedef Factory * (__stdcall *DLLGETFACTORY)(void);

int main(int argc, char* argv[])
{
    printf("Hello World!\n");

    HINSTANCE hInstDll = LoadLibrary("dll_test.dll");
    if(!hInstDll) printf("Failed to load dll\n");

    DLLGETFACTORY pDllGetFactory = 
      (DLLGETFACTORY) GetProcAddress(hInstDll, "returnFactory");
    // Create the object using the factory function
    Factory * pMyFactory = (pDllGetFactory)();
    if (pMyFactory == NULL)
        return 0;
    Derived * d = (Derived *)pMyFactory->CreateProduct(PRODUCT1);
    
    int i = d->test1();
    float f = d->test2();
    int j = d->test3();
    float e = d->test4();

    system("PAUSE");

    return 0;
}

你必须记住,为了通过 DLL 使用任何类,你必须在应用程序中包含它的头文件。

© . All rights reserved.