Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Converting FormCreate to use Constructors



Permlink Replies: 7 - Last Post: May 10, 2017 11:25 AM Last Post By: Remy Lebeau (Te... Threads: [ Previous | Next ]
William Macon

Posts: 19
Registered: 2/24/16
Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 5, 2017 8:26 AM
In my project to migrate legacy CodeGear C++Builder2007 code to Embarcadero C++Builder10, I am stuck on a FormCreate issue or something related to CreateForm. The current problem involves code to bring up a htm help file browser, as shown below (including some AnsiString changes necessary for successful compile):

wgs.cpp snippets
================

#include <vcl.h>
USEFORM("wgshelp.cpp", Help);
Application->CreateForm(__classid(THelp), &Help);

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(THelp), &Help);
}

wgshelp.cpp code
================

//---------------------------------------------------------------------------

#include <vcl.h>
#include <stdio.h>
#pragma hdrstop

#include "wgshelp.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SHDocVw_OCX"
#pragma link "shdocvw_ocx"
#pragma resource "*.dfm"
THelp *Help;
//---------------------------------------------------------------------------
__fastcall THelp::THelp(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall THelp::update_browser(char anchor[255])
{
char temp[255];
char url[255];
TSearchRec sr;
String currdir;

wgsbrowser->Left = 0;
wgsbrowser->Top = 0;
wgsbrowser->Width = Help->ClientWidth;
wgsbrowser->Height = Help->ClientHeight;

currdir = GetCurrentDir();
sprintf(temp, ".
Data
helpfiles
*.htm");
if (FindFirst(temp, faAnyFile, sr) == 0)
{
if (strlen(anchor) > 0)
{
sprintf(url, "%s
Data
helpfiles
%s#%s", currdir.c_str(), sr.Name.c_str(), anchor);
// sprintf(url, "%s
Data
helpfiles
%s#%s", currdir.c_str(), AnsiString(sr.Name).c_str(), anchor);
}
else
{
sprintf(url, "%s
Data
helpfiles
%s", currdir.c_str(), sr.Name.c_str());
// sprintf(url, "%s
Data
helpfiles
%s", currdir.c_str(), AnsiString(sr.Name).c_str());
}
FindClose(sr);
// wgsbrowser->Navigate(WideString(url), 0, NULL, NULL, NULL);
wgsbrowser->Navigate((wchar_t*)WideString(url), 0, NULL, NULL, NULL);
}
}

//---------------------------------------------------------------------------

void __fastcall THelp::FormActivate(TObject *Sender)
{
wgsbrowser->Left = 0;
wgsbrowser->Top = 0;
wgsbrowser->Width = Help->ClientWidth;
wgsbrowser->Height = Help->ClientHeight;

wgsbrowser->Refresh();
}

//---------------------------------------------------------------------------

void __fastcall THelp::FormResize(TObject *Sender)
{
wgsbrowser->Left = 0;
wgsbrowser->Top = 0;
wgsbrowser->Width = Help->ClientWidth;
wgsbrowser->Height = Help->ClientHeight;
}

//---------------------------------------------------------------------------
void __fastcall THelp::FormCreate(TObject *Sender)
{
update_browser("");
}
//---------------------------------------------------------------------------

The code compiles, but I get run-time errors. The Debugger Exception Notification reads:
'Project wgs.exe raised exception class $C0000005 with message 'access violation at 0x00661bdb: read of address 0x00000058'

Selecting 'Break" points back to the update_browser(""); line above.

I understand I should not use the FormCreate event moving forward, but to use the constructor instead. And NEVER use the OnCreate (and OnDestroy) event in C++, in ANY version. OK, I tried changing the FormCreate section (and the .h file) to:

void __fastcall THelp::THelp(TComponent* Owner)
: TForm(Owner)
{
update_browser("");
}

But this gave me errors (E2238, E2344) for multiple/earlier declaration of '__fastcall THelp::THelp(TComponent*)'. I also tried using the FMX.WebBrowser.TWebBrowser function but quickly discovered the incompatibilities between using vcl and fmx. I recognize that I need to do something to convert this FormCreate event and perhaps the CreateForm call also, but not sure what exactly. Also, not sure if the FormActivate and FormResize events also need to be converted and how. The program has about 40 'Application->CreateForm' events and I will probably need to update all of the forms. So, I'm looking for some guidance for how to proceed, hopefully with some relatively simple specific changes I need to make rather than major code revisions. And if there's a good reference that describes how to convert legacy forms to TForms using constructors, that would be helpful. Thanks.

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 5, 2017 10:01 AM   in response to: William Macon in response to: William Macon
William wrote:

In my project to migrate legacy CodeGear C++Builder2007 code
to Embarcadero C++Builder10, I am stuck on a FormCreate issue
or something related to CreateForm.

Form creation semantics have not changed over the years. You really shouldn't
have been using the OnCreate event in C++ to begin with, but at least its
behavior hasn't changed between CB2007 and CB10. Just make sure that the
Form's OldCreateOrder property is false.

The current problem involves code to bring up a htm help file browser,
as shown below (including some AnsiString changes necessary for
successful compile):

Why is wgs.cpp calling Application->CreateForm(THelp) in global scope outside
of WinMain()? It should not be doing that. There should be only one call
to CreateForm(THelp) and it needs to be inside of WinMain() between Application->Initialize()
and Application->Run(), eg:

//---------------------------------------------------------------------------
 
#include <vcl.h>
#pragma hdrstop
#include <tchar.h>
//---------------------------------------------------------------------------
 
// declare other forms...
USEFORM("wgshelp.cpp", Help);
// declare other forms...
//---------------------------------------------------------------------------
WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
    try
    {
        Application->Initialize();
        Application->MainFormOnTaskBar = true;
        // create other forms...
        Application->CreateForm(__classid(THelp), &Help);
        // create other forms...
        Application->Run();
    }
    catch (Exception &exception)
    {
        Application->ShowException(&exception);
    }
    catch (...)
    {
        try
        {
            throw Exception("");
        }
        catch (Exception &exception)
        {
            Application->ShowException(&exception);
        }
    }
    return 0;
}
//---------------------------------------------------------------------------


void __fastcall THelp::update_browser(char anchor[255])

Why are you using char[] for everything? Use System::String instead, since
that is what FindFirst() and TWebBrowser are actually expecting.

And you should not be using the "Help" global pointer from inside of THelp's
own methods. Use the implicit 'this' pointer instead.

Try this:

__fastcall THelp::THelp(TComponent *Owner)
    : TForm(Owner)
{
    // alternatively, move this to the OnShow event instead...
    update_browser("");
}
 
void __fastcall THelp::update_browser(const String &anchor)
{
    TSearchRec sr;
 
    // this is redundant, you can get rid of this completely and set the
    // browser's Align property to alClient at design-time instead...
    //
    wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);
 
    String HelpFilesDir = ExtractFilePath(Application->ExeName) + _D("Data\\helpfiles\\");
    if (FindFirst(HelpFilesDir + _D("*.htm"), faAnyFile, sr) == 0)
    {
        String url = HelpFilesDir + sr.Name;
        FindClose(sr);
 
        if (anchor.Length() > 0)
            url.cat_sprintf(_D("#%s"), anchor.c_str());
 
        wgsbrowser->Navigate(url);
    }
}


void __fastcall THelp::FormActivate(TObject *Sender)
{
// this is redundant, you can get rid of this completely and set the
// browser's Align property to alClient at design-time instead...
//
wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);

wgsbrowser->Refresh();
}

void __fastcall THelp::FormResize(TObject *Sender)
{
// this is redundant, you can get rid of this completely and set the
// browser's Align property to alClient at design-time instead...
//
wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);
}
{code}

