BDS2006(C++Builder)からRubyを使う(2)

前回の続き

メソッドの実行結果を受け取り、結果を表示します。

フォームにメモとボタンをを貼り付けます。

Unit1.h の private に次のメソッドを追加します。

__declspec(dllexport) char* (*rb_string_value_cstr)(volatile unsigned long*);

Unit1.cpp の TForm1::TForm1(TComponent* Owner) に次の行を追加します。

rb_string_value_cstr = (char* (*)(volatile unsigned long*))GetProcAddress(hDll,"rb_string_value_cstr");

ボタンのイベントを追加します。

void __fastcall TForm1::Button3Click(TObject *Sender)
{
  if (!hDll)
  {
    Application->MessageBox("msvcrt-ruby18.dll が見つかりませんでした。",
                            "DLL エラー",
                            MB_ICONSTOP | MB_OK);
    return;
  }
  //Rubyインタプリタの初期化
  ruby_init();

  //スクリプトの実行
  unsigned long value = rb_eval_string("'実行結果'");
  //実行結果の文字列を取得する
  AnsiString result = rb_string_value_cstr(&value);
  Memo1->Lines->Add(result);

  //スクリプトの実行
  value = rb_eval_string("File.open('test.rb', 'r' ).read");

  //実行結果の文字列を取得する
  TStringList* list = new TStringList();
  list->Text = rb_string_value_cstr(&value);
  Memo1->Lines->Add(list->Text);
  delete list;

  //Rubyインタプリタのクリーンアップ
  ruby_cleanup(0);

}

ボタンをクリックして、メモに「実行結果」と「test.rb」の内容が表示されたら成功です。

BDS2006(C++Builder)からRubyを使う

Borland Developer Studio 2006(C++Builder)からRubyを使ってみます。

箏音の日記 – C++とRubyC#からRubyのコードを実行するテスト を参考にしました。

Rubyをコンパイルする手間を省くために、Ruby-mswin32 から 最新リリース版である ruby-1.8.4-i386-mswin32.zip をダウンロードします。

ダウンロードした ruby-1.8.4-i386-mswin32.zip を展開し、bin\msvcrt-ruby18.dll をC++Builderのプロジェクトを作成するディレクトリにコピーします。

C++Builderを起動し、VCLフォームアプリケーションを作成します。

まず、起動時にDLLを読み込み、終了時に解放します。

Unit1.hに次のコードを追加。

private:
  HINSTANCE hDll;

Unit1.cppに次のコードを追加。

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  hDll = LoadLibrary("msvcrt-ruby18.dll");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  if (hDll) FreeLibrary(hDll);
}

DLLが正しく読み込まれていることを確認します。

次は、Rubyプログラムを実行してみます。

Unit1.hに次のコードを追加。

private:
  __declspec(dllexport) void (*ruby_init)(void);
  __declspec(dllexport) unsigned long (*rb_eval_string)(const char*);
  __declspec(dllexport) int (*ruby_cleanup)(int);

Unit1.cppに次のコードを追加。

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  hDll = LoadLibrary("msvcrt-ruby18.dll");
  if (hDll)
  {
    ruby_init = (void (*)(void))GetProcAddress(hDll,"ruby_init");
    rb_eval_string = (unsigned long (*)(const char*))GetProcAddress(hDll,"rb_eval_string");
    ruby_cleanup = (int (*)(int))GetProcAddress(hDll,"ruby_cleanup");
  }
}

フォームにボタンを追加し、ボタンを押したときのイベントを記述します。

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  if (!hDll)
  {
    Application->MessageBox("msvcrt-ruby18.dll が見つかりませんでした。",
                            "DLL エラー",
                            MB_ICONSTOP | MB_OK);
    return;
  }
  //Rubyインタプリタの初期化
  ruby_init();
  //スクリプトの実行
  rb_eval_string("File.open('a.txt', 'w' ) { |f| f.puts 'success' }");
  //Rubyインタプリタのクリーンアップ
  ruby_cleanup(0);
}

実行してみましょう。
ボタンを押すとsuccessと記述されたa.txtが作成されると成功。

次に、スクリプトをファイルから読み込んで実行します。

Unit1.hに次のコードを追加。

private:
  __declspec(dllexport) void (*ruby_init_loadpath)(void);
  __declspec(dllexport) void (*rb_load)(unsigned long, int);
  __declspec(dllexport) unsigned long (*rb_str_new2)(const char*);

Unit1.cppに次のコードを追加。

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  hDll = LoadLibrary("msvcrt-ruby18.dll");
  (中略)
  ruby_init_loadpath = (void (*)(void))GetProcAddress(hDll,"ruby_init_loadpath");
  rb_load = (void (*)(unsigned long, int))GetProcAddress(hDll,"rb_load");
  rb_str_new2 = (unsigned long (*)(const char*))GetProcAddress(hDll,"rb_str_new2");

フォームにもう一つボタンを作成し、ボタンを押したときのイベントを記述します。

//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  if (!hDll)
  {
    Application->MessageBox("msvcrt-ruby18.dll が見つかりませんでした。",
                            "DLL エラー",
                            MB_ICONSTOP | MB_OK);
    return;
  }

  //Rubyインタプリタの初期化
  ruby_init();
  ruby_init_loadpath();
  // スクリプトをファイルから読み込んで実行
  rb_load(rb_str_new2("test.rb"), 0);
  //Rubyインタプリタのクリーンアップ
  ruby_cleanup(0);
}

