VCL



リストボックスからリストボックスへ

Delphi の文字列はString型で、C++Builderの文字列はAnsiStringクラスなので、こんなのになってしまいます。

[C++Builder]

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    ListBox2->Items->>Add(ListBox1->Items->Strings[ListBox1->ItemIndex]);
}


[Delphi]

procedure TForm1.Button1Click(Sender: TObject);
begin
    ListBox2.Items.Add(ListBox1.Items[ListBox1.ItemIndex]);
end;






StatusBarへのアクセス

ヘルプを鵜呑みにしてはいけないことを知りました。

[C++Builder]

void __fastcall TForm1::Button1Click(TObject *Sender)
{
StatusBar1->Panels->Items[1]->Text = "ステータスバー";
}


[Delphi]

procedure TForm1.Button1Click(Sender: TObject);
begin
StatusBar1.Panels[1].Text := 'ステータスバー';
end;






スレッド

スレッドは書き方よりも動き方を把握した上で記述すると失敗は少なくなります。

[C++Builder]

[Unit1.h]
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "cgauges.h"
#include "Thread.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
    TButton *Button1;
    TButton *Button2;
    TCGauge *CGauge1;
    void __fastcall FormCreate(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall Button2Click(TObject *Sender);
private: // ユーザー宣言
    Th *ThObj; // スレッド変数
    bool bThRun; // スレッド起動中フラグ true:起動中
    void __fastcall ThEnd(TObject *Sender); // OnTerminateイベントハンドラ
public: // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif


[Unit1.cpp]
//---------------------------------------------------------------------------
#include
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "cgauges"
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    ThObj = new Th(false);
    ThObj->FreeOnTerminate = true;
    ThObj->OnTerminate = ThEnd; // OnTerminateイベントハンドラの設定
    bThRun = true;
}
//---------------------------------------------------------------------------
// OnTerminateのイベントハンドラ
void __fastcall TForm1::ThEnd(TObject *Sender)
{
    if (Sender == ThObj) bThRun = false; // スレッドが終了
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    ThObj->Terminate(); // スレッドの停止
}
//---------------------------------------------------------------------------
// [一時停止]ボタン
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    ThObj->Suspend(); // スレッドの一時停止
}
//---------------------------------------------------------------------------
// [再スタート]ボタン
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    ThObj->Resume(); // スレッドの再開始
}
//---------------------------------------------------------------------------


[Thread.h]

//---------------------------------------------------------------------------
#ifndef ThreadH
#define ThreadH
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class Th : public TThread
{
private:
    int iProgress;
    void __fastcall _fnUpdateGauge(void); // Synchronizeで呼ばれる
protected:
    void __fastcall Execute();
public:
    __fastcall Th(bool CreateSuspended);
};
//---------------------------------------------------------------------------
#endif


