我想到了一个解决方案,应该是可移植的(但我还没有测试过):
#define ID_PATTERN 0x11223344
#define SIZE_OF_BLUEPRINT 128
typedef int (__cdecl * UNIQUE_CALLBACK)(int arg);
int uniqueCallbackBlueprint(int arg)
{
int id = ID_PATTERN;
printf("%x: Hello unique callback (arg=%d)...\n", id, arg);
return (id);
}
UNIQUE_CALLBACK createUniqueCallback(int id)
{
UNIQUE_CALLBACK result = NULL;
char *pUniqueCallback;
char *pFunction;
int pattern = ID_PATTERN;
char *pPattern;
char *startOfId;
int i;
int patterns = 0;
pUniqueCallback = malloc(SIZE_OF_BLUEPRINT);
if (pUniqueCallback != NULL)
{
pFunction = (char *)uniqueCallbackBlueprint;
#if defined(_DEBUG)
pFunction += 0x256;
#endif
memcpy(pUniqueCallback, pFunction, SIZE_OF_BLUEPRINT);
result = (UNIQUE_CALLBACK)pUniqueCallback;
pPattern = (char *)&pattern;
startOfId = NULL;
for (i = 0; i < SIZE_OF_BLUEPRINT; i++)
{
if (pUniqueCallback[i] == *pPattern)
{
if (pPattern == (char *)&pattern)
startOfId = &(pUniqueCallback[i]);
if (pPattern == ((char *)&pattern) + sizeof(int) - 1)
{
pPattern = (char *)&id;
for (i = 0; i < sizeof(int); i++)
{
*startOfId++ = *pPattern++;
}
patterns++;
break;
}
pPattern++;
}
else
{
pPattern = (char *)&pattern;
startOfId = NULL;
}
}
printf("%d pattern(s) replaced\n", patterns);
if (patterns == 0)
{
free(pUniqueCallback);
result = NULL;
}
}
return (result);
}
使用方法如下:
int main(void)
{
UNIQUE_CALLBACK callback;
int id;
int i;
id = uniqueCallbackBlueprint(5);
printf(" -> id = %x\n", id);
callback = createUniqueCallback(0x4711);
if (callback != NULL)
{
id = callback(25);
printf(" -> id = %x\n", id);
}
id = uniqueCallbackBlueprint(15);
printf(" -> id = %x\n", id);
getch();
return (0);
}
我发现一个有趣的行为,如果使用调试信息编译(Visual Studio),则pFunction = (char *)uniqueCallbackBlueprint;
获取的地址会偏移若干字节。可以使用显示正确地址的调试器来获得此差异。这个偏移量从构建到构建不同,我认为这与调试信息有关?对于发布版本没有问题。因此,也许应该将其放入作为“发布”构建的库中。
另一件要考虑的事情是pUniqueCallback
的字节对齐可能会成为问题。但是,将函数开头对齐到64位边界并不难添加到此代码中。
在pUniqueCallback
内,您可以实现任何您想要的内容(注意更新SIZE_OF_BLUEPRINT以便不会错过函数尾部)。函数被编译并且生成的代码在运行时被重复使用。创建唯一函数时,id的初始值被替换,因此原始函数可以处理它。