为了允许从脚本语言(用C编写)访问Win32 API,我想编写如下函数:
void Call(LPCSTR DllName, LPCSTR FunctionName,
LPSTR ReturnValue, USHORT ArgumentCount, LPSTR Arguments[])它将一般地调用任何Win32 API函数。
( LPSTR参数实际上是作为字节数组使用的--假设它们的大小是正确的,以便在函数外部获得正确的数据类型。另外,我认为区分指针和非指针参数还需要一些额外的复杂性,但为了这个问题,我忽略了这一点)。
我遇到的问题是将参数传递到Win32 API函数中。因为这些都是stdcall,所以我不能使用varargs,所以“调用”的实现必须事先知道参数的数量,因此它不能是泛型的.
我认为我可以通过程序集代码(通过遍历参数,将每个参数推到堆栈)来实现这一点,但是这在纯C中是可能的吗?
更新:,我已经标记了“不,这是不可能的”答案,目前已被接受。如果一个基于C的解决方案被曝光,我当然会改变这一点。
更新: 红宝石/dl看起来可能是使用合适的机制实现的。关于这方面的任何细节将不胜感激。
发布于 2009-03-09 11:09:38
不,我认为不写汇编是不可能的。原因是在调用目标函数之前,需要对堆栈上的内容进行精确控制,在纯C中没有真正的方法可以做到这一点。当然,在汇编中这样做很简单。
而且,对于所有这些参数,您都使用了PCSTR,这实际上就是const char *。但是由于所有这些arg都不是字符串,所以您实际上想要用于返回值和Arguments[]的是void *或LPVOID。这是当您不知道参数的真正类型时应该使用的类型,而不是将它们转换为char *。
发布于 2009-03-09 11:11:09
首先:在C中不能将类型作为参数传递,剩下的唯一选项是宏。
只要您执行一个LoadLibrary/GetProcAddress来调用Win32函数,这个方案就可以进行一些修改(用于参数的void*数组)。如果没有函数名字符串,那就没用了。在C中,调用函数的唯一方法是通过它的名称(标识符),在大多数情况下,该名称会衰减到指向函数的指针。您还必须处理转换返回值的问题。
我最好的选择是:
// define a function type to be passed on to the next macro
#define Declare(ret, cc, fn_t, ...) typedef ret (cc *fn_t)(__VA_ARGS__)
// for the time being doesn't work with UNICODE turned on
#define Call(dll, fn, fn_t, ...) do {\
HMODULE lib = LoadLibraryA(dll); \
if (lib) { \
fn_t pfn = (fn_t)GetProcAddress(lib, fn); \
if (pfn) { \
(pfn)(__VA_ARGS__); \
} \
FreeLibrary(lib); \
} \
} while(0)
int main() {
Declare(int, __stdcall, MessageBoxProc, HWND, LPCSTR, LPCSTR, UINT);
Call("user32.dll", "MessageBoxA", MessageBoxProc,
NULL, ((LPCSTR)"?"), ((LPCSTR)"Details"),
(MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2));
return 0;
}发布于 2009-04-08 06:49:28
其他帖子是正确的,几乎肯定需要组装或其他非标准的技巧来进行调用,更不用说实际调用约定的所有细节了。
Windows至少对函数使用两个不同的调用约定:stdcall和cdecl。您需要同时处理这两种情况,甚至可能需要确定使用哪一种。
处理此问题的一种方法是使用现有的库来封装许多细节。令人惊讶的是,有一个:利布菲。在脚本环境中使用它的一个例子是Lua 外星人的实现,它是一个Lua模块,允许在纯Lua中创建任意DLL的接口,而不是外国人本身。
https://stackoverflow.com/questions/625784
复制相似问题