MASATOの開発日記 2001年1月


1/14

CEditクラスを拡張して、読み取り専用でかつ背景色が白で、 簡単に文字列を「追加」できるCLogEditというクラスを作ってみました。
今作成中のソフトに必要があったから作ったのですが、 また将来ログを表示するためのエディットボックスが 欲しいな〜と思ったらさくっとこのクラスを使えば良いわけです。 過去を思うと毎回そのたびにクラスを作っていましたね。(なんと非効率な!) こうやって便利なクラスを作り貯めておくのも将来の楽に繋がるかもしれません。
本当ならばActiveX等で作っておくともっと互換性が広がるのでしょうが・・・
まあこれは難しそうなので暇があったら挑戦してみたいと思います。

(Visual C++)ファイルのバージョンの読み込み方法。

MFCのプロジェクトで、バージョン番号を上げたいと思った時は、次の2つの事をしなくてはなりません。

  1. CAboutDlgを編集し、バージョン番号を書きかえる。
  2. リソースのバージョン情報を編集する。
どちらも簡単といえば簡単なのですが、バージョンアップの度に毎回書きかえると結構面倒です。 どちらか片方を忘れてしまうこともあるでしょう。 そこで、リソースのバージョン情報を編集するだけで、CAboutDlgの方も更新されるように してしまいましょう。
手順は次のようになります。
CAboutDlgを編集し、バージョンを表示するスタティックコントロールを、IDC_VERSION、 著作権を表示するスタティックコントロールをIDC_COPYRIGHTとしましょう。
CAboutDlgに、WM_INITDIALOGのハンドラを追加し、 そこに次のように記述します。
TCHAR path[MAX_PATH];
DWORD handle;
::GetModuleFileName(AfxGetInstanceHandle(), path, sizeof(path));
DWORD size = ::GetFileVersionInfoSize(path, &handle);
CByteArray buf;
buf.SetSize(size);
::GetFileVersionInfo(path, handle, buf.GetSize(), buf.GetData());
LPVOID item;
UINT itemsize;
::VerQueryValue(buf.GetData(), _T("\\VarFileInfo\\Translation"), &item, &itemsize);
CString verstr;
verstr.Format("\\StringFileInfo\\%04X%04X\\", LOWORD(*((LPDWORD)item)), HIWORD(*((LPDWORD)item)));
::VerQueryValue(buf.GetData(), (LPTSTR)(LPCTSTR)(verstr + _T("FileDescription")), &item, &itemsize);
CString name = (LPTSTR)item;
::VerQueryValue(buf.GetData(), (LPTSTR)(LPCTSTR)(verstr + _T("FileVersion")), &item, &itemsize);
CString version = (LPTSTR)item;
::VerQueryValue(buf.GetData(), (LPTSTR)(LPCTSTR)(verstr + _T("LegalCopyright")), &item, &itemsize);
CString copy = (LPTSTR)item;

SetDlgItemText(IDC_VERSION, name + " ver " + version);
SetDlgItemText(IDC_COPYRIGHT, copy);
これで、リソースの情報だけ書きかえればバージョン情報ダイアログにも反映されるようになりました。 このコードでは、FileDescriptionFileVersionを参照しています。 他の情報を表示したい場合は書き換えて使って下さい。

応用としては、GetModuleFileNameを使わずに、pathになんらかのファイル名を入れてこのコードを 実行すれば、そのファイルのバージョン情報が取得出来ます。バージョン関係のAPIは、他にも色々あるようですね。

でもこうやってバージョン変更を簡単にしても、私はバージョンアップの際のリソースの編集を良く忘れます。 うーーむ・・・

1/10

前回の続きになりますが、Documentにどうやってデーターを置くか?という事ですが、 最近は、データー管理用のクラスAを作り、さらにそれを継承してクラスBを作り、 クラスBに表示処理を行わせて居ます。 クラスAは、まさしくデーターの管理や計算をするだけで、 アプリケーションの種類には依存しないようにします。 クラスBは、アプリケーションにべたべたに依存して書きます。 他の環境に移植する時は、クラスAだけをそのまま移し、 そこでクラスAを継承してクラスCをつくり、クラスCに表示処理を行わせます。 データー管理はクラスAで、表示処理はそれを継承したクラスで、という事です。
クラスAで、例えば、"Hoge"と表示したいと思った時は、 Output("Text");と呼びます。Outputは、virtual関数で、A::Output()は何もしません。 このクラスBやCでは、このOutputを継承し、それぞれのアプリケーションに応じた 文字列出力処理を行います。
今の所、この方法は面倒なだけですね。この形でプログラムを書いたのは良いのですが、 まだ移植作業をやっていないのです。(^^;;;
この先、移植作業をやる事がありましたら、この構造のプログラムの移植が 簡単だったのか難解だったのかの結果が出ると思いますので、それまでお待ち下さい。

(Visual C++)カスタムリソースの読み込み方法。

Windowsでは、リソースにデーターを含ませる事が出来ます。 BITMAPやメニュー情報を含ませる事が出来るのはもちろん、実はバイナリデーターならば 何でも出来ます。VC++ならば、メニューから挿入->リソース->カスタムで挿入できます。 このリソースは、カスタムリソースと呼ぶようです。
カスタムリソースは、リソースのタイプやIDを自分で指定します。 とりあえずリソースのタイプは"BIN"、名前は"DATA"とします。 もしも名前やタイプをIDで指定するならば、MAKEINTRESOURCE(ID_DATA)というように、 MAKEINTRESOURCEを使えば、文字列に変換できます。

では、そのカスタムリソースの読み込み方法を見てみます。
バイナリデーターなのですから、LPBYTE型のポインタにデーターの先頭アドレスが入れば 完了ですね。

HRSRC hrsrc;
HGLOBAL hglobal;
LPBYTE data;
DWORD rsize;
if ((hrsrc = ::FindResource(AfxGetInstanceHandle(), "DATA", "BIN")) == NULL
	|| (rsize = ::SizeofResource(AfxGetInstanceHandle(), hrsrc)) == 0
	|| (hglobal = ::LoadResource(AfxGetInstanceHandle(), hrsrc)) == NULL
	|| (data = (LPBYTE)::LockResource(hglobal)) == NULL){
	// ●リソース読み込み失敗●
}else {
	// ●リソース読み込み成功処理●
}
FindResourceの引数の名前とタイプの順番に注意して下さい。 私はよくこれを間違えます。 リソース読み込みに成功すれば、変数dataにデーターの先頭アドレス、 変数rsizeにデーターの大きさが入っています。 あとはご自由にアクセスして下さい。

1/8

あけましておめでとうございます。今年もよろしくお願い致します。

2001年と書こうとするといつも20001年と書いてしまいます。 2000と1年をそのまま書くと20001になるのですね。2000+1=20001なのです。 どうやら頭の中まで文字列クラスが進入してきてしまったようです。

最近開発をしていて、コードの再利用性というのが気になるようになってきました。 1度書いたコードを、2度書くことが面倒だからです。 でも再利用するために最初に面倒な思いをするのも大変ですね。 作る前は再利用するかどうか分からない時も多々ありますし。 MFCのドキュメント/ビューアーキテクチャを使っている時の コードの再利用性を上げるコツは、ずばりこれです。 Documentに直接データーを置かない! これらでは無く、データー管理用のクラスを作って、 そちらでデーターを管理するようにしましょう。 こうすると、ダイアログベースのアプリケーションに移植! という場合にも、比較的容易に行う事が出来ます。 ViewやAppクラスにはデーターを置いても良いのかというと、 これらのクラスには、そもそもデーターを置くべきではありません。 さらに、データーのみに依存する操作と、データーを表示するための操作は、 別クラスに分けたりすると効果的です。 データーのみ依存する操作は、移植しても変化はありませんが。 表示するための操作は、別の環境に移植すると大幅に変わってしまうかもしれない からです。 操作を分けるといってもどうやって分けるのか?についてはまた次回という事で。

(Visual C++)MFCにおけるクリティカルセクション。

クリティカルセクションとは、危険領域の事です。この単語は、 マルチスレッドを扱う時に使われます。 2つのスレッドが同時に実行してはならない場所。それが危険領域です。 片方のスレッドが危険領域に入っている時には、 もう片方のスレッドは、危険領域突入を待たなければなりません。 これをやってくれるのがMFCのCCriticalSectionクラスと CSingleLockクラスです。

危険領域部分の書き方は以下のようになります。

CCriticalSection cs;	// ●それぞれのスレッドで共通の変数。●
{
	CSingleLock sl(&cs, TRUE);
	// ●ここに危険領域の部分のコードを書く。●
	// ●ここに書かれたコードは、同時に1スレッドしか実行できません。●
}
変数slのコンストラクタでロックされ、デストラクタでそれが解除されます。 これで危険領域部分へのアクセスは、同時に1スレッドだけが行えます。
複数の危険領域をまとめて扱いたい時は、CMultiLockを使うと良いでしょう。
しかし、危険領域は、出来る限り短く、またロックされる可能性を低くしたい所ですので、 複数まとめてロックするのは、避けられれば避けた方が良いでしょう。
CMultiLockを避ける余り、CSingleLockでロックしている最中に、 さらにCSingleLockでロックすると デッドロックという非常に危険な状態を招く可能性があります。CSingleLockのネストを行うならば、 CMultiLockで対応した方が良いでしょう。


戻る

masato@mb.kcom.ne.jp