C++Builder 6 (Borland, 2002) je RAD prostředí nad knihovnou VCL a překladačem BCC32 5.6. Formulář se navrhuje myší, kód událostí se píše v C++.
| Soubor | Obsah |
|---|---|
Project1.bpr | Projekt (XML/makefile): nastavení, seznam jednotek |
Project1.cpp | WinMain, vytvoření formulářů, Application->Run() |
Unit1.cpp / Unit1.h | Kód a deklarace třídy formuláře |
Unit1.dfm | Vlastnosti komponent na formuláři (binárně nebo textově) |
Project1.res | Ikona, verze, další prostředky |
*.obj, *.tds, *.il? | Výstupy překladu a ladicí info (lze mazat) |
*.bpk / *.bpl | Zdroj / binárka balíčku komponent |
| Klávesa | Akce |
|---|---|
| F9 | Přeložit a spustit |
| Ctrl+F9 | Přeložit (make) |
| Shift+F9 | Build – přeložit vše |
| F12 | Přepnout formulář / kód |
| F11 | Object Inspector |
| Ctrl+F12 / Shift+F12 | Seznam jednotek / formulářů |
| F5 | Breakpoint |
| F7 / F8 | Krok do / přes |
| Ctrl+F2 | Ukončit laděný program |
| Ctrl+Space | Code Completion |
| Ctrl+Shift+C | Class Completion |
| Ctrl+Shift+↑/↓ | Přeskok mezi deklarací a definicí |
Hlavní soubor projektu vypadá takto:
#include <vcl.h>
#pragma hdrstop
USEFORM("Unit1.cpp", Form1);
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try {
Application->Initialize();
Application->Title = "Moje aplikace";
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &e) {
Application->ShowException(&e);
}
return 0;
}
Každý formulář je třída odvozená od TForm. Komponenty z něj jsou ukazatele v sekci __published, kterou spravuje IDE. VCL objekty se vždy vytvářejí přes new a nikdy ne na zásobníku.
| Klíčové slovo | Význam |
|---|---|
__fastcall | Volací konvence VCL (registry), povinná u metod a událostí VCL |
__published | Sekce viditelná v Object Inspectoru, ukládá se do .dfm |
__property | Vlastnost s čtecí/zapisovací metodou nebo položkou |
__closure | Ukazatel na metodu konkrétního objektu (typ události) |
__classid(T) | Metatřída (TMetaClass*) pro CreateForm, InheritsFrom |
dynamic_cast<T*> | Bezpečné přetypování Sender |
Hlavička formuláře (Unit1.h):
#ifndef Unit1H
#define Unit1H
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
class TForm1 : public TForm
{
__published: // spravuje IDE
TButton *Button1;
TEdit *Edit1;
void __fastcall Button1Click(TObject *Sender);
private:
int FPocet;
void __fastcall SetPocet(int Value);
public:
__fastcall TForm1(TComponent* Owner);
__property int Pocet = { read = FPocet, write = SetPocet };
};
extern PACKAGE TForm1 *Form1;
#endif
Implementace (Unit1.cpp):
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner), FPocet(0)
{
}
void __fastcall TForm1::SetPocet(int Value)
{
FPocet = Value;
Caption = "Počet: " + IntToStr(FPocet);
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Pocet = Pocet + 1; // volá SetPocet
}
Dynamické vytvoření komponenty a přiřazení události za běhu. Vlastník (this) komponentu sám uvolní, Parent určuje, kde se zobrazí:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TButton *b = new TButton(this);
b->Parent = this;
b->SetBounds(10, 10, 120, 25);
b->Caption = "Dynamické";
b->Tag = 42;
b->OnClick = DynClick; // metoda: void __fastcall DynClick(TObject*)
}
void __fastcall TForm1::DynClick(TObject *Sender)
{
TButton *b = dynamic_cast<TButton*>(Sender);
if (b) ShowMessage("Tag = " + IntToStr(b->Tag));
}
Další formulář můžeš otevřít modálně, nebo vytvořit až za běhu. Druhý formulář v tom případě odeber z Project → Options → Forms → Auto-create:
#include "Unit2.h"
TForm2 *f = new TForm2(this);
try {
if (f->ShowModal() == mrOk)
Edit1->Text = f->Edit1->Text;
}
__finally {
delete f;
}
Všechny vizuální komponenty sdílejí vlastnosti Left, Top, Width, Height, Visible, Enabled, Align, Anchors, Hint, Tag a události OnClick a OnKeyDown. Tabulka uvádí jen to, co je pro komponentu specifické.
| Komponenta (záložka) | Klíčové vlastnosti | Hlavní události |
|---|---|---|
TLabel (Standard) | Caption, Font, AutoSize, WordWrap | OnClick |
TButton (Standard) | Caption, Default, Cancel, ModalResult | OnClick |
TEdit (Standard) | Text, MaxLength, PasswordChar, ReadOnly | OnChange, OnKeyPress, OnExit |
TMemo (Standard) | Lines (TStrings), ScrollBars, WordWrap | OnChange |
TListBox (Standard) | Items, ItemIndex, MultiSelect, Selected[i], Sorted | OnClick, OnDblClick |
TComboBox (Standard) | Items, ItemIndex, Text, Style (csDropDownList) | OnChange |
TCheckBox (Standard) | Checked, State, AllowGrayed | OnClick |
TRadioGroup (Standard) | Items, ItemIndex, Columns | OnClick |
TPanel (Standard) | Align, BevelOuter, Caption | OnResize |
TMainMenu / TPopupMenu | Items (TMenuItem) | OnClick položky |
TStringGrid (Additional) | Cells[c][r], ColCount, RowCount, FixedRows, Options | OnSelectCell, OnDrawCell |
TPageControl (Win32) | ActivePage, ActivePageIndex | OnChange |
TStatusBar (Win32) | SimpleText, Panels[i]->Text | – |
TTimer (System) | Interval (ms), Enabled | OnTimer |
TEdit a TMemo:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Add("Zadáno: " + Edit1->Text);
Edit1->Clear();
Edit1->SetFocus();
}
// povolit jen číslice v TEdit
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if ((Key < '0' || Key > '9') && Key != VK_BACK)
Key = 0;
}
TListBox a TComboBox:
ListBox1->Items->Add("Jablko");
ListBox1->Items->Add("Hruška");
if (ListBox1->ItemIndex >= 0)
Label1->Caption = ListBox1->Items->Strings[ListBox1->ItemIndex];
ListBox1->Items->Delete(0);
ComboBox1->Style = csDropDownList;
ComboBox1->Items->Assign(ListBox1->Items);
ComboBox1->ItemIndex = 0;
TStringGrid:
StringGrid1->ColCount = 3;
StringGrid1->RowCount = 5;
StringGrid1->Cells[0][0] = "Jméno"; // [sloupec][řádek]
StringGrid1->Cells[1][0] = "Věk";
for (int r = 1; r < StringGrid1->RowCount; r++)
StringGrid1->Cells[0][r] = "Položka " + IntToStr(r);
StringGrid1->Options << goEditing << goColSizing; // Set<> operátor <<
Dialogy a zprávy:
ShowMessage("Hotovo");
if (MessageDlg("Uložit změny?", mtConfirmation,
TMsgDlgButtons() << mbYes << mbNo, 0) == mrYes)
Ulozit();
AnsiString jmeno = InputBox("Dotaz", "Zadej jméno:", "");
Application->MessageBox("Text", "Titulek", MB_OK | MB_ICONINFORMATION);
AnsiString (alias String) je základní řetězec VCL. Indexuje se od 1, ne od 0, a sčítání má operátorem +. Na char* ho převedeš metodou c_str().
| Metoda / funkce | Příklad | Výsledek |
|---|---|---|
Length() | s.Length() | počet znaků |
SubString(od, delka) | "Ahoj".SubString(2, 2) | "ho" |
Pos(podretezec) | s.Pos("x") | pozice od 1, 0 = nenalezeno |
UpperCase() / LowerCase() | s.UpperCase() | nový řetězec |
Trim() / TrimLeft() / TrimRight() | s.Trim() | bez mezer na okrajích |
Insert(t, pos) / Delete(pos, n) | s.Delete(1, 3) | mění s |
IsEmpty() | s.IsEmpty() | bool |
ToInt() / ToDouble() | s.ToInt() | číslo, při chybě výjimka |
StringReplace | StringReplace(s, ",", ".", TReplaceFlags() << rfReplaceAll) | nový řetězec |
IntToStr / StrToInt | IntToStr(42) | "42" |
StrToIntDef | StrToIntDef("abc", 0) | 0 (bez výjimky) |
FloatToStr / StrToFloat | FloatToStr(3.5) | "3,5" (podle DecimalSeparator) |
FormatFloat | FormatFloat("0.00", x) | "3,14" |
Format | Format("%s má %d let", ARRAYOFCONST((jm, vek))) | formátovaný text |
IntToHex | IntToHex(255, 4) | "00FF" |
Ukázka:
AnsiString s = " Ahoj světe ";
s = s.Trim(); // "Ahoj světe"
int p = s.Pos(" "); // 5
AnsiString prvni = s.SubString(1, p - 1); // "Ahoj"
char c = s[1]; // 'A' – indexace od 1!
const char *cs = s.c_str(); // pro WinAPI / C funkce
std::string std_s = s.c_str(); // převod na std::string
AnsiString zpet = std_s.c_str();
// bezpečný převod vstupu
double cena;
try {
cena = StrToFloat(Edit1->Text);
} catch (EConvertError &e) {
ShowMessage("Neplatné číslo");
}
Label1->Caption = Format("Cena: %.2f Kč, kusů: %d",
ARRAYOFCONST((cena, 3)));
TDateTime je double: celá část jsou dny od 30. 12. 1899, desetinná část je čas.
TDateTime ted = Now(); // datum + čas
TDateTime dnes = Date(); // jen datum
Label1->Caption = FormatDateTime("dd.mm.yyyy hh:nn:ss", ted);
TDateTime zitra = dnes + 1;
int dni = (int)(zitra - dnes);
unsigned short r, m, d;
DecodeDate(dnes, r, m, d);
TDateTime dt = EncodeDate(2024, 12, 24);
TDateTime x = StrToDate("24.12.2024"); // podle místního nastavení
TStringList je univerzální seznam řetězců. Hodí se na řazení, dvojice klíč=hodnota i rozdělení textu:
TStringList *sl = new TStringList;
try {
sl->Sorted = true;
sl->Duplicates = dupIgnore;
sl->Add("Petr"); sl->Add("Anna"); sl->Add("Petr");
// sl: Anna, Petr
sl->Clear(); sl->Sorted = false;
sl->Values["jmeno"] = "Jan"; // "jmeno=Jan"
AnsiString j = sl->Values["jmeno"];
sl->CommaText = "a,b,c"; // rozdělení podle čárky
for (int i = 0; i < sl->Count; i++)
Memo1->Lines->Add(sl->Strings[i]);
int idx = sl->IndexOf("b"); // 1, -1 = nenalezeno
}
__finally {
delete sl;
}
Textové soubory je nejjednodušší načíst a uložit přes TStrings (Memo1->Lines, TStringList). Na binární data použij TFileStream.
| Funkce | Účel |
|---|---|
FileExists(cesta) / DirectoryExists(cesta) | Existence souboru / složky |
ForceDirectories(cesta) | Vytvoření složek včetně nadřazených |
DeleteFile(cesta) / RenameFile(stare, nove) | Smazání / přejmenování |
ExtractFilePath / ExtractFileName / ExtractFileExt | Části cesty |
ChangeFileExt(cesta, ".bak") | Změna přípony |
ExtractFilePath(Application->ExeName) | Složka programu |
FindFirst / FindNext / FindClose | Procházení souborů ve složce |
Načtení a uložení textu s dialogy:
void __fastcall TForm1::OtevritClick(TObject *Sender)
{
OpenDialog1->Filter = "Textové soubory (*.txt)|*.txt|Všechny soubory (*.*)|*.*";
if (OpenDialog1->Execute())
Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
}
void __fastcall TForm1::UlozitClick(TObject *Sender)
{
SaveDialog1->DefaultExt = "txt";
if (SaveDialog1->Execute())
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
}
Binární zápis a čtení struktury:
struct TZaznam { int id; double hodnota; char jmeno[32]; };
void Uloz(const AnsiString &soubor, const TZaznam &z)
{
TFileStream *fs = new TFileStream(soubor, fmCreate);
try {
fs->WriteBuffer(&z, sizeof(z));
}
__finally {
delete fs;
}
}
bool Nacti(const AnsiString &soubor, TZaznam &z)
{
if (!FileExists(soubor)) return false;
TFileStream *fs = new TFileStream(soubor, fmOpenRead | fmShareDenyWrite);
try {
fs->ReadBuffer(&z, sizeof(z));
}
__finally {
delete fs;
}
return true;
}
Výpis souborů ve složce:
TSearchRec sr;
if (FindFirst("C:\\Data\\*.txt", faAnyFile, sr) == 0) {
do {
ListBox1->Items->Add(sr.Name + " (" + IntToStr(sr.Size) + " B)");
} while (FindNext(sr) == 0);
FindClose(sr);
}
Nastavení v INI souboru (#include <IniFiles.hpp>):
TIniFile *ini = new TIniFile(ChangeFileExt(Application->ExeName, ".ini"));
try {
ini->WriteInteger("Okno", "Left", Left);
ini->WriteString("Uzivatel", "Jmeno", Edit1->Text);
Left = ini->ReadInteger("Okno", "Left", 100);
bool b = ini->ReadBool("Volby", "Zvuk", true);
}
__finally {
delete ini;
}
Registr Windows (#include <Registry.hpp>):
TRegistry *reg = new TRegistry;
try {
reg->RootKey = HKEY_CURRENT_USER;
if (reg->OpenKey("Software\\MojeFirma\\MojeApp", true)) { // true = vytvořit
reg->WriteString("Posledni", OpenDialog1->FileName);
if (reg->ValueExists("Pocet"))
int n = reg->ReadInteger("Pocet");
reg->CloseKey();
}
}
__finally {
delete reg;
}
VCL výjimky jsou třídy odvozené od Exception a chytá se vždy referencí. Rozšíření __finally zaručí uvolnění prostředků a nahrazuje RAII u VCL objektů.
| Výjimka | Kdy vzniká |
|---|---|
EConvertError | StrToInt, StrToFloat, StrToDate s neplatným vstupem |
EStringListError | Index mimo rozsah v TStrings |
EFOpenError / EFCreateError | Soubor nelze otevřít / vytvořit |
EAccessViolation | Přístup na neplatnou adresu (např. smazaný objekt) |
EZeroDivide / EDivByZero | Dělení nulou (float / int) |
EDatabaseError | Chyby databázových komponent |
EAbort | Tichá výjimka z Abort(), nezobrazí se |
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TStringList *sl = new TStringList;
try {
try {
sl->LoadFromFile("data.txt");
int n = StrToInt(sl->Strings[0]);
if (n < 0)
throw Exception("Záporná hodnota: " + IntToStr(n));
}
catch (EFOpenError &e) {
ShowMessage("Soubor nelze otevřít: " + e.Message);
}
catch (EConvertError &e) {
ShowMessage("Špatný formát čísla");
}
catch (Exception &e) {
ShowMessage(e.ClassName() + ": " + e.Message);
}
}
__finally {
delete sl; // provede se vždy
}
}
Vlastní třída výjimky:
class EMojeChyba : public Exception {
public:
int Kod;
__fastcall EMojeChyba(const AnsiString &Msg, int kod)
: Exception(Msg), Kod(kod) {}
};
throw EMojeChyba("Neplatný stav", 17);
Globální zachycení nezachycených výjimek (např. pro logování):
// v deklaraci: void __fastcall AppException(TObject *Sender, Exception *E);
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->OnException = AppException;
}
void __fastcall TForm1::AppException(TObject *Sender, Exception *E)
{
TStringList *log = new TStringList;
try {
AnsiString f = ExtractFilePath(Application->ExeName) + "chyby.log";
if (FileExists(f)) log->LoadFromFile(f);
log->Add(DateTimeToStr(Now()) + " " + E->Message);
log->SaveToFile(f);
}
__finally { delete log; }
Application->ShowException(E);
}
Nástroje ladění:
Breakpoint (F5): pravým tlačítkem na něm nastavíš Condition nebo Pass count.
Watch (Ctrl+F5) a Evaluate/Modify (Ctrl+F7): sledování a změna hodnot za běhu.
Call Stack (Ctrl+Alt+S) a Local Variables (Ctrl+Alt+L).
OutputDebugString("text") vypisuje do View → Debug Windows → Event Log.
CodeGuard (Project → Options → CodeGuard) hlásí úniky paměti a přístupy mimo pole.
Zastavování debuggeru na výjimkách vypneš v Tools → Debugger Options → Language Exceptions.
Kresli v události OnPaint (formuláře nebo TPaintBox). Jinak se kresba při překreslení okna ztratí. Kresba do TImage->Canvas se pamatuje v bitmapě a zůstává.
Člen TCanvas | Použití |
|---|---|
Pen->Color, Pen->Width, Pen->Style | Barva a styl čáry |
Brush->Color, Brush->Style | Výplň (bsClear = průhledná) |
Font->Name, Font->Size, Font->Color, Font->Style | Písmo |
MoveTo(x,y), LineTo(x,y) | Čára |
Rectangle, Ellipse, RoundRect, Polygon | Tvary |
FillRect(TRect) | Vyplnění obdélníku štětcem |
TextOut(x,y,s), TextWidth(s), TextHeight(s) | Text a jeho rozměry |
Pixels[x][y] | Jednotlivý pixel (pomalé) |
Draw(x,y,graphic), StretchDraw(rect,graphic) | Vykreslení bitmapy |
Barvy: konstanty clRed, clBlue, clBtnFace apod. nebo (TColor)RGB(r, g, b).
void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
TCanvas *c = PaintBox1->Canvas;
c->Brush->Color = clWhite;
c->FillRect(PaintBox1->ClientRect);
c->Pen->Color = clBlue;
c->Pen->Width = 2;
c->MoveTo(0, PaintBox1->Height / 2);
for (int x = 0; x < PaintBox1->Width; x++) {
int y = PaintBox1->Height / 2 - 40 * sin(x / 20.0); // #include <math.h>
c->LineTo(x, y);
}
c->Brush->Color = (TColor)RGB(255, 200, 0);
c->Ellipse(10, 10, 60, 60);
c->Brush->Style = bsClear;
c->Font->Size = 12;
c->Font->Style = TFontStyles() << fsBold;
c->TextOut(70, 20, "sin(x)");
}
Plynulá kresba přes bitmapu v paměti (bez blikání):
Graphics::TBitmap *bmp = new Graphics::TBitmap;
try {
bmp->Width = PaintBox1->Width;
bmp->Height = PaintBox1->Height;
bmp->Canvas->Brush->Color = clBlack;
bmp->Canvas->FillRect(Rect(0, 0, bmp->Width, bmp->Height));
// ... kreslení do bmp->Canvas ...
PaintBox1->Canvas->Draw(0, 0, bmp);
}
__finally {
delete bmp;
}
Načtení obrázku do TImage. Pro JPEG přidej #include <jpeg.hpp>:
Image1->Picture->LoadFromFile("foto.jpg");
Image1->Stretch = true;
Image1->Proportional = true;
Image1->Picture->SaveToFile("kopie.bmp"); // jen bitmapa
Animace časovačem a překreslení:
// v .h: int FX; (člen třídy, nastav na 0 v konstruktoru)
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Timer1->Interval = 30; // ms
Timer1->Enabled = true;
DoubleBuffered = true; // méně blikání
}
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
FX = (FX + 3) % ClientWidth;
Invalidate(); // vyvolá OnPaint
StatusBar1->SimpleText = TimeToStr(Time());
}
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Brush->Color = clRed;
Canvas->Ellipse(FX, 50, FX + 30, 80);
}
Všechny tři technologie používají stejný řetězec: dataset (tabulka nebo dotaz) → TDataSource → data-aware komponenty (TDBGrid, TDBEdit, TDBNavigator). Pro nové projekty a MS SQL/Access doporučuji ADO. BDE je zastaralé a pod Wine/Win10+ problematické.
| Technologie | Záložka | Komponenty | Vhodné pro |
|---|---|---|---|
| ADO | ADO | TADOConnection, TADOQuery, TADOTable, TADOCommand | MS SQL, Access, Excel, ODBC |
| BDE | BDE | TDatabase, TTable, TQuery | Paradox, dBase, staré aplikace |
| dbExpress | dbExpress | TSQLConnection, TSQLQuery + TClientDataSet | InterBase, Oracle, MySQL 3.x, DB2 |
| InterBase Express | InterBase | TIBDatabase, TIBQuery, TIBTransaction | InterBase / Firebird |
Běžné operace s datasetem (společné pro TTable, TQuery, TADOQuery…):
| Metoda / vlastnost | Účel |
|---|---|
Open() / Close() / Active | Otevření / zavření |
First(), Next(), Prior(), Last(), Eof, Bof | Pohyb po záznamech |
FieldByName("x")->AsString / AsInteger / AsFloat / AsDateTime | Čtení a zápis pole |
Append() / Insert() / Edit() / Post() / Cancel() / Delete() | Úpravy |
Locate("pole", hodnota, TLocateOptions()) | Vyhledání záznamu |
RecordCount, IsEmpty() | Počet záznamů |
DisableControls() / EnableControls() | Zrychlení hromadného průchodu |
ADO – připojení a dotaz s parametrem:
#include <ADODB.hpp>
ADOConnection1->ConnectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Data\\firma.mdb;";
// MS SQL: "Provider=SQLOLEDB;Data Source=server;Initial Catalog=db;"
// "Integrated Security=SSPI;"
ADOConnection1->LoginPrompt = false;
ADOConnection1->Connected = true;
ADOQuery1->Connection = ADOConnection1;
ADOQuery1->SQL->Text = "SELECT * FROM Zakaznici WHERE Mesto = :mesto";
ADOQuery1->Parameters->ParamByName("mesto")->Value = "Praha";
ADOQuery1->Open();
DataSource1->DataSet = ADOQuery1;
DBGrid1->DataSource = DataSource1;
Průchod výsledkem:
ADOQuery1->DisableControls();
try {
double suma = 0;
for (ADOQuery1->First(); !ADOQuery1->Eof; ADOQuery1->Next())
suma += ADOQuery1->FieldByName("Castka")->AsFloat;
Label1->Caption = FormatFloat("#,##0.00 Kč", suma);
}
__finally {
ADOQuery1->EnableControls();
}
Zápis – přes dataset, nebo příkazem SQL:
ADOTable1->Append();
ADOTable1->FieldByName("Jmeno")->AsString = Edit1->Text;
ADOTable1->FieldByName("Vek")->AsInteger = StrToInt(Edit2->Text);
ADOTable1->Post();
ADOCommand1->Connection = ADOConnection1;
ADOCommand1->CommandText = "UPDATE Zakaznici SET Aktivni = 0 WHERE Id = :id";
ADOCommand1->Parameters->ParamByName("id")->Value = 15;
ADOCommand1->Execute();
Transakce:
ADOConnection1->BeginTrans();
try {
// ... více příkazů ...
ADOConnection1->CommitTrans();
}
catch (...) {
ADOConnection1->RollbackTrans();
throw;
}
BDE – TQuery nad aliasem (alias se nastavuje v BDE Administrator):
Query1->DatabaseName = "DBDEMOS";
Query1->SQL->Clear();
Query1->SQL->Add("SELECT Name, Capital FROM country WHERE Population > :p");
Query1->ParamByName("p")->AsInteger = 10000000;
Query1->Open();
// pro INSERT/UPDATE/DELETE: Query1->ExecSQL();
Dlouhé operace patří do potomka TThread. Ke komponentám VCL smíš přistupovat jen z hlavního vlákna, takže aktualizace GUI jde vždy přes Synchronize(). Šablonu vytvoříš v File → New → Other → Thread Object.
| Člen | Význam |
|---|---|
TThread(bool CreateSuspended) | true = spustit až voláním Resume() |
Execute() | Tělo vlákna (přepisuješ) |
Synchronize(metoda) | Spustí metodu v hlavním vlákně (BCB6: metoda bez parametrů) |
Terminate() / Terminated | Požadavek na ukončení / jeho test v Execute |
FreeOnTerminate | Objekt se po skončení sám smaže |
WaitFor() | Čekání na dokončení |
OnTerminate | Událost po skončení (běží v hlavním vlákně) |
Priority | tpLower, tpNormal, tpHigher… |
Vlákno s průběhem v TProgressBar:
// Vypocet.h
class TVypocet : public TThread
{
private:
int FPozice;
void __fastcall Aktualizuj();
protected:
void __fastcall Execute();
public:
__fastcall TVypocet(bool CreateSuspended);
};
// Vypocet.cpp
#include "Unit1.h"
__fastcall TVypocet::TVypocet(bool CreateSuspended)
: TThread(CreateSuspended)
{
FreeOnTerminate = true;
}
void __fastcall TVypocet::Execute()
{
for (int i = 0; i <= 100 && !Terminated; i++) {
Sleep(50); // náročná práce
FPozice = i;
Synchronize(Aktualizuj); // bezpečný přístup ke GUI
}
}
void __fastcall TVypocet::Aktualizuj()
{
Form1->ProgressBar1->Position = FPozice;
}
Spuštění a zastavení z formuláře:
// v Unit1.h: TVypocet *FVlakno; (člen TForm1, v konstruktoru = NULL)
void __fastcall TForm1::StartClick(TObject *Sender)
{
FVlakno = new TVypocet(true);
FVlakno->OnTerminate = VlaknoSkoncilo;
FVlakno->Resume();
}
void __fastcall TForm1::StopClick(TObject *Sender)
{
if (FVlakno) FVlakno->Terminate();
}
void __fastcall TForm1::VlaknoSkoncilo(TObject *Sender)
{
FVlakno = NULL; // objekt se smaže sám (FreeOnTerminate)
ShowMessage("Hotovo");
}
Sdílená data mezi vlákny chraň kritickou sekcí (#include <SyncObjs.hpp>):
TCriticalSection *Zamek = new TCriticalSection;
Zamek->Acquire();
try {
SdilenySeznam->Add("položka");
}
__finally {
Zamek->Release();
}
Samostatné EXE bez DLL/BPL. Aby program běžel na jiném PC bez instalace C++Builderu:
Project → Options → Packages: vypni Build with runtime packages.
Project → Options → Linker: vypni Use dynamic RTL (jinak je potřeba cc3260mt.dll).
Project → Options → Compiler: tlačítko Release (bez ladicích informací).
Udělej Build (Shift+F9), ne jen Make.
Při použití BDE je navíc potřeba nainstalovat BDE. ADO využívá MDAC, který je součástí Windows.
| Problém | Příčina / řešení |
|---|---|
[Linker Fatal Error] Unable to open file 'XYZ.OBJ' | Chybějící jednotka nebo knihovna – přidej ji do projektu nebo doplň Library path |
Unresolved external '__fastcall TForm1::Button1Click(...)' | Deklarace v .h bez definice v .cpp (smazaný handler) – dopiš tělo nebo smaž deklaraci |
| Handler zmizel po uložení | IDE maže prázdné handlery – napiš do něj alespoň komentář |
Access violation at address ... | Práce s již smazaným objektem, NULL ukazatel, přístup ke GUI z vlákna |
VCL style classes must be constructed using operator new | VCL objekt vytvořený na zásobníku – použij new |
| Pomalý překlad | Zapni Pre-compiled headers a drž #pragma hdrstop hned za #include <vcl.h> |
.dfm nejde otevřít / konflikt v Gitu | Ulož formuláře jako text: pravé tlačítko na formuláři → Text DFM |
| Čeština se zobrazuje špatně | VCL 6 není Unicode – nastav Font->Charset = EASTEUROPE_CHARSET, zdrojové soubory ukládej v CP1250 |
| Desetinná čárka vs. tečka | StrToFloat používá DecimalSeparator – případně nastav DecimalSeparator = '.'; |
Rozdíly oproti modernímu C++. BCC32 5.6 podporuje C++98 jen částečně. Chybí auto, lambdy, nullptr a std::unique_ptr (je jen std::auto_ptr). Složitější šablony a část Boostu nepřeložíš. STL (STLport) je k dispozici: #include <vector>, <map>, <string>. Členy třídy nejde inicializovat v deklaraci, dělej to v konstruktoru.
C++Builder 6 pod Linuxem (Wine). Instalátor IDE běží v 32bitovém prefixu. Hotové VCL aplikace běží pod Wine většinou bez problémů:
export WINEPREFIX=~/.wine-bcb6 WINEARCH=win32
winecfg # Windows XP
wine /media/cdrom/install.exe
winetricks mdac28 jet40 # pro ADO/Access
wine explorer /desktop=bcb6,1920x1080 \
"C:\\Program Files\\Borland\\CBuilder6\\Bin\\bcb.exe"
BDE pod Wine vyžaduje v BDE Administrator nastavit LOCAL SHARE = TRUE a změnit NET DIR na zapisovatelnou složku.
Překlad z příkazové řádky:
bpr2mak Project1.bpr
make -f Project1.mak
REM nebo jednoduchý konzolový program:
bcc32 -WC -O2 program.cpp