The code compiles, but I get run-time errors. The Debugger Exception
Notification reads:

'Project wgs.exe raised exception class $C0000005 with message 'access
violation at 0x00661bdb: read of address 0x00000058'

AVs at addrsses near 0 usually mean you are accessing a NULL pointer.

Selecting 'Break" points back to the update_browser(""); line above.

Then it is showing you the wrong location, because the actual CALL instruction
to invoke update_browser() can't throw an exception. It would have to be
thrown inside of update_browser() instead.

I understand I should not use the FormCreate event moving forward,
but to use the constructor instead.

Yes.

Alternatively, call it in the OnShow event instead, so you do not slow down
Form creation anymore, and you delay loading the browser until the Form is
actually being displayed to the user.

OK, I tried changing the FormCreate section (and the .h file) to:

void __fastcall THelp::THelp(TComponent* Owner)
: TForm(Owner)
{
update_browser("");
}

But this gave me errors (E2238, E2344) for multiple/earlier
declaration of '__fastcall THelp::THelp(TComponent*)'.

That can only happen if you are defining THelp() twice. It should only be
defined once. REMOVE FormCreate() altogether, and USE the THelp() body
that already existed in your original code. Don't create a second copy of
it.

I also tried using the FMX.WebBrowser.TWebBrowser function but quickly
discovered the incompatibilities between using vcl and fmx.