test.rbファイルはこう書いてみました。

File.open('b.txt', 'w' ) { |f|
  f.puts 'success'
}

実行してみましょう。
ボタンを押すとsuccessと記述されたb.txtが作成されると成功。

続く

BDS2006(BCB)でWinHelpを使う

C++ Builder 6 で作成したプロジェクトを Borland Developer Studio 2006 に更新すると、ヘルプファイルを開けなくなる。

Application->HelpContext() などでヘルプを表示しようとすると、「状況関知型ヘルプがインストールされていません。」というエラーメッセージが表示されるようになった。

原因は、標準のヘルプシステムが WinHelp から HTMLHelp に変更されたため。

Borland Developer Studio 2006 で WinHelp を使用するには、Main フォームに次の行を追加する。

#pragma link "WinHelpViewer"

これで、C++ Builder 6 と同じように WinHelp を使用することができる。

EVariantErrorをキャッチしないと、CodeGuardがメモリリークを検出する

例外が投げられたときにEVariantErrorをキャッチしないと、CodeGuardがメモリリークを検出します。
Exceptionはキャッチしなくてもいいようです。 (BDS2006 C++Builder Update2)

これはメモリリークを検出しません。

try
{
  VarToDateTime("");
}
catch (EVariantError &E)
{
}

これはメモリリークを報告します。

try
{
  VarToDateTime("");
}
catch (...)
{
}

BDS(Delphi/BCB)用SQLiteライブラリ

Borland Developer Studio(Delphi/BCB)でSQLiteを扱うためのライブラリを調べました。

■追記
Delphi2009用のSQLiteライブラリを開発されている方がいました。

■さらに追記
C++Builder XEでSQLiteを使ってみました。

BDS2006でstd::ofstreamを使うと、CodeGuardがエラーを報告する

BDS2006でstd::ofstreamを使うと、CodeGuardがエラーを報告します。

関数に不正なファイル/パイプ ストリーム (0xXXXXXXXX) が渡されました。

[QCにありました。][1]

とりあえず、メニューの「プロジェクト」→「オプション」→「リンカ(ilink32)」→「リンク」の「動的RTLを使う」のチェックをはずすことで回避することにしました。

これが最善策かどうかはわかりません。

[1]: http://qc.borland.com/wc/qcmain.aspx?d=22688

BDS2006(C++Builder)レビュー(2)

BDS2006(C++Builder)を使っていて気がついたこと。

* プロジェクトマネージャーのファイルをドラッグ&ドロップで、順番を入れ替えることができる。
C++Builder6でもできた。

* ToDoリストがおかしい
アクション項目が表示されないものがある。
項目を開いたとき、カーソルの位置がずれるものがある。

* コーディング時とデバッグ時でレイアウトが自動的に変わって便利。

* デスクトップツールバーの「Classic Undocked」を選択するとBCB6に似たレイアウトになる。

BDS2006(C++Builder)レビュー(1)

[Borland Developer Studio 2006 Update 2][1] が公開されたので、C++Builderを使ってみました。

C++Builder6と比較して。

* エディタの機能強化が強化されている

もともと補完機能ぐらいしか取り柄のないシンプルなエディタでしたが、 BDS2006では、機能が向上していました。
エディタは一番よく使う機能なので助かります。

* メモリの消費量がそんなに多くない

パッケージには、メモリが512MB(推奨!GB)以上必要とありましたが、 C++Builderだけなら、そんなに必要ないようです。
400MBでも十分余っていました。

* ヘルプファイルが使いにくい。

ヘルプが非常に使いにくい。
必要ない.NETなどのヘルプまであるからでしょうか。
慣れるまで苦労しそう。

* リファクタリングの機能がおかしい。

リファクタリングの機能を使うと、関係ない箇所が変更されます。
(追記) Hotfixが公開されていました。
[Hotfixes for Borland Developer Studio 2006 Update 2のHotfix 6][2]

[1]: http://info.borland.com/06/bds/bds2006_reg_updates_down.html
[2]: http://support.borland.com/entry.jspa?externalID=4725&categoryID=385

API を使って縦書きなどのフォントを指定する

[API を使って縦書きなどのフォントを指定する][1] を参考に BCB6 でやってみたけど、文字化けした。

GetObject() を使えばうまく行きました。以下がそのコード。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
TLogFont lbl;
GetObject(Canvas->Font->Handle, sizeof(TLogFont), &lbl);
// 日本語の場合、幅:高さ=1:2にする
lbl.lfWidth = 10;
lbl.lfHeight = 20;
// 文字の太さ(0~1000)
// 「標準」の太さは400、「太字」の太さは700
lbl.lfWeight = 400;
// 半時計回りの角度(単位は1/10度)
lbl.lfEscapement = 2700;
// 下線なし(デフォルトでは「あり」)
lbl.lfUnderline = false;
// 打ち消し線なし(デフォルトでは「あり」)
lbl.lfStrikeOut = false;
// 斜体無効(デフォルトでは有効)
lbl.lfItalic = false;
// 縦書き用の「@」がつくフォントを使用する
strcpy(lbl.lfFaceName, “@MS ゴシック”);

// フォントを作成
Canvas->Font->Handle = CreateFontIndirect(&lbl);
// 背景を透明に
Canvas->Brush->Style = bsClear;
// 文字列を描画
Canvas->TextOut(300, 300, “縦書き文字”);
}

[1]: http://www2.big.or.jp/~osamu/Delphi/Tips/key.cgi?key=0#0137.txt