c++ - 为什么使用可变参数调用 DLL 检索到错误的结果,并且使用额外未使用的参数解决了问题
问题描述
平台:x64;配置:调试;在 x86 下无法重现。
编译器:用于 EXE 和 DLL 的 msvc。
嗨,我最近在开发 DLL 时遇到了一个问题。当我的示例程序尝试使用可变参数调用 DLL 导出函数时,我发现当我尝试以我想要的方式调用它时,它检索到的值与我传入的值不同。在一次随机尝试中,我添加了一个unused
参数,并令人惊讶地发现它在执行此操作后检索到了正确的值。
虽然我认为我的目标可以通过shapeType
将最后一个命名参数作为可变参数部分的第一个参数来实现,但我仍然想知道为什么会这样。
我已将代码简化如下(省略包括在内)并记录了我在图像的地址视图中找到的内容。图像中的红线表示地址args
( va_list
,实际上char*
是在编译时由宏定义的) 指向的地址。我已经向上移动了几行,以便也可以看到其他论点。由于在这段代码中重现问题不需要有意义的HWND
值,我只是在那里放了一个随机数。如果您需要包括项目文件在内的整个源文件包,请与我联系。
如果您能帮助我,那就太好了,在此先感谢。
exe.cpp
#define UNUSED 1
#if UNUSED
typedef int(__cdecl* ADDLINE)(HWND hWnd, int shapeType, int unused, int fillType, float boldness, float a, float r, float g, float b, float x1, float y1, float x2, float y2);
#else
typedef int(__cdecl* ADDLINE)(HWND hWnd, int shapeType, int fillType, float boldness, float a, float r, float g, float b, float x1, float y1, float x2, float y2);
#endif
int main() {
HINSTANCE hinstLib;
hinstLib = LoadLibrary(L"dll.dll");
ADDLINE addLine = (ADDLINE)GetProcAddress(hinstLib, "addShape");
#if UNUSED
addLine((HWND)0x00000000000b11bc, 16, 2, 1, 10.0f, 127.0f, 255.0f, 0.0f, 0.0f, 0.0f, 0.0f, 500.0f, 500.0f);
#else
addLine((HWND)0x00000000000b11bc, 16, 1, 10.0f, 127.0f, 255.0f, 0.0f, 0.0f, 0.0f, 0.0f, 500.0f, 500.0f);
#endif
return 0;
}
dll.cpp
#define UNUSED 1
extern "C" __declspec(dllexport) int addShape(HWND hWnd, int shapeType, ...) {
va_list args;
va_start(args, shapeType);
#if UNUSED
va_arg(args, int); //unused
#endif
va_arg(args, int); //first int
std::cout<< va_arg(args, float); //first float
va_end(args);
return 0;
}
文字版
0x00000062C7B6F7C0 bc 11 0b 00 00 00 00 00
0x00000062C7B6F7C8 10 00 00 00 ff 7f 00 00
0x00000062C7B6F7D0 01 00 00 00 00 00 00 00
0x00000062C7B6F7D8 00 00 00 00 00 00 00 00
0x00000062C7B6F7E0 00 00 fe 42 cc cc cc cc
0x00000062C7B6F7E8 00 00 7f 43 cc cc cc cc
UNUSED=0 (without unused), output=0
0x000000FFC20FF600 bc 11 0b 00 00 00 00 00
0x000000FFC20FF608 10 00 00 00 ff 7f 00 00
0x000000FFC20FF610 02 00 00 00 00 00 00 00
0x000000FFC20FF618 01 00 00 00 00 00 00 00
0x000000FFC20FF620 00 00 20 41 cc cc cc cc
0x000000FFC20FF628 00 00 fe 42 cc cc cc cc
UNUSED=1 (with unused), output=10
IEEE-754 Floating Point: 0x41200000==10.0
更新 2
dll.cppextern "C" __declspec(dllexport) int addShape(HWND hWnd, int shapeType, ...) {
va_list args;
va_start(args, shapeType);
va_arg(args, int); //first int
std::cout<< va_arg(args, float); //first float
va_end(args);
return 0;
}
exe.cpp
extern "C" typedef int(__cdecl* ADDLINE)(HWND hWnd, int shapeType, ...);
int main() {
HINSTANCE hinstLib;
hinstLib = LoadLibrary(L"dll.dll");
ADDLINE addLine = (ADDLINE)GetProcAddress(hinstLib, "addShape");
addLine((HWND)0x00000000000b11bc, 16, 1, 10.0f, 127.0f, 255.0f, 0.0f, 0.0f, 0.0f, 0.0f, 500.0f, 500.0f);
return 0;
}
更新 1
我在评论中尝试了一些建议,但我不确定我是否做得正确。把文件exe.cpp
改成下面那个后,除了UNUSED=1还是不行。注意:UNUSED in如果是in则dll.cpp
设置为 ,否则设置为。1
1
exe.cpp
0
#define UNUSED 0
extern "C"{
#if UNUSED==1
typedef int(__cdecl* ADDLINE)(HWND hWnd, int shapeType, int unused, int fillType, float boldness, float a, float r, float g, float b, float x1, float y1, float x2, float y2);
#elif UNUSED==2
typedef int(__cdecl* ADDLINE)(HWND hWnd, int shapeType, ...);
#else
typedef int(__cdecl* ADDLINE)(HWND hWnd, int shapeType, int fillType, float boldness, float a, float r, float g, float b, float x1, float y1, float x2, float y2);
#endif
}
int main() {
HINSTANCE hinstLib;
hinstLib = LoadLibrary(L"dll.dll");
ADDLINE addLine = (ADDLINE)GetProcAddress(hinstLib, "addShape");
#if UNUSED==1
addLine((HWND)0x00000000000b11bc, 16, 2, 1, 10.0f, 127.0f, 255.0f, 0.0f, 0.0f, 0.0f, 0.0f, 500.0f, 500.0f);
#else
addLine((HWND)0x00000000000b11bc, 16, 1, 10.0f, 127.0f, 255.0f, 0.0f, 0.0f, 0.0f, 0.0f, 500.0f, 500.0f);
#endif
return 0;
}
解决方案
推荐阅读
- javascript - html body的背景颜色效果按钮的背景颜色
- javascript - 使用 Javascript 在页面加载时随机化背景视频
- python - 带有 scikit-learn 版本 0.19.2 的管道回归器集成
- flutter - 在颤动中将图像黑色反转为白色
- python - 解释 tf 模型结果的最佳方法
- php - Azure Cosmos DB 的 PHP REST 身份验证令牌
- javascript - 如何在 HTML 中的用户输入上更改\交换多个 CSS 文件?
- linux - 停止多个 PID
- r - R Shiny Application中的材料设计,折叠和展开卡片
- java - 测试显示文件未找到,尽管文件在那里