Yes, do not mix VCL and FMX together.

I recognize that I need to do something to convert this FormCreate event
and perhaps the CreateForm call also, but not sure what exactly.

No, you don't need to change the call to Application->CreateForm(). But
you do have a redundant erroneous call that needs to be removed.

Also, not sure if the FormActivate and FormResize events also need to
be converted and how.

No, they do not. But they are also redundant, since you should be using
the browser's Align property instead of resizing the browser manually.

The program has about 40 'Application->CreateForm' events

Why? You should not be auto-creating that many Forms at app startup. It
is best practice to auto-create only the MainForm, and then dynamically create
the other Forms using 'new' when they are actually needed.

and I will probably need to update all of the forms.

Do all of them use the TForm::OnCreate event?

--
Remy Lebeau (TeamB)
William Macon

Posts: 19
Registered: 2/24/16
Re: Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 5, 2017 1:31 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
TCppWebBrowser *wgsbrowser;
Remy Lebeau (TeamB) wrote:
Why is wgs.cpp calling Application->CreateForm(THelp) in global scope outside
of WinMain()? It should not be doing that. There should be only one call
to CreateForm(THelp) and it needs to be inside of WinMain() between Application->Initialize()
and Application->Run(), eg:

It's there, I just didn't include those parts in my code snippet. The THelp call is towards the end of the other 40-some calls.

Why are you using char[] for everything? Use System::String instead, since
that is what FindFirst() and TWebBrowser are actually expecting.

It's the legacy code I got to work with, not my programming. This program started about 15 years ago or so with Borland C++ and got migrated to CodeGear several years ago. I'm just trying to improve it. I've been savvy enough to make edits over the past year, but this migration effort is making me learn C++ all over again.

void __fastcall THelp::update_browser(const String &anchor)
{
TSearchRec sr;

wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);

String HelpFilesDir = ExtractFilePath(Application->ExeName) + _D("Data
helpfiles
");
if (FindFirst(HelpFilesDir + _D("*.htm"), faAnyFile, sr) == 0)
{
String url = HelpFilesDir + sr.Name;
FindClose(sr);

if (anchor.Length() > 0)
url.cat_sprintf(_D("#%s"), anchor.c_str());

wgsbrowser->Navigate(url);
}
}

I'm getting compiler errors E2451 Undefined symbol ' ' and E2379 Statement missing ; at the anchor.Length() line, and cannot find what may be causing these. Any suggestions?

Do all of them use the TForm::OnCreate event?

I did a search for OnCreate and found no events. Am I misunderstanding what you are asking?

I'll add snippets from the wgshelp.h file:

class THelp : public TForm
{
__published: // IDE-managed Components
TCppWebBrowser *wgsbrowser;
void __fastcall FormActivate(TObject *Sender);
void __fastcall FormResize(TObject *Sender);
// void __fastcall FormCreate(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall THelp(TComponent* Owner);
// void __fastcall THelp::update_browser(char anchor[255]);
void __fastcall THelp::update_browser(const String &anchor);
};

Is the 'TCppWebBrowser *wgsbrowser;' syntax still correct or does this need a change? Thanks.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 8, 2017 1:10 PM   in response to: William Macon in response to: William Macon
William wrote:

It's there, I just didn't include those parts in my code snippet. The
THelp call is towards the end of the other 40-some calls.

Again, why are you auto-creating 40+ Forms? You really should not be doing
that at all.

I'm getting compiler errors E2451 Undefined symbol ' ' and E2379
Statement missing ; at the anchor.Length() line, and cannot find
what may be causing these. Any suggestions?

