以前没有封装dll动态库,接触新的工作之后需要用,折腾了一整天,终于搞定了
毕竟是生手,就简单分析注意点和遇到的一些问题及解决方案:
(1)首先准备要生成DLL的工程。
(1.1)我用的是BCB(C++ Builder),File->New->Other...->C++ Builder Projects->Dynamic-Link Library,再根据之后的提示进行,此时工程里有一个File1.cpp,这里就是导出接口的地方了;
(1.2)在Project1.dll上右击->Add New...->Unit,创建一个新的文件,在.cpp和.h文件中编辑你自己的类,一下是我写的一个类,和正常类差不多,就多一个_declspec(dllexport),并且在成员函数前添加__stdcall;我想,既然有了头文件中类的声明,这些类的成员函数在.cpp中该怎么写不必细说吧!头文件之类的我也都省略了!
class _declspec(dllexport) CEmployeeInfo
{
public:
__stdcall CEmployeeInfo(void);
__stdcall ~CEmployeeInfo(void);
HRESULT __stdcall CreateInfo(int nID, const char * pcName, const char * pcPhone);
HRESULT __stdcall UpdateInfo(int nID, const char * pcNewName, const char * pcNewPhone);
HRESULT __stdcall DeleteInfo(int nID);
private:
INFO_T m_stInfo;
};
(1.3)接下来要在File1.cpp中导出供外部访问dll动态库的接口。要把函数接口声明为extern "C"标准,并且接口前面要表明__declspec(dllexport),以后外部就能通过这几个函数访问dll内部的东西了。
CEmployeeInfo *gm_pInfo_Dll = NULL;
extern "C"{
__declspec(dllexport) HRESULT __stdcall CreateEmployeeInfo(int nID,char *pName,char *pPhone);
__declspec(dllexport) HRESULT __stdcall UpdateEmployeeInfo(int nID,char *pName,char *pPhone);
__declspec(dllexport) HRESULT __stdcall DeleteEmployeeInfo(int nID);
}
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
__declspec(dllexport) HRESULT __stdcall CreateEmployeeInfo(int nID,char *pcName,char *pcPhone)
{
return S_OK;
}
__declspec(dllexport) HRESULT __stdcall UpdateEmployeeInfo(int nID,char *pcName,char *pcPhone)
{
return S_OK;
}
__declspec(dllexport) HRESULT __stdcall DeleteEmployeeInfo(int nID)
{
return S_OK;
}
(1.4)做到这里基本搞定,只要编译下,在工程目录Debug下会生成一个Project1.dll文件,就是我们要的动态库!
(2)接下来就要加载调用我们生成的dll动态库了,再创建一个新的工程,File->New->Other...->C++ Builder Projects->VCL Forms Application,双击Form窗体,就能进入代码编辑区。
(2.1)首先在头文件的class TForm1 : public TForm类中定义私有成员变量;
private:// User declarations
HINSTANCE hdll;//用来存放动态库句柄的
(2.2)要在.cpp中声明几个函数指针,放在 TForm1 *Form1之前即可,需要注意的是,接口形式要跟dll中提供的接口一致!!
HRESULT __stdcall (*CreateEmployeeInfo)(int nSerial,char *pName,char *pPhone);
HRESULT __stdcall (*UpdateEmployeeInfo)(int nSerial,char *pName,char *pPhone);
HRESULT __stdcall (*DeleteEmployeeInfo)(int nSerial);
(2.3)接下来加载动态库,ExtractFilePath(Application->ExeName)用来获取当前工程目录Debug的路径,LoadLibrary用来加载库,GetProcAddress用来根据动态库中的函数符号获取接口的地址,这些函数指针的用法与C语言中的回调函数完全一样;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
hdll = NULL;
AnsiString str = ExtractFilePath(Application->ExeName) + "Project1.dll";
hdll = ::LoadLibrary(str.c_str());//动态加载
(FARPROC&)CreateEmployeeInfo = GetProcAddress(hdll, "CreateEmployeeInfo");
(FARPROC&)UpdateEmployeeInfo = GetProcAddress(hdll, "UpdateEmployeeInfo");
(FARPROC&)DeleteEmployeeInfo = GetProcAddress(hdll, "DeleteEmployeeInfo");
}
(2.4)对工程进行编译,一开始会报错,但是在工程目录下会生成Debug目录,把之前得到的DLL库放到Debug目录下,再次对工程进行编译即可;
(2.5)编程过程中可能需要经常对DLL工程的源码编辑再生成,如果一直用复制的方式很麻烦,可以在DLL工程右击->Options->Directories and Conditionals->Final OutPut Directory设置编译输出路径!
另外,具体的工程代码我已经放到CSDN下载区了,当然,具体工程的文件名称和文章中讲的会有出入,虽然会显得粗糙点,但对初学者还是可以借鉴的,http://download.csdn.net/detail/lushangqiushui/7140883,赚点积分!如果缺积分,也可以邮件讨要的([email protected])!
对自己的小结:
一开始不懂要用C接口导出,以为要直接外部对类进行实例化,于是把DLL工程目录下的dll,lib和相应的头文件都复制到Project1的Debug目录下,就出现DLL中的类的成员函数都没有定义!其实要解决这个问题很简单,只要把Project1.lib附加到工程中就可以,但是主管的要求是只能用到DLL动态库,完全不涉及LIB或.h文件,而且做动态库主要是为了封装,外部是不可以用到DLL中的类,这说明我对封装的概念还是不了解,所以外部只能通过DLL导出的接口访问内部的一切!不过这次懂了,以后再写都是小意思了!