By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,385 Members | 847 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 432,385 IT Pros & Developers. It's quick & easy.

Export C++ class from a Borland DLL and use it in Microsoft VC

P: n/a
Hi All,

I'm new of this group and I do not know if this is the correct group
for my question.
I have a DLL with its export library (.lib) wrote in Borland C++ 6. In
borland everything is OK and I am able to include the lib and use the
class that i have exported creating an instance with new etc... I
would like to export this class in microsoft VC++ using the same .lib
file. Obviously it doesn' t work.

Is it possible to generate or convert the import library into a
compatible format?

Thank you in advance

Fabry

Sep 29 '06 #1
Share this Question
Share on Google+
17 Replies


P: n/a
I'm new of this group and I do not know if this is the correct group
for my question.
I have a DLL with its export library (.lib) wrote in Borland C++ 6. In
borland everything is OK and I am able to include the lib and use the
class that i have exported creating an instance with new etc... I
would like to export this class in microsoft VC++ using the same .lib
file. Obviously it doesn' t work.

Is it possible to generate or convert the import library into a
compatible format?
Nope.

There is no binary standard for compiled C++ code. Each compiler uses its
own binary layout.
This means that C++ code is incompatible between different compilers, and
even different compiler version.

Using Borland C++ dlls in msvc is not possible.
This is not a Microsoft problem btw. It is true for all compilers. Microsoft
even has a very good record for compatibility, gcc the worst.

If you want to export C++ code you have to stick with 1 compiler.
If you want to mix different compiler environments, you have to stay with
ANSI C interfaces.

--

Kind regards,
Bruno van Dooren
br**********************@hotmail.com
Remove only "_nos_pam"
Sep 29 '06 #2

P: n/a
Bruno van Dooren [MVP VC++] wrote:
>I'm new of this group and I do not know if this is the correct group
for my question.
I have a DLL with its export library (.lib) wrote in Borland C++ 6. In
borland everything is OK and I am able to include the lib and use the
class that i have exported creating an instance with new etc... I
would like to export this class in microsoft VC++ using the same .lib
file. Obviously it doesn' t work.

Is it possible to generate or convert the import library into a
compatible format?

Nope.

There is no binary standard for compiled C++ code. Each compiler uses its
own binary layout.
This means that C++ code is incompatible between different compilers, and
even different compiler version.

Using Borland C++ dlls in msvc is not possible.
I think, that's not true. For example, I use a DLL written in Intel
Fortran with Borland C++. IMHO DLL-usage is compiler - and even though -
language independant. If it really doesn't work with the MS compiler (I
did not do this), it is in fact a Microsoft problem.

Markus
This is not a Microsoft problem btw. It is true for all compilers. Microsoft
even has a very good record for compatibility, gcc the worst.

If you want to export C++ code you have to stick with 1 compiler.
If you want to mix different compiler environments, you have to stay with
ANSI C interfaces.
Sep 29 '06 #3

P: n/a
>>I'm new of this group and I do not know if this is the correct group
>>for my question.
I have a DLL with its export library (.lib) wrote in Borland C++ 6. In
borland everything is OK and I am able to include the lib and use the
class that i have exported creating an instance with new etc... I
would like to export this class in microsoft VC++ using the same .lib
file. Obviously it doesn' t work.

Is it possible to generate or convert the import library into a
compatible format?

Nope.

There is no binary standard for compiled C++ code. Each compiler uses its
own binary layout.
This means that C++ code is incompatible between different compilers, and
even different compiler version.

Using Borland C++ dlls in msvc is not possible.

I think, that's not true. For example, I use a DLL written in Intel
Fortran with Borland C++. IMHO DLL-usage is compiler - and even though -
language independant. If it really doesn't work with the MS compiler (I
did not do this), it is in fact a Microsoft problem.
Not true.
The question is clearly about C++ classes that are exported from a dll.
Of course, dlls can be created with any C/C++ compiler.

But compiled C++ classes are NOT compatible between different C++ compilers.
This has nothing to do with Microsoft. It is a result from the fact that it
took a very long time for C++ to be standardized, while different vendors
went along, doing their own thing.