What are the EXACT error messages? Please copy-paste the error messages
as-is. And indicate which lines of code they are referring to.

I did a search for OnCreate and found no events.
Am I misunderstanding what you are asking?

Clearly, yes. In the code you have shown so far, FormCreate() is the event
handler that the IDE has auto-generated for the Form's OnCreate event. Do
all of your Forms have a similar FormCreate() method in them? If so, you
need to get rid of it and move their body codes to the Form constructors
instead.

// void __fastcall THelp::update_browser(char anchor[255]);
void __fastcall THelp::update_browser(const String &anchor);

Remove the "THelp::" portion from the .h file, it only belongs in the .cpp
file.

Is the 'TCppWebBrowser *wgsbrowser;' syntax still correct or does this
need a change?

It is fine as-is.

--
Remy Lebeau (TeamB)
William Macon

Posts: 19
Registered: 2/24/16
Re: Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 9, 2017 9:38 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
What are the EXACT error messages? Please copy-paste the error messages
as-is. And indicate which lines of code they are referring to.

Remove the "THelp::" portion from the .h file, it only belongs in the .cpp
file.

I removed the THelp portion and still getting compiler errors. I'm providing the entire .h and .cpp code as corrected and exact error messages.

Here's the .h file:
//---------------------------------------------------------------------------
#ifndef wgshelpH
#define wgshelpH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "SHDocVw_OCX.h"
#include <OleCtrls.hpp>
#include "shdocvw_ocx.h"
//---------------------------------------------------------------------------
class THelp : public TForm
{
__published: // IDE-managed Components
TCppWebBrowser *wgsbrowser;
void __fastcall FormActivate(TObject *Sender);
void __fastcall FormResize(TObject *Sender);
// void __fastcall FormCreate(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall THelp(TComponent* Owner);
// void __fastcall THelp::update_browser(char anchor[255]);
void __fastcall update_browser(const String &anchor);
};
//---------------------------------------------------------------------------
extern PACKAGE THelp *Help;
//---------------------------------------------------------------------------
#endif

Here's the .cpp file, with errors listed after the lines in question:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <stdio.h>
#pragma hdrstop
#include "wgshelp.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SHDocVw_OCX"
#pragma link "shdocvw_ocx"
#pragma resource "*.dfm"
THelp *Help;
//---------------------------------------------------------------------------
__fastcall THelp::THelp(TComponent* Owner)
: TForm(Owner)
{
update_browser("");
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall THelp::update_browser(const String &anchor)
{
TSearchRec sr;
wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);
[bcc32 Error] wgshelp.cpp(61): E2451 Undefined symbol ' '
[bcc32 Error] wgshelp.cpp(61): E2379 Statement missing ;
String HelpFilesDir = ExtractFilePath(Application->ExeName) + _D("Data
helpfiles
");
[bcc32 Error] wgshelp.cpp(63): E2379 Statement missing ;
if (FindFirst(HelpFilesDir + _D("*.htm"), faAnyFile, sr) == 0)
[bcc32 Error] wgshelp.cpp(64): E2451 Undefined symbol 'HelpFilesDir'
{
String url = HelpFilesDir + sr.Name;
FindClose(sr);
  if (anchor.Length() > 0)
[bcc32 Error] wgshelp.cpp(69): E2379 Statement missing ;
url.cat_sprintf(_D("#%s"), anchor.c_str());
  wgsbrowser->Navigate(url);
[bcc32 Error] wgshelp.cpp(69): E2379 Statement missing ;
}
}

//---------------------------------------------------------------------------
void __fastcall THelp::FormActivate(TObject *Sender)
{
wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);
wgsbrowser->Refresh();
}

//---------------------------------------------------------------------------
void __fastcall THelp::FormResize(TObject *Sender)
{
wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);
}

I will also note that whenever I try to save changes to the file, I get a popup error message "Field Help.wgsbrowser does not have a corresponding component. Remove the component?" The component is the 'TCppWebBrowser *wgsbrowser;' line in .h which is why I asked about it. If I remove it then I get other errors, so I have left it in.

