一、三种库函数的链接方式
用过C语言的都知道库函数的重要:我们将功能比较独立的部分做成一个个函数,供我们复用。最终对库函数的引用有3种方式:
方式 | 链接时刻 | 库指定时刻 | 特点 |
静态链接 | 编译时 | 编译时 | 编译时检查链接错误,编入运行程序(运行程序独立) |
动态链接 | 运行时 | 编译时检查链接错误,运行时调入依赖库 | |
运行时 | 运行时 | 编译时不做任何检查,运行时调入依赖库 |
其中前两种是我们熟悉的,最后一种是“完全”动态方式,包括库文件的指定都是由代码完成的。本文主要讨论这一种。
这种情况的应用场景是:我们定义了一组接口函数,但我们并不知道该接口是如何实现的(由其他人完成),在运行时,我们根据某个传入参数,动态调入包含这些接口实现的库,按规定的方式运行它。从以上的描述,是不是像虚函数的特点?
这种场景比较适合于一个框架(开发者A),多个不同的细节实现(开发者B,C....)这样一种应用。
从上面的描述,要完成本项功能,包括调用框架的编写(相当于服务器端)和被调用库函数的编写(相当于“客户端”)。而要做到跨平台,这两者必须都是跨平台的。
二、动态库的编写
C语言有不带类的C和带类的C(C++),我们知道,C++在编译时,实际上是要被转换为C的,所以从调用的角度来讲,只有函数,没有类。这样问题就来了:类如何导出?即调用者如何得到类的信息?
对于后出现的语言,例如Java,C#,这根本不是问题,语言的开发者已为我们考虑了这个情况。在微软的世界里(Windows),这也不是大问题,微软自己有一套规则。微软的COM的一个特点就是用来描述导出类的,但可惜并没有被大家接受。在Linux世界里,似乎只有一个个的函数能被“开放出来”,供其他开发者所调用。
如果要开发跨平台的库(不提供源代码),还是老老实实回归C语言吧。
1. Windows下函数的导出
有两种方式。
- 建立一个.def文件,将需要导出的函数按如下格式编写:
- LIBRARY "dlldemo"
- EXPORTS
- DllOK @1
- DllName @2
LIBRARY "dlldemo" EXPORTS DllOK @1 DllName @2
在.h声明导出函数时,采用extern C包裹它们,如下所示:
- extern "C"
- {
- void DllOK();
- const char *DllName(constchar *name);
- }
extern "C" { void DllOK(); const char *DllName(const char *name); }
.c或.cpp的编写不做任何变化。
- 特别声明每个需要导出的函数,如下所示:
__declspec(dllexport) void __cdecl DllOK(void);为简化以上的声明,通常将__declspec(dllexport)定义为一个宏。__declspec(dllexport) const char *__cdecl DllName(const char *);
2.Linux下函数的导出
Linux下实现起来比较简单,只在.h声明导出函数时,采用extern C包裹它们,如上面的例子所示。
这样,库函数导出的写法就有两种,如果函数不多,建议编写一个.def文件方便一些,如果函数较多,且其声明不断变化,采用宏定义__declspec(dllexport),__cdecl较好,这样维护方便。
三、调用框架的编写
代码如下:
- #if defined(_WIN32_PLATFROM_)
- #include <windows.h>
- typedef HMODULE MODULE_HANDLE;
- #endif
- #if defined(_LINUX_PLATFROM_)
- #include <dlfcn.h>
- typedef void * MODULE_HANDLE;
- #endif
- MODULE_HANDLE gdl_Open(constchar *plname)
- {
- #if defined(_WIN32_PLATFROM_)
- return LoadLibraryA (plname);
- #endif
- #if defined(_LINUX_PLATFROM_)
- return dlopen( plname, RTLD_NOW|RTLD_GLOBAL);
- #endif
- }
- void gdl_Close(MODULE_HANDLE h)
- {
- if(h)
- {
- #if defined(_WIN32_PLATFROM_)
- FreeLibrary(h);
- #endif
- #if defined(_LINUX_PLATFROM_)
- dlclose (h);
- #endif
- }
- }
- void *gdl_GetProc(MODULE_HANDLE h,const char *pfname)
- {
- if(h)
- {
- #if defined(_WIN32_PLATFROM_)
- return (void *)GetProcAddress(h, pfname);
- #endif
- #if defined(_LINUX_PLATFROM_)
- return dlsym(h,pfname);
- #endif
- }
- return NULL;
- }
- void gdl_GetLastErrorMsg(char *p,int size)
- {
- #if defined(_WIN32_PLATFROM_)
- gxsprintf(p,size, "dll error(%u)",::GetLastError());
- #endif
- #if defined(_LINUX_PLATFROM_)
- gxsprintf(p,size, "%s",dlerror());
- #endif
- }
- class CDynamicLibrary
- {
- public:
- CDynamicLibrary()
- {
- m_hModule = NULL;
- }
- ~CDynamicLibrary()
- {
- gdl_Close(m_hModule);
- }
- inline bool Open(constchar *lpname)
- {
- m_strDllName = lpname;
- m_hModule = gdl_Open(lpname);
- return m_hModule!=NULL;
- }
- inline void *GetProc(constchar *pfname)
- {
- return gdl_GetProc(m_hModule, pfname);
- }
- const char *GetLastErrorMsg()
- {
- char msg[100];
- gdl_GetLastErrorMsg(msg, 99);
- m_strMsg = msg;
- return m_strMsg.c_str();
- }
- private:
- std::string m_strMsg;
- std::string m_strDllName;
- MODULE_HANDLE m_hModule;
- };
#if defined(_WIN32_PLATFROM_)
#include <windows.h>
typedef HMODULE MODULE_HANDLE;
#endif
#if defined(_LINUX_PLATFROM_)
#include <dlfcn.h>
typedef void * MODULE_HANDLE;
#endif
MODULE_HANDLE gdl_Open(const char *plname)
{
#if defined(_WIN32_PLATFROM_)
return LoadLibraryA (plname);
#endif
#if defined(_LINUX_PLATFROM_)
return dlopen( plname, RTLD_NOW|RTLD_GLOBAL);
#endif
}
void gdl_Close(MODULE_HANDLE h)
{
if(h)
{
#if defined(_WIN32_PLATFROM_)
FreeLibrary(h);
#endif
#if defined(_LINUX_PLATFROM_)
dlclose (h);
#endif
}
}
void *gdl_GetProc(MODULE_HANDLE h, const char *pfname)
{
if(h)
{
#if defined(_WIN32_PLATFROM_)
return (void *)GetProcAddress(h, pfname);
#endif
#if defined(_LINUX_PLATFROM_)
return dlsym(h,pfname);
#endif
}
return NULL;
}
void gdl_GetLastErrorMsg(char *p, int size)
{
#if defined(_WIN32_PLATFROM_)
gxsprintf(p,size, "dll error(%u)",::GetLastError());
#endif
#if defined(_LINUX_PLATFROM_)
gxsprintf(p,size, "%s",dlerror());
#endif
}
class CDynamicLibrary
{
public:
CDynamicLibrary()
{
m_hModule = NULL;
}
~CDynamicLibrary()
{
gdl_Close(m_hModule);
}
inline bool Open(const char *lpname)
{
m_strDllName = lpname;
m_hModule = gdl_Open(lpname);
return m_hModule!=NULL;
}
inline void *GetProc(const char *pfname)
{
return gdl_GetProc(m_hModule, pfname);
}
const char *GetLastErrorMsg()
{
char msg[100];
gdl_GetLastErrorMsg(msg, 99);
m_strMsg = msg;
return m_strMsg.c_str();
}
private:
std::string m_strMsg;
std::string m_strDllName;
MODULE_HANDLE m_hModule;
};
以上代码中,实际上封装了4个函数,动态库的打开(Open),关闭(Close),得到函数(GetProc),得到错误信息(GetError),如果应用,只需关注CDynamicLibrary即可。
四、运行时调用实例
以下是调用的实例代码。
- void testdll()
- {
- typedef void (*DllOK_Fun)();
- typedef constchar * (*DllName_Fun)(constchar *);
- CDynamicLibrary dll;
- #if defined(_WIN32_PLATFROM_)
- char *pdllname = "dlldemo.dll";
- #endif
- #if defined(_LINUX_PLATFROM_)
- char *pdllname = "libdlldemo.so";
- #endif
- if(!dll.Open(pdllname))
- {
- printf("\nDll Error: %s",dll.GetLastErrorMsg());
- }
- else
- {
- DllOK_Fun DllOK = (DllOK_Fun)dll.GetProc("DllOK");
- if(!DllOK)
- {
- printf("\nFunction DllOK Error: %s",dll.GetLastErrorMsg());
- }
- else
- {
- DllOK();
- }
- DllName_Fun DllName = (DllName_Fun)dll.GetProc("DllName");
- if(!DllName)
- {
- printf("\nFunction DllName Error: %s",dll.GetLastErrorMsg());
- }
- else
- {
- const char *p = DllName("Hello");
- printf("\nFunction DllName Returned value: %s",p);
- }
- }
- }
void testdll()
{
typedef void (*DllOK_Fun)();
typedef const char * (*DllName_Fun)(const char *);
CDynamicLibrary dll;
#if defined(_WIN32_PLATFROM_)
char *pdllname = "dlldemo.dll";
#endif
#if defined(_LINUX_PLATFROM_)
char *pdllname = "libdlldemo.so";
#endif
if(!dll.Open(pdllname))
{
printf("\nDll Error: %s",dll.GetLastErrorMsg());
}
else
{
DllOK_Fun DllOK = (DllOK_Fun)dll.GetProc("DllOK");
if(!DllOK)
{
printf("\nFunction DllOK Error: %s",dll.GetLastErrorMsg());
}
else
{
DllOK();
}
DllName_Fun DllName = (DllName_Fun)dll.GetProc("DllName");
if(!DllName)
{
printf("\nFunction DllName Error: %s",dll.GetLastErrorMsg());
}
else
{
const char *p = DllName("Hello");
printf("\nFunction DllName Returned value: %s",p);
}
}
}