第20回エンバカデロ デベロッパーキャンプで喋れなくなりました。
明日のセッションA4「アンドキュメンテッド(?) VCL 〜逆引きVCL新機能〜」は自分の諸事情により急遽講演中止となりました。
セッションを申し込まれた方、大変申し訳ありません。
明日の分は、後日Webセミナーとして実施する予定です。
RAD Studio XEとF-Secure Internet Security 2011の相性問題
メインマシンのセキュリティソフトをウイルスバスターからエフセキュア インターネット セキュリティ 2011に乗り換えたのだけど、RAD Studio XEを起動すると「使用許諾コードが不正」と判断されてRAD Studioが起動しなかった。
これは、エフセキュア インターネット セキュリティのディープガードに由来するもので、以下の手順でBDS.EXEをディープガードの検索対象から外せばOK。
- F-Secure Internet Security 2011を開く
- [コンピュータ|ウイルスとスパイウェア スキャン]を選択
- [除外したオブジェクトを表示]をクリック
- [オブジェクト]タブを選択
- [追加]をクリックして、"C:\Program Files (x86)\Embarcadero\RAD Studio\8.0\bin\bds.exe"を選択
- [OK]をクリック
- [OK]をクリック
- [閉じる]をクリック
以上でRAD Studioが検索対象から外れるので、RAD Studioが問題なく実行される。
C++BuilderではOpen Tools APIを使ったアドオンをデバッグできない件についての回避方法
C++BuilderではOpen Tools APIを使ったアドオンをデバッグできない件があり、これをQC#92188として登録したのだけど、Embarcaderoから回答があって一応の解決法が見つかりました。
以下の手順でアドオンのデバッグが可能。
- OTAを使ったプロジェクトをC++Builderで作成する。
- プロジェクトをビルドして、アドオンをインストールする。
- 一旦IDEを終了する。
- もう一度、IDEを起動してOTAを使ったプロジェクトを読み込む。
- ブレークポイントを設定する。
- プロジェクトを読み込んだIDEとは別のIDEを起動する。この段階であとから起動したIDEでもアドオンは有効になっている。
- プロジェクトを読み込んだIDEで、[実行|プロセスにアタッチ]を選択し、あとから起動したIDEのプロセスにデバッガをアタッチする。
- アタッチしたIDEでアドオンを実行する。
いわゆる「運用で回避」と言えなくはないけど、Delphiと比べて少々面倒なのは事実。
C++Builderでのアドオン作成は諦めていたから、朗報と言えば朗報。
Universal Character Set Detector C LibraryをDelphi XEから使用する
C++Builderで試した、Mozillaのエンコーディング自動判別ライブラリである「universalchardet」をDLL化したのをDelphiで動作させてみました。バイナリはhttp://a7m.sakura.ne.jp/SOURCE/universalchardet-CB.7zに用意したもので、C++Builder XE(BCC32.EXE 6.31)でコンパイル。
Delphiでuniversalchardet.dllを使うためには、DLLの宣言を記述したユニットを作成しなければならない。
unit universalchardet; interface type chardet_t = Pointer; const CHARDET_RESULT_OK = 0; CHARDET_RESULT_NOMEMORY = -1; CHARDET_RESULT_INVALID_DETECTOR = -2; CHARDET_MAX_ENCODING_NAME = 64; CHARDET_ENCODING_ISO_2022_JP = 'ISO-2022-JP'; CHARDET_ENCODING_ISO_2022_CN = 'ISO-2022-CN'; CHARDET_ENCODING_ISO_2022_KR = 'ISO-2022-KR'; CHARDET_ENCODING_ISO_8859_5 = 'ISO-8859-5'; CHARDET_ENCODING_ISO_8859_7 = 'ISO-8859-7'; CHARDET_ENCODING_ISO_8859_8 = 'ISO-8859-8'; CHARDET_ENCODING_BIG5 = 'BIG5'; CHARDET_ENCODING_GB18030 = 'GB18030'; CHARDET_ENCODING_EUC_JP = 'EUC-JP'; CHARDET_ENCODING_EUC_KR = 'EUC-KR'; CHARDET_ENCODING_EUC_TW = 'EUC-TW'; CHARDET_ENCODING_SHIFT_JIS = 'SHIFT_JIS'; CHARDET_ENCODING_IBM855 = 'IBM855'; CHARDET_ENCODING_IBM866 = 'IBM866'; CHARDET_ENCODING_KOI8_R = 'KOI8-R'; CHARDET_ENCODING_MACCYRILLIC = 'MACCYRILLIC'; CHARDET_ENCODING_WINDOWS_1251 = 'WINDOWS-1251'; CHARDET_ENCODING_WINDOWS_1252 = 'WINDOWS-1252'; CHARDET_ENCODING_WINDOWS_1253 = 'WINDOWS-1253'; CHARDET_ENCODING_WINDOWS_1255 = 'WINDOWS-1255'; CHARDET_ENCODING_UTF_8 = 'UTF-8'; CHARDET_ENCODING_UTF_16BE = 'UTF-16BE'; CHARDET_ENCODING_UTF_16LE = 'UTF-16LE'; CHARDET_ENCODING_UTF_32BE = 'UTF-32BE'; CHARDET_ENCODING_UTF_32LE = 'UTF-32LE'; CHARDET_ENCODING_HZ_GB_2312 = 'HZ-GB-2312'; CHARDET_ENCODING_X_ISO_10646_UCS_4_3412 = 'X-ISO-10646-UCS-4-3412'; CHARDET_ENCODING_X_ISO_10646_UCS_4_2143 = 'X-ISO-10646-UCS-4-2143'; // Unused CHARDET_ENCODING_ISO_8859_2 = 'ISO-8859-2'; CHARDET_ENCODING_WINDOWS_1250 = 'WINDOWS-1250'; CHARDET_ENCODING_TIS_620 = 'TIS-620'; function chardet_create(var pdet: chardet_t): integer; stdcall; external 'universalchardet.dll' name '_chardet_create'; procedure chardet_destroy(det: chardet_t); stdcall; external 'universalchardet.dll' name '_chardet_destroy'; function chardet_handle_data(det: chardet_t; const data: PAnsiChar; len: Cardinal): integer; stdcall; external 'universalchardet.dll' name '_chardet_handle_data'; function chardet_data_end(det: chardet_t): integer; stdcall; external 'universalchardet.dll' name '_chardet_data_end'; function chardet_reset(det: chardet_t): integer; stdcall; external 'universalchardet.dll' name '_chardet_reset'; function chardet_get_charset(det: chardet_t; namebuf: PAnsiChar; buflen: Cardinal): integer; stdcall; external 'universalchardet.dll' name '_chardet_get_charset'; implementation end.
このファイルをuniversalchardet.pasとして保存する。
実際に使用する場合は、users節にuniversalchardetを追加する。サンプルコードは以下の通り。
procedure TForm1.Button1Click(Sender: TObject); var ms: TMemoryStream; enc: TEncoding; encname: array[0..CHARDET_MAX_ENCODING_NAME] of AnsiChar; det: chardet_t; res: Integer; begin ms := TMemoryStream.Create; // ファイルのロード ms.LoadFromFile(filename); // オフセットをストリームの先頭に ms.Position := 0; // エンコーディングの判別をする det := nil; chardet_create(det); res := chardet_handle_data(det, ms.Memory, ms.Size); chardet_data_end(det); // エンコーディング名の取得 chardet_get_charset(det, encname, CHARDET_MAX_ENCODING_NAME); chardet_destroy(det); // 自動判別結果を元にエンコーディングを判別して読み込み enc := nil; try enc := TEncoding.GetEncoding(encname); except on EEncodingError do enc := TEncoding.Default; end; Memo1.Lines.LoadFromStream(ms, enc); ms.Free; end;
7月21日のウェブセミナーと、第20回エンバカデロ デベロッパーキャンプで喋ります。
7/21のオンラインセミナーは、13/14回デベロッパーキャンプでやったポインタというかメモリ管理ネタの再演。
このネタの評判がいいとの話しでオファーがあったのでオンラインセミナーとして再演します。
9/6の第20回デベロッパーキャンプは「アンドキュメンテッド(?)VCL」と称して、残念ながらドキュメントが整備されていないVCLのクラスやコンポーネントについての使用法。BDS2006以降の新機能が中心。
一応、逆引き形式なので、何かリクエストがあれば、アンドキュメンテッドなもので無くても、自分が判る範囲で対応します。
リクエストはこの記事のコメントか、Twitterの#dcamp_jpのハッシュタグでつぶやいてください。
7/26 追記:
Q&Aで、おすすめの本は?と言うのがあったので、追記。
とりあえず、こんなものかな?
Debug Hacks -デバッグを極めるテクニック&ツール
- 作者: 吉岡弘隆,大和一洋,大岩尚宏,安部東洋,吉田俊輔
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/04/27
- メディア: 単行本(ソフトカバー)
- 購入: 12人 クリック: 419回
- この商品を含むブログ (73件) を見る
Binary Hacks ―ハッカー秘伝のテクニック100選
- 作者: 高林哲,鵜飼文敏,佐藤祐介,浜地慎一郎,首藤一幸
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2006/11/14
- メディア: 単行本(ソフトカバー)
- 購入: 23人 クリック: 383回
- この商品を含むブログ (223件) を見る
- 作者: John R. Levine,榊原一矢,ポジティブエッジ
- 出版社/メーカー: オーム社
- 発売日: 2001/09
- メディア: 単行本
- 購入: 7人 クリック: 181回
- この商品を含むブログ (54件) を見る
- 作者: 稲葉一浩
- 出版社/メーカー: 秀和システム
- 発売日: 2007/07/11
- メディア: 単行本
- 購入: 4人 クリック: 235回
- この商品を含むブログ (36件) を見る
- 作者: ビョルン・カールソン,村上雅章
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2008/10/07
- メディア: 単行本(ソフトカバー)
- 購入: 9人 クリック: 61回
- この商品を含むブログ (22件) を見る
スマートポインタの使い方 その3:メモリ以外のリソースをスマートポインタで管理する
デリーターとは
スマートポインタが管理できるのは、メモリ領域だけではなく「構築」と「破棄」がペアになっている任意のリソースも管理できる。
例えば、Cのfopen関数でオープンしたファイルをfclose関数でクローズしたり、あるいは、Windows APIのGDIオブジェクトをDeleteObject関数で破棄したりとか。
その仕組みを実現するのが「デリーター」で、厳密にはスマートポインタのデフォルトのデリーターが"operator delete"となっている。
#pragma hdrstop #include <windows.h> #include <boost/tr1/memory.hpp> // スマートポインタがGDIオブジェクトを破棄するデリーター struct GDIDeleter { void operator()(HANDLE handle) { ::DeleteObject(handle); } }; void foo() { std::tr1::shared_ptr<void> Pen(::CreatePen(PS_SOLID, 20, RGB(255,255,0)), GDIDeleter()); ::SelectObject(hDC, Pen.get()); // ハンドルはgetメソッドで取得 }
shared_ptrの場合は、コンストラクタにデリーターのインスタンスを渡す。デリーターは関数オブジェクトなのでoperator()でリソースの破棄を行う処理を記述する。
unique_prtの場合は、型宣言のテンプレートにデリーターの型を記述する。
void foo() { std::unique_ptr<void, GDIDeleter> Pen(::CreatePen(PS_SOLID, 20, RGB(255,255,0))); ::SelectObject(hDC, Pen.get()); // ハンドルはgetメソッドで取得 }
C++Builder(BCC32)でのちょっとした問題とその回避
C++Builderではvoidへのポインタをstd::unique_ptrで管理出来ない*1ので適当な型へのポインタとしてキャストするか、以下の例のようにラッパークラスを作成して誤魔化す。
//--------------------------------------------------------------------------- #include <memory> #include <boost/shared_ptr.hpp> #include <tchar.h> #include <iostream> //--------------------------------------------------------------------------- typedef int MAPHANDLE; MAPHANDLE LoadMap(const char* MapFilePath) { MAPHANDLE MapID = 100; // あくまでもダミー std::cout << "Loaded Map ID = " << MapID << std::endl; return MapID; } //--------------------------------------------------------------------------- void ReleaseMap(MAPHANDLE MapID) { std::cout << "Release Map ID = " << MapID << std::endl; } //--------------------------------------------------------------------------- void ProcessMap(MAPHANDLE MapID, int Code) { std::cout << "Process Map ID = " << MapID << std::endl; } //--------------------------------------------------------------------------- class CMapHandle { private: //typedef void Ty_; // C++Builderだと、void*はstd::unique_ptrで管理できない。orz typedef int Ty_; public: CMapHandle(MAPHANDLE hMap) : instance((Ty_ *)hMap) { } operator MAPHANDLE() const { return (MAPHANDLE)instance.get(); } private: class Deleter { public: void operator()(Ty_* ptr) { ReleaseMap((MAPHANDLE)ptr); } }; std::unique_ptr<Ty_, Deleter> instance; }; //--------------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { // 地図ファイルを読み込む CMapHandle hMap(LoadMap("D:\\Maps\\test1.shp")); // 何らかの処理 ProcessMap(hMap, 1); // ReleaseMap(hMap); // 後始末はデリーターが行うので不要 return 0; }
このコードはハンドルの型がポインタではなく整数型である例。「構築」と「破棄」がペアにさえなっていればスマートポインタとして管理できる。
Console2を導入する
コマンドプロンプトの補完アプリであるConsole2を導入してみる。
アーカイブはプロジェクトページの[Files|console-devel|2.00]より、最新版をダウンロード。
Console2はそのままだと日本語が入力が出来ないので、http://wiki.pythonpath.jp/moin/ConsoleIme よりIME対応バイナリをダウンロード。アーカイブのConsole.exeをそのまま上書き。
Dirコマンドなどを実行したときにカラム位置がずれる場合は、以下の手順を実行。
- [View|Console Window]を選択して、コンソールウィンドウを表示する。
- コンソールウィンドウの[プロパティ]を選択。
- プロパティダイアログの[フォント]タブを選択。
- フォントを「MS ゴシック」にする。
Console2は[Edit|Settings|tabs]でタブを作成すると、タブ切り替えによって任意のコマンドプロンプトやシェルを起動できる。
Title | Mingw |
Shell | C:\msys\bin\bash.exe -l |
Startup dir | %HOME% |
同様に、RAD Studio/C++BuilderのコマンドプロンプトをConsole2から起動したい場合は、以下の設定でタブを作成する。
Title | RAD Studio Command Prompt |
Shell | %comspec% /K "C:\Program Files (x86)\Embarcadero\RAD Studio\8.0\bin\rsvars.bat" |
Startup dir | %HOME% |
起動時引数に「-t タブ名」とすると、Tabsで設定した環境で起動する。
例えば、Console2のショートカットのリンク先を「"C:\Program Files\Console2\Console.exe" -t "RAD Studio Command Prompt"」とすると、RAD Studio/C++BuilderのコマンドプロンプトがConsole2で実行される。