‘Win32’のエントリ

メモリリークでかなり悩む。昔の誰かのソースを移植していたのだが,

strdup

が原因だった。この関数は文字列をコピーするが,勝手に malloc するらしい。

list 使用時に,list 要素のコピーコンストラクタがないとのエラーが出る。コピーコンストラクタはあるはずなのに…と調べていると,コピーコンストラクタの引数が const になっていなかった。

unsigned int i = strtol("0x80000000", &p, 16);

とやって,i が 0x7fffffff になる。正解は strtoul。

しばらく Visual Basic をやっているが,C に戻ってみると,

if(a = 1)

とやってしまう。ちなみにやりたいのは ==。

メンバに string を入れると他の string が破壊されるという現象に悩まされる。こんな怪現象にはリビルドだ,とか思ってリビルドしても直らない。結局 1 日悩んだが,インクルードパスに古いバージョンが残っていて,ファイルごとにインクルードするファイルのバージョンが違うということだった。ふう。

いつか hr みたいな線の作り方を書いたけど,それよりよさそうな方法。

  • ピクチャーコントロールを貼り付ける
  • スタイルのくぼみをチェックする

これだけ。

MFC 利用スタティックライブラリで

定義されていない基本クラス

とか言われる。StdAfx.h に

#include <afxext.h>

を追加。


複数のプロジェクトを扱う時,他のプロジェクトを変更したときにリコンパイルする方法。プロジェクトの依存関係で依存するプロジェクトをチェック。


libjpeg を入れる。

  1. VC98bin にある Vcvars32.bat を実行。
  2. jconfig.vc を jconfig.h に,makefile.vc を makefile に直す。
  3. nmake でデバッグ版が完成。
  4. namke nodebug=1 でリリース版が完成。

ちなみに環境変数の領域が足りない場合は config.sys に

shell=c:command.com /e:4096 /p

を追加。(前書いたっけ?)

VC++ + SQL Server で作っていたクラス群を Linux + Postgres へ移植。Turbo Linux 4.0 の gcc とかなり違っていて大変だが,今回修正が必要だった部分を書いておく。以下,gcc と書いたら Turbo Linux 4.0 付属のものとする(多分現時点で最新だと思う)。

  1. VC では ios_base があるが,gcc では古い ios しかない。
  2. ソースファイルは EUC にしないと変なエラーが出ることがある。
  3. stringstream がない。代わりに古い(?)strstream がある。ちなみに istrstream のコンストラクタは string ではなく char * をとる。
  4. basic_string::compare(size_type, size_type, const string &) がない。これは const string & が最初の引数になっていた。
  5. VC では <cxxx> が std 名前空間になっていないが,gcc ではちゃんと std 名前空間になっている。
  6. stream の ! 演算子がない(* もかな?)。good() などで代用。
  7. VC の stricmp は gcc では strcasecmp になっている。
  8. VC の itoa がない(と思う)。
  9. <iostream> がなく,<iostream.h>。

全体的には,コンパイラは gcc の方が標準に準拠しているが,標準 C++ ライブラリ,STL は VC++ の方が新しいという感じ。てことは STL だけ Linux でコンパイルすれば最高の環境になるのか,な。どうだろ。

あるウィンドウで IME 入力を禁止する方法

ImmAssociateContext

で,第 1 引数をハンドル,第 2 引数を NULL にして呼び出す。

リサイズ時のちらつきをなくす。PreCreateWindow を以下のようにする。

if(!CView::PreCreateWindow(cs))
{
return FALSE;
}

// CS_HREDRAW,CS_VREDRAW を外す
cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS, AfxGetApp()->LoadStandardCursor(IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1));

これをビューに対して行うときは,メインフレームにも追加すること。今回はこれをしないで悩んだ。


リストボックスに水平スクロールバーを表示させる。リストボックスは勝手に付けてくれないので,自分で大きさを計算する必要がある。

int nSize = 0;

CClientDC dc(&m_listboxFile);
CFont *pOldFont = dc.SelectObject(m_listbox.GetFont());
for(int i = 0; i < m_listbox.GetCount(); i++)
{
CString str;
m_listbox.GetText(i, str);
if(dc.GetTextExtent(str).cx > nSize)
{
nSize = dc.GetTextExtent(str).cx;
}
}

