Menü’s verschoben

Hier beschreibe ich einen “Bug”, der einen richtig zum Narren halten kann. Zumindest hat er das mit mir getan: Die Hauptmenü’s von Programmen sind plötzlich (fast) alle nach links verschoben.

Seit längerer Zeit arbeite ich an einer kleinen Software mit grafischer Oberfläche, die ich mit dem Embarcadero C++ Builder XE2 entwickle. Ich wollte, dass die GUI richtig toll aussieht, und habe mich daher für de Verwendung von TMS Components entschieden.

Da ich an diesem Projekt meist zu Hause arbeite, mache ich sehr viel an meinem privaten Windows7-Notebook. Alles war sehr toll, bis mir eines Tages eine Unschönheit in der GUI auffiel: Das Hauptmenü war irgendwie verschoben:

R[el]Ational_2015-04-13_10-18-28-

Das Menü zeigte plötzlich nach links, anstelle dass es wie gewohnt symmetrisch nach unten ausgerichtet war.

Auf meinem Zweitcomputer hatte ich ebenfalls eine Entwicklungsumgebung eingerichtet und dort war alles so, wie ich es erwarten würde:

R[el]Ational_2015-04-13_10-18-28

Nach etlichen Untersuchungen und Vergleichen glaubte ich die Ursache gefunden zu haben: Es mussen die TMS Components sein, da ich von diesen 2 unterschiedliche Versionen im Einsatz hatte. Da diese eh schon etliche Semester auf dem Buckel hatten entschied ich mich kurzerhand zu einem Upgrade und installierte die neueste Version auf meinem Notebook.

Eine Stunde und runde 250€ später war das Ergebnis ernüchternd: alles war beim alten. Und erst jetzt fiel mir auf, dass auch andere Programme von diesem Problem betroffen waren. So z.B. Notepad, das vermutlich nicht auf TMS Components zurück greift:

Unbenannt - Editor_2015-04-13_10-01-30

Nach einiger Recherche fand ich heraus, dass es sich um ein Problem von Windows 7 handelt und dass dieses Problem häufig in Zusammenhang mit einem Upgrade auf SP1 stehen könnte: Es seien die Tablet PC Einstellungen, die auf “Linkshand” eingerichtet werden müssen:

Tablet PC-Einstellungen_2015-04-13_10-22-15

Der Dialog sei in der Systemsteuerung hinterlegt. Bei mir war das allerdings nicht so, ich musste den Dialog mit folgendem Befehl aufrufen:

Start / Ausführen und:

shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}

Option Linkshändig aktivieren hat dann das Problem behoben

 

Kind-Klasse von TDataModule oder TForm und Stack Overflow

In C++ Builder Programmen kann es bei der Vererbung von TDataModule oder TForm Klassen zu einer bösen Überraschung in Form eines Stapelüberlaufs kommen:

class TInvoices : public TDataModule
{
    __published:

    private:
        int FCustomerId;
    public:
        // standard constructor
        __fastcall TInvoices( TComponent* Owner );
        // überladener constructor mit zusätzlichem parameter
        __fastcall TInvoices( TComponent* Owner, int ACustomerId );

        __property int CustomerId = { read=FCustomerId, write=FCustomerId };
};

Grund für den Stack Overflow ist der 2. Parameter (“int ACustomerId”) im überladenen constructor, der nachträglich eingefügt wurde.

Mögliche Lösungen des Problems:

  • Hinter dem 2. Parameter weitere Parameter anfügen
  • Die beiden Parameter vertauschen (int/TComponet)
  • Den Datentyp des 2. Parameter ändern (z.B. unsigned long anstelle von int)

UPDATE mit JOIN unter MSSQL

Eine Sache, die im SQL-“Standard” überall ein wenig anders ist, ist das UPDATE unter Einbezug einer zweiten Tabelle:

UPDATE T1
SET T1.articleNumber = T2.articleId
FROM TableOne T1
JOIN TableTwo T2 ON T2.id = T1.id

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 {...