关闭

如何在Dll中导出class

1252人阅读 评论(0) 收藏 举报
说明:新建两个工程,其中一个是dll工程(我的示例程序中这个工程名为DllClass),另一个是测试和使用dll的工程(名字为DllClassTest)
注意点:
1。Dll工程编译完成后,将.lib和.dll文件拷贝到测试和使用dll的工程的适当目录下(我的程序是release目录),然后将Dll工程中的导出类的头文件(以下简称头文件)拷贝到测试和使用dll的工程的适当目录下(在我的程序中是拷贝到此工程文件所在目录),然后把这个拷贝过来的头文件通过下面2和3进行修改。
2。在Dll工程中的头文件使用的是#defineDLLCLASS_API__declspec(dllexport)表示导出
在测试和使用dll的工程中的头文件使用的是#defineDLLCLASS_API__declspec(dllimport)表示导入
3。不论Dll工程的头文件如何实现(比如说包括内联函数等等),
在测试和使用dll的工程中的头文件中全部都是声明而没有定义(即将类成员函数的实现部分去掉)

4。在测试和使用dll的工程的那个文件中包含对dll库的引入,分两步:
首先在是测试和使用dll的工程的setting菜单(按ALT+F7可以调出)中的LINK选项卡中的Object/Librarymodules:下填写对dll的连接,比如dll文件名字为DllClass.lib和DllClass.dll,则填写Release/DllClass.lib(注意目录,我的程序中.lib文件是在目录release下)。
然后,添加对头文件的包含,比如dll文件名字为DllClass.lib和DllClass.dll,头文件为DllClass.h,则包含头文件为#include"DllClass.h"
由此,就可以使用了。
相关源码如下:
dll工程中的DllClass.h内容:
//此处在dll的头文件中为dllexport,在应用文件中为dllimport
#defineDLLCLASS_API__declspec(dllexport)

//导出一个类(包括其方法、属性)
classDLLCLASS_APICDllClass{
public:
CDllClass(void);
voidMSG(constchar*conststr);
};

dll工程中的DllClass.cpp内容:
#include"stdafx.h"
#include"DllClass.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;
}

CDllClass::CDllClass()
{
return;
}

voidCDllClass::MSG(constchar*conststr)
{
MessageBox(NULL,str,"",MB_OK);
}

Dll测试工程中的DllClass.h内容:
//此处在dll的头文件中为dllexport,在应用文件中为dllimport
#defineDLLCLASS_API__declspec(dllimport)

//导出一个类(包括其方法、属性)
classDLLCLASS_APICDllClass{
public:
CDllClass(void);
voidMSG(constchar*conststr);
};
Dll测试工程中DllClassTest.cpp内容:
首部:
#include"DllClass.h"
classDllClass;

代码部分:
CDllClassCTest;
CTest.MSG("thisisastringintodll");































能不能在一个dll中定义一个类,然后在程序中动态链接这个dll,然后导出这个类呢?
--------------------------------------------------------------------------------
要用 MFC 的扩展DLL写倒出类定义
//定义一个导出类
class AFX_EXT_CLASS CMouseHook : public CObject  
{
public:
CMouseHook();
virtual ~CMouseHook();
BOOL StopMouseHook();
BOOL StartMouseHook( HWND hOwner );
};
OK了,其它和DLL编写是一样的
--------------------------------------------------------------------------------
用API也可以:
class extern "C" __declspec(dllimport) 类名
{
    定义;
};
--------------------------------------------------------------------------------
一、DLL的不同类型
    使用MFC可以生成两种类型的DLL:MFC扩展DLL和常规DLL。常规DLL有可以分为动态连接和静态连接。Visual C++还可以生成WIN32 DLL,但不是这里讨论的主要对象。
1、MFC扩展DLL
   每个DLL都有某种类型的接口:变量、指针、函数、客户程序访问的类。它们的作用是让客户程序使用DLL,MFC扩展DLL可以有C++的接口。也就是它可以导出C++类给客户端。导出的函数可以使用C++/MFC数据类型做参数或返回值,导出一个类时客户端能创建类对象或者派生这个类。同时,在DLL中也可以使用DLL和MFC。
   Visual C++使用的MFC类库也是保存在一个DLL中,MFC扩展DLL动态连接到MFC代码库的DLL,客户程序也必须要动态连接到MFC代码库的DLL。(这里谈到的两个DLL,一个是我们自己编写的DLL,一个装MFC类库的DLL)现在MFC代码库的DLL也存在多个版本,客户程序和扩展DLL都必须使用相同版本的MFC代码DLL。所以为了让MFC扩展DLL能很好的工作,扩展DLL和客户程序都必须动态连接到MFC代码库DLL。而这个DLL必须在客户程序运行的计算机上。
2、常规DLL
   使用MFC扩展DLL的一个问题就是DLL仅能和MFC客户程序一起工作,如果需要一个使用更广泛的DLL,最好采用常规DLL,因为它不受MFC的某些限制。常规DLL也有缺点:它不能和客户程序发送指针或MFC派生类和对象的引用。一句话就是常规DLL和客户程序的接口不能使用MFC,但在DLL和客户程序的内部还是可以使用MFC。
   当在常规DLL的内部使用MFC代码库的DLL时,可以是动态连接/静态连接。如果是动态连接,也就是常规DLL需要的MFC代码没有构建到DLL中,这种情况有点和扩展DLL类似,在DLL运行的计算机上必须要MFC代码库的DLL。如果是静态连接,常规DLL里面已经包含了需要的MFC代码,这样DLL的体积将比较大,但它可以在没有MFC代码库DLL的计算机上正常运行。 
