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