nSize += LIST_MARGIN;

m_listbox.SetHorizontalExtent(nSize);
dc.SelectObject(pOldFont);

こんな感じ。この際なかなかサイズが合わず,とりあえずフォントを設定したらやっとうまくいった。CClientDC では自動でフォントを合わせてくれないのね。


スクロールバーの右下のやつ。最初は自分で描画していたが,どうしても更新がうまくいかないので独立したウィンドウにしてしまう。しかし Spy++ で VS を調べると ScrollBar になっている。なんかもっとちゃんとした方法があるのだろうか。

アイコンの 16×16 とかの削除。リソースエディタで [イメージ]-[デバイスイメージの削除] で削除できる。

ウィンドウの破棄。今更って感じだが,実は詳しく知らなかった。ウィンドウは通常外部から直接 delete はせず,以下の手順で行う。詳細はMSDN「テクニカル ノート 17: ウィンドウ オブジェクトの破棄」参照。

  • CWnd::PostNcDestroy をオーバーライドし,delete this を行う(親クラスの呼び出しはいらない。)
  • 破棄するときは pWnd->DestroyWindow() のように行う。その際,子ウィンドウの場合は親ウィンドウの破棄時に自動的の行われるので考えなくてよい。

Install Shield。ライセンスの文字列を変える。が,ファイルを修正しても一向に反映されない。かなり悩んだが,[ビルド]-[メディア]-[ビルド] を行うことで解決。ていうか VS そっくりにするんだったらこの項目は [ビルド]-[コンパイル] の下にもってきて欲しい。


ファイルの関連づけがうまくいかない。

aaa.Documentshellopencommand

に実行ファイル名とパラメータを設定しているのだが。起動はするんだが,ちゃんとファイルを読み込まない。で 1 回起動するとうまくいく。レジストリの違いは,自分で設定したものはロングファイル名だが,自動で設定されたものはショートファイル名ってとこ。で,自動でショートファイル名を指定したらうまくいった。ウーム。他にないんですかね。ちなみにファイル名をダブルクォーテーションで囲ってもダメだった。


開発環境の入っていないソーテックマシンでインストールを試みる。で関連づけを試す。が,どうしてもメモ帳が立ち上がる。何故だろう。と思ったら,ファイル名の最後に .txt が付けられてた。「登録されている拡張子は表示しない」になっていたのだ。うぅ。

スプラッシュスクリーンを追加。スプラッシュってのはアプリケーションが開始する時に出る,タイトル画面みたいなアレである。自分で作ると結構手間がかかりそうだが,VC のコンポーネントギャラリから追加したのでほとんど何もせずにできた。まあ後でフルカラー対応にしたりとかするかも知れないけどとりあえず完了。

ツールバーアイコンをフルカラーにする。さてどうしようと悩んでダイアログバーでオーナードローボタンでもつけるか,とか考えたが,めんどくさすぎてあきらめる。いろいろ調べていると新しいコモンコントロールで,ツールバーに直接イメージリストをセットできることが判明。CMainFrame::OnCreate に,

// ツールバーを作成
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC))
{
TRACE0("Failed to create toolbarn");
return -1;      // 作成に失敗
}

// イメージリストの作成
CBitmap bitmap;
bitmap.LoadBitmap(IDB_TOOLBAR);

m_pImageToolBar->Create(32, 32, ILC_COLOR24 | ILC_MASK, TOOLBAR_MAX, 1);
m_pImageToolBar->Add(&bitmap, RGB(255, 255, 255));

m_wndToolBar.SetSizes(CSize(39, 39), CSize(32, 32));
m_wndToolBar.GetToolBarCtrl().SetImageList(m_pImageToolBar);

// ボタンの追加
UINT command[] =
{
ID_FILE_NEW,
...
};

m_wndToolBar.SetButtons(command, TOOLBAR_MAX);

てな感じで ok。透明色とかももちろんやってくれるので簡単。ちなみにこの際,最初イメージリストをスタックに作ってて反映されなかった。これはメンバに持つことで解決。