二、建立DLL
    利用Visual C++提供的向导功能可以很容易建立一个不完成任何实质任务的DLL,这里就不多讲了,主要的任务是如何给DLL添加功能,以及在客户程序中利用这个DLL
1、导出类
   用向导建立好框架后,就可以添加需要导出类的.cpp .h文件到DLL中来,或者用向导创建C++ Herder File/C++ Source File。为了能导出这个类,在类声明的时候要加“_declspec(dllexport)”,如:
class _declspec(dllexport) CMyClass
{
     ...//声明
}
如果创建的MFC扩展DLL,可以使用宏:AFX_EXT_CLASS:
class AFX_EXT_CLASS CMyClass
{
     ...//声明
}
这样导出类的方法是最简单的,也可以采用.def文件导出,这里暂不详谈。
2、导出变量、常量、对象
   很多时候不需要导出一个类,可以让DLL导出一个变量、常量、对象,导出它们只需要进行简单的声明:_declspec(dllexport) int MyInt;
  _declspec(dllexport) extern const COLORREF MyColor=RGB(0,0,0);
  _declspec(dllexport) CRect rect(10,10,20,20);
要导出一个常量时必须使用关键字extern,否则会发生连接错误。
注意:如果客户程序识别这个类而且有自己的头文件,则只能导出一个类对象。如果在DLL中创建一个类,客户程序不使用头文件就无法识别这个类。
  当导出一个对象或者变量时,载入DLL的每个客户程序都有一个自己的拷贝。也就是如果两个程序使用的是同一个DLL,一个应用程序所做的修改不会影响另一个应用程序。
  我们在导出的时候只能导出DLL中的全局变量或对象,而不能导出局部的变量和对象,因为它们过了作用域也就不存在了,那样DLL就不能正常工作。如:
MyFunction()
{
      _declspec(dllexport) int MyInt;
      _declspec(dllexport) CMyClass object;
}
3、导出函数
导出函数和导出变量/对象类似,只要把_declspec(dllexport)加到函数原型开始的位置:
_declspec(dllexport) int MyFunction(int);
如果是常规DLL,它将和C写的程序使用,声明方式如下:
extern "c" _declspec(dllexport) int MyFunction(int);
实现:
extern "c" _declspec(dllexport) int MyFunction(int x)
{
     ...//操作
}
如果创建的是动态连接到MFC代码库DLL的常规DLL,则必须插入AFX_MANAGE_STATE作为导出函数的首行,因此定义如下:
extern "c" _declspec(dllexport) int MyFunction(int x)
{
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     ...//操作
}
有时候为了安全起见,在每个常规DLL里都加上,也不会有任何问题,只是在静态连接的时候这个宏无效而已。这是导出函数的方法,记住只有MFC扩展DLL才能让参数和返回值使用MFC的数据类型。
4、导出指针
导出指针的方式如下:
_declspec(dllexport) int *pint;
_declspec(dllexport) CMyClass object = new CMyClass;
如果声明的时候同时初始化了指针,就需要找到合适的地方类释放指针。在扩展DLL中有个函数DllMain()。(注意函数名中的两个l要是小写字母),可以在这个函数中处理指针:
# include "MyClass.h"
_declspec(dllexport) CMyClass *pobject = new CMyClass;
DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)
{
    if(dwReason == DLL_PROCESS_ATTACH)
    {
        .....//
    }
    else if(dwReason == DLL_PROCESS_DETACH)
    {
        delete pobject;
    }
}
常规DLL有一个从CWinApp派生的类对象处理DLL的开和关,可以使用类向导添加InitInstance/ExitInstance函数。
int CMyDllApp::ExitInstance()
{
    delete pobject;
    return CWinApp::ExitInstance();
}
三、在客户程序中使用DLL
    编译一个DLL时将创建两个文件.dll文件和.lib文件。首先将这两个文件复制到客户程序项目的文件夹里,这里需要注意DLL和客户程序的版本问题,尽量使用相同的版本,都使用RELEASE或者都是DEBUG版本。
   接着就需要在客户程序中设置LIB文件,打开Project Settings--->Link--->Object/library Modules中输入LIB的文件名和路径。如:Debug/SampleDll.lib。除了DLL和LIB文件外,客户程序需要针对导出类、函数、对象和变量的头文件,现在进行导入添加的关键字就是:_declspec(dllimport),如:
_declspec(dllimport) int MyFunction(int);
_declspec(dllimport) int MyInt;
_declspec(dllimport) CMyClass object;
extern "C" _declspec(dllimport) int MyFunction(int);
在有的时候为了导入类,要把相应类的头文件添加到客户程序中,不同的是要修改类声明的标志:
class _declspec(dllimport) CMyClass,如果创建的是扩展DLL,两个位置都是:
class AFX_EXT_CLASS CMyClass。

--------------------------------------------------------------------------------
最好不要把整个类暴露出来,这样有可能引起不安全性。
可以写几个全局函数,用全局函数去连接类里面的成员函数,然后只暴露这几个全局函数就可以调用类的功能了
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:193443次
    • 积分:2973
    • 等级:
    • 排名:第11775名
    • 原创:90篇
    • 转载:34篇
    • 译文:12篇
    • 评论:15条
    最新评论