Borland is incompatible with microsoft is incompatible with gcc is
incompatible with icc is incompatible with ... as far as C++ classes are
concerned.

DLL usage itself is no problem. The problem is that what's inside that dll
is not a class as msvc would have compiled it.
Hence, it does not work.

--

Kind regards,
Bruno van Dooren
br**********************@hotmail.com
Remove only "_nos_pam"
Sep 29 '06 #4

P: n/a
"Markus Donath" <ne******@microsoft.comwrote in message
news:Oj**************@TK2MSFTNGP06.phx.gbl...
>Using Borland C++ dlls in msvc is not possible.

I think, that's not true. For example, I use a DLL written in Intel
Fortran with Borland C++. IMHO DLL-usage is compiler - and even though -
language independant. If it really doesn't work with the MS compiler (I
did not do this), it is in fact a Microsoft problem.
If one is careful, functions that one exports from DLLs may be called by
client applications in just about any language. If that were not so, Visual
Basic and PowerBuilder applications, for example, could not make use of the
Win32 API. QED.

But, as Bruno points out there is in general no sharing of classes because
there exists no standard name mangling / decorating convention across
compilers (as if the ISO standard were in danger of becoming bloated <g>)

FWIW: If you should again find a need to use a Fortran DLL with an MS
application, this article

http://support.microsoft.com/?id=131313

explains how you should be able to do it.

Regards,
Will
Sep 29 '06 #5

P: n/a
Fabry wrote:
I have a DLL with its export library (.lib) wrote in Borland C++ 6. In
borland everything is OK and I am able to include the lib and use the
class that i have exported creating an instance with new etc... I
would like to export this class in microsoft VC++ using the same .lib
file. Obviously it doesn' t work.
Borland uses a different C runtime (including a different memory
manager) than Visual Studio. What you're asking for won't even work
between VC++6 and VC++8.

Of course, it is not impossible to use a DLL across compilers, even
across different languages. You can write DLLs in VC++ and use them from
Delphi. You just have to follow very strict (and super inconvenient)
rules to ensure cross-compiler compatibility.

You have a couple of choices here:

1A. Use a C-style DLL. Flatten your object hierarchy into C-style
(extern "C" __stdcall) functions. Ensure that all your exported
functions have pure C function arguments. So pass a string as const
char*, pass a vector<Tas const T*, and so on. Every memory that you
DLL allocates MUST be freed by the same DLL. You either have to use the
concept of allocators, or export your functions in pairs (for each
function that allocates, there must be another function that frees).

1B. You can go ahead and use C++ in a restricted way. Note that due to
name mangling you can't export C++ classes themselves. You still need to
use pure extern "C" exports, but the functions themselves can accept and
return C++ classes. Instead of exporting those classes, you have to bind
them using virtual function calls. Your DLL should define an interface
for each class, which is linked to both the DLL and the EXE that uses
the DLL. Function calls are dispatched via the VMT, which is guaranteed
to be portable between Borland and Microsoft. But you still need to
ensure that any memory allocated by the DLL is deleted by the same DLL.
You must avoid using STL altogether in the exported functions' argument
list (no std::string, no std::vector). Please see my draft article at:

http://tweakbits.com/articles/dll/index.html

I do this all the time, I share DLLs between VC++ and C++Builder
routinely without any problem. You just have to follow my article very
very closely. It's a major pain, because it requires this extra layer of
plugin architecture, but it's nothing you can't implement fairly easily.
It's just a lot of extra code, attention and time.

Special note: By default, enums are treated as bytes in C++Builder
(because Delphi does the same), but they're integers in VC++. If you
pass or return them by value, it doesn't matter that much, but if you
pass an enum by pointer or reference, your application is going to crash
for sure. You must either not use enums in your interface, or modify
Borland's compiler settings for your particular cpp file (not the entire
project). The setting is called "treat enums as integers" or something
like that. This is the only major binary layout incompatiblity between
the two compilers, and I shoot myself in the foot a few times with that.

2. COM/ActiveX. I find COM much harder and more cumbersome than either
1A or 1B, but it's always a possibility to link different compilers /
languages together.

