跨平台的动态链接库的调用

一、三种库函数的链接方式

用过C语言的都知道库函数的重要:我们将功能比较独立的部分做成一个个函数,供我们复用。最终对库函数的引用有3种方式:

方式 链接时刻 库指定时刻 特点
静态链接编译时编译时编译时检查链接错误,编入运行程序(运行程序独立)
动态链接运行时编译时检查链接错误,运行时调入依赖库
运行时运行时编译时不做任何检查,运行时调入依赖库

其中前两种是我们熟悉的,最后一种是“完全”动态方式,包括库文件的指定都是由代码完成的。本文主要讨论这一种。

这种情况的应用场景是:我们定义了一组接口函数,但我们并不知道该接口是如何实现的(由其他人完成),在运行时,我们根据某个传入参数,动态调入包含这些接口实现的库,按规定的方式运行它。从以上的描述,是不是像虚函数的特点?

这种场景比较适合于一个框架(开发者A),多个不同的细节实现(开发者B,C....)这样一种应用。

从上面的描述,要完成本项功能,包括调用框架的编写(相当于服务器端)和被调用库函数的编写(相当于“客户端”)。而要做到跨平台,这两者必须都是跨平台的。

二、动态库的编写

C语言有不带类的C和带类的C(C++),我们知道,C++在编译时,实际上是要被转换为C的,所以从调用的角度来讲,只有函数,没有类。这样问题就来了:类如何导出?即调用者如何得到类的信息?

对于后出现的语言,例如Java,C#,这根本不是问题,语言的开发者已为我们考虑了这个情况。在微软的世界里(Windows),这也不是大问题,微软自己有一套规则。微软的COM的一个特点就是用来描述导出类的,但可惜并没有被大家接受。在Linux世界里,似乎只有一个个的函数能被“开放出来”,供其他开发者所调用。

如果要开发跨平台的库(不提供源代码),还是老老实实回归C语言吧。

1. Windows下函数的导出

有两种方式。

  • 建立一个.def文件,将需要导出的函数按如下格式编写:

  1. LIBRARY    "dlldemo" 
  2.  
  3. EXPORTS 
  4. DllOK     @1 
  5. DllName @2 
LIBRARY    "dlldemo"

EXPORTS
DllOK     @1
DllName @2

在.h声明导出函数时,采用extern C包裹它们,如下所示:

 

 

  1. extern "C" 
  2. void DllOK(); 
  3. 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) const char *__cdecl DllName(const char *);
为简化以上的声明,通常将__declspec(dllexport)定义为一个宏。

2.Linux下函数的导出

Linux下实现起来比较简单,只在.h声明导出函数时,采用extern C包裹它们,如上面的例子所示。

这样,库函数导出的写法就有两种,如果函数不多,建议编写一个.def文件方便一些,如果函数较多,且其声明不断变化,采用宏定义__declspec(dllexport),__cdecl较好,这样维护方便。

三、调用框架的编写

