在C++中使用 Delphi DLL-如何正确调用函数?

mznpcxlj  于 2023-01-08  发布在  其他
关注(0)|答案(1)|浏览(256)

在工作中,我们正在使用的一个品牌的电池循环仪有一个专有的编码数据格式,我想直接读取我的脚本,而不是更大的ASCII版本的数据。制造商提供了一个DLL(没有库或头)连同一些 Delphi 示例代码一起使用,很少的文档和一个示例可执行文件(运行失败,错误为0xc 000007 b)。在this tutorial之后,我设法创建了一个.lib并将我的VS 2018项目链接到DLL。
使用下面的代码,我可以从DLL调用所需的函数之一:

#pragma comment(lib, "MacReadDataFileLIB.lib")

#include <string>

extern "C" int OpenDataFile(const char*);

int main()
{
    std::string path = "K:\\Testfile.036";
    auto test = OpenDataFile(path.c_str());
}

当我单步执行代码时,函数返回-1001,即失败并抛出异常。
运行时检查失败#0 -未在函数调用中正确保存ESP的值。这通常是由于调用了用一种调用约定声明的函数,而函数指针用另一种调用约定声明。
从文档中我知道使用了stdcall
文件中的其他相关信息:
函数打开数据文件(文件名:PChar):整数32;函数加载并获取下一时间数据(句柄:整数32; P时间数据:指针):整数32;程序关闭数据文件(句柄:整数32);
用OpenDataFile打开Maccor数据文件,返回一个句柄,如果句柄大于等于0,则文件打开成功。(...)由于读取整个数据文件是常见的,因此有一个双重用途函数LoadAndGetNextTimeData,加载时间数据并获取/返回最常用的数据。所有函数至少使用OpenDataFile返回的句柄。(...)若要读取数据,请一直调用LoadAndGetNextTimeData,直到它返回0。最后,必须使用CloseDataFile关闭文件以释放分配的内存。
当我将函数声明更改为

extern "C" int __stdcall OpenDataFile(const char*);

编译失败,出现2个错误代码:
LNK 1120 1未解决的外部问题
LNK 2019函数_main中引用了无法解析的外部符号_OpenDataFile@4
我知道这是由于C++名称损坏,但在这一点上我卡住了。

**Edit 1:**在not directly related question中找到一段开箱即用的代码:

typedef int(__stdcall* tMyFunction)(const char* filename);

int main(int argc, char* argv[]) {

    std::string path = "K:\\Testfile.036";

    HINSTANCE m_dllHandle = LoadLibrary("MacReadDataFileLIB.dll");
    tMyFunction function = (tMyFunction)GetProcAddress(m_dllHandle, "OpenDataFileASCII");
    int value = function(path.c_str());

    FreeLibrary(m_dllHandle);
    m_dllHandle = NULL;

    return 0;
}
vjrehmav

vjrehmav1#

当我将函数声明更改为

extern "C" int __stdcall OpenDataFile(const char*);

编译失败,出现2个错误代码:

LNK1120 1 unresolved externals

LNK2019 unresolved external symbol _OpenDataFile@4 referenced in function _main

添加__stdcall是正确的做法。
若要解决链接器错误,可以使用Visual Studio的lib.exe工具创建导入.lib文件,该工具带有/DEF选项,以指定一个.def文件,该文件将编译器的修饰符号_OpenDataFile@4Map到DLL实际导出的未修饰符号OpenDataFile。请参见How To Create 32-bit Import Libraries Without .OBJs or Source
或者,你可以使用MSVC的delay-loaded DLL特性。继续使用你已有的.lib文件,并在你的项目设置中将DLL标记为延迟加载。然后,在你的代码中,你可以使用一个延迟加载的通知钩子,使DLL函数在dliNotePreGetProcAddress阶段使用未修饰的符号而不是修饰的符号导入。

#include <string>
#include <cstring>
#include <delayimp.h>
#pragma comment(lib, "MacReadDataFileLIB.lib")

ExternC int __stdcall OpenDataFile(const char*);

FARPROC WINAPI myDelayLoadHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
    if ((dliNotify == dliNotePreGetProcAddress) &&
        (std::strcmp(pdli->szDll, "MacReadDataFileLIB.dll") == 0) &&
        pdli->dlp.fImportByName &&
        (std::strcmp(pdli->dlp.szProcName, "_OpenDataFile@4") == 0))
    {
        return (FARPROC) GetProcAddress(pdli->hmodCur, "OpenDataFile");
    }
    return NULL;
}

extern "C" const PfnDliHook __pfnDliNotifyHook2 = myDelayLoadHook;

int main()
{
    std::string path = "K:\\Testfile.036";
    auto test = OpenDataFile(path.c_str());
}

相关问题