3. .NET. I find .NET many times easier and infinitely more flexible than
COM, but it requires the .NET runtime. Besides, C++Builder doesn't have
Managed C++ or C++/CLI, so it's not really an otpion for you, but it's
worth mentioning nonetheless.

++ ... I'm sure there are other component sharing technologies, like Web
services, CORBA, etc.

Tom
Sep 29 '06 #6

P: n/a
"Tamas Demjen" <td*****@yahoo.comwrote in message
news:u0****************@TK2MSFTNGP04.phx.gbl...
You have a couple of choices here:
[ good summary of options snipped ]

Of course, 1B only works because 2 works. There's no general requirement
that vtable layout be compatible for even pure abstract (interface) classes,
except as a consequence of both compilers being COM compatible. Your 1B is
just a simplified version of COM that ignores the myriad of issues that
don't occur in the vast majority of programs that don't have to interoperate
with VB or OLE automation (which is really what COM was desiged for).

-cd

Sep 29 '06 #7

P: n/a
"Carl Daniel [VC++ MVP]" <cp*****************************@mvps.org.nospam >
wrote in message news:%2****************@TK2MSFTNGP02.phx.gbl...
Your 1B is just a simplified version of COM that ignores the myriad of
issues that don't occur in the vast majority of programs that don't have
to interoperate with VB or OLE automation (which is really what COM was
desiged for).
I've always thought that COM exists largely to give VB "developers" a way to
use components crafted by their much less lazy C++ colleagues. <gd&r>

But, if I understand him correctly, Tony Williams in this video

http://channel9.msdn.com/shows/Behind_The_Code

says it has as much to do with Bill G. telling one of the Office groups
(PowerPoint I think though whatever it was it it was in the days before
Office was a suite) that he was not going to pay for them to reinvent the
wheel but rather that they would use OLE to be able to embed a component of
another group in their own application.

Regards,
Will



Sep 30 '06 #8

P: n/a

Tamas Demjen ha scritto:
Fabry wrote:
I have a DLL with its export library (.lib) wrote in Borland C++ 6. In
borland everything is OK and I am able to include the lib and use the
class that i have exported creating an instance with new etc... I
would like to export this class in microsoft VC++ using the same .lib
file. Obviously it doesn' t work.

Borland uses a different C runtime (including a different memory
manager) than Visual Studio. What you're asking for won't even work
between VC++6 and VC++8.

Of course, it is not impossible to use a DLL across compilers, even
across different languages. You can write DLLs in VC++ and use them from
Delphi. You just have to follow very strict (and super inconvenient)
rules to ensure cross-compiler compatibility.

You have a couple of choices here:

1A. Use a C-style DLL. Flatten your object hierarchy into C-style
(extern "C" __stdcall) functions. Ensure that all your exported
functions have pure C function arguments. So pass a string as const
char*, pass a vector<Tas const T*, and so on. Every memory that you
DLL allocates MUST be freed by the same DLL. You either have to use the
concept of allocators, or export your functions in pairs (for each
function that allocates, there must be another function that frees).

1B. You can go ahead and use C++ in a restricted way. Note that due to
name mangling you can't export C++ classes themselves. You still need to
use pure extern "C" exports, but the functions themselves can accept and
return C++ classes. Instead of exporting those classes, you have to bind
them using virtual function calls. Your DLL should define an interface
for each class, which is linked to both the DLL and the EXE that uses
the DLL. Function calls are dispatched via the VMT, which is guaranteed
to be portable between Borland and Microsoft. But you still need to
ensure that any memory allocated by the DLL is deleted by the same DLL.
You must avoid using STL altogether in the exported functions' argument
list (no std::string, no std::vector). Please see my draft article at:

http://tweakbits.com/articles/dll/index.html

I do this all the time, I share DLLs between VC++ and C++Builder
routinely without any problem. You just have to follow my article very
very closely. It's a major pain, because it requires this extra layer of
plugin architecture, but it's nothing you can't implement fairly easily.
It's just a lot of extra code, attention and time.

