首页 > 解决方案 > 为什么使用可变参数调用 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.cpp
extern "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设置为 ,否则设置为。11exe.cpp0

#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;
}

标签: c++memoryvisual-c++dllvariadic-functions

解决方案


推荐阅读