为C++实现类似C#的import DLL用法

最近在玩的一款游戏支持内嵌C#语言完成高级自定义功能,在游戏外编写时需要import指定的DLL。在Visual Studio中试了一下,发现确实能够编写。另外,每当我import之后,Visual Studio就会进行parse,之后便可以获得方法的详细信息进行提示(智能提示窗)。对此我很好奇,首先尝试解析DLL文件,发现不是有效的PE文件(尴尬...)...然后,就没有然后了,愉快的开始了游戏内代码的测试。

一番测试下来,忽然想到为什么C++只能include不能import呢?

折腾一番之后,得到两个宏

#define import(DynamicLibraryName) do{ \
int ret=_cpplib_buildin_namespace::dllhelper.addDLL(DynamicLibraryName);\
if(ret<0)\
    throw runtime_error("Cannot Find DynamicLibrary: " DynamicLibraryName); \
}while(0)

#define getfunc(ReturnType,FunctionName,ArgsList...) ReturnType(*FunctionName)(##ArgsList)=NULL;do{ \
FunctionName=(ReturnType(*)(##ArgsList))_cpplib_buildin_namespace::dllhelper.getproc(#FunctionName); \
if(FunctionName==NULL) \
    throw runtime_error("Cannot Find Function: " #FunctionName); \
}while(0)

很明显,import用于引进DLL文件,getfunc用于引进方法

测试如下

int main()
{
    import("test.dll");
    getfunc(int,HelloWorld);
    int ret=HelloWorld();
    printf("%d\n",ret);
    return 0;
}
其中test.dll中方法定义如下

extern "C"{

int DLL_EXPORT HelloWorld()
{
    printf("Hello World!\n");
    return 5;
}

}

编译-运行成功!

不过这两个宏还是不够灵活,首先import和getfunc不能用在函数外,其次如果import或getfunc失败会抛出异常,而异常是运行时才能检测到的。相比之下C#都是在编译期间完成二者的检查。


==================分割线==================

刚才我忘记了Lambda这个神奇的东西!使用Lambda之后就可以在函数外部进行初始化啦!这样就和C#的import非常类似了。

实现方法如下

#define import(DynamicLibraryName,Label) \
unique_ptr<_cpplib_buildin_namespace::dlibctrl_manager> Label=[]()->unique_ptr<_cpplib_buildin_namespace::dlibctrl_manager>\
{\
dlibctrl* p=new dlibctrl;\
int ret=p->open(DynamicLibraryName);\
if(ret<0)\
{\
delete p;\
return NULL;\
}\
_cpplib_buildin_namespace::dlibctrl_manager* manager=new _cpplib_buildin_namespace::dlibctrl_manager;\
manager->dbptr=p;\
_cpplib_buildin_namespace::dlibctrl_center.add(manager);\
return unique_ptr<_cpplib_buildin_namespace::dlibctrl_manager>(manager);\
}()

#define getfunc(ReturnType,FunctionName,Args...) ReturnType(*FunctionName)(##Args)=[]()->ReturnType(*)(##Args)\
{\
void* p=NULL;\
if((p=_cpplib_buildin_namespace::dlibctrl_center.find(#FunctionName))==NULL)\
{\
    return NULL;\
}\
return (ReturnType(*)(##Args))p;\
}()


#define getfuncin(Label,ReturnType,FunctionName,Args...) ReturnType(*FunctionName)(##Args)=[]()->ReturnType(*)(##Args)\
{\
void* p=NULL;\
if((p=_cpplib_buildin_namespace::dlibctrl_center.findin(#FunctionName,Label.get()))==NULL)\
{\
    return NULL;\
}\
return (ReturnType(*)(##Args))p;\
}()


相比于之前那种实现方法,import多了一个Label。需要保证Label是全局唯一的。

这种实现方法可以在内部import,这样import便有了作用域。同样的,getfunc和getfuncin也有了作用域。非常棒!

额外需要定义一些东西

namespace _cpplib_buildin_namespace
{
    /// DECL
    class dlibctrl_manager
    {
    public:
        dlibctrl* dbptr;
        ~dlibctrl_manager();
    };

    class _dlibctrl_center_class
    {
    public:
        void add(dlibctrl_manager* ptr)
        {
            m.lock();
            vec.push_back(ptr);
            m.unlock();
        }
        int del(dlibctrl_manager* ptr)
        {
            m.lock();
            int sz=vec.size();
            for(int i=0;i<sz;i++)
            {
                if(vec.at(i)==ptr)
                {
                    vec.erase(vec.begin()+i);
                    m.unlock();
                    return 0;
                }
            }
            m.unlock();
            return -1;
        }
        void* find(const char* name)
        {
            m.lock();
            for(auto& ptr:vec)
            {
                void* p;
                if((p=ptr->dbptr->getproc(name))!=NULL)
                {
                    m.unlock();
                    return p;
                }
            }
            m.unlock();
            return NULL;
        }
        void* findin(const char* name,dlibctrl_manager* incptr)
        {
            m.lock();
            bool found=false;
            for(auto& ptr:vec)
            {
                if(ptr==incptr)
                {
                    found=true;
                    break;
                }
            }
            if(!found)
            {
                m.unlock();
                return NULL;
            }
            void* p=incptr->dbptr->getproc(name);
            m.unlock();
            return p;
        }
    private:
        vector<dlibctrl_manager*> vec;
        mutex m;
    }dlibctrl_center;

    ///IMPL
    dlibctrl_manager::~dlibctrl_manager()
    {
        dlibctrl_center.del(this);
        delete dbptr;
    }
}

dlibctrl_center存储所有manager,并提供基于mutex的线程安全的添加与删除操作

dlibctrl_manager用于包装dlibctrl,并在析构的时候从center中删去自身.

测试代码如下:

import("test.dll",x);
getfunc(int,HelloWorld);
int main()
{
    int ret=HelloWorld();
    printf("%d\n",ret);
    return 0;
}
编译~运行 成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值