アプリケーションのアイコンとメインフレームのアイコンを別のものにする。というかまだしてなくて調べただけだが。IDR_MAINFRAME はあくまでメインフレームのアイコンであり,一番リソース ID の小さいアイコンが EXE のアプリケーションのアイコンとなる。らしい。

# その後確認しました。

ダイアログのエディットボックス内のテキストが最初に選択されない。選択されるものもあるので調べると,最初にフォーカスがないのが原因だった。タブオーダーでエディットボックスを最初にして解決。


相対パスから絶対パスへの変換。真面目にやろうとするとかなり大変だが,_fullpath という関数があった。ワーイ。

マルチスレッド。ウィンドウを AfxBeginThread パラメータで渡し,メンバ関数を呼ぶが ASSERT。そういえば CWnd を他のスレッドに渡せないんだっけ。ということは今まで CWnd をマルチスレッドのパラメータにしたことなかったのか。一応上のはハンドルを渡し,ウィンドウメッセージを使うことで解決。


FTP クライアントを作成。しかしデバッグのために Hi-HO へダイヤルアップしてたらアレなのでフリーの FTP サーバー,WAR-FTPD をインストール。ローカルで試すだけなので割合設定は楽だったが,一応設定を。

  • [Property]-[Security]-[Edit User] で mortarco を作成,FileAccess で FTP 用の pub ディレクトリを指定。パーミッションも適当に設定。
  • [Property]-[Option]-[Virtual File System] で仮想ディレクトリを作成。AutoStart にし,パスを c:pub,pub のように設定。

以上で FTP クライアントから接続ができた。

もう 1 台 LAN で接続してるんだから Linux で,って手もあるけどやっぱりローカルで手軽にって方がいいですな。


でクライアント本体の話。MFC の FTP クラスを使っててメモリリーク。調べてみると CInternetSession::GetFtpConnection で得たオブジェクトは自分で解放しなければならないらしい。ってヘルプにそんなこと書いてあったっけ? サンプル見れってことか。

ウィザードの作り方。CPropertySheet を普通に作成し,SetWizardMode を呼ぶだけ。

で,「次へ」とかを無効にするのに EnableWindow を使っていたが,反映されたりされなかったりで悩む。結局,CPropertySheet::SetWizardButtons を理解してなかったのが原因。この関数でボタンを作成し,EnableWindow で有効,無効を設定するんだと思っていたが,CPropertySheet::SetWizardButtons で引数を 0 にすると「次へ」「戻る」が無効。PSWIZB_BACK だと「次へ」が無効になる,という意味だった。ので,EnableWindow を使う必要はない。

HTML ヘルプに挑戦。しようかと思う。とりあえず Help WorkShop を探すが,手元にはないので MS のサイトへ。しかしどこにあるのかさっぱりだが,いろいろ調べて

http://msdn.microsoft.com/workshop/author/htmlhelp/download.asp

からダウンロードできることを発見。4MB。ダウンロードするか,と思ったが,Visual Studio の SP3 に入っていることが分かったので,そっからインストール。したが,まだインストールしただけ。

クラスの新規作成。今まで MFC クラスからの派生でないクラスを作成するときは *.h,*.cpp を手動で作成していたが,IDE のツリーの「*クラス」の右クリックメニューにあるクラスの新規作成でできることを発見。ていうかこの機能って VC5.0(以前)にもあったっけ?

どうでもいいが,VC のアウトプットウィンドウのスクロールバーを操作している最中にキーを押すと文字が書き込めることを発見。って本当にどうでもいいけど。


印刷に挑戦。デフォルトの印刷処理ではダメなので,まず CView::OnBeginPrinting に処理を加えるが呼び出されない。ここで数分悩んだが,原因判明。結局派生クラスを作ったときに勝手に CView::OnBeginPrinting オーバーライドされていたからだった。印刷処理を基底クラス(CEditorView)で記述していたのに,実際には派生クラス(CXXXView)の仮想関数が呼ばれていたのだ。


フォントダイアログ。@ 付きのフォントを選択させない方法。CFontDialog のコンストラクタの第 2 引数に CF_SCREENFONTS | CF_NOVERTFONTS を指定する。

MFC でドキュメントを保存するかを聞くダイアログボックスを出す方法。CDocument::SaveModified を呼び出す。

だったが,その後 CWinApp::OpenDocumentFile を直接呼び出すことでファイルを開くという目的は達せられたので使わなかった。


エディタ。SHIFT を押していないのに選択されてしまうというバグにずっと悩んでいたが,問題らしき箇所がやっと見つかった。

if(::GetAsyncKeyState(VK_SHIFT))

となっていたのだが,正しくは

if(::GetAsyncKeyState(VK_SHIFT) < 0)

というか今まで正しく使っていたのだが,何故か今回はこうなっていた。これで直るといいんだが,必ず再現する方法が見つからないため確かめられない。

# でいいようだ。


ユーザー定義メッセージ。コンボボックス内のエディットコントロールをサブクラス化し,WM_KILLFOCUS をユーザー定義メッセージでコンボボックスに通知しようとする。が,メッセージが流れてこない。でいろいろ調べてみると,ユーザー定義メッセージの WM_USER + n は MFC がいくつか使用しているとのこと。これをバーンと WM_USER + 1000 にしたら無事動いた。でも前作ったやつは WM_USER + 1 で動いてたんだけど。まあいいか。

# ::RegisterWindowMessage を使用した方がよい。

STL。copy アルゴリズムを使おうとして落ちる。copy アルゴリズムとかは勝手に要素数を拡張してくれるわけではないのか。

copy(l.begin(), l.end(), back_inserter(v));

のように back_inserter を使うことで解決。

Visual Studio の DLL 等再配布情報を探す。Visual Studio ディレクトリの redist.txt に書いてあった。

メモリリークで,行番号が分からないときに問題箇所を発見する方法。C Magazine(ソフトバンク)1999.3 天元広海「VisualC++6.0 デバッギングテクニック」より

デバッガのクイックウォッチに,以下を追加

{,,msvcrtd.dll}_crtBreakAlloc=XXX

ここで XXX はメモリリーク検出時に出るアロケート回数。


コンボボックスのエディットボックスの取得。

combo.GetTopWindow();

以下のコードでメモリリーク。

struct A
{
};

struct B : public A
{
string b;
};

A *p = new B;
delete p;

原因は B のデストラクタが呼ばれないためで,A に仮想デストラクタを用意することで解決。

エディットコントロールなどをウィンドウに直接置く方法。

たまにハマるが,コントロールはメンバに持つようにする。スタックに作ると関数を抜けた瞬間にデストラクトされてしまう。必要なときに Create で作成すればよい。

m_edit.Create(WS_VISIBLE | WS_CHILD, rect, pParent, 0);

コールバック関数の定義。

typedef void (* CallBack)(void *pParam);
virtual void SetCallBack(callBack callBack, void *pParam);

のようにする。

今なら関数オブジェクトを使うが。

VC のリリース版でデバッグする方法。プロジェクトの設定で,C/C++ デバッグ情報-行番号のみ,リンク-デバッグ情報を生成する。で ok。もちろんエディットコンティニューはできないが。

ファイルオープンダイアログのカスタマイズ。まずダイアログテンプレートを作成し,stc32=0x45f の不可視グループボックスを作成。ダイアログはチャイルド,境界線なし,兄弟ウィンドウをクリップ,可視で作成。その後 CFileDialog 派生のクラスを作り,class wizard でテンプレートと結合。コンストラクタで

m_ofn.Flags |= OFN_ENABLETEMPLATE;
m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILE_DIALOG_EX);

とする。


コンボボックスのプルダウン幅の変更。リソースエディタでコンボボックスの矢印をクリックする。

太字にした時の文字の太さが GetTextExtent と違う。これは TEXTMETRIC の tmOverHang を加えればいい。が,選択したときに背景を反転させて描画するとその大きさまで反転してくれないので,自分で背景を描くことにする。


DrawText と TextOut を比較。TextOut の方が大分早いみたいだ。こっちを使うことにした。