How to export a global variables/arrays from the dll built using VS compiler to the client built using MingW compiler? - c++

General info [optional]:
I have recently acquantied with static & dynamic libraries.
Now I am trying to learn how to use DLLs, I try to immitate all possible variants of usage in order to encounter possible bottlenecks and methods to prevent them.
My goal:
Is to find out how to export a global variable from a DLL which was compiled with one compiler for ex. VS compiler (IDE Visual studio 2010) to the client which was compiled using another compiler MingW(IDE Qt Creator 5.0). Actually I am interested in specific case, not common, but if info for common case will be provided - it will be great.
Also important that connection of the dll to the client is implicit(not expilcit then we manually connect library).
Also I have posed such question because it is interesting for me how to support a client`s application by providing updated dll, because version of compiler used for client and dll at the beggining of a project can be the same, but as time passes by they may vary, so how to solve this binary compatibility issue?
I got stuck trying to export an array defined in dll to the client.
DLL & Client
/* header file. Is used by both: dll and client */
#ifdef EXPORT
#define MYLIB __declspec(dllexport)
#else
#define MYLIB __declspec(dllimport)
#endif
extern "C" { // My be this directive not supported by MingW???
#ifdef VS2010
extern MYLIB char ImplicitDLLName[];
#else
Q_DECL_IMPORT extern char ImplicitDLLName[];
#endif
}
DLL
/* .cpp file in dll: */
#define EXPORT ""
#define VS2010 ""
char ImplicitDLLName[] = "MySUMoperator";
Client
/* Client .cpp */
void MainWindow::on_pushButtonAdd_clicked()
{
// ...
printf("%s",ImplicitDLLName);
}
Attempt to use the array in the Client results in the following error raised by the linker:
error: undefined reference to `_imp__ImplicitDLLName'
I am aware of names mangling and compatibilty issue that may arise from that, but I am trying to resolve it by disabeling it using
extern "C"{}
By the error returned from the clients linker I can tell that I have failed to disable it, because it reports that reference on _imp__ImplicitDLLName wasnt found, so I guess that it is ImplicitDLLName only decorated with additional symobls(name mangling).
I wonder may be this issue arose due to different implementation of arrays in different compilers or alignment of arrays in memory??
Question: how to solve this binary comptability issue??

Related

Loading DLL fails on Windows

As a Xcode developer I have to use my written code on windows, too. I think I have successful master all cross platform issues but now I have a real problem understanding the DLL hell on Windows.
I used my code with Xcode and Gcc (Ubuntu) successful. On Windows I get a error message:
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.
I read much about this uses but I have my problems to understand the issue.
Normally on windows I have something like
#define MYLIB_API __declspec(dllimport)
I cannot find this inside the header of the Bass Library (bass.h). There is only one line
#define BASSDEF(f) WINAPI f
Now, I try to dynamic load the DLL functions in my code. You can see the dynamic loading header as link on bottom. To much to copy here. This dynamic loading is working for .dylib and .so libs well, not for .dll
My target is to load the DLL dynamic and not static with an additional lib.
In my code I use the bass.h and the bassdecode.h. In my code I call as sample:
bool returnVar = _BASS_SetConfig(BASS_CONFIG_DEV_DEFAULT,1);
And here I get the calling convention message.
What I have to do in my header file to successful import DLL functions on Windows?
You can download the files at: header files to download
Ok, for all who run into the same problem, the solution is the Answer from Hans Passant. I cannot mark this answer as solution so I want to give him the reputation.
My original typedef of the function:
typedef BOOL (*BASS_SetConfig_Type)(DWORD option, DWORD value);
Was searched in DLL
_BASS_SetConfig = (BASS_SetConfig_Type)DllFindSym(m_hMod, "BASS_SetConfig")
Where DLLFindSym is defined as:
#define DllFindSym(handle,name) (GetProcAddress(handle,name))
Now changed the typedef to
typedef BOOL (__stdcall *BASS_SetConfig_Type)(DWORD option, DWORD value);
Now everything works like a charm in Windows. Many thanks to the quick hint from Hans Passant.

How to implement conditional compilation without messing up the library API?

