Hi,
I would like to blog an article about my current work on mixing compilers. I try to add support for MinGW compiler beside my current Open Watcom compiler. While porting my base library, adding some unit tests to check my base functionality, I encountered some trouble with mixing compilers.
I especially have problems when using a DLL loaded at runtime. This is because I load nearly all my DLL’s at runtime. The function in question that fails, only fails if I do not printf something on the screen in that function. That is strange. Also I only couldn’t use Open Watcom DLL’s from MinGW code, but I can on the opposite.
The listed code and makefile should be complete to follow this issue and I hope to get some answers in forums where I write about this blog. If not, the code can be reviewed here: http://lbdmf.cvs.sourceforge.net/viewvc/lbdmf/CPP/Test/Console/testmingw/minimaltest/
To follow the code, I will explain, what code compiles to which DLL or EXE.
testdll.cpp compiles to testdll.dll using MinGW to load the DLL at start time by linking against it.
testdll.cpp compiles to testdllmingw.dll using MinGW to load the DLL at run time.
testdllow.cpp compiles to testdllow.dll using Open Watcom compiler and it is loaded at runtime.
test.cpp compiles to testow.exe using Open Watcom and testmingw.exe using MinGW.
When running these applications I’ll get the following output (as documented in code):
// Q:\develop\Projects\CPP\Test\Console\testmingw\minimaltest>testow
// Hello from test DLL. Text is ‘From text.exe’.
// Instance of Test created.
// Hello from test DLL. Text is ‘From object out of test.exe’.
// Hello from test DLL. Text is ‘p2′.
// Have true.
// Success: ITest->getInt() returns 1
// Test::release() called.
// Instance of Test deleted.
// Test MINGW DLL.
// Loading testdllmingw DLL dynamically from Open Watcom EXE:
// Instance of Test created.
// Success: ITest->getInt() returns 1
// Test if value is not NULL.
// Test if value is not NULL.
// Success: ITest->getInt() returns 1
// Test OW DLL.
// Loading testdllow DLL dynamically from Open Watcom EXE:
// Test instance from OW DLL created via gettestow.
// Test instance from OW DLL created via _gettestow.
// OW Instance of Test created.
// Success: ITest->getInt() returns 1
// Test if value is not NULL.
// Test if value is not NULL.
// Success: ITest->getInt() returns 1
//
// Q:\develop\Projects\CPP\Test\Console\testmingw\minimaltest>testmingw
// Hello from test DLL. Text is ‘From text.exe’.
// Instance of Test created.
// Hello from test DLL. Text is ‘From object out of test.exe’.
// Hello from test DLL. Text is ‘p2′.
// Have true.
// Success: ITest->getInt() returns 1
// Test::release() called.
// Instance of Test deleted.
// Test MINGW DLL.
// Loading testdllmingw DLL dynamically from MINGW32 EXE:
// Instance of Test created.
// Success: ITest->getInt() returns 1
// Test if value is not NULL.
// Test if value is not NULL.
// Success: ITest->getInt() returns 1
// Test OW DLL.
// Loading testdllow DLL dynamically from MINGW32 EXE:
// Test instance from OW DLL created via gettestow.
// Test instance from OW DLL created via _gettestow.
// OW Instance of Test created.
// Error: ITest->getInt() doesn’t return 1
// Test if value is not NULL.
// Test if value is not NULL.
// Success: ITest->getInt() returns 1
Here is the code I am using to test this behaviour:
itest.h:
#define API __stdcall
//#define API __cdecl
enum lbErrCodes {
ERR_NONE = 0,
ERR_FAIL = 1,
ERR_SOWISO = 2
};
class ITest {
public:
virtual lbErrCodes API getInt(char* _int, bool showmsg) = 0;
virtual bool API getbool() = 0;
virtual void API test(char* text, char* p2) = 0;
virtual void API release() = 0;
};
owtestdll.h:
#undef DLLEXPORT
#ifdef OWDLL
#define DLLEXPORT __declspec(dllexport)
#endif
#ifndef OWDLL
#define DLLEXPORT __declspec(dllimport)
#endif
#ifdef __WATCOMC__
extern “C” DLLEXPORT ITest* API gettestow();
#endif
#ifdef __MINGW32__
extern “C” DLLEXPORT ITest* API _gettestow();
extern “C” DLLEXPORT ITest* API gettestow();
#endif
testdll.h:
#undef DLLEXPORT
#ifdef DLL
#define DLLEXPORT __declspec(dllexport)
#endif
#ifndef DLL
#define DLLEXPORT __declspec(dllimport)
#endif
#ifdef __WATCOMC__
extern “C” void DLLEXPORT API test(char* text);
#endif
#ifdef __MINGW32__
extern “C” void DLLEXPORT API _test(char* text);
extern “C” void DLLEXPORT API test(char* text);
#endif
#ifdef __WATCOMC__
extern “C” bool DLLEXPORT API getbool();
#endif
#ifdef __MINGW32__
extern “C” bool DLLEXPORT API _getbool();
extern “C” bool DLLEXPORT API getbool();
#endif
#ifdef __WATCOMC__
extern “C” DLLEXPORT ITest* API gettest();
#endif
#ifdef __MINGW32__
extern “C” DLLEXPORT ITest* API _gettest();
extern “C” DLLEXPORT ITest* API gettest();
#endif
testdll.cpp:
#define DLL
#include “itest.h”
#include “testdll.h”
#include <stdio.h>
class Test : public ITest {
public:
lbErrCodes API getInt(char* _int, bool showmsg);
bool API getbool();
void API test(char* text, char* p2);
void API release();
public:
Test();
virtual ~Test();
};
extern “C” DLLEXPORT ITest* API gettest() {
return _gettest();
}
extern “C” DLLEXPORT ITest* API _gettest() {
return new Test();
}
void test_impl(char* text) {
printf(“Hello from test DLL. Text is ‘%s’.\n”, text);
}
Test::Test() {
printf(“Instance of Test created.\n”);
}
Test::~Test() {
printf(“Instance of Test deleted.\n”);
}
bool API Test::getbool() {
return true;
}
lbErrCodes API Test::getInt(char* _int, bool showmsg) {
if (showmsg) printf(“Test if value is not NULL.\n”);
if (_int == NULL) return ERR_FAIL;
return ERR_NONE;
}
void API Test::test(char* text, char* p2) {
test_impl(text);
test_impl(p2);
}
void API Test::release() {
printf(“Test::release() called.\n”);
delete this;
}
DLLEXPORT void API _test(char* text) {
test_impl(text);
}
DLLEXPORT bool API _getbool() {
return true;
}
DLLEXPORT void API test(char* text) {
test_impl(text);
}
DLLEXPORT bool API getbool() {
return _getbool();
}
testdllow.cpp:
#define OWDLL
#include “itest.h”
#include “owtestdll.h”
#include <stdio.h>
class Test : public ITest {
public:
lbErrCodes API getInt(char* _int, bool showmsg);
bool API getbool();
void API test(char* text, char* p2);
void API release();
public:
Test();
virtual ~Test();
};
extern “C” DLLEXPORT ITest* API _gettestow() {
printf(“Test instance from OW DLL created via _gettestow.\n”);
return new Test();
}
extern “C” DLLEXPORT ITest* API gettestow() {
printf(“Test instance from OW DLL created via gettestow.\n”);
return _gettestow();
}
void test_impl(char* text) {
printf(“Hello from test DLL. Text is ‘%s’.\n”, text);
}
Test::Test() {
printf(“OW Instance of Test created.\n”);
}
Test::~Test() {
printf(“OW Instance of Test deleted.\n”);
}
bool API Test::getbool() {
return true;
}
lbErrCodes API Test::getInt(char* _int, bool showmsg) {
if (showmsg) printf(“Test if value is not NULL.\n”);
if (_int == NULL) return ERR_FAIL;
return ERR_NONE;
}
void API Test::test(char* text, char* p2) {
test_impl(text);
test_impl(p2);
}
void API Test::release() {
printf(“OW Test::release() called.\n”);
delete this;
}
test.cpp:
#include “itest.h”
#include “testdll.h”
#include “owtestdll.h”
#include <stdio.h>
#include <windows.h>
typedef ITest* (API *Functor)();
Functor getFunctor(char* name, char* dll) {
HINSTANCE hinst;
Functor functor;
hinst = LoadLibrary(dll);
if (hinst != NULL) {
functor = (Functor) GetProcAddress(hinst, name);
if (functor != NULL) {
return functor;
} else {
return NULL;
}
}
printf(“Error: Couln’t find DLL.\n”);
return NULL;
}
void testDynamicLoading(char* dll) {
Functor functor;
functor = getFunctor(“gettestow”, dll);
if (functor == NULL) {
functor = getFunctor(“_gettestow”, dll);
}
if (functor == NULL) {
functor = getFunctor(“gettestow@0″, dll);
}
if (functor == NULL) {
functor = getFunctor(“_gettestow@0″, dll);
}
if (functor == NULL) {
functor = getFunctor(“gettest”, dll);
}
if (functor == NULL) {
functor = getFunctor(“_gettest”, dll);
}
if (functor == NULL) {
functor = getFunctor(“gettest@0″, dll);
}
if (functor == NULL) {
functor = getFunctor(“_gettest@0″, dll);
}
if (functor != NULL) {
// Test loading Open Watcom DLL from application dynamically
#ifdef __MINGW32__
printf(“Loading %s DLL dynamically from MINGW32 EXE:\n”, dll);
#endif
#ifdef __WATCOMC__
printf(“Loading %s DLL dynamically from Open Watcom EXE:\n”, dll);
#endif
ITest* d = functor();
if (d == NULL) {
printf(“Couldn’t load object.\n”);
return;
}
if (d->getInt(NULL, false) != ERR_FAIL) printf(“Error: ITest->getInt() doesn’t return %d\n”, ERR_FAIL);
if (d->getInt(NULL, false) == ERR_FAIL) printf(“Success: ITest->getInt() returns %d\n”, ERR_FAIL);
if (d->getInt(NULL, true) != ERR_FAIL) printf(“Error: ITest->getInt() doesn’t return %d\n”, ERR_FAIL);
if (d->getInt(NULL, true) == ERR_FAIL) printf(“Success: ITest->getInt() returns %d\n”, ERR_FAIL);
} else {
printf(“Error: Couln’t find functor.\n”);
}
}
int main() {
test(“From text.exe”);
ITest* t = gettest();
t->test(“From object out of test.exe”, “p2″);
if (getbool()) printf(“Have true.\n”);
if (t->getInt(NULL, false) != ERR_FAIL) printf(“Error: ITest->getInt() doesn’t return %d\n”, ERR_FAIL);
if (t->getInt(NULL, false) == ERR_FAIL) printf(“Success: ITest->getInt() returns %d\n”, ERR_FAIL);
t->release();
printf(“Test MINGW DLL.\n”);
testDynamicLoading(“testdllmingw”);
printf(“Test OW DLL.\n”);
testDynamicLoading(“testdllow”);
return 0;
}
all: testdllow.dll testdll.dll testdllmingw.dll testow.exe testmingw.exe
CALL=-5r
testdll.o: testdll.cpp testdll.h
g++ -c testdll.cpp
test.obj: test.cpp testdll.h
wpp386 -DWINDOWS -w3 -e25 -zq -otexan $(CALL) -of+ -xr /DLBDMF_PRE test.cpp
testdllmingw.o: testdll.cpp testdll.h
g++ -c testdll.cpp -o testdllmingw.o
testdllmingw.dll: testdllmingw.o
g++ -shared -o testdllmingw.dll testdllmingw.o
wlib -q -n -b testdllmingw.lib +testdllmingw.dll
wlib -m testdllmingw.dll > exportsmingw.txt
testdllow.obj: testdllow.cpp testdll.h
wpp386 -DWINDOWS -bd -hd -w3 -e25 -zq -otexan $(CALL) -of+ -xr /DLBDMF_PRE testdllow.cpp
testdllow.dll.lnk: makefile testdllow.obj
echo NAME testdllow.dll > $@
echo FIL testdllow.obj >> $@
testdllow.dll: testdllow.obj testdllow.dll.lnk
@echo Link testdllow.dll
wlink op q op symf d dwarf SYS nt_dll op m op maxe=25 @testdllow.dll.lnk
@wlib -q -n -b testdllow.lib +testdllow.dll
test.o: test.cpp testdll.h
g++ -c test.cpp
testdll.dll: testdll.o
g++ -shared -o testdll.dll testdll.o
wlib -q -n -b testdll.lib +testdll.dll
wlib -m testdll.dll > exports.txt
testow.exe: test.obj
echo Link testow.exe
echo NAME testow.exe > test.lnk
echo FIL test.obj,testdll.lib >> test.lnk
cmd /C “attrib -r *.bak”
wlink op q op symf d dwarf @test.lnk
testmingw.exe: test.o
rm testdll.lib
g++ -o testmingw.exe test.o -L. -ltestdll
clean:
-del *.err *.exe *.obj *.o *.dll *.bak *.sym *.lnk *.map *.lib exports.txt exportsmingw.txt
-rm *.err *.exe *.obj *.o *.dll *.bak *.sym *.lnk *.map *.lib exports.txt exportsmingw.txt
makefile:
all: testdllow.dll testdll.dll testdllmingw.dll testow.exe testmingw.exe
CALL=-5r
testdll.o: testdll.cpp testdll.h
g++ -c testdll.cpp
test.obj: test.cpp testdll.h
wpp386 -DWINDOWS -w3 -e25 -zq -otexan $(CALL) -of+ -xr /DLBDMF_PRE test.cpp
testdllmingw.o: testdll.cpp testdll.h
g++ -c testdll.cpp -o testdllmingw.o
testdllmingw.dll: testdllmingw.o
g++ -shared -o testdllmingw.dll testdllmingw.o
wlib -q -n -b testdllmingw.lib +testdllmingw.dll
wlib -m testdllmingw.dll > exportsmingw.txt
testdllow.obj: testdllow.cpp testdll.h
wpp386 -DWINDOWS -bd -hd -w3 -e25 -zq -otexan $(CALL) -of+ -xr /DLBDMF_PRE testdllow.cpp
testdllow.dll.lnk: makefile testdllow.obj
echo NAME testdllow.dll > $@
echo FIL testdllow.obj >> $@
testdllow.dll: testdllow.obj testdllow.dll.lnk
@echo Link testdllow.dll
wlink op q op symf d dwarf SYS nt_dll op m op maxe=25 @testdllow.dll.lnk
@wlib -q -n -b testdllow.lib +testdllow.dll
test.o: test.cpp testdll.h
g++ -c test.cpp
testdll.dll: testdll.o
g++ -shared -o testdll.dll testdll.o
wlib -q -n -b testdll.lib +testdll.dll
wlib -m testdll.dll > exports.txt
testow.exe: test.obj
echo Link testow.exe
echo NAME testow.exe > test.lnk
echo FIL test.obj,testdll.lib >> test.lnk
cmd /C “attrib -r *.bak”
wlink op q op symf d dwarf @test.lnk
testmingw.exe: test.o
rm testdll.lib
g++ -o testmingw.exe test.o -L. -ltestdll
clean:
-del *.err *.exe *.obj *.o *.dll *.bak *.sym *.lnk *.map *.lib exports.txt exportsmingw.txt
-rm *.err *.exe *.obj *.o *.dll *.bak *.sym *.lnk *.map *.lib exports.txt exportsmingw.txt