Special note: By default, enums are treated as bytes in C++Builder
(because Delphi does the same), but they're integers in VC++. If you
pass or return them by value, it doesn't matter that much, but if you
pass an enum by pointer or reference, your application is going to crash
for sure. You must either not use enums in your interface, or modify
Borland's compiler settings for your particular cpp file (not the entire
project). The setting is called "treat enums as integers" or something
like that. This is the only major binary layout incompatiblity between
the two compilers, and I shoot myself in the foot a few times with that.

2. COM/ActiveX. I find COM much harder and more cumbersome than either
1A or 1B, but it's always a possibility to link different compilers /
languages together.

3. .NET. I find .NET many times easier and infinitely more flexible than
COM, but it requires the .NET runtime. Besides, C++Builder doesn't have
Managed C++ or C++/CLI, so it's not really an otpion for you, but it's
worth mentioning nonetheless.

++ ... I'm sure there are other component sharing technologies, like Web
services, CORBA, etc.

Tom

Hi Tom,

first of all I would like to thank you very much for your help! I have
just tried the solution to export the C++ class instead of the DLL
using a virtual interface but I had some problems. I have the DLL
internal class (the class I want to export) that implement a virtual
interface. The external project that use the class exported using the
"C" convention I include the interface header. The problem was the
deconstructor of the class. The compiler said me that it is not
possible create a virtual deconstructor...(I will read your article I
hope there is the solution to my problems) Probably I have to use a
proxy class together with the virtual interface...

ADDITIONAL QUESTION
I have another simple question for you. I have defined some methods
pointers in my class in order to implement for examples some print
methods. I have defined this methods pointer with the borland
__closure. Is it possible assign an external VC methods to a DLL
written in Borland? There is some standard conventions?

Thank you very much

Fabry

Oct 3 '06 #9

P: n/a
Fabry wrote:
The compiler said me that it is not
possible create a virtual deconstructor...
What is the relevant code, and the exact error message?

Usually here's how you implement a DLL-ready interface:

class Interface
{
public:
virtual ~Interface() { }

virtual void Member1() = 0;
virtual void Member2() = 0;
virtual void Member3() = 0;
};

Note that the virtual destructor must have an implementation (empty
body), while the other functions don't. You link this interface to both
the DLL and the EXE. This is the only piece of code both must share (via
#include).

The implementation goes to the DLL's code:

class Impl : public Interface
{
public:
virtual void Member1() { /* TODO */ }
virtual void Member2() { /* TODO */ }
virtual void Member3() { /* TODO */ }
};

You don't export this class at all. You export a C-style function that
creates an instance:

extern "C" Interface* CreateImpl()
{
return new Impl;
}

This goes to the DLL, and this is what the EXE has to call to create the
instance.

I told you that the DLL must release every memory it creates, therefore
you either export one more function:

extern "C" void DeleteImpl(Interface* p)
{
delete p;
}

or you handle it as a member function:

class Interface
/* ... */
virtual void DeleteMe() = 0;
};

class Impl
/* ... */
virtual DeleteMe() { delete this; }
};

The EXE doesn't iclude the implementation, only the interface, and the
CreateImpl function:

Interface* p = CreateImpl();
p->Member1();
p->Member2();
p->DeleteMe(); // or DeleteImpl(p); but not delete p;

ADDITIONAL QUESTION
I have another simple question for you. I have defined some methods
pointers in my class in order to implement for examples some print
methods. I have defined this methods pointer with the borland
__closure. Is it possible assign an external VC methods to a DLL
written in Borland? There is some standard conventions?
The __closure concept can be implemented in standard C++ in a partable
way. See boost::function at http://boost.org, and my implementation at
http://tweakbits.com/articles/events/index.html. However, these
implementations are not byte compatible with Borland's.

Do not use Borland's __closure in your DLL interfaces if you want to use
the DLL with VC++. I'm positive that boost::function can be used across
DLL boundaries, and both of my event implementations are binary portable
too. boost::function works with Borland but not with the preferred
syntax (you have to fall back to compatibility mode). My implementation,
too, works with Borland and VC++ as well.

