问题
我试图编写一个玩具语言的解释器,我希望它能够调用位于DLL中的函数。 在一些external.dll我有:
#include <cstdio> extern "C" { __declspec(dllexport) void print(int val) { printf("%in",val); } __declspec(dllexport) int add(int a,int b) { return a + b; } ... more functions **that I don't kNow then names of** }
假设我有一个std::string func; 这是DLL中的一个proc的名字,可能是"print"或"add" ,以及一个std::vector<int> args; 其大小是目标函数的参数个数。 我将如何相应地调用正确的DLL函数? 理想情况下,我希望能够调用任何可以使用GetProcAddress加载的函数。
我的解决方法
我目前正在使用MSVC的内联汇编程序来做我想要的。 这是符合以下几点的:
int WorkaroundCall(const std::string& func,const std::vector<int>& args) { void* proc = GetProcAddress(hmod,func.c_str()); // hmod is the DLL's HMODULE void* spsave,* argframe; size_t argsize = sizeof(int) * args.size(); const int* argdata = args.data(); __asm { mov eax,esp sub eax,argsize mov argframe,eax } memcpy(argframe,argdata,argsize); __asm { mov spsave,esp mov esp,argframe xor edx,edx call proc mov esp,spsave } }
但是,这显然不是一个好的解决scheme,因为它使用汇编,并依赖于系统(东西告诉我,这不会在64位工作)。 我怎样才能做得更好?
在Windows 64位上实现具有自定义堆栈的沙箱
debugging对于Linux
在大会strlen
在NASM中使用DB指令可以在.text段中创build可写variables吗?
是否有可能加载到内存的二进制文件,并在Windows中执行它
(x86_64 linux程序集)为什么使用float格式string的printf只能与rsp%0x10 = 0一起工作
如何从汇编(从Windows设备驱动程序)更改cpu频率
为什么Linux程序的.text部分从0x0804800开始,栈顶从0xbffffff开始?
如何从自身内部获取指向程序特定部分的指针? (也许与自我)
就像是:
#define EXTERNAL_API __declspec(dllimport) typedef void (EXTERNAL_API* LpprintFN)( int ); typedef int (EXTERNAL_API* LPADDFN)( int,int ); // After loading the module you get the functions. LpprintFN pfnPrint = (LpprintFN) GetProcAddress( hmod,"print" ); LPADDFN pfnAdd = (LPADDFN) GetProcAddress( hmod,"add" );
现在,既然你有这些字符串,你可能想把它们映射到一个唯一的值(假设地图是全局的):
typedef enum FuncType { nothing = 0,PrintFunc = 1,AddFunc = 2 } EFuncType; typedef map< string,EFuncType > TFuncNameMap; TFuncNameMap funcNameMap; if( pfnPrint != NULL ) funcNameMap["print"] = PrintFunc; if( pfnAdd != NULL ) funcNameMap["add"] = AddFunc;
int SlightlyBetterCall( const std::string& func,const std::vector<int>& args ) { TFuncNameMap::iterator iFuncId = funcNameMap.find(func); if( iFuncId == funcNameMap.end() ) return -1; // return some error? int result = 0; switch( iFuncId->second ) { case PrintFunc: pfnPrint( args[0] ); break; case AddFunc: result = pfnAdd( args[0],args[1] ); break; } return result; }
你真的不需要那张地图…没有什么可以阻止你走的:
if( func == "print" && pfnPrint != NULL ) { pfnPrint( args[0] ); } else if( func == "add" && pfnAdd != NULL ) { result = pfnAdd( args[0],args[1] ); }
这整个事情似乎有点怀疑我,但我希望在任何情况下,帮助你=)
好的,我知道了:-
您正在堆栈上创建空间,然后将偏移量传递给proc 。
如果堆栈不是唯一的要求,那么它很好地传递堆中的数据地址。
但是,您可以使用数组在堆栈上分配空间。
int WorkaroundCall(const std::string& func,const std::vector<int>& args) { void (*proc)(int); void* proc = GetProcAddress(hmod,* argframe; size_t argsize = sizeof(int) * args.size(); const int* argdata = args.data(); int *dat= new int[argsize]; memcpy(dat,argsize); proc(dat); delete[] dat; }
我得到的一般意义,唯一的解决办法是使用Libhf的Mehrdad(这不幸,不支持MSVC),或继续使用大会和维护不同版本的32位和64位(这是什么我会尽力去做)。
由Chris Becke建议的生成所有方法签名是不切实际的,因为玩具语言的类型不仅仅是int。
再次,对不起 – 我应该明确指出,DLL中的函数名在编译时并不为程序所知。
在C(以及C ++)中这样做的唯一方法就是声明所有可能的函数签名,然后根据参数个数调用相应的签名。
接下来要注意的是,在64位构建中,“int”不会削减它 – 基本参数大小将是64位类型。 由于这是一个Windows问题,我将使用Windows sdk中的类型来确保它在Win32和Win64中编译和正常工作。
#include <windows.h> typedef INT_PTR CALLBACK Tfunc0(void); typedef INT_PTR CALLBACK Tfunc1(INT_PTR p1); typedef INT_PTR CALLBACK Tfunc2(INT_PTR p1,INT_PTR p2); INT_PTR CallProc(FARPROC proc,const std::vector<INT_PTR>& args) { switch(args.size()) { case 0: return ((TFunc0*)proc)(); case 1: return ((TFunc1*)proc)(args[0]); case 2: return ((TFunc2*)proc)(args[0],args[1]); } throw some_kind_of_exception; }
或者,一个模板化的方法可以让你更easilly调用procs:
template<typename RetT> RetT CallProc(FARPROC proc){ return (RetT)(((TFunc0*)proc)()); } template<typename RetT,typename Param1T> RetT CallProc(FARPROC proc,Param1T p1){ return (RetT)(((TFunc1*)proc)((INT_PTR)p1)); } //etc.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。