Problems linking a BCB-DLL (.lib) under MSVC

G

Guest

Hello,

I'm trying to use a DLL (by static linking) which was compiled with Borland
C++Builder (BCB) in Visual C++ (Visual-Studio 2003).

All functions are declared with the directive 'extern "C"' so that a
cross-compiler-usage must be possible.

I created an import library (.lib) for MSVC by using the LIB function and
using a .DEF file (as described in KB131313 -
http://support.microsoft.com/default.aspx?scid=kb;en-us;131313)

My DUMPBIN showed the following exports:
ordinal hint RVA name
2 0 000012E4 @SayHello2
1 1 000012D0 SayHello1
3 2 000012F8 _SayHello3
4 3 0000130C _SayHello4
5 4 000090F8 ___CPPdebugHook

So I created a .DEF as follows:
EXPORTS
@SayHello2
SayHello1
_SayHello3
_SayHello4

The .lib and .exp generation succeeded so far.

The declaration (.h) used in BCB and included in MSVC later on is as follows:
#ifndef __BCB2VC_DLL_H
#define __BCB2VC_DLL_H

#ifdef __BUILD_DLL__
#define __DECL_SPEC__ __declspec(dllexport)
#else
#define __DECL_SPEC__ __declspec(dllimport)
#endif //#ifdef __BUILD_DLL__

extern "C" __DECL_SPEC__ void __stdcall SayHello1(void);
extern "C" __DECL_SPEC__ void __fastcall SayHello2();
extern "C" __DECL_SPEC__ void __cdecl SayHello3();
extern "C" __DECL_SPEC__ void SayHello4();

#endif //#ifndef __BCB2VC_DLL_H

In my MSVC application I simply try to call the functions:
int _tmain(int argc, _TCHAR* argv[])
{
SayHello1();
SayHello2();
SayHello3();
SayHello4();
}

This lead to the result, that I received the following linker errors:
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp__SayHello4', verwiesen in Funktion '_main'
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp__SayHello3', verwiesen in Funktion '_main'
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp_@SayHello2@0', verwiesen in Funktion '_main'
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp__SayHello1@0', verwiesen in Funktion '_main'
Debug/USE_BCB_DLL.exe : fatal error LNK1120: 4 unaufgelöste externe Verweise

---

when I create the .LIB with the .DEF like:
EXPORTS
SayHello2
SayHello1
SayHello3
SayHello4

and call the functions with (calling SayHello1 and SayHello2 doesn't succeed
at all):
int _tmain(int argc, _TCHAR* argv[])
{
//SayHello1();
//SayHello2();
SayHello3();
SayHello4();
}

then my application starts (which means linking succeeds). But while
starting, a MessageBox appears, which says:
'The entrypoint of "SayHello3" in "Test.dll" could not be found.'

So what is wrong here? How do I manage to get to get my DLL run without
changing the calling conventions (__stdcall / __fastcall) ?

Thanks,
Uli
 
T

Tamas Demjen

I think __fastcall is Borland specific, it uses registers to pass
function arguments, and it's not compatible with Microsoft. The two
others must work (__stdcall and __cdecl). I think in the DEF file you
just have to list the names, without underscores:

EXPORTS
SayHello1
SayHello3
SayHello4

the extern "C" keyword ensures that all mangling of function names is
turned off, so you just use the real name of the funciton, in plain
ASCII. No @, no _.

I usually use LoadLibrary and GetProcAddress, and this always works very
nicely between compilers (BCB and VC++), in both directions (VC++ DLL
and BCB app; BCB DLL and VC++ app).

Tom
Hello,

I'm trying to use a DLL (by static linking) which was compiled with Borland
C++Builder (BCB) in Visual C++ (Visual-Studio 2003).

All functions are declared with the directive 'extern "C"' so that a
cross-compiler-usage must be possible.

I created an import library (.lib) for MSVC by using the LIB function and
using a .DEF file (as described in KB131313 -
http://support.microsoft.com/default.aspx?scid=kb;en-us;131313)

My DUMPBIN showed the following exports:
ordinal hint RVA name
2 0 000012E4 @SayHello2
1 1 000012D0 SayHello1
3 2 000012F8 _SayHello3
4 3 0000130C _SayHello4
5 4 000090F8 ___CPPdebugHook

So I created a .DEF as follows:
EXPORTS
@SayHello2
SayHello1
_SayHello3
_SayHello4

The .lib and .exp generation succeeded so far.

The declaration (.h) used in BCB and included in MSVC later on is as follows:
#ifndef __BCB2VC_DLL_H
#define __BCB2VC_DLL_H

#ifdef __BUILD_DLL__
#define __DECL_SPEC__ __declspec(dllexport)
#else
#define __DECL_SPEC__ __declspec(dllimport)
#endif //#ifdef __BUILD_DLL__

extern "C" __DECL_SPEC__ void __stdcall SayHello1(void);
extern "C" __DECL_SPEC__ void __fastcall SayHello2();
extern "C" __DECL_SPEC__ void __cdecl SayHello3();
extern "C" __DECL_SPEC__ void SayHello4();

#endif //#ifndef __BCB2VC_DLL_H

In my MSVC application I simply try to call the functions:
int _tmain(int argc, _TCHAR* argv[])
{
SayHello1();
SayHello2();
SayHello3();
SayHello4();
}

This lead to the result, that I received the following linker errors:
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp__SayHello4', verwiesen in Funktion '_main'
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp__SayHello3', verwiesen in Funktion '_main'
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp_@SayHello2@0', verwiesen in Funktion '_main'
Test.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp__SayHello1@0', verwiesen in Funktion '_main'
Debug/USE_BCB_DLL.exe : fatal error LNK1120: 4 unaufgelöste externe Verweise

---

when I create the .LIB with the .DEF like:
EXPORTS
SayHello2
SayHello1
SayHello3
SayHello4

and call the functions with (calling SayHello1 and SayHello2 doesn't succeed
at all):
int _tmain(int argc, _TCHAR* argv[])
{
//SayHello1();
//SayHello2();
SayHello3();
SayHello4();
}

then my application starts (which means linking succeeds). But while
starting, a MessageBox appears, which says:
'The entrypoint of "SayHello3" in "Test.dll" could not be found.'

So what is wrong here? How do I manage to get to get my DLL run without
changing the calling conventions (__stdcall / __fastcall) ?

Thanks,
Uli
 
G

Guest

Hello Tom,

first thanks for your answer, but I'm not satisfied with the state I
actually reached. What I managed so far is:
- BCB-App <= MSVC-DLL (static, using .LIB via IMPLIB)
- BCB-APP <= MSVC-DLL (dynamic, using LoadLibrary, GetProcAddress)
- MSVC-APP <= BCB-DLL (dynamic, using LoadLibrary, GetProcAddress)
with functions of all the calling conventions (__cdecl, __stdcall,
__fastcall).

So I think there MUST be a way to make run
- MSVC-App <= BCB-DLL (static, using .LIB via LIB)
because when you're able to do so under BCB - I think you also must be able
to proceed like this under MSVC!

So, my question is still an open point ...

Uli.
 
C

Carl Daniel [VC++ MVP]

Tamas said:
I think __fastcall is Borland specific, it uses registers to pass
function arguments, and it's not compatible with Microsoft. The two
others must work (__stdcall and __cdecl).

Only __stdcall must work, since that's the only ABI defined by Windows. All
other calling conventions are tool-specific and may or may not be
compatible.

Which of the techniques discussed in KB131313 did you (the OP) use? The
stub-generating technique should always work, and always produce the correct
exported names.

-cd
 
G

Guest

Hello,

as I described, I use the technique "Creating a .DEF file" from KB131313.
(I don't want to use the stubbing technique because it means too much much
in a subsequent migration process (for a lot of DLL with a lot of functions).
And besides that, I can't image, why it is or should be <SO> complicated to
port a BCB-DLL (of pure C-Sytle exported functions!) to MSVC. I mentioned,
that the opposite way (MSVC-DLL => BCB-App) was quiet easy by simply using
one single IMPLIB command! (I think MS products are innovative and it's
promised that everything is easy ... :)

---

Next attempt:
Now I reduced my test to just one single function:

[Test.h]
#ifndef __BCB2VC_DLL_H
#define __BCB2VC_DLL_H

#ifdef __BUILD_DLL__
#define __DECL_SPEC__ __declspec(dllexport)
#else
#define __DECL_SPEC__ __declspec(dllimport)
#endif //#ifdef __BUILD_DLL__

// tried another return value (int) here:
extern "C" __DECL_SPEC__ int __stdcall SayHello1(void);


#endif //#ifndef __BCB2VC_DLL_H


[Test.DEF]
EXPORTS
SayHello1


[Command-Line]
LIB /DEF:Test.def /OUT:Test.lib


[MSVC-App]
// Project-Options: Linker - Input - add. dependencies: "Test.lib"

....
#include "Test.h"

int _tmain(int argc, _TCHAR* argv[])
{
int iRet = SayHello1();

retun 0;
}


=> USE_BCB_DLL.obj : error LNK2019: Nicht aufgelöstes externes Symbol
'__imp__SayHello1@0', verwiesen in Funktion '_main'
=> Debug/USE_BCB_DLL.exe : fatal error LNK1120: 1 unaufgelöste externe
Verweise

---

When I refer to this function for example in Excel-VBA like this:
' Private Declare Sub SayHello1 Lib "Test" () '
it works without problems.

Is there a simple trick missing or is it a malfunction of MSVC ??? ...


Uli.
 
T

Tamas Demjen

I agree with you, Borland's implib is excellent. You just need to have a
DLL file, exporting pure C functions (usually via __stdcall), and you
don't need any LIB files, implib will create that automatically. I don't
know of such a tool for VC++.

The KB131313 article (http://support.microsoft.com/kb/q131313/) has a
fault. It can't work, because the VC++ compiler does not respect the
extern "C" with __stdcall, unless a DEF file is present. I think it's a
bug in every Microsoft compiler, but let's not discuss this now. The
bottom line is that if you want to use extern "C" with __stdcall, you
must have a DEF file, in order to prevent the name mangling.

So here's what you have to do. I assume that you use __stdcall, as
recommended by Carl in an earlier email. __cdecl may work with Borland,
but I'm not sure anymore. :)

1. Create a file MyDll.c with the following:
__declspec(dllexport) int __stdcall MyFunction(void) { return 0; }
(Actually I found it easier to do it in C, because you don't need extern
"C" that way)

2. Make a DEF file called MyDll.def:
EXPORTS MyFunction

3. Type in from the command line:
cl /c /Ob0 MyDll.c
lib /def:a.def a.obj

And the mistake in the KB131313 article is that you must have a DEF file
with __stdcall, otherwise the name mangling will be on. That's why
you're getting runtimes errors that __imp__MyFunction@0 is missing, or
something like that.

I know it's tough, but it works in the end. Let me know if you need more
help. I just reproduced this with a Borland C++Builder 6 DLL and with
VC++ 2005 using a command line W32 project. It should work with other
versions of C++Builder and VC++ as well.

Good luck, and don't hesitate to ask if it's still not working, this
topic is not easy at all, and Microsoft didn't document this properly.

Tom
 
T

Tamas Demjen

Correcting

3. Type in from the command line:
cl /c /Ob0 MyDll.c
lib /def:MyDll.def MyDll.obj
 
G

Guest

Hello Tom,

thank you very much for your assistance - for my little "HelloWorld-Program"
it worked so far. It was a long and tedious way! Now I'll (have to) see, if
it's working in real life :)

Uli.
 
Top