Visual C DLL in C Builder Engine - Part 2: C SHADOWSTAR's Home: http://shadowstar.126.com/ Source: http://www.bcbdev.com/articles/vcdll2.htm Note : This article describes how to introduce C classes from Visual C DLL into BCB project. Before we started, I think I have to give a little warning. This article is not really ready to release. If the "article" is ups and downs, it is difficult to read, or contain mistakes, I earn! I have no time to improve it. I decided to continue and released the only reason because many developers asked how to deal with this problem. I think that a very bad article is always better than anything. I hope this collection of this non-coherent concept will bring you help. In the previous article, "Use Visual C DLL in C Builder Engine", I describe how to create a Borland compatible introduction library for MSVC DLL. The main difficulty is that the function naming format used by MSVC and Borland is different. For example, Borland believes that the __cdecl function has a downline in their beginning, but MSVC is not. Fortunately, you can use the Borland command line utility to overcome the names, these tools have TDUMP, IMPLIB, IMPDEF, and Coff2omf. The method is to create a Borland-compatible introduction library with Borland compatible functions with these command line tools. Once you have Borland compatible introduction library, you can start working. You can easily connect to the library to use the MSVC DLL. Unfortunately, this strategy can't take you out of this forest. In the end of the last DLL article, I lost a small bomb. You can only call a simple C function in the MSVC DLL, and cannot introduce a class or class member function. Doh! So what do you need to do from the MSVC DLL into the C class? Ah ... this, if so, you are closing the corner, there is no room for choosable (usually when you return to the corner, your option is not satisfactory). This document describes three ways to take you out of the corner. Bad news: When you are going to study this garbage, I think it is forced to warn. All three technologies require you have Microsoft Visual C . You don't need to have the source code of the DLL to be called, but you need to have a tool that can be toned. All three technologies are more or less packaging technology, and we use MSVC to pack DLL into some form that can be used in BCB.
Three technical summary technology 1: Package C class to C Cult Technology 2: Create a COM Packaging Technology 3: Download three technical summary OK using the abstract base class (Pseudo-COM) with virtual functions (Pseudo-COM), now ugly. This is the three technologies.
Create a DLL with MSVC, wrap the C class package into a simple C function. A simple C function can be introduced in the BCB. Create a COM object with MSVC, and the C class is restricted. BCB can call the VC COM object as a COM client. Pack the C class with abstract classes, this abstract class only has some virtual functions without entities. This is essentially COM, but there is no ugly portion. More details of various technologies are described below. In each example, we will assume that the formal form of the MSVC DLL is as follows: Class Cfoo {
PUBLIC:
Cfoo (int x);
~ Cfoo ();
INT DOSMETHING (INT Y);
}
Technology 1: Package the C class to the C libura in the previous article about the article of VC DLL, we know that the simple C function exported from a MSVC DLL in a Borland project is possible. Using this information, we can know that we can create a DLL project in the MSVC to export simple C functions to BCB. The DLL of this MSVC package will be used as a client of C DLL. The package DLL will export a simple C function to create a CFOO object, call the Cfoo member function, and destroy the CFOO object. The CFOO class contains three functions we care about: constructor, destructor, and all important DOSMETHING functions. We need to wrap each function to the C function with its equivalent. // Original Class
Class Cfoo
{
PUBLIC:
Cfoo (int X);
~ Cfoo ();
INT DOSMETHING (INT Y);
}
// Flattened C C code
Void * __stdcall new_cfoo (int x)
{
Return new cfoo (x);
}
INT __STDCALL CFOO_DOSMETHING (Void * Handle, Int Y)
{
Cfoo * foo = reinterpret_cast
Return foo-> DOSMETHING (Y);
}
void __stdcall delete_cfoo (void * handle)
{
Cfoo * foo = reinterpret_cast
Delete foo;
}
There are several important places to pay attention to here. First, pay attention to each C member function is mapped to a simple C function. Second, observed that we explicitly use the __stdcall call habit for the C function. In the previous DLL article, we know that simple calls in the MSVC DLL are really troublesome. If we give up, we can use it through it, we can make this effort to be a little easier. Let Borland call Microsoft DLL's easiest way is that DLL exports unformatted, no modifications, __ stdcall call habits C functions. Borland and Microsoft have different processing of the __cdecl function. Typically, they are different from the __stdcall function because the MSVC modified __stdcall function, but we can prevent this behavior by adding a DEF file to the MSVC engineering. See the example of the download section has an example of a DEF file. Other things about the code should be noted that the new_cfoo function returns a pointer to the CFOO object. The BCB caller must save this pointer locally. This may look a bit contradictory with the theme of this article. After all, I think BCB can't use C objects from MSVC DLL? If that is correct, then we have to return a cfoo object pointer? The answer is that the BCB cannot invoke the member function of the MSVC DLL export class. However, this does not mean that it cannot store the address of this object. New_cfoo Returns a pointer for a CFOO object. The BCB client can store this pointer but cannot be used. BCB cannot discard it (should not try this). Let this view easier to understand, new_cfoo returns an empty pointer (in short it can't return something else). In BCB, in addition to storing it, then pass it back to the DLL, there is no way to securely handle this empty pointer. OK, before we continue to advance, there are two other places to pay attention. First, pay attention to cfoo_dosomething to take an empty pointer as its first parameter. This empty pointer returns to new_cfoo is the same empty pointer. The empty pointer is traced back to the cfoo object with ReinterPret_cast (you know, when you see a reinterpret_cast, you are handling is a ubiquitous code). The DOSMETHING member function is called after the conversion. Finally, note that the empty pointer is also the parameter of the delete_cfoo function. Packaging DLL delete object is critical. You should not call DELETE in the BCB. Obviously it doesn't do it according to what you want. The following program list shows the DLL header file of the C function. This header file can be shared between MSVC and BCB. // DLL Header File # IFNDEF DLL_H
#define dll_h
#ifdef build_dll
#define dllapi __declspec (dllexport)
#ELSE
#define dllapi __declspec (dllimport)
#ELSE
#ifdef __cplusplus
Extern "C" {
#ENDIF
DLLAPI VOID * __STDCALL New_CFOO (INT X);
DLLAPI INT __STDCALL CFOO_DOSMETHING (Void * Handle, Int Y);
DLLAPI VOID __STDCALL DELETE_CFOO (Void * Handle);
#ifdef __cplusplus
}
#ENDIF
#ENDIF
This is a typical DLL header file. Take note of a curious thing, I can't see the CFOO class in the header file. The header file contains only a form of format C function to pack CFOO. The following program list shows how to call the DLL in the BCB. #include "dll.h" void bar ()
{
INT x = 10;
INT Y = 20;
Int z;
Void * foo = new_cfoo (x);
Z = cfoo_dosomething (foo, y);
Delete_cfoo (foo);
}
that's it. Although not beautiful, it can be used. In fact, regardless of this technology, this method can also be used in other ways to call the DLL. For example, the Delphi programmer uses the same technique because Delphi cannot call C member functions. The Delphi programmer must wrap the C class into C code and connect to the C Obj file. Open Source Tool SWIG (SWIG.ORG) is designed to generate a packaging function like this, where you allow you to use a C object similar to Python's corner. Technology 2: Creating a COM package unfortunately, I have no examples of this technology (Hi, I said this article is not prepared for the golden time). But this idea is working like this. Create a COM object in the MSVC. Maybe you can run the wizard. Create a server (such as DLL, not exe). Again, confirm that you have created a COM object instead of an automatic control object. Automatic control will only make everything more difficult. Unless you also need to use C classes in the VB or ASP page, you can also use unformatted COM without automatic control. Create a new COM object within the COM project. MSVC probably wants you to create a COM interface. Since we are packaging a class called Cfoo, a good interface name should be iFoo. MSVC will also make you named the COM object that is executed. CfooIMPL is a good candidate. The COM object should be packaged with a polymeric package C DLL class. In other words, the COM object contains a CFOO member variable. Don't try to inherit your COM class from Cfoo. For each C DLL class (cfoo) member function, create a similar function in your COM object. If possible, with the same name, pass the same parameters, return the same type of value. You need to adjust some things. For example, string is usually passed to BSTR in COM. Similarly, the return value is passed to the output parameter because the COM method should return an error code. When you finish these, each member function of the C class should have a corresponding function in the COM package. After your Build COM package, register it with regsrv32.exe. Once registered, you should exempt this COM object and call the member function of the Package with BCB code. Once again, I have no apologize for this technique introduced above. Technical 3: A Pseudo-COM method using an abstract base class with virtual functions (Pseudo-COM) technology 3 is a PSEUDO-COM method. COM is a binary object specification. The COM object can be called by BCB and MSVC, regardless of the COM object to compile. Therefore, what magical work is used this binary? The answer is based on this technology to be told. The COM function call is assigned by a function lookup table. Magical is the same way to work with the same method with the C virtual function table with the C virtual function. In fact, they are the same. COM is just a form of virtual functions and virtual functions. COM can work because BCB and MSVC really use the same virtual assignment system. COM depends on most Win32 C compilers to generate and use VTABLE's facts with the same way. Because two compilers use the same virtual assignment system, we can create a packaging class in MSVC with virtual functions, which can be called by BCB. This is what COM did. This is a DLL header file of the Pseudo-COM packaging class. It includes an abstract base class, IFOO, which serves the Pseudo-COM interface.
It also includes two C functions to create and delete the IFOO object. This header is shared between MSVC and BCB. #ifndef dll_h # define DLL_H
#ifdef build_dll
#define dllapi __declspec (dllexport)
#ELSE
#define dllapi __declspec (dllimport)
#ENDIF
// psuedo com interface
Class ifoo
{
PUBLIC:
Virtual int __stdcall dosomething (int x) = 0;
Virtual __stdcall ~ ifoo () = 0;
}
#ifdef __cplusplus
Extern "C" {
#ENDIF
DLLAPI ifoo * __stdcall new_ifoo (int x);
DLLAPI VOID __STDCALL DELETE_IFOO (ifoo * f);
#ifdef __cplusplus
}
#ENDIF
#ENDIF
Note the function of two C functions like technology 1, in addition to their cooperation with IFOO, not empty pointers. This technique provides more security than the first one. Here is the source code of MSVC packaging. It includes a class called CfooImpl from IFOO. CfooIMPL is an implementation of the IFOO interface. #define build_dll
#include "dll.h"
Ifoo :: ~ ifoo ()
{
// Must Implement Base Class Destructor
// Even if ITS Abstract
}
// NOTE: WE DECLARE The Class Here Because No One Outside Needs to Be Concerned
// with it.
Class CfooImpl: Public IFOO
{
Private:
Cfoo M_foo; // The Real C Class from The EXISTING MSVC C DLL
PUBLIC:
CfooIMPL (int X);
Virtual ~ cfooImpl ();
Virtual int __stdcall dosomething (int x);
}
CfooImpl :: CfooImpl (int X)
: m_foo (x)
{
}
INT __STDCALL CFOOIMPL :: DOSMETHING (INT X)
{
Return m_foo.dosomething (x);
}
CfooImpl :: ~ cfooImpl ()
{
}
Ifoo * __stdcall new_ifoo (int X)
{
Return New CfooImpl (X);
}
Void __stdcall delete_ifoo (ifoo * f)
{
delete f;
}
There are many good material materials here. First, notice that we have a class file that is shared between BCB and MSVC. It seems to be a good thing. More importantly, it is noted that the BCB project will only be derived with the IFOO class. The real IFOO implementation is provided by a derived class called CfooImpl, which is in the MSVC project. The BCB client code will cooperate with the IFOO object. To get a packaging instance, the BCB code can call the new_ifoo function. New_ifoo's work like a function factory that provides new IFOO instances. New_foo returns a pointer to the IFOO instance. However, the pointer is polymorphic. The static type of the pointer is IFOO, but its actual dynamic type will be pointed to cfooIMPL (BCB code does not know the truth). This is the code of the BCB client. #include "dll.h" void bar ()
{
INT x = 10;
INT Y = 20;
Int z;
Ifoo * foo = new_ifoo (x);
Z = foo-> dosomething (y);
Delete_ifoo (foo);
}
Annotation is now given in technology 3. First, it is critical to delete the IFOO pointer from the MSVC DLL. This is done by calling the delete_ifoo function to complete the IFOO pointer. Don't try to delete objects from BCB. Void bar ()
{
Ifoo * foo = new_ifoo (x);
Delete foo; // boom !!!
}
This code will die in pain. The problem is that ifoo is created by the new_ifoo function in the MSVC packaging DLL. Similarly, the memory occupied by the IFOO object is assigned by the MSVC Memory Manager. When you delete an object, you only have the right to delete the object created with the same memory manager. If you call DELETE in BCB, you call DELETE, then you are deleted with Borland Memory Manager. Now, I may be wrong, but I am willing to give my house and a genital bet, either two, can't try to work with the Microsoft Memory Manager and Borland Memory Manager. When you use the Borland Memory Manager to delete the pointer, don't it try to contact the Microsoft Memory Manager, let it know which memory it should be released? Also explain one, the BCB code works completely according to the IFOO virtual function interface. In BCB, you can't see any CfooImpl classes. CfooIMPL is in the MSVC packaging engineering. When you call DOSMETHING from BCB, call to CfooImpl through the virtual function table. If you understand this concept, don't worry. I may have not described it very well. The following content can help understand, on BCB, you can use the CPU Viewer single-step tracking code. It allows you to track each assembler instruction, see how Vtable is looking for work.
note:
If you use this Pseudo-COM technology, make sure you have not tried to overload virtual functions. In other words, don't create an interface like this: Class Ifoo
{
PUBLIC:
Virtual int __stdcall dosomething (int x) = 0;
Virtual Int __stdcall dosomething (float x) = 0;
Virtual Int __stdcall dosomething (const char * x) = 0;
}
The reason why the virtual interface function should not be overloaded is that the MSVC and BCB are not possible (perhaps not) on VTABLE. When I tried overload, DOSMETHING (Int) was called in BCB, and I am equipped to DOSMETHING (FLOAT) on the MSVC. Borland and Microsoft are consistent when the VTable format is not overloaded. This may explain why COM objects do not use overload functions. IF you need to Wrap A C Class with overloaded functions, the you shop create a distinct function name for each one. Class ifoo {
PUBLIC:
Virtual int __stdcall dosomething_int (int x) = 0;
Virtual int __stdcall dosomething_float (float x) = 0;
Virtual int __stdcall dosomething_str (const char * x) = 0;
}
Conclusion: OK, where are we? Ah, in the article, we told about why BCB cannot call the C member function in the DLL, if the DLL is compiled by MSVC. The reason is that the two compilers are not identified in the member function. We discussed three (a little annoying) working methods. Each working method is a MSVC packaging DLL established for C DLL. Packaging DLL reveals C classes with some BCBs that can be understood. The first technique puts each C class member function packaged into a formatted C function. The second technique maps each member function into a member of the COM object. The last technique dependent on the virtual function is the fact that the lookup table assigned rather than the name. In this strategy, each C member function is mapped into a virtual function of an abstract class. The download section includes example code for this article. The first download contains the original MSVC C DLL, we managed to cooperate with it. Each routine of the three technologies use the same DLL. There is still no example for technology 2. download
Download CPPDLL.ZIP VC 5 DLL project for this article, export the code of C Cfoo class VCDL2Tech1.zip technology 1, wrap the class package into C function vcdll2tech3.zip technology 3 code, virtual function / abstract base class package