Again, why are you auto-creating 40+ Forms? You really should not be doing
that at all.
In the code you have shown so far, FormCreate() is the event
handler that the IDE has auto-generated for the Form's OnCreate event. Do
all of your Forms have a similar FormCreate() method in them? If so, you
need to get rid of it and move their body codes to the Form constructors
instead.

Sorry, I have no idea why the original programmer chose this approach many years ago; this is what I got to work with. Regardless, it was working before with CodeGear C++Builder2007 and my intent is to make whatever specific changes are necessary to migrate the code to C++Builder10.2. Once I get past this first Form issue, I'll see what other Forms need similar corrections. Hopefully not all of them but probably so.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 9, 2017 10:54 AM   in response to: William Macon in response to: William Macon
William wrote:

wgsbrowser->SetBounds(0, 0, ClientWidth, ClientHeight);
[bcc32 Error] wgshelp.cpp(61): E2451 Undefined symbol ' '

Is that EXACTLY what really appears in the quotes? Because there is no possible
way any compiler would treat a whitespace character as a symbol identifier.

Whatever. I was trying to be helpful by using SetBounds() to set all dimensions
in one go instead of 4. Just go back to the original code:

wgsbrowser->Left = 0;
wgsbrowser->Top = 0;
wgsbrowser->Width = ClientWidth;
wgsbrowser->Height = ClientHeight;


Or, just do what I said earlier - get rid of all these manual position/size
adjustments and just set the browser's Align property to alClient instead,
let the VCL handle the adjustments for you.

The rest of the errors are likely all just side effects of the first error.
Fix that, and the rest should disappear.

I will also note that whenever I try to save changes to the file, I
get a popup error message "Field Help.wgsbrowser does not have a
corresponding component. Remove the component?" The component is the
'TCppWebBrowser *wgsbrowser;' line in .h which is why I asked about
it. If I remove it then I get other errors, so I have left it in.

The IDE is complaining that the corresponding TCppWebBrowser component is
missing in the Form Designer. Did you remove it? If you can't see it visually,
double check the data in the DFM file. If the TCppWebBrowser objct is really
missing, delete the declaration from the .h file and re-drop a new TCppWebBrowser
component on the Form Designer to recreate it.

--
Remy Lebeau (TeamB)
William Macon

Posts: 19
Registered: 2/24/16
Re: Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 10, 2017 6:29 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
The IDE is complaining that the corresponding TCppWebBrowser component is
missing in the Form Designer. Did you remove it? If you can't see it visually,
double check the data in the DFM file. If the TCppWebBrowser objct is really
missing, delete the declaration from the .h file and re-drop a new TCppWebBrowser
component on the Form Designer to recreate it.

This may be the key to the wgsbrowser issues. Yes, the TCppWebBrowser component is missing from the DFM file. I checked the old form in the CodeGear IDE and it's there, with a subordinate object 'wgsbrowser TCppWebBrowser' to 'Help THelp', but simply copying the old project files into the current project produced an error "Error Reading Form: 'Help', Class TCppWebBrowser not found..." So I tried to recreate the component starting with my current backup files. However, while the old CodeGear IDE Tool Palette has TCppWebBrowser under the Internet category, the new RadStudio 10.2 Tool Palette does not have an Internet category and I could not find the TCppWebBrowser component in any other category. How can I recreate this component? Is there a manual process for adding Class TCppWebBrowser back into the Form Designer?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Converting FormCreate to use Constructors
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 10, 2017 11:25 AM   in response to: William Macon in response to: William Macon
William wrote:

Yes, the TCppWebBrowser component is missing from the DFM file.
I checked the old form in the CodeGear IDE and it's there, with a
subordinate object 'wgsbrowser TCppWebBrowser' to 'Help THelp',
but simply copying the old project files into the current project
produced an error "Error Reading Form: 'Help', Class TCppWebBrowser
not found..."

There you go then. The Internet package that registers TCppWebBrowser was
not installed in the IDE at the time the project was ported, so the IDE removed
it from the DFM, but not the rest of the code.

So I tried to recreate the component starting with my current backup
files. However, while the old CodeGear IDE Tool Palette has TCppWebBrowser
under the Internet category, the new RadStudio 10.2 Tool Palette does
not have an Internet category and I could not find the TCppWebBrowser
component in any other category.

You need to install the relevant package (bcbie250.bpl) into the IDE.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02