Let's assume for a minute that you have an OWL application you're moving to C++Builder. As part of the conversion process, you'll have to consider converting your dialog boxes to VCL forms. You could argue that VCL forms are easier to use than traditional dialog boxes. But if you're converting a large OWL application to C++Builder, it will be tedious to convert all of the standard dialog boxes that OWL uses to VCL forms. What if you have dozens or even hundreds of dialog boxes? Wouldn't it be nice to know you could use them, and the OWL TDialog class, in your VCL applications? The good news is that you can, as long as you have Borland C++ 5.02. This article will show you how.
In theory, Borland has "fixed" this problem by allowing you to declare the BI_NAMESPACE symbol (BI_NAMESPACE was first implemented in version 5.02 of Borland C++). When this symbol is defined, the OWL classes are wrapped in the namespace called OWL. This means that you should be able to do something like the following:
OWL::TEdit* owlEdit; // an OWL TEdit Stdctrls::TEdit* vclEdit; // a VCL TEditThe problem is that in some cases, defining BI_NAMESPACE results in compiler errors in the OWL headers, so you never get this far. Borland's plan was a good one, but somewhere along the way it wasn't completely implemented. So, we have to go back to the drawing board. But rather than dwell on the negative, let's look ahead to what's required to be able to use a TDialog in a VCL application.
You'll probably have an RH file as well as the RC file. The RH file is a resource header that contains identifiers for the dialog box resource and for the controls contained on the dialog box. A resource header for a simple dialog box with only two edit controls would look like this:
#define IDD_DIALOG 101 #define IDC_FIRSTNAMEEDIT 102 #define IDC_LASTNAMEEDIT 103You should include the resource header in any units that reference the resources contained in the RC file. (More on this in just a bit.)
$(BCB)\include;$(BCB)\include\vcl;c:\bc5\ činclude
#include <owl\dialog.h>
#pragma hdrstop
#include "OWLUnit.h"
#include "OWLDlg.rh"
void CallOWLDialog(HWND hWndParent)
{
TWindow vclWindow(hWndParent);
TDialog* dlg =
new TDialog(&vclWindow, IDD_DIALOG);
dlg->Execute();
delete dlg;
}
Let's take a moment to analyze this code. First, notice that we include the
DIALOG.H header file, which is the header for the OWL TDialog class. Notice
also that we don't include VCL.H. Since a new unit always adds a line that
includes VCL.H, you'll need to delete that line for any units that contain OWL
code exclusively. Failure to remove that line will result in the very namespace
problems we're trying to avoid.
Next, we include the files OWLUNIT.H and OWLDLG.RH. The header for this unit would probably contain only the function prototype of any public functions in the unit. For example:
#ifndef OWLUnitH #define OWLUnitH extern "C" void CallOWLDialog(HWND hWndParent); #endifNote that we use the extern "C" syntax as we would for a function called from a DLL. Doing so is necessary to avoid linker errors later on. The code within the function is as follows:
TWindow vclWindow(hWndParent); TDialog* dlg = new TDialog(&vclWindow, IDD_DIALOG); dlg->Execute(); delete dlg;The first line creates an OWL TWindow object from the window handle of the VCL application's main form. This process is known as aliasing a non-OWL window--it's a great OWL feature. We do this because we need a TWindow pointer to call the dialog box, and aliasing the VCL window serves this purpose. Once we have the TWindow alias, we can construct the TDialog object just as we would in an OWL application. In an OWL program, you normally pass this for the parent of the dialog box. Here, we pass the address of the TWindow object we just created. We also pass the resource ID of the dialog box (defined in the RC file when the dialog box was created).
Finally, we call the Execute() method of TDialog to show the modal dialog box. After the dialog box returns, we free the memory associated with it by calling delete. All in all, it's a pretty simple operation.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
CallOWLDialog(Handle);
}
That's all there is to it. We need the window handle (HWND) of the form to
create an OWL TWindow alias for the VCL form, so we pass the Handle
property to the CallOWLDialog() function.
At this point, the application will compile. It won't link, however, because
the linker doesn't know where to find the definition of the TDialog class and
its functions.
When you static link, however, no DLLs are required. Instead, the OWL code needed to run your application is extracted from the OWL and runtime library (RTL) static libraries and is linked into your application at link time.
In theory, you should be able to choose either one of these options for the OWL code in your VCL applications. In reality, there are some problems associated with dynamic linking of the OWL libraries. I won't go into the details, but suffice it to say that the safest route is to choose static linking. While this isn't the final word on the subject, you should note that static linking is the easier and more reliable method at this time.
So how do you choose either dynamic or static linking? By the specific libraries you link into your C++Builder application. For static linking, you need to add the following library files to your project:
OWLWF.LIB BIDSF.LIBIf you were to attempt dynamic linking, then you'd need to add the OWLWFI.LIB and BIDSFI.LIB files. The easiest way to add the library files to your project is with the C++Builder's Add To Project feature. Since you've already modified your project's library path to point to your OWL library path, you can just add the filenames without path information. For example, when you choose Add To Project, a file selection dialog box opens; simply enter OWLWF.LIB in the edit box and then click the Open button. The alternative is to navigate to the \BC5\OWL\LIB directory and find the appropriate library file.
When you use Add To Project to add a library file, C++Builder will add the following lines to your project's source file:
USELIB("owlwf.lib");
USELIB("bidsf.lib");
If you prefer, you can manually add these lines to the project source file.
Listing A: OWLDLGU.H
//---------------------------------------------
#ifndef OWLDlgUH
#define OWLDlgUH
//---------------------------------------------
#include <vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
//---------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TLabel *Label3;
TLabel *Label4;
TEdit *FirstNameEdit;
TEdit *LastNameEdit;
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------
extern TForm1 *Form1;
//---------------------------------------------
#endif
Listing B: OWLDLGU.CPP
//---------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop
#include "OWLDlgU.h"
#include "OWLUnit.h"
//---------------------------------------------
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
// Create transfer buffer structure, fill it
// with the contents of the edit components,
// and pass it to the CallOWLDialog function.
TransferBuffer buffer;
strcpy(buffer.FirstName,
FirstNameEdit->Text.c_str());
strcpy(buffer.LastName,
LastNameEdit->Text.c_str());
// Call the OWL dialog.
CallOWLDialog(Handle, buffer);
// Transfer the results from the transfer
// buffer structure to the edit components.
FirstNameEdit->Text = buffer.FirstName;
LastNameEdit->Text = buffer.LastName;
}
Listing C: OWLUNIT.H
#ifndef OWLUnitH
#define OWLUnitH
struct TransferBuffer {
char FirstName[20];
char LastName[20];
};
extern "C"
void CallOWLDialog(
HWND hWndParent, TransferBuffer& tb);
#endif
Listing D: OWLUNIT.CPP
#include <owl\dialog.h>
#include <owl\edit.h>
#pragma hdrstop
#include "OWLUnit.h"
#include "OWLDlg.rh"
void CallOWLDialog(HWND hWndParent, TransferBuffer& tb)
{
// Create a TWindow alias for the VCL form.
TWindow vclWindow(hWndParent);
// Create the TDialog object.
TDialog* dlg =
new TDialog(&vclWindow, IDD_DIALOG);
// Create the edit controls required for the
// transfer buffer.
new TEdit(dlg, IDC_FIRSTNAMEEDIT, 20);
new TEdit(dlg, IDC_LASTNAMEEDIT, 20);
// Set the transfer buffer.
dlg->SetTransferBuffer(&tb);
// Show the dialog.
dlg->Execute();
delete dlg;
}
Listing E: OWLDLG.RH#define IDD_DIALOG 101 #define IDC_FIRSTNAMEEDIT 102 #define IDC_LASTNAMEEDIT 103Listing F: OWLDLG.RC
#include "OWLDlg.rh"
IDD_DIALOG DIALOG 0, 0, 240, 120
STYLE DS_MODALFRAME | DS_3DLOOK | \
DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | \
WS_CAPTION | WS_SYSMENU
CAPTION "OWL Dialog in a VCL App"
FONT 8, "MS Sans Serif"
{
CONTROL "", IDC_FIRSTNAMEEDIT, "edit", \
ES_LEFT | WS_CHILD | WS_VISIBLE | \
WS_BORDER | WS_TABSTOP, 60, 44, 100, 12
CONTROL "", IDC_LASTNAMEEDIT, "edit", \
ES_LEFT | WS_CHILD | WS_VISIBLE | \
WS_BORDER | WS_TABSTOP, 60, 72, 100, 12
CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON |\
BS_CENTER | WS_CHILD | WS_VISIBLE |\
WS_TABSTOP, 186, 6, 50, 14
CONTROL "Cancel", IDCANCEL, "BUTTON", \
BS_PUSHBUTTON | BS_CENTER | WS_CHILD |\
WS_VISIBLE | WS_TABSTOP, 186, 26, 50, 14
CONTROL "This is an OWL Dialog.", 101, \
"static", SS_LEFT | WS_CHILD | WS_VISIBLE, \
44, 20, 88, 8
CONTROL "Frame1", 102, "static",SS_ETCHEDFRAME\
| WS_CHILD | WS_VISIBLE, 8, 9, 160, 99
CONTROL "First Name:", 104, "static", \
SS_LEFT | WS_CHILD | WS_VISIBLE, 20, 47, \
40, 8
CONTROL "Last Name:", 105, "static", \
SS_LEFT | WS_CHILD | WS_VISIBLE, 20, 73, \
40, 8
}