My article is a little bit old and I just noticed that I'm using the
wrong syntax. Every Method::Member should be replaced by
&Method::Member, so instead of

Button1.OnClick.Connect<TAppManager, TAppManager::Button1Click>(AppManager);

you should write

Button1.OnClick.Connect<TAppManager,
&TAppManager::Button1Click>(AppManager);

Tom
Oct 3 '06 #10

P: n/a
Hi Tom,

I congratulate you for your great solution. I' ve read the article and
I think it is great!!
Now due to strong competence, I would like to ask you an opinion
related to my problem. My intention is to create a DLL library written
in Borland C++ which contains some drivers in form of C++ classes. The
most important point is that the drivers extend TThread class. In order
to be portable I use a proxy class that include the driver. Now, using
your precious advices, the Proxy class that will be exported in other
application extend Interface. On the .EXE side I use the approach
described in your article
The structure of a general driver is shown in the following:

/** DLL side
*/
class TDriver:public TThread
{
friend class TProxyDriver;
private:
int Attribute;
void __fastcall TDriver::Execute(void);
public:
__fastcall TDriver::TDriver(void);
__fastcall TDriver::~TDriver(void);
void TDriver::Method(int Value);
}

class TProxyDriver:public Interface //virtual class of your example
{
friend class TDriver;
private:
TDriver* Driver;
public:
TProxyDriver::TProxyDriver(void):Driver(){};
void TProxyDriver::Method(int Value){Driver->Method(Value);};
void TProxyDriver::SetAttribute(int Value){Driver->Attribute=Value;};
}
In this way I'm able to export the Driver that extend TThread (or other
Borland Components) in other languages. However I think that this
solution is not optimized because it relies on the Proxy for hiding the
Driver and, in my opinion it slows down the execution of my program.
So I've tried to erase the Proxy and to apply the interface extension
at the TDriver class:

class TDriver:public TThread,public Interface
{

}

This approach seems to work, but it create problem during delete
(realized internally on DLL as you suggested in your article) throwing
a strange exception message. I think that the problem is related to the
__fastcall specification required by TThread.

The question is: It is possible to use a better (and faster solution)
in order to exploit TThread Driver avoiding use the Proxy?
If not, is it possible to speed up the use of Proxy?

I hope in your useful suggestions.Anyway, many thanks in advance.

Antonio.

Oct 12 '06 #11

P: n/a
Tamas Demjen wrote:
>
What is the relevant code, and the exact error message?

Usually here's how you implement a DLL-ready interface:

class Interface
{
public:
virtual ~Interface() { }

virtual void Member1() = 0;
virtual void Member2() = 0;
virtual void Member3() = 0;
};
Tamas:

Your method 1B is (I think) what I call "Do it yourself COM" or "COM
without the baggage". I have not used it with Borland, but it certainly
works between different VC versions.

I do not use a virtual destructor with this pattern (neither does COM).
Rather I export two C-style functions from the DLL

Interface* CreateObject()
{
return new Impl;
}

void DestroyObject(Interface* pInterface)
{
delete (Impl*)pInterface;
}

If you use virtual destructor, doesn't that lead to situation where you
free memory in one module that was created in another? [Sorry, I didn't
look at your article, maybe you addressed this.]

David Wilkinson
Oct 12 '06 #12

P: n/a
David Wilkinson wrote:
If you use virtual destructor, doesn't that lead to situation where you
free memory in one module that was created in another? [Sorry, I didn't
look at your article, maybe you addressed this.]
Yes, I stated earlier that the interface should not be deleted from
outside of the DLL that implements it. Perhaps I should make the
destructor non-virtual and protected, so that delete can not be called
on it accidentally. It's a good idea to enforce this at compile time.
Although I never made the mistake of calling delete on an interface, one
could easily overlook this and store it in an auto_ptr or shared_ptr.

Thanks for the hint. I'll try this and update my article accordingly.

Tom
Oct 12 '06 #13

