C Pass arguments as void-pointer-list to imported function from LoadLibrary()
我有一个问题,我想创建一个通用的命令行应用程序,可以用来加载库dll,然后调用库dll中的函数。函数名在命令行上指定,参数也在实用程序命令行上提供。
我可以使用
我可以将一个空指针列表传递给我由
为了使示例代码简单,我删除了错误检查。有没有办法让这样的工作:
1 2 3 4 5 | //Somewhere in another dll int DoStuff(int a, int b) { return a + b; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int main(int argc, char **argv) { void *retval; void *list = argv[3]; HMODULE dll; void* (*generic_function)(void*); dll = LoadLibraryA(argv[1]); //argv[2] ="DoStuff" generic_function = GetProcAddress(dll, argv[2]); //argv[3] = 4, argv[4] = 7, argv[5] = NULL retval = generic_function(list); } |
如果我忘记提到必要的信息,请告诉我。提前谢谢
在调用函数指针之前,需要将
1 2 3 4 5 6 7 8 9 | void Call_II(void (*fn_)(), char **args) { void (*fn)(int, int) = (void (*)(int, int))fn_; fn(atoi(args[0]), atoi(args[1])); } void Call_IS(void (*fn_)(), char **args) { void (*fn)(int, char *) = (void (*)(int, char *))fn_; fn(atoi(args[0]), args[1]); } ...various more functions |
然后从
1 2 3 4 5 6 7 8 9 10 | void* (*generic_function)(); dll = LoadLibraryA(argv[1]); //argv[2] ="DoStuff" generic_function = GetProcAddress(dll, argv[2]); //argv[3] = 4, argv[4] = 7, argv[5] = NULL Call_II(generic_function, &argv[3]); |
问题是,您需要知道获得指针的函数的类型,并调用适当的适配器函数。这通常意味着创建一个函数名/适配器表并在其中进行查找。
相关的问题是,没有一个类似于
库dll包含作为库一部分的函数的对象代码以及一些附加信息,以使该dll可用。好的。
但是,库dll不包含确定库dll中包含的函数的特定参数列表和类型所需的实际类型信息。库dll中的主要信息是:(1)DLL导出的函数列表以及将函数调用连接到实际函数二进制代码的地址信息;(2)库dll中的函数使用的任何所需dll的列表。好的。
你可以在一个文本编辑器中打开一个库dll,我建议用一个小的,扫描二进制代码的神秘符号,直到你到达包含库dll中函数列表和其他所需dll的部分。好的。
因此,库dll包含(1)在库dll中查找特定函数以便可以调用它所需的最低信息;(2)库dll中的函数所依赖的其他所需dll的列表。好的。
这与通常具有类型信息的COM对象不同,后者支持执行基本反射的操作,并探索COM对象的服务以及访问这些服务的方式。您可以使用Visual Studio和其他IDE来实现这一点,这些IDE生成已安装的COM对象列表,并允许您加载COM对象并对其进行浏览。Visual Studio还具有一个工具,该工具将生成提供存根的源代码文件,并包含用于访问COM对象的服务和方法的文件。好的。
但是,库dll与COM对象不同,并且随COM对象提供的所有附加信息都不能从库dll中获得。相反,库dll包通常由以下部分组成:(1)库dll本身,(2)一个.lib文件,其中包含库dll的链接信息以及存根和功能,以便在构建使用库dll的应用程序时满足链接器的要求,(3)一个包含文件,其中包含t中函数的函数原型图书馆DLL。好的。
因此,您可以通过调用库dll中的函数(但使用include文件中的类型信息)并与关联的.lib文件的存根链接来创建应用程序。此过程允许Visual Studio自动执行使用库DLL所需的大部分工作。好的。
也可以使用
如果您知道库dll中函数的实际函数名和函数原型,那么您可以做的是让命令行实用程序需要以下信息:好的。
- 在命令上作为文本字符串调用的函数的名称线
- 在命令行上用作一系列文本字符串的参数列表
- 描述函数原型的附加参数
这类似于接受未知参数类型的变量参数列表的C和C++运行时函数的工作方式。例如,用于打印参数值列表的
因此,如果您的实用程序有如下命令行:好的。
1 | dofunc"%s,%d,%s" func1"name of" 3" things" |
库dll有一个函数,其原型如下:好的。
1 | void func1 (char *s1, int i, int j); |
然后,该实用程序将通过将命令行的字符串转换为要调用的函数所需的实际类型来动态生成函数调用。好的。
这适用于采用普通旧数据类型的简单函数,但是更复杂的类型(如
附录一:一个简单的例子好的。
下面是我在调试器中运行的Visual Studio Windows控制台应用程序的源代码。属性中的命令参数是
注意:下面的示例依赖于基于堆栈的参数传递约定,这与大多数x86 32位编译器使用的约定相同。除了基于堆栈的参数传递(如Visual Studio的
注意函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // dllfunctest.cpp : Defines the entry point for the console application. // #include"stdafx.h" typedef struct { UCHAR *myList[4]; } sarglist; typedef void ((*libfunc) (sarglist q)); /* * do a load library to a DLL and then execute a function in it. * * dll name.dll"funcname" */ int _tmain(int argc, _TCHAR* argv[]) { HMODULE dll = LoadLibrary(argv[1]); if (dll == NULL) return 1; // convert the command line argument for the function name, argv[2] from // a TCHAR to a standard CHAR string which is what GetProcAddress() requires. char funcname[256] = {0}; for (int i = 0; i < 255 && argv[2][i]; i++) { funcname[i] = argv[2][i]; } libfunc generic_function = (libfunc) GetProcAddress(dll, funcname); if (generic_function == NULL) return 2; // build the argument list for the function and then call the function. // function prototype for PifLogAbort() function exported from the library DLL // is as follows: // VOID PIFENTRY PifLogAbort(UCHAR *lpCondition, UCHAR *lpFilename, UCHAR *lpFunctionname, ULONG ulLineNo); sarglist xx = {{(UCHAR *)"xx1", (UCHAR *)"xx2", (UCHAR *)"xx3", (UCHAR *)1245}}; generic_function(xx); return 0; } |
这个简单的例子说明了一些必须克服的技术障碍。您需要知道如何将各种参数类型转换为内存区域中正确的对齐方式,然后将内存区域推到堆栈上。好的。
这个示例函数的接口非常相似,因为大多数参数都是
附录二:扩展简单示例好的。
另一种可能是有一组助手函数以及不同版本的
因此,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | typedef struct { UCHAR myList[128]; } sarglist2; typedef struct { int i; sarglist2 arglist; } sarglistlist; typedef void ((*libfunc2) (sarglist2 q)); void pushInt (sarglistlist *p, int iVal) { *(int *)(p->arglist.myList + p->i) = iVal; p->i += sizeof(int); } void pushChar (sarglistlist *p, unsigned char cVal) { *(unsigned char *)(p->arglist.myList + p->i) = cVal; p->i += sizeof(unsigned char); } void pushVoidPtr (sarglistlist *p, void * pVal) { *(void * *)(p->arglist.myList + p->i) = pVal; p->i += sizeof(void *); } |
然后,将使用
1 2 3 4 5 6 7 8 | sarglistlist xx2 = {0}; pushVoidPtr (&xx2,"xx1"); pushVoidPtr (&xx2,"xx2"); pushVoidPtr (&xx2,"xx3"); pushInt (&xx2, 12345); libfunc2 generic_function2 = (libfunc2) GetProcAddress(dll, funcname); generic_function2(xx2.arglist); |
好啊。