Home > Software design >  Using a Delphi DLL in C - How to properly call functions?
Using a Delphi DLL in C - How to properly call functions?

Time:01-10

At work one brand of battery cyclers we are using has a proprietarily encodd data format which I would like to directly read in my scripts instead of the much larger ASCII version of the data. The manufacturer provided a DLL (no library or header) along with some Delphi example code of its use, very little documentation and an example executable (which fails to run with error 0xc000007b). Following this tutorial I managed to create a .lib and link my VS2018 project to the DLL.

With the following code I can call one of the desired functions from the 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());
}

When I step through the code, the function returns -1001 i. e. fails and an exception is thrown.

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

From the documentation I know that stdcall is used.

Other relevant information from the documentation:

function OpenDataFile(FileName: PChar): int32; function LoadAndGetNextTimeData(Handle: int32; PTimeData: pointer): int32; procedure CloseDataFile(Handle: int32);

Open the Maccor data file with OpenDataFile and get a handle returned. If the handle is equal to or larger than 0, the file is successfully open. (...) Since it is common to read the entire data file, there is a dual purpose function, LoadAndGetNextTimeData, to load the time data and get/return the most common data. All functions takes at minimum the handle returned by OpenDataFile. (...) To read the data, keep calling LoadAndGetNextTimeData as long as it returns 0. Finally, the file must be closed with CloseDataFile to free up allocated memory.

When I change the function declaration to

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

It fails to compile with 2 error codes:

LNK1120 1 unresolved externals

LNK2019 unresolved external symbol _OpenDataFile@4 referenced in function _main

I understand that this is due to C name mangling, but at this point I am stuck.

CodePudding user response:

When I change the function declaration to

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

It fails to compile with 2 error codes:

LNK1120 1 unresolved externals

LNK2019 unresolved external symbol _OpenDataFile@4 referenced in function _main

Adding __stdcall is the correct thing to do.

To solve the linker error, you can create an import .lib file using Visual Studio's lib.exe tool with the /DEF option to specify a .def file that maps the compiler's decorated symbol _OpenDataFile@4 to the undecorated symbol OpenDataFile that the DLL actually exports. See How To Create 32-bit Import Libraries Without .OBJs or Source

Alternatively, you can use MSVC's delay-loaded DLL feature instead. Continue using the .lib file you already have, and just mark the DLL as delay-loaded in your project settings. Then, in your code, you can use a delay-loaded notification hook to make the DLL function be imported using the undecorated symbol rather than the decorated symbol during the dliNotePreGetProcAddress stage.

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

CodePudding user response:

I found a piece of code in a not directly related question that works out of the box:

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;
}
  • Related