Form->Close() und __property

Das mag manchem C++ Builder Programmierer schon widerfahren sein: Man denkt sich nichts böses, schließt eine Form und plötzlich erscheint eine noch nicht da gewesene AV:

Benachrichtigung über Debugger-Exception_2015-03-31_15-27-27

In diesem Fall ist der Debugger gleich zur Setter-Methode einer __property gesprungen:

void __fastcall TFormSetup::SetDatabaseConnected( bool AConnected )
{
    this->FDatabaseConnected = AConnected;
   /*
      Do something else...
   */
}

Hier noch das Entsprechende aus dem Header:

#ifndef UnitFormSetupH
#define UnitFormSetupH

#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>

class TFormSetup : public TForm
{
    __published:
        /*
        ....
        */
    private:
        bool FDatabaseConnected;
    public:
        __fastcall TFormSetup(TComponent* Owner);

        void __fastcall SetDatabaseConnected( bool AConnected );

        __property bool DatabaseConnected = { 
            read=FDatabaseConnected, 
            write=SetDatabaseConnected };
};

#endif

Grund für die AV ist nun, dass die TForm beim Schließen auch noch gleich eine geöffnete Datenbank-Komponente schließt (nicht im Code-Beispiel angeführt). Diese wiederum setzt im AfterDisconnect()-Event die Property DatabaseConnected auf false.

Zu diesem Zeitpunkt sind die Klassenmember aber bereits nicht mehr verfügbar, daher muss dies im Setter der __property berücksichtigt werden:

void __fastcall TFormSetup::SetDatabaseConnected( bool AConnected )
{
    if ( this != NULL )
    {
        this->FDatabaseConnected = AConnected;
        /*
        Do something else...
        */
    }
}

Dies ist ein funktionierender Weg. Ob er elegant ist, sei dahingestellt. Sollte ich einen eleganteren finden, werde ich ihn posten. Sollte jemand einen solchen kennen, bitte ich um Kommenar 🙂

SelectDirectory

Beim Versuch einen Auswahldialog für ein Verzeichnis zu erstellen, wollte der Compiler zuerst nicht complilieren.

AnsiString s;
SelectDirectory( s, TSelectDirOpts(), 0 );

Das wurde ganz einfach mit folgendem Fehler abgelehnt:

[BCC32 Fehler] UnitMain.cpp(28): E2285 Keine Übereinstimmung für 'SelectDirectory(AnsiString,TSelectDirOpts,int)' gefunden
 Vollständiger Parser-Kontext
 UnitMain.cpp(24): Analyse: void _fastcall TFormMain::ButtonBrowseClick(TObject *)

Ein Blick in den Header Vcl.FileCtrl.hpp hat dann auch gleich auf die Antwort hingewiesen:

extern PACKAGE bool __fastcall SelectDirectory(
  System::UnicodeString &Directory, 
  TSelectDirOpts Options, 
  int HelpCtx)/* overload */;

extern PACKAGE bool __fastcall SelectDirectory(
  const System::UnicodeString Caption, 
  const System::WideString Root, 
  System::UnicodeString &Directory, 
  TSelectDirExtOpts Options = (TSelectDirExtOpts() << TSelectDirExtOpt::sdNewUI ), 
  Vcl::Controls::TWinControl* Parent = (Vcl::Controls::TWinControl*)(0x0))/* overload */;

Lösung: Verzeichnisname muss vom Typ UnicodeString sein.

UnicodeString s;
SelectDirectory( s, TSelectDirOpts(), 0 );

Anmerkung: SelectDirectory wirft den selben Fehler, wenn anstelle eines AnsiString einfach nur eine Property Variable übergeben wird:

__property System::UnicodeString s {...