I have a library which can do GPU computation using the OpenCL framework. Sadly, OpenCL is not available on all platforms. However I would still like to be able to compile my code on those platforms, just excluding OpenCL functionality.
I think this question applies to all situations where you want to conditionally compile some external resource which may not always be available, and it messes with your library API.
Currently I have it set up like this:
CMake:
if(ENABLE_OPENCL)
add_definitions(-DENABLE_OPEN_CL)
find_package(OpenCL REQUIRED)
include_directories(${OpenCL_INCLUDE_DIR})
target_link_libraries(mylibrary ${OpenCL_LIBRARY})
endif()
C++
// settings.hpp, exposed to public API
class settings
{
int general_setting_1;
bool general_setting_2;
// ... Other general settings
#ifdef ENABLE_OPEN_CL
int open_cl_platform_id;
// ... Other settings available only when OpenCL is available
#endif
// More settings, possibly also conditionally compiled on other external libraries
};
// computation.cpp, internal to the library
#ifdef ENABLE_OPEN_CL
#include <CL/cl.hpp>
#endif
void do_things()
{
// ...
#ifdef ENABLE_OPEN_CL
if(settings.open_cl_platform_id != -1)
{
// Call OpenCL code
}
#endif
// ...
}
So when I compile the library, if I want to enable OpenCL I do cmake .. -DENABLE_OPEN_CL.
This works, but if the client is consuming the library compiled with ENABLE_OPEN_CL, it forces the client to define the same ENABLE_OPEN_CL, otherwise the included library's header file don't match the one used in the client, and very bad things happen.
This opens a whole can of worms, for example what if the client forgets to do it? What if it uses the same identifier name for something else?
Can I avoid this? If not, is there some way I could verify that the header files match on the client and the library, and cause a compilation error? Or at least throw a run-time exception? What is the correct approach to this scenario?
The obvious way to do it, is to leave open_cl_platform_id as a member of settings even if OpenCL is not supported. The user then gets a run-time error if they try to use OpenCL functionality when the library hasn't been compiled for it.
Alternatively, have two header files settings_no_open_cl.hpp and settings_open_cl.hpp, and require the user to include the right one.

Using a .def in Visual Studio instead of dllimport/dllexport with global variables

In the PluginLoader.exe's main.cpp:
int Lives = 9;
The project also contains a .def file that exports the Lives variable (mangled C++):
EXPORTS
?Lives##3HA
Using dependency walker, I verified when opening PluginLoader.exe that ?Lives##3HA is indeed being exported. A .lib is also being exported which should contain the stubs that we can link against in other projects. Using dumpbin.exe on the stub PluginLoader.lib I get:
4 ?Lives##3HA
1 __IMPORT_DESCRIPTOR_PluginLoader
2 __NULL_IMPORT_DESCRIPTOR
4 __imp_?Lives##3HA
3 ⌂PluginLoader_NULL_THUNK_DATA
PluginLoader is loading a SimplePlugin.dll using LoadLibrary / GetProcAddress. SimplePlugin.dll has a main.cpp that looks like this:
extern int Lives;
extern "C" __declspec(dllexport) void PluginMain()
{
++Lives;
}
SimplePlugin also links against the stub PluginLoader.lib. When trying to increment Lives, I always crash with an ACCESS VIOLATION. It would appear that my SimplePlugin.dll is pseudo getting its own version of the Lives variable, even though it linked against the stub.
If I change ONLY the SimplePlugin's Lives to:
__declspec(dllimport) extern int Lives;
Everything works as expected. Why is this? I thought the purpose of a .def was to not have to use dllexport/dllimport. My current hypothesis is that dllimport with a global variable is doing some trickery behind the scenes (how does &Lives work in the dll vs the exe?). Does this have something to do with the __imp_?Lives##3HA?
Note: Importing function pointers without dllimport works just fine. Its only with global variables that I get a crash. This reproduces in VS 2010 and 2012
Sample project: https://db.tt/maV0oWop
Compiler indeed generates indirection code when you use dllimport on data.
The reason is that a DLL can only export a pointer to the exported data and dllimport does the magic of dereferencing that pointer for you. That obviously is not required for function pointers.
MS docs on that

C++11 on MicroVision 5.13 and ARMCC 5.05

