为了允许从脚本语言(用C编写)访问Win32 API,我想编写如下函数:
void Call(LPCSTR DllName, LPCSTR FunctionName, LPSTR ReturnValue, USHORT ArgumentCount, LPSTR Arguments[])
一般来说,它会调用任何Win32 API函数.
(LPSTR参数基本上用作字节数组 - 假设它们的大小正确,以便在函数外部采用正确的数据类型.另外我认为需要一些额外的复杂性来区分指针和非指针参数但我为了这个问题的目的,我忽略了这一点.
我遇到的问题是将参数传递给Win32 API函数.因为这些是stdcall我不能使用varargs所以'Call'的实现必须提前知道参数的数量,因此它不能是通用的...
我想我可以使用汇编代码(通过循环遍历参数,将每个推送到堆栈)来实现这一点但是在纯C中这是可能的吗?
更新:我已经标记了现在接受的'不可能'答案.如果基于C的解决方案曝光,我当然会改变这一点.
更新: ruby/dl看起来可能使用合适的机制实现.任何有关这方面的细节将不胜感激.
首先要做的事情是:你不能在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; }
不,我不认为没有写一些装配就可以做到.原因是你需要在调用目标函数之前精确控制堆栈上的内容,并且在纯C中没有真正的方法可以做到这一点.当然,在汇编中这很简单.
此外,您正在将PCSTR用于所有这些参数,这实际上就是这样const char *
.但由于所有这些args都不是字符串,你实际上想要用于返回值和Arguments []的是void *
或者LPVOID
.当您不知道参数的真实类型而不是将它们转换为时,应该使用此类型char *
.