P: n/a
an*******@tiscali.it wrote:
However I think that this
solution is not optimized because it relies on the Proxy for hiding the
Driver and, in my opinion it slows down the execution of my program.
Yes, there's a very minimal overhead introduced by the virtual function
call. How many times do you want to call SetAttribute? I don't see a
problem unless you call it thousands of times. Are you sure that a
single virtual function call causes such an overhead that it kills your
perofrmance? I can imagine such a situation, but in that case I would
export pure C functions, or forget about the DLL implementation altogether.
This approach seems to work, but it create problem during delete
(realized internally on DLL as you suggested in your article) throwing
a strange exception message. I think that the problem is related to the
__fastcall specification required by TThread.
Not only is TThread::TThread a __fastcall (note: Borland __fastcall is
not binary compatible with VC++ __fastcall), but it's declared virtual:

virtual __fastcall TThread::TThread(TObject*);

It's a Borland language extension that lets you use C++ syntax to create
a Delphi object. This is their way of making Object Pascal binary
compatible with C++.

Note that my technique relies on the fact that standard vtables are
implemented the same way in both compilers. As Carl Daniel noted
previously "Of course, 1B only works because 2 works. There's no
general requirement that vtable layout be compatible[...]"

That being said, there's no requirement that an Object Pascal vtable is
comaptible with a VC++ vtable. TThread has a virtual constructor in its
vtable, which is a Pascal thing only.

I strongly recommend that you use standard C++ in your inhertance. Your
proxy solution is the correct one. If the other one doesn't work, it
means TThread's vtable is not compatible with COM. That's all I can say.

If you plan to call C++Builder VCL classes from VC++, Boian Mitov has a
solution for that:

http://mitov.com/html/vcl_for_vc__.html

Tom
Oct 12 '06 #14

P: n/a
an*******@tiscali.it wrote:
This approach seems to work, but it create problem during delete
(realized internally on DLL as you suggested in your article) throwing
a strange exception message. I think that the problem is related to the
__fastcall specification required by TThread.
Actually the virtual __fastcall constructor shouldn't matter, because
it's never called outside of the DLL. It doesn't matter that you have
non-standard calls in your implementation, as long as they're not in the
interface. For a moment I had doubts in my previous message, but
thinking it over I believe the vtables in Delphi should be compatible
with VC++, I'm positive about that.

Your problem seems to happen when you delete the object, so it's
completely unrelated to the interface layer. You seem to have a bug
within your thread, which happens randomly, and independently from the
DLL interfacing. When your proxy solution didn't crash, I think you were
just lucky. It's just a matter of time before that one will eventually
crash as well.

Perhaps you're deleting a thread that's not running anymore. If
TThread::FreeOnTerminate == true, then the thread sends a window message
to the framework, which deletes the thread automatically, so in that
case you must not delete it yourself at all. Maybe you have a more
subtle synchronization problem, a race condition. The bottom line is
that the exception occurs in your DLL, when you delete the object, and
it indicates a bug in the implementation, not in the interfacing.

The question is: It is possible to use a better (and faster solution)
in order to exploit TThread Driver avoiding use the Proxy?
One extra function call is always needed, but you can eliminate the
virtual function call this way:

extern "C"
{
typedef void* DriverHandle;
DriverHandle CreateDriver();
void DriverMethod(DriverHandle h, int value);
void DeleteDriver(DriverHandle h);
}

This is your interface. You export all of those functions. The
implementation goes to the DLL:

DriverHandle CreateDriver()
{
return new TDriver;
}

void DriverMethod(DriverHandle h, int value)
{
reinterpret_cast<TDriver*>(h)->Method;
}

void DeleteDriver(DriverHandle h)
{
delete reinterpret_cast<TDriver*>(h);
}

Probably you'll like this, because it's very compact, but it requires
more functions to be exported/imported.

Tom
Oct 12 '06 #15

P: n/a
Dear Tom,

Thanks you very much for your fast and very useful answer.
Using your precious advices I have studied a solution that seems quite
good. The main idea is
to exploit the Plugin class in the .exe program in order to reproduct
the Borland Event outside the DLL.
In the following the example tries to explain in detail the concept.

#define MY_DECLARATION // the problem of the solution!!!! Which is the
correct assignment (__stdcall,__cdecl or other?)