I have a working uVision 5.13 project for the STM32F407 processor, I'm also using the RTX operating system and I'm trying to use some C++11 features like scoped enums but when I put the --cpp11 compiler option I receive this error from one of the cmsis headers:
compiling RTX_Conf_CM.c...
C:\Keil\ARM\PACK\ARM\CMSIS\4.2.0\CMSIS_RTX\INC\RTX_CM_lib.h(250): error: #390: function "main" may not be called or have its address taken osThreadDef_t os_thread_def_main = {(os_pthread)main, osPriorityNormal, 1, 4*OS_MAINSTKSIZE };
RTE\CMSIS\RTX_Conf_CM.c: 0 warnings, 1 error**
That´s compiling the same sources that was working just fine without the --cpp11 option.
Then if I add one of the supported C++11 features like this:
namespace TestNamespace
{
enum class Test : std::int16_t
{
TestValue1 = 0
};
class TestClass
{
//All the class code here
};
}
then I start to receive messages from windows that "The ARM C/C++ Compiler has stopped working" every time the header file containing the scoped enum is compiled. This is the problem signature in windows:
Problem Event Name: APPCRASH
Application Name: ArmCC.exe
Application Version: 5.5.0.106
Application Timestamp: 547650a9
Fault Module Name: ArmCC.exe
Fault Module Version: 5.5.0.106
Fault Module Timestamp: 547650a9
Exception Code: c0000005
Exception Offset: 003f566a
OS Version: 6.1.7601.2.1.0.256.1
Locale ID: 1033
Additional Information 1: 0a9e
Additional Information 2: 0a9e372d3b4ad19135b953a78882e789
Additional Information 3: 0a9e
Additional Information 4: 0a9e372d3b4ad19135b953a78882e789
So, I'm doing something wrong or those are ARMCC bugs??
My uVision version is 5.13 and the compiler version is 5.05 update 1 build 106.
The first error is absolutely correct, even in C++98 the practice was banned.
The compiler crash however is a ARMCC bug, regardless of your code. Even if you tried to compile an .mp3 file, it shouldn't crash.
For posterity, I've filed a bug with ARM and they told me this:
The internal fault is cause by a known issue to do with having scoped enums, and browse
information selected (--omf_browse command line option, Output->Browse Information in the
gui).
The fact the CMSIS-RTOS kernel does not compile with --cpp11 I shall raise with the technical
team as a fault.
I suppose they will fix both problems in future versions.
Did you said --cpp11 in "Misc Controls" of c/c++ page?
You said cpp11 mode to all files. To .cpp and to .c
try test.c with --cpp11:
//an C file: test.c
#ifdef __cplusplus
#error c++ mode
#endif
or see to *.obj for mangled symbols
So, it is two and a half years later and they still haven't fixed it?
So there a two things one has to do it seems.
I re-factored my program into:
int main(void){
main_rtx();
}
then in RTX_CM_lib.h, I changed line 414 to
extern int main_rtx(void);
That fixed the "address of error"
Then there are four extern "C"declarations one has to do on lines 72-76:
extern "C" OS_TID rt_tsl_self(void);
extern "C" void rt_mut_init(OS_ID mutex);
extern "C" OS_RESULT rt_mut_relase(OS_ID mutex);
extern "C" OS_RESULT rt_mut_wait(OS_ID mutex, int16_t timeout);
as well as on line 215
extern "C" void osTimerThread(void const *argument);
There may be more, but odds are that if you have a linker error about unresolved symbols, it is due to a missing "C" in the extern declarations.
This is hack fix, just for me to test C11 with exceptions on the STM32F746. I would rather put in a
#ifdef __cplusplus
extern "C" {
#endif
//external declarations
#ifdef __cplusplus
} //extern "C"
#endif
around all the external declarations.
NB. int main_rtx(void) must be declared with cpp linkage, i.e. not within an extern "C" group.

Python in C++: Unresolved external

I try to embed Python in my C++ application, but the linker keeps saying this error:
[ILINK32 Error] Error: Unresolved external '_PyModule_Create2TraceRefs' referenced from E:\CPP PROJECTS\ANDERLICHT\WIN32\DEBUG\ANDERLICHT.OBJ
I'm using Embarcadero C++ Builder XE2, so I converted the python33.lib with coff2omf.exe.
This is my code in main.cpp:
#include "anderlicht.c"
#pragma comment(lib, "python33_omf.lib")
// In main():
PyImport_AppendInittab("anderlicht",PyInit_anderlicht);
Py_SetProgramName(programName.w_str());
Py_Initialize();
In anderlicht.c the Python.h is included. What do I have to do to fix this error?
I had the same problem, but I found a solution that doesn't need rebuild.
If you are developing a new application, you are in debug mode: the compiler defines _DEBUG. In the file "pyconfig.h" (near line 336 for python 3.6.3) you can find:
#ifdef _DEBUG
#define Py_DEBUG
#endif
=> Remove this code.
If you leave that code,you are in Py_Debug mode, so in object.h triggers this:
#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS)
#define Py_TRACE_REFS
#endif
That in modsupport.h defines this alias:
#ifdef Py_TRACE_REFS
/* When we are tracing reference counts, rename module creation functions so
modules compiled with incompatible settings will generate a
link-time error. */
#define PyModule_Create2 PyModule_Create2TraceRefs
#define PyModule_FromDefAndSpec2 PyModule_FromDefAndSpec2TraceRefs
#endif
So your compiler needs a custom version of Python.
Now enjoy your standard embedded python.
The problem is most likely that you're using different compiler flags in building your code than were used in building the Python DLL. In particular, PyModule_Create2TraceRefs is only defined if you have -DPy_TRACE_REFS (which usually passed in via EXTRA_CFLAGS in the make command on Unix; I have no idea how you do it with Embarcadero C++ Builder on Windows). Usually, this isn't defined—in particular, if you're using a DLL from a pre-build Python binary, it won't have it defined.
So, if you want to have custom flags in building your code, you need to rebuild Python itself with the same flags. Otherwise, you need to get the flags that were used to build Python, and use the same ones when building your code.
On Unix, this is trivial: Just call python3.3-config --cflags and python3.3-config --ldflags to get the flags to pass to your compile and link steps. On Windows, it's less trivial. The Building C and C++ Extensions on Windows chapter in the docs explains how to do it when you're using the same toolchain used to build Python itself (usually MSVC), and if you're using mingw with its MSVC-compat features there's documentation elsewhere on how to do that… but if you're using a different toolchain, you will need to figure some of it out yourself.