zoukankan      html  css  js  c++  java
  • 跨平台的动态链接库的调用

    一、三种库函数的链接方式
    用过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

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

    extern "C"
    {
     void DllOK();
     const char *DllName(const char *name);
    }

    .c或.cpp的编写不做任何变化,VS项目的属性中,将上面的.def文件添加到链接器->输入->模块定义文件中(注意路径,可利用宏)。
     

    特别声明每个需要导出的函数,如下所示:
     

    __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较好,这样维护方便。

    三、调用框架的编写
    代码如下:

    #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 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(" Dll Error: %s",dll.GetLastErrorMsg());
        }
        else
        {
            DllOK_Fun DllOK = (DllOK_Fun)dll.GetProc("DllOK");
            if(!DllOK)
            {
                printf(" Function DllOK Error: %s",dll.GetLastErrorMsg());
            }
            else
            {
               DllOK();
            }
            DllName_Fun DllName = (DllName_Fun)dll.GetProc("DllName");
            if(!DllName)
            {
                printf(" Function DllName Error: %s",dll.GetLastErrorMsg());
            }
            else
            {
              const char *p = DllName("Hello");
              printf(" Function DllName Returned value: %s",p);
            }
     
        }
    }
     

  • 相关阅读:
    asp.net 自定义文本框
    单机运行k8s以及e2e
    编译k8s1.3的代码
    etcd的简单使用
    How Tencent Tests Software
    ps引发的血案
    配置IDE查看kubernetes源码
    读书笔记<<不懂带人,你就自己干到死>>
    书摘<<互联网世界观>>
    读书笔记《异类》
  • 原文地址:https://www.cnblogs.com/lidabo/p/15386976.html
Copyright © 2011-2022 走看看