许多 Win32 API 采用指向具有特定布局的结构的指针。其中,一个大子集遵循一个通用模式,其中第一个 DWORD 必须在调用之前初始化为具有结构的大小。有时他们需要传递一块内存,他们将向其中写入一个结构,并且内存块的大小必须通过首先使用 NULL 指针调用相同的 API 并读取返回值以发现正确的尺寸。一些 API 分配一个结构并返回一个指向它的指针,因此必须在第二次调用时释放该指针。
如果可以一次有效地调用的 API 集(可以从简单的字符串表示形式转换的单个参数)非常小,我不会感到惊讶。
为了使这个想法普遍适用,我们必须走极端:
typedef void DynamicFunction(size_t argumentCount, const wchar_t *arguments[],
size_t maxReturnValueSize, wchar_t *returnValue);
DynamicFunction *GenerateDynamicFunction(const wchar_t *code);
您可以将一个简单的代码片段传递给 GenerateDynamicFunction,它会将该代码包装在一些标准样板文件中,然后调用 C 编译器/链接器以从中生成一个包含该函数的 DLL(有很多免费选项可用)。然后它会LoadLibrary
使用那个 DLLGetProcAddress
来查找函数,然后返回它。这会很昂贵,但您会执行一次并缓存生成的 DynamicFunctionPtr 以供重复使用。您可以通过将指针保存在哈希表中来动态地执行此操作,并由代码片段本身键入。
样板文件可能是:
#include <windows.h>
// and anything else that might be handy
void DynamicFunctionWrapper(size_t argumentCount, const wchar_t *arguments[],
size_t maxReturnValueSize, wchar_t *returnValue)
{
// --- insert code snipped here
}
因此,该系统的示例用法是:
DynamicFunction *getUserName = GenerateDynamicFunction(
"GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))");
wchar_t userName[100];
getUserName(0, NULL, sizeof(userName) / sizeof(wchar_t), userName);
您可以通过GenerateDynamicFunction
接受参数计数来增强这一点,因此它可以在包装器的开头生成检查是否已传递正确数量的参数。如果你在其中放置一个哈希表来缓存每个遇到的代码片段的函数,你可以接近你的原始示例。Call 函数将采用代码片段而不仅仅是 API 名称,但在其他方面是相同的。它会在哈希表中查找代码片段,如果不存在,它将调用 GenerateDynamicFunction 并将结果存储在哈希表中以备下次使用。然后它将执行对该函数的调用。示例用法:
wchar_t userName[100];
Call("GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))",
0, NULL, sizeof(userName) / sizeof(wchar_t), userName);
当然,除非这个想法是为了打开某种一般的安全漏洞,否则这样做没有多大意义。例如作为网络服务公开Call
。您最初的想法存在安全隐患,但不太明显,因为您建议的原始方法不会那么有效。我们制造的功能越强大,安全问题就越大。
根据评论更新:
.NET 框架有一个称为 p/invoke 的功能,它的存在正是为了解决您的问题。因此,如果您将其作为一个学习项目的项目,您可以查看 p/invoke 以了解它的复杂程度。您可以使用您的脚本语言来定位 .NET 框架 - 您可以将它们编译为 IL,而不是实时解释脚本或将它们编译为您自己的字节码。或者,您可以托管 .NET 上已有的许多现有脚本语言。