代码如下:

  1. #if defined(_WIN32_PLATFROM_) 
  2. #include <windows.h> 
  3. typedef HMODULE  MODULE_HANDLE; 
  4. #endif 
  5.  
  6. #if defined(_LINUX_PLATFROM_) 
  7. #include <dlfcn.h> 
  8. typedef void *  MODULE_HANDLE; 
  9. #endif 
  10.  
  11.  
  12.  
  13. MODULE_HANDLE gdl_Open(constchar *plname) 
  14. #if defined(_WIN32_PLATFROM_) 
  15.     return LoadLibraryA (plname); 
  16. #endif 
  17.  
  18. #if defined(_LINUX_PLATFROM_) 
  19.    return dlopen( plname, RTLD_NOW|RTLD_GLOBAL); 
  20. #endif 
  21. void gdl_Close(MODULE_HANDLE h) 
  22.   if(h) 
  23.   { 
  24. #if defined(_WIN32_PLATFROM_) 
  25.    FreeLibrary(h); 
  26. #endif 
  27. #if defined(_LINUX_PLATFROM_) 
  28.   dlclose (h); 
  29. #endif 
  30.   } 
  31. void *gdl_GetProc(MODULE_HANDLE h,const char *pfname) 
  32.    if(h) 
  33.    { 
  34. #if defined(_WIN32_PLATFROM_) 
  35.     return (void *)GetProcAddress(h, pfname); 
  36. #endif 
  37.  
  38. #if defined(_LINUX_PLATFROM_) 
  39.     return dlsym(h,pfname); 
  40. #endif 
  41.     } 
  42.     return NULL; 
  43.  
  44. void gdl_GetLastErrorMsg(char *p,int size) 
  45. #if defined(_WIN32_PLATFROM_) 
  46.     gxsprintf(p,size, "dll error(%u)",::GetLastError()); 
  47. #endif 
  48.  
  49. #if defined(_LINUX_PLATFROM_) 
  50.     gxsprintf(p,size, "%s",dlerror()); 
  51. #endif 
  52.  
  53.  
  54.  
  55. class CDynamicLibrary 
  56. public
  57.     CDynamicLibrary() 
  58.     {  
  59.        m_hModule = NULL; 
  60.     } 
  61.     ~CDynamicLibrary() 
  62.     { 
  63.        gdl_Close(m_hModule); 
  64.     } 
  65.     inline bool Open(constchar *lpname) 
  66.     { 
  67.        m_strDllName = lpname; 
  68.        m_hModule = gdl_Open(lpname); 
  69.        return m_hModule!=NULL; 
  70.     } 
  71.     inline void *GetProc(constchar *pfname) 
  72.     { 
  73.        return gdl_GetProc(m_hModule, pfname); 
  74.     } 
  75.     const char *GetLastErrorMsg() 
  76.     { 
  77.        char msg[100]; 
  78.        gdl_GetLastErrorMsg(msg, 99); 
  79.        m_strMsg = msg; 
  80.        return m_strMsg.c_str(); 
  81.     } 
  82. private
  83.     std::string m_strMsg; 
  84.     std::string m_strDllName; 
  85.     MODULE_HANDLE m_hModule; 
  86. }; 
#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即可。

四、运行时调用实例

以下是调用的实例代码。

  1. void testdll() 
  2.    typedef void (*DllOK_Fun)(); 
  3.    typedef constchar * (*DllName_Fun)(constchar *); 
  4.  
  5.     CDynamicLibrary dll; 
  6. #if defined(_WIN32_PLATFROM_) 
  7.     char *pdllname = "dlldemo.dll"
  8. #endif 
  9. #if defined(_LINUX_PLATFROM_) 
  10.     char *pdllname = "libdlldemo.so"
  11. #endif 
  12.  
  13.     if(!dll.Open(pdllname)) 
  14.     { 
  15.         printf("\nDll Error: %s",dll.GetLastErrorMsg()); 
  16.     } 
  17.     else 
  18.     { 
  19.         DllOK_Fun DllOK = (DllOK_Fun)dll.GetProc("DllOK"); 
  20.         if(!DllOK) 
  21.         { 
  22.             printf("\nFunction DllOK Error: %s",dll.GetLastErrorMsg()); 
  23.         } 
  24.         else 
  25.         { 
  26.            DllOK(); 
  27.         } 
  28.         DllName_Fun DllName = (DllName_Fun)dll.GetProc("DllName"); 
  29.         if(!DllName) 
  30.         { 
  31.             printf("\nFunction DllName Error: %s",dll.GetLastErrorMsg()); 
  32.         } 
  33.         else 
  34.         { 
  35.           const char *p = DllName("Hello"); 
  36.           printf("\nFunction DllName Returned value: %s",p); 
  37.         } 
  38.  
  39.     } 
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);
		}

	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值