class TInterfaceDriver
{
virtual void MY_DECLARATION Start(int Time) = 0;
virtual void MY_DECLARATION Stop(void) = 0;
virtual void MY_DECLARATION Event(void) = 0;
};

class TInterfacePlugin:public TInterfaceDriver
{
virtual void MY_DECLARATION Write(const char* Line) = 0;
};
/**DLL Side (Borland)
*/
class TDriver: public TInterfaceDriver
{
private:
TInterfacePlugin* Plugin;
TTimer* Timer;

void __fastcall TDriver::OnTimer(TObject *Object){if(Plugin)
Plugin->Write("TIMER ON\n");};//Timer Event
public:
TDriver::TDriver(TInterfacePlugin* Plugin){
Timer = new TTimer(NULL);
Timer=OnTimer = OnTimer;
Timer->Enabled = false;
this->Plugin=Plugin; // Link from Plugin and Driver inside DLL
};
void MY_DECLARATION Start(int Time){Timer->Interval =
Time;Timer->Enabled = true;}; //Setting of Timer (in milliseconds)
void MY_DECLARATION Stop(void) {Timer->Enabled = false;};
void MY_DECLARATION Event(void){if(Plugin) Plugin->Write("TIMER
ON\n");}; //Event Simulation that it is possible to call directly

};

/*** EXE Side
*/
class TPlugin: public TInterfacePlugin
{
/** Definition constructor, destructor, pointer to function, HINSTANCE
to DLL, etc... as you better explain in your article.
*/
private:
TInterfaceDriver* Driver;
public:
void MY_DECLARATION TPlugin::Write(const char* Line){printf(Line);};
// This is the extern implementation of the DLL event
void MY_DECLARATION TPlugin::Start(int Time){Driver->Start(Time);};
void MY_DECLARATION TPlugin::Stop(void){Driver->Stop();};
void MY_DECLARATION TPlugin::Event(void){Driver->Event();};
};

/**GENERIC MAIN FUNCTION
main()
{
TPlugin* Plugin = new TPlugin();
Plugin->Start(1000); //1' way to obtain event
Plugin->Event(); //2' way to obtain event (not so useful, anyway)
}
In this example, when the timer event OnTimer is on, the driver
executes the Write implemented on .EXE side. It is also possible to
obtain the event
directly calling Event by Plugin, as reported in main.
This solution works very well in Borland. In Visual Studio I'm able
only to use the Plugin->Event(), with some problem according to the
MY_DECLARATION.
I think that, in order to realize a standard solution for all the
compilers, develop environment and so on, I have to assign the correct
declaration at
the function. I've read that the default call for the Borland is
__stdcall whereas for the Visual Studio is the __cdecl.
Is it possible to assign the correct declaration at MY_DECLARATION in
order to make really portable the solution? For example something like:

#ifdef __BORLAND //Borland Constant
#define MY_DECLARATION __stdcall
#endif
#ifdef __VISUAL_STUDIO//Visual Studio Constant
#define MY_DECLARATION __cdecl
#endif

Do you think that this solution should not work for other reasons.
Please, give me some advice. I think that this approach (of course with
correct
adjustments) can be a very good solution. Please let me think what you
think about.
Thanks in advance.

Oct 18 '06 #16

P: n/a
Dear Antonio,

I have exactly the problem that you have well described... Unfortunatly
I have no solutions. I think that the only possibility is an idea of
Tom!

Best Regards

Fabry

an*******@tiscali.it ha scritto:
Dear Tom,

Thanks you very much for your fast and very useful answer.
Using your precious advices I have studied a solution that seems quite
good. The main idea is
to exploit the Plugin class in the .exe program in order to reproduct
the Borland Event outside the DLL.
In the following the example tries to explain in detail the concept.

#define MY_DECLARATION // the problem of the solution!!!! Which is the
correct assignment (__stdcall,__cdecl or other?)

class TInterfaceDriver
{
virtual void MY_DECLARATION Start(int Time) = 0;
virtual void MY_DECLARATION Stop(void) = 0;
virtual void MY_DECLARATION Event(void) = 0;
};

class TInterfacePlugin:public TInterfaceDriver
{
virtual void MY_DECLARATION Write(const char* Line) = 0;
};
/**DLL Side (Borland)
*/
class TDriver: public TInterfaceDriver
{
private:
TInterfacePlugin* Plugin;
TTimer* Timer;

void __fastcall TDriver::OnTimer(TObject *Object){if(Plugin)
Plugin->Write("TIMER ON\n");};//Timer Event
public:
TDriver::TDriver(TInterfacePlugin* Plugin){
Timer = new TTimer(NULL);
Timer=OnTimer = OnTimer;
Timer->Enabled = false;
this->Plugin=Plugin; // Link from Plugin and Driver inside DLL
};
void MY_DECLARATION Start(int Time){Timer->Interval =
Time;Timer->Enabled = true;}; //Setting of Timer (in milliseconds)
void MY_DECLARATION Stop(void) {Timer->Enabled = false;};
void MY_DECLARATION Event(void){if(Plugin) Plugin->Write("TIMER
ON\n");}; //Event Simulation that it is possible to call directly

};

/*** EXE Side
*/
class TPlugin: public TInterfacePlugin
{
/** Definition constructor, destructor, pointer to function, HINSTANCE
to DLL, etc... as you better explain in your article.
*/
private:
TInterfaceDriver* Driver;
public:
void MY_DECLARATION TPlugin::Write(const char* Line){printf(Line);};
// This is the extern implementation of the DLL event
void MY_DECLARATION TPlugin::Start(int Time){Driver->Start(Time);};
void MY_DECLARATION TPlugin::Stop(void){Driver->Stop();};
void MY_DECLARATION TPlugin::Event(void){Driver->Event();};
};

/**GENERIC MAIN FUNCTION
main()
{
TPlugin* Plugin = new TPlugin();
Plugin->Start(1000); //1' way to obtain event
Plugin->Event(); //2' way to obtain event (not so useful, anyway)
}
In this example, when the timer event OnTimer is on, the driver
executes the Write implemented on .EXE side. It is also possible to
obtain the event
directly calling Event by Plugin, as reported in main.
This solution works very well in Borland. In Visual Studio I'm able
only to use the Plugin->Event(), with some problem according to the
MY_DECLARATION.
I think that, in order to realize a standard solution for all the
compilers, develop environment and so on, I have to assign the correct
declaration at
the function. I've read that the default call for the Borland is
__stdcall whereas for the Visual Studio is the __cdecl.
Is it possible to assign the correct declaration at MY_DECLARATION in
order to make really portable the solution? For example something like:

#ifdef __BORLAND //Borland Constant
#define MY_DECLARATION __stdcall
#endif
#ifdef __VISUAL_STUDIO//Visual Studio Constant
#define MY_DECLARATION __cdecl
#endif

Do you think that this solution should not work for other reasons.
Please, give me some advice. I think that this approach (of course with
correct
adjustments) can be a very good solution. Please let me think what you
think about.
Thanks in advance.
Oct 18 '06 #17

P: n/a
an*******@tiscali.it wrote:
I've read that the default call for the Borland is
__stdcall whereas for the Visual Studio is the __cdecl.
The default is __cdecl for both, unless you changed it.
#ifdef __BORLAND //Borland Constant
#define MY_DECLARATION __stdcall
#endif
#ifdef __VISUAL_STUDIO//Visual Studio Constant
#define MY_DECLARATION __cdecl
#endif
This is not good, they must absolutely be the same. It doesn't matter
which calling convention you use, but they must be the same on both
sides. The two calling conventions are so incompatible that unless your
function is void f(), a mismatch will certainly cause a crash.

Avoid using __fastcall in the interface, though, because there are
differences between Borland and Microsoft there. __stdcall and __cdecl
are both portable; anything else is not guaranteed.

BTW, if you need to detect the compiler, use this:
#if defined(__BORLANDC__)
// Borland C++
#elif defined(_MSC_VER)
// Visual C++
#else
// Other
#endif

Tom
Oct 18 '06 #18

This discussion thread is closed

Replies have been disabled for this discussion.