[Thread.cpp]
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Thread.h"
#include "Unit1.h" // Formに張り付けたコンポーネントを呼ぶ為
#pragma package(smart_init)
//---------------------------------------------------------------------------
// 注意: VCL オブジェクトのメソッドとプロパティを使用するには, Synchronize
// を使ったメソッド呼び出しでなければなりません。次に例を示します。
//
// Synchronize(UpdateCaption);
//
// ここで, UpdateCaption は次のように記述できます。
//
// void __fastcall Th::UpdateCaption()
// {
// Form1->Caption = "スレッドから書き換えました";
// }
//---------------------------------------------------------------------------
__fastcall Th::Th(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
// フォームに張り付けたGaugeにアクセスする
void __fastcall Th::_fnUpdateGauge(void)
{
    Form1->CGauge1->Progress = iProgress;
}
//---------------------------------------------------------------------------
void __fastcall Th::Execute()
{
//---- スレッドのコードをここに記述 ----
    iProgress = 0;
    while (true) {
        if (Terminated) break; // スレッドの終了判断
        Synchronize(_fnUpdateGauge);
        if (iProgress >= 100) iProgress = 0;
        else iProgress++;
// Sleep(1);
    }
}
//---------------------------------------------------------------------------


[Delphi]

[Unit1.pas]

unit Unit1;

interface

uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    Thread, StdCtrls, ComCtrls, Gauges;

type
TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Gauge1: TGauge;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
private
    { Private 宣言 }
    ThObj: Th; // スレッド変数
    bThRun: boolean; // スレッド起動中フラグ true:起動中
procedure ThEnd(Sender: TObject); // OnTerminateイベントハンドラ
public
    { Public 宣言 }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
    ThObj := Th.Create(false);
    ThObj.OnTerminate := ThEnd; // OnTerminateイベントハンドラの設定
    bThRun := true;
end;
//OnTerminateのイベントハンドラ
procedure TForm1.ThEnd(Sender: TObject);
begin
    if Sender = ThObj then bThRun := false; // スレッドが終了
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    ThObj.Terminate; // スレッドの停止
end;
// [一時停止]ボタン
procedure TForm1.Button1Click(Sender: TObject);
begin
    ThObj.Suspend; // スレッドの一時停止
end;
// [再スタート]ボタン
procedure TForm1.Button2Click(Sender: TObject);
begin
    ThObj.Resume; // スレッドの再開始
end;

end.


[Thread.pas]

unit Thread;

interface

uses
Classes,
Windows; // sleepを使用する為

type
Th = class(TThread)
private
    { Private 宣言 }
    iProgress: integer;
    procedure _fnUpdateGauge; // Synchronizeで呼ばれる
protected
procedure Execute; override;
public
    constructor Create(mode: boolean);
end;

implementation

uses Unit1; // Formに張り付けたコンポーネントを呼ぶ為

{注意:
メインスレッドが所有する VCL のメソッド/関数/プロパティを
扱うには、このクラスに他のオブジェクトを参照するための
メソッドを追加し,Synchronize メソッドの引数として渡す必要が
あります。

たとえば,UpdateCaption メソッドを以下のように定義し,

procedure Th.UpdateCaption;
begin
Form1.Caption := 'スレッドから書き換えました';
end;

Execute メソッドの中で Synchronize メソッドに渡します。

Synchronize(UpdateCaption);
}

{ Th }

//
constructor Th.Create(mode: boolean);
begin
    inherited Create(mode);
end;
// フォームに張り付けたGaugeにアクセスする
procedure Th._fnUpdateGauge;
begin
    Form1.Gauge1.Progress := iProgress;
end;
//
procedure Th.Execute;
begin
    { スレッドとして実行したいコードをここに記述 }
    iProgress := 0;
    while true do begin
        if Terminated then exit; // スレッドの終了判断
        Synchronize(_fnUpdateGauge);
        if iProgress >= 100 then iProgress := 0
        else inc(iProgress);
//     sleep(1);
    end;
end;

end.


 「ObjectPascal」と「C++」で、コンストラクターの記述方式の違いがあります。ですが、行っていることは同じ です(^^)v。

 スレッドやって始めてWindowsのタスクの大まかな仕組みが解った気がします。僕は以前、仕事の関係で2種類の「マルチタスクOS」にタッチしていました。一つは「UNIX系」(と言ってもUNIXではシングルタスクのソフトしか書いが無く、それに近い(と、思われる(^^;)「OS/9」ではマルチタスクのソフトを書きました)です。で、もう一つは「iRMX」(このOSは、僕はあまり深入りする機会がありませんでした)です。で、僕としてはそれぞれ「UNIX系」をTSS(TimeSharingSystem)で、「iRMX」をイベント駆動型として大まかに認識していました。で、Windowsはというと、「UNIX系」に分類されると思いました。とすると、「OS/9のプログラミングのノウハウが生かせるな」となりました。  sleep(1); がスレッドの「スレッドとして実行したいコード」の中にあるのはその為です。僕がスレッドを使用してプログラミングする時は次の点を注意します。
これらを守っていれば、「とりあえず動作するかな」と思います。


Win95,98/WinNTの不思議(その1)

 実は「sleep」の存在は以外な所から発見しました。僕が最初に書いたプログラムは通信処理のプログラムで通信制御をスレッドで行うものでした。開発環境はWin95で実行環境がWinNTでした。開発がほぼ終了し、いざNTで実行してみると、

 「おぉ〜遅い。なに〜、これ!!??」

ビックリしました。何たって、GUIの表示が所々止まっちゃうものね〜。で、パフォーマンスモニタで僕の作ったソフトをモニタしてみると、何と、僕の書いたスレッドのCPU使用率が100%で表示されていました。そこで僕なりの考えを推理して到達したのが「UNIX系にはsleepみたいな命令があったな」でした。で、存在を発見し、入れてみると、見事にCPU使用率が0%に落ちました。そうするとGUIも快適に動作しました。
 よく考えると、「NTの開発者はDECでUNIXを開発していた人だったな」と何かの本で読んだのを思いだし、だとすると「タスクの優先順位はプライオリティだな」とかいろいろと想像した内容がAPIで定義されていました。逆に、「ここまで似せるならCreateProcssでなくてforkにすればいいじゃん」と思ったものです。
 さて、本題の「95/NTの不思議(その1)」になりますが、僕が一番不思議に思うのは、NTで「メチャ遅いソフト」が95では「遜色無く動作するソフト」である事です。で思うに「95のOSの内、カーネルの一部分は特殊にチューニングしているのではないかな?」と思うのですが、調べていません(^^);。
 実は、NTを使ってみた印象で一番良かったのは、何といっても
 「全然、落ちないな!」
だったのです。「C++Builder」インストール後、一番最初に作ったソフトが暴走してしまって、それ以後95がおかしくなってしまいました。その為、開発環境のOSを再インストールする際、NTに変えてしまったのでした _o_。


Win95,98/WinNTの不思議(その2)

 僕が今使っているパソコンはIBMのTP600でCPUはP?266です。このパソコンで上記サンプルプログラムを動かすと、「sleep」がなくてもストレス無く動作します。
「???」
 で、前まで使っていた東芝のTECRA730(CPUはペンタ150)で動かすと、
「やっぱり遅い」
 で、パソコンをTP600に戻し、「Synchronize」の行と、「sleep」の行をコメントにして動かすと、
「今度は、遅いな?」

 で、ここからは僕の推論ですが、「NTはハードウエアの影響を受けやすいのではないか?」ということです。
というのも、パフォーマンスモニタでCPU使用率をみると、「sleep」の行をコメントにしたプログラムでCPU使用率が100%を指していません。ところが、「Synchronize」の行と、「sleep」の行をコメントにしたプログラムを動かしてパフォーマンスモニタでみると、CPU使用率が100%を指します。コードの中にAPIをコールする部分が無くなった為、CPU割合時間分フルに動作している為でしょう。僕も最初は、「アクセラレータ関係のスピードの影響がタスクのスイッチングに影響しているのかな?」と思ったのですが、それだけではなさそうです(通信関係のAPIの中にはAPIの中でループしているものがあり、その中でループしている時は、プログラムが適度に止まって見えます。しかし、この場合API内でループしている為、パフォーマンスモニターで自分のプログラムをモニターしても、CPU使用率は0%と表示されるので注意)。ではどうすれば良いかですが、デバック段階でパフォーマンスモニタを見ながら、ターゲット環境に合わせこんでいくしか方法が今のところないのかな、と思います。







日付の扱い方

C++Builderでは一部の機能がメソッドとして実装されています。

[C++Builder]
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TDateTime       tDate = Now();
    unsigned short  year, month, day;
    unsigned short  hour, min, sec, msec;
    tDate.DecodeDate(&year, &month, &day);
    Label1->text =
        IntToStr(year)+"-"+
        IntToStr(month)+":"+
        IntToStr(day);
    tDate.DecodeTime(&hour, &min, &sec, &msec);
    Label2->Text =
        IntToStr(hour)+":"+
        IntToStr(min)+":"+
        IntToStr(sec)+":"+
        IntToStr(msec);
}


[Delphi]
procedure TForm1.Button1Click(Sender: TObject);
var
    Year, Month, Day, Hour, Min, Sec, MSec: Word;
    tDate: TDateTime;
begin
    tDate := Now;
    DecodeDate(tDate,Year,Month,Day);
    Label1.Text :=
        IntToStr(Year)+'-'+
        IntToStr(Month)+'-'+
        IntToStr(Day);
    DecodeTime(tDate,Hour,Min,Sec,MSec);
    Label2.Text :=
        IntToStr(Hour)+':'+
        IntToStr(Min)+':'+
        IntToStr(Sec)+':'+
    IntToStr(MSec);
end;

※日付関係の関数はC++Builder6でさらに充実しています。近い内にTipsに追加予定です。






StringGridでEnterで次のセルに移動
StringGridのセルにカーソルがある時、[Enter]キーで次のセルに移動する方法です。

void __fastcall TForm1::StringGrid1KeyPress(TObject *Sender, char &Key)
{
    if ( Key == VK_RETURN && StringGrid1->EditorMode) {
        if (StringGrid1->Col < StringGrid1->ColCount - 1)
            StringGrid1->Col = StringGrid1->Col + 1;
        else {
            if (StringGrid1->Row < StringGrid1->RowCount - 1)
                StringGrid1->Row = StringGrid1->Row + 1;
            else
                StringGrid1->Row = StringGrid1->FixedRows;
            StringGrid1->Col = StringGrid1->FixedCols;
        }
    }
}






StringGridの文字を右寄せや中央寄せや下寄せ、複数行で表示
DefaultDrawingプロパティをfalseにしてOnDrawCellイベントで表示命令を書きます。そのときDrawTextというAPIを使えば自由に表示できます。
文字の表示位置以外は元のままにしたい場合は次のようにします。

void __fastcall TForm1::_fnDrawCell(TObject *Sender, long Col, long Row,TRect &Rect, TGridDrawState State)
{
    RECT r = RECT(Rect);
    // 固定セルの背景色をセット
    if (State.Contains(gdFixed))
        sg->Canvas->Brush->Color= sg->FixedColor;
    //フォーカスのあるセルの背景色をセット
    else if (State.Contains(gdFocused))
        sg->Canvas->Brush->Color= sg->Options.Contains(goDrawFocusSelected)? clHighlight: sg->Color;
    //選択されているセルの背景色をセット
    else if (State.Contains(gdSelected))
        sg->Canvas->Brush->Color= clHighlight;
    //普通のセルの背景色をセット
    else
        sg->Canvas->Brush->Color= sg->Color;

    //背景色で消去
    sg->Canvas->FillRect(Rect);

    //固定セルの立体枠を描く
    if (sg->Ctl3D&&State.Contains(gdFixed))
        DrawEdge(sg->Canvas->Handle,&r,BDR_RAISEDINNER,BF_RECT);

    //フォーカスのあるセルの文字色をセット
    if (State.Contains(gdFocused)) {
        sg->Canvas->Font->Color = sg->Options.Contains(goDrawFocusSelected)? clHighlightText: sg->Font->Color;
        //フォーカス枠を描く
        DrawFocusRect(sg->Canvas->Handle,&r);
    }
    //選択されているセルの文字色をセット
    else if (State.Contains(gdSelected))
        sg->Canvas->Font->Color = clHighlightText;
    //普通のセルの文字色をセット
    else
        sg->Canvas->Font->Color= sg->Font->Color;

    //テキスト表示領域の設定
    InflateRect(&r,-2,-2);

    //テキストの表示
    DrawText(sg->Canvas->Handle,sg->Cells[Col][Row].c_str(),-1,&r,DT_RIGHT); //右寄せ
//  DrawText(sg->Canvas->Handle,sg->Cells[Col][Row].c_str(),-1,&r,DT_CENTER); //中央寄せ(水平)
//  DrawText(sg->Canvas->Handle,sg->Cells[Col][Row].c_str(),-1,&r,DT_VCENTER|DT_SINGLELINE); //中央寄せ(垂直)
//  DrawText(sg->Canvas->Handle,sg->Cells[Col][Row].c_str(),-1,&r,DT_BOTTOM|DT_SINGLELINE); //下寄せ
//  DrawText(sg->Canvas->Handle,sg->Cells[Col][Row].c_str(),-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE); //水平・垂直とも中央寄せ
//  DrawText(sg->Canvas->Handle,sg->Cells[Col][Row].c_str(),-1,&r,DT_WORDBREAK); //複数行表示
}






StringGridをソートするには
StringGridには一行のデータをTStringsとして取得できるRowsプロパティがあります。これを使って行単位で入れ替えていけばソートができます。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    _fnGridSort(StringGrid1, 1);    //1列目をキーにソートする
}
// StringGridのソート
void __fastcall TForm1::_fnGridSort(TStringGrid *p,       //StringGrid
                                    long col,             //ソートのキーとなる列
                                    bool desc = false)    //false:昇順, true:降順
{
    if ((0 <= col) && (p->ColCount <= col)) ;    //範囲外の指定
    else return;
    for (int i=p->FixedRows ; i<p->RowCount-1 ; i++) {
        for (int j=i+1 ; j<p->RowCount ; j++) {
            bool b;
            // 数値として比べてみてだめだったら文字で比べる(8と10などを文字で比べたときに逆転するのを防ぐ)
            try {
                b = p->Cells[col][i].ToDouble() > p->Cells[col][j].ToDouble();
            } catch(...) {
                b = p->Cells[col][i] > p->Cells[col][j];
            }
            // 行の入れ替え
            if (desc? !b: b) {
                TStringList *sl=new TStringList;
                sl->Assign(sg->Rows[i]);
                p->Rows[i] = p->Rows[j];
                p->Rows[j]= sl;
                sl->Free();
            }
        }
    }
}






長時間処理を行う
良い方法とは思わないので推奨しませんが、次の方法で長時間処理をしている間にイベント処理を行う事ができます。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    for (int i=0 ; i<10000 ; i++) {
        Label1->Caption = IntToStr(i);      // カウンタ表示の更新
        Application->ProcessMessages();     // 溜まっているイベントの処理
    }
}

Application->ProcessMessages();を入れると他のイベント処理を行ってくれる為、Label1を描画します。






スプラッシュの表示
メイン・フォームの前にタイトルを出すには、まずスブラッシュウインドゥを作成し、メインプログラムを呼び出す前にスブラッシュウンンドゥを呼び出すようにプロジェクトソースを書き換えます。

Application->Initialize();
SplashWindow=new TSplashWindow(Application);
SplashWindow->Position=poScreenCenter;
SplashWindow->Show();
Application->ProcessMessages();
Application->CreateForm(__classid(TForm1), &Form1);
SplashWindow->Hide();
SplashWindow->Free();
Application->Run();

※Form1 はメインフォーム、SplashWindow がスプラッシュに使うフォーム






ラベルやチェックボックスのキャプションを複数行に
改行位置に改行文字( \n )を入れば複数行に出来ます。ただしオブジェクト・インスペクタからは設定出来ません。

フォーム上で右クリックすると出てくる、「テキストで表示」から
Hint ='ここで折れる'#13'2行目'

プログラム中で
Label1->Caption = "ここでおれる\n二行目";

※TLavel,TCheckBox等のCaption,Hintは折れますが、TBotton,TFormのCaptionは折れません。






AnsiStringのメソッド
AnsiStringのメソッドは結構便利です。
AnsiString s = "abcABCabc";とします。

・n番目の文字を取り出します
s[2] → "b"
注意:(s[1]が一番前の文字になります)

・char p[]へ代入する
strcpy(p, s.c_str());
注意:char *p と定義して p=s.c_str() とはしない方が良い。

・一番最後の文字を取り出す
s.AnsiLastChar() → "c"(スペース)

・一部分を削除する
s.Delete(4,3) → "abcabc"

・間に文字を追加する
s.Insert("123",3) → "abc123ABCabc"

・長さが0かどうか調べる
s.IsEmpty() → false(s=""のときtrueなる)

・n番目の文字がパスの区切り記号(\)かどうか調べる
s.IsPathDelimiter(3) → false("a:\\file"のときtrueになる)

・指定した文字と一致する一番最後の場所を得る
s.LastDelimiter("b") → 8

・長さを得る
s.Length() → 9

・すべて小文字にする
s.LowerCase() → "abcabcabc"

・指定した文字と一致する一番最初の場所を得る
s.Pos("b") → 2

・長さを決める
s.SetLength(3) → "abc"

・同じ文字をならべる
s.StringOfChar('*',5) → "*****"

・すべて大文字にする
s.UpperCase() → "abcABCabc"

・大文字小文字を区別して比較する
s.AnsiCompare("abcabcabc") → 1(大きい。小さいときは-1、等しいときは0)

・大文字小文字を区別せずに比較する
s.AnsiCompareIC("abcabcabc") → 0(等しい。大きいときは1、等しいときは0)

次にAnsiString s = " abc "; とします。

・前の空白を取り除く
s.TrimLeft() → "abc "

・後ろの空白を取り除く
s.TrimRight() → " abc"

・前後の空白を取り除く
s.Trim() → "abc"

AnsiString s = "123"; とします。

・double型に変換する
s.ToDouble() → 123 変換出来ないときは例外(EConvertError)が起こる

・int型に変換する
s.ToInt() → 123 変換出来ないときは例外(EConvertError)が起こる

・int型に変換する
s.ToIntDef(-1) → 123 変換できなければ指定した数値になる






Iniファイルに書き込む
TIniFileクラスを使います。
整数型の書き込みにはWriteInteger()関数、bool型関数にはWriteBool()関数を使用します。
この他に、WriteString()関数もあります。

#include <IniFiles.hpp>

AnsiString FileName;
FileName = ExtractFilePath(Application->ExeName)+"MyApp.ini";

TIniFile *ini_file = new TIniFile(szFileName);

ini_file->WriteInteger("Position", "xpos_pas", fPassMaker->Top);
ini_file->WriteInteger("Position", "ypos_pas", fPassMaker->Left);
ini_file->WriteBool("Check", "check_status", Form1->CheckBox1->Checked);

delete ini_file;






Iniファイルから読み込む
書き込みと同様にTIniFileクラスを使います。

#include <IniFiles.hpp>

AnsiString FileName;
FileName = ExtractFilePath(Application->ExeName)+"MyApp.ini";

TIniFile *ini_file = new TIniFile(szFileName);

Form1->Top = ini_file->ReadInteger("Position", "xpos_pas", 0);
Form1->Left = ini_file->ReadInteger("Position", "ypos_pas", 0);
Form1->CheckBox1->Checked = ini_file->ReadBool("Check", "check_status", true);

delete ini_file;






レジストリから情報を取り込む
レジストリから情報を得るには、TRegistoryクラスを使用します。
以下のソースは、レジストリから文字列情報を取得するソースです。
Keyに検索するキー、Nameに最後に検索する値の名前、RootKeyにレジストリのルートキーを指定します。

#include <Registry.hpp> //TRegistoryを使うときに必要

AnsiString GetReg(AnsiString Key, AnsiString Name, HKEY RootKey)
{
  AnsiString s;
  TRegistry *Reg = new TRegistry();
  Reg->RootKey = RootKey;
  if (Reg->OpenKey(Key,false)) {
    if (Reg->ValueExists(Name))
      s = Reg->ReadString(Name);
    Reg->CloseKey();
  }
  Reg->Free();
  return Value;
}






メニューの動的な追加削除
動的にメニューを追加・削除する簡単なプログラムです。Addボタンを押すとメニューが追加され、ItemAddボタンを押すと最後に追加されたメニューにメニュー項目が追加されます。
Delete,ItemDeleteボタンは対応するAdd〜ボタンで追加された最後のメニュー(メニュー項目)を削除します。

//---------------------------------------------------------------------------
//  Unit1.h
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
  __published: // IDE 管理のコンポーネント
  TMainMenu *MainMenu1;
  TButton *ButtonAdd;
  TButton *ButtonDelete;
  TButton *ButtonItemAdd;
  TButton *ButtonItemDelete;
  void __fastcall ButtonAddClick(TObject *Sender);
  void __fastcall ButtonDeleteClick(TObject *Sender);
  void __fastcall ButtonItemAddClick(TObject *Sender);
  void __fastcall ButtonItemDeleteClick(TObject *Sender);
private:
  void __fastcall UserMenuClick(TObject * Sender); // ユーザー宣言
public: // ユーザー宣言
  __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
//  Unit1.cpp
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TForm1::UserMenuClick(TObject * Sender)
{
  Application->MessageBox(((TMenuItem*)Sender)->Caption.c_str(),"AddMenu",MB_OK);
}
//---------------------------------------------------------------------------
//メニューの追加
void __fastcall TForm1::ButtonAddClick(TObject *Sender)
{
  TMenuItem *new_item = new TMenuItem(MainMenu1);
  new_item->Caption = "新メニュー"+IntToStr(MainMenu1->Items->Count);
  new_item->OnClick = UserMenuClick;
  MainMenu1->Items->Insert(MainMenu1->Items->Count, new_item);//最後のメニューに追加
}
//---------------------------------------------------------------------------
//メニューの削除
void __fastcall TForm1::ButtonDeleteClick(TObject *Sender)
{
  if (MainMenu1->Items->Count > 0) {
    MainMenu1->Items->Delete(MainMenu1->Items->Count-1); //最後のメニューを削除
  }
}
//---------------------------------------------------------------------------
//メニューの項目の追加
void __fastcall TForm1::ButtonItemAddClick(TObject *Sender)
{
  TMenuItem *to_item;
  TMenuItem *new_item;
  if (MainMenu1->Items->Count > 0) {
    to_item = MainMenu1->Items->Items[MainMenu1->Items->Count-1];
    new_item = new TMenuItem(to_item);
    new_item->Caption = "新メニュー項目"+IntToStr(to_item->Count);
    new_item->OnClick = UserMenuClick;
    to_item->OnClick = NULL;
    to_item->Insert(to_item->Count, new_item); //最後のメニュー項目に追加
  }
}
//---------------------------------------------------------------------------
//メニュー項目の削除
void __fastcall TForm1::ButtonItemDeleteClick(TObject *Sender)
{
  TMenuItem *to_item;
  if (MainMenu1->Items->Count > 0) {
   to_item = MainMenu1->Items->Items[MainMenu1->Items->Count-1];
   if (to_item->Count > 0) {
     to_item->Delete(to_item->Count-1); //最後のメニュー項目を削除
   }
 }
}






Memoのタブ幅を変えるには
 Memoのタブ幅を変えるには、TMemoコンポーネントに対してEM_SETTABSTOPSメッセージを送ります。 タブ幅を4文字に変更するサンプルです。

  // フォントのピクセル単位を格納する構造体
  struct FontSize
  {
    long x;
    long y;
  } FontPixel;

  // タブ幅のサイズを計算
  FontPixel.x = (GetDialogBaseUnits()&0xffff)/2*4;

  // メッセージを送る
  SendMessage(Memo1->Handle, EM_SETTABSTOPS, 1, long(&FontPixel.x));






MemoやRichEditで内容が変更されているか知るには
 MemoやRichEditで内容が変更されているか知るには、Modifiedプロパティを確認することでテキストが編集されたか知ることができます。
Modifiedプロパティがtrueの場合は編集されています。






MemoやRichEditで操作を一つ元に戻すには
 MemoやRichEditで操作を一つ元に戻すには、TMemoコンポーネント等に対してEM_UNDOメッセージを送ります。

  // メッセージを送る
  SendMessage(Memo1->Handle, EM_UNDO, 0, 0);

  その他に、Performメソッドを利用することもできます。
  Memo1->Perform(EM_UNDO, 0, 0)






MemoやRichEditで現在のカーソル位置を得るには
 MemoやRichEditで現在のカーソル位置を得るには、TMemoコンポーネント等に対してEM_LINEFROMCHARメッセージとEM_LINEINDEXメッセージを送ることで取得できます。






MemoやRichEditで扱える文字数を制限するには
 MemoやRichEditで扱える文字数を制限するには、TMemoコンポーネント等に対してMaxLengthプロパティを設定することで、入力できる文字の最大長を指定することができます。
 設定する文字の長さはバイト単位です。(日本語は2バイト使用します)

  // 最大値の設定
  Memo1->MaxLength = 80;






ループ時間中に他のイベントの処理をするには
 ループ時間中に他のイベントの処理をするには、ループ処理中に下記の命令を実行させます。
 下記の命令を実行すると長時間の処理中でも中断のようなことができます。

Application->ProcessMessages();






このページ直接たどり着いた方は、Topページに戻ってください。

by kenji-b

Copyright (C) 1998-2001 kenji-b homepage All Rights Reserved.