‘プログラミング’のエントリ

Visual Studio のエディタ。左余白をクリックすると 1 行選択されるが,そのまま Shift を押しながら上の行の左余白をクリックして 1 行選択していくと下にも 1 行ずつ選択範囲が拡張されていく。

ってそんな使い方する人いないか。


ステータスバー。前に使ったことはあるが,日記には書いてなくてどうやるか悩んだのでここに書いておく。

  • String リソースに indicators の ID を追加
  • CMainFrame の indicators に ID を追加
  • View 等に コマンドのハンドラを作成

IME の使用。

void CXXXView::OnImeStartComposition(WPARAM wParam, LPARAM lParam)
{
    COMPOSITIONFORM cpf;
    HIMC hIMC;

    // ウィンドウハンドルを取得
    HWND hWnd = GetSafeHwnd();
    if(!hWnd)
    {
        return;
    }

    // IME のコンテキストを得る
    hIMC = ::ImmGetContext(hWnd);
    if(!hIMC)
    {
        return;
    }

    // CompositionWinow
    cpf.dwStyle = CFS_POINT;
    cpf.ptCurrentPos.x = GetCaretPos().x;
    cpf.ptCurrentPos.y = GetCaretPos().y;

    // フォント
    LOGFONT logFont;
    m_font.GetLogFont(&logFont);

    ::ImmSetCompositionFont(hIMC, &logFont);
    ::ImmSetCompositionWindow(hIMC, &cpf);

    // IME のコンテキストを解放
    ::ImmReleaseContext(hWnd, hIMC);

    // デフォルトプロシージャ
    DefWindowProc(WM_IME_STARTCOMPOSITION, wParam, lParam);
}

void CXXXView::OnImeChar(WPARAM wParam, LPARAM lParam)
{
    // 何もしない
}

void CXXXView::OnImeComposition(WPARAM wParam, LPARAM lParam)
{
    if(lParam & GCS_RESULTSTR)
    {
        COMPOSITIONFORM cpf;
        HIMC hIMC;

        // ウィンドウハンドルを取得
        HWND hWnd = GetSafeHwnd();
        if(!hWnd)
        {
            return;
        }

        // IME のコンテキストを得る
        hIMC = ::ImmGetContext(hWnd);
        if(!hIMC)
        {
            return;
        }

        // 文字列の取得
        int nSize = ::ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
        char *buf = new char[nSize + 1];
        ::ImmGetCompositionString(hIMC, GCS_RESULTSTR, buf, nSize);
        buf[nSize] = '';

        // IME のコンテキストを解放
        ::ImmReleaseContext(hWnd, hIMC);

        // 入力
        Input(buf);
        delete [] buf;

        // ウィンドウの移動
        cpf.dwStyle = CFS_POINT;
        cpf.ptCurrentPos.x = GetCaretPos().x;
        cpf.ptCurrentPos.y = GetCaretPos().y;

        ::ImmSetCompositionWindow(hIMC, &cpf);
    }

    // デフォルトプロシージャ
    DefWindowProc(WM_IME_COMPOSITION, wParam, lParam);
}

ドラッグアンドドロップ。前エクスプローラへのドロップ,ショートカットへのドロップができなかったが,できるようになった。

エクスプローラへのドロップは,DROPFILES にパラメータとファイル名を設定すればよい。(以前うまくいかなったんだが,今回はできた。)

// DROPFILES のサイズを計算
int nSize = sizeof(DROPFILES);
nSize += 1000;  // 適当に

// DROPFILES を作成
HGLOBAL hData = ::GlobalAlloc(GPTR, nSize);
if(hData == NULL)
{
throw cant_cache();
}
DROPFILES *pDropFiles = (DROPFILES *)::GlobalLock(hData);
memset((void *)pDropFiles, NULL, nSize);
pDropFiles->pFiles = sizeof(DROPFILES);
pDropFiles->pt = CPoint(0, 0);
pDropFiles->fNC = FALSE;
pDropFiles->fWide = FALSE;

// ファイル名をセット
char *pFileName = (char *)pDropFiles + sizeof(DROPFILES);
// ( 区切りでコピー)
*pFileName = '';

// キャッシュ
::GlobalUnlock(hData);                                                 
pDataSource->CacheGlobalData(CF_HDROP, hData);

ショートカットへのドロップは Shell IDList Array 形式をサポートすればいいのだが,ちょっと長いし私も完全に理解しているわけではないので手順だけ。

  • デスクトップフォルダの IShellFolder へのポインタを得る
  • ITEMIDLIST を作成。その際最初のデータがフォルダへの ITEMIDLIST で,次のデータからがファイルへの ITEMIDLIST。
  • CIDA を作る。
  • CIDA,ITEMIDLIST をキャッシュ。

Install Shield3 だが,コンパイルしようとすると

Unable to get current directory

というエラーがでる。どうやらディレクトリ名が長すぎるようなので,ルートディレクトリとかでやったらうまくいった。

CButton にビットマップを貼り付けていたのだが,Windows2000 で見ると背景色がダイアログの背景と違う。Win98 と Win2000 で微妙に色が変わっていた訳だが,結局

CBitmap::LoadBitmap() で CButton に CButton::SetBitmap()

していたのを

CBitmap::LoadMappedBitmap() (引数はデフォルト)

に変えることで背景がきちんと対応するシステムカラーに置き換えられた。

(とりあえず 16 色ビットマップだけしか試してないが。)


CWnd 派生クラスで WM_CHAR が送られてこないで困る。実際には BackSpase とかを押すと警告が鳴る。詳しくは調べてないが,結局 MFC が WM_CHAR をとっているからで,PreTranslateMessage で WM_CHAR を横取ることで解決。

でも昔 CView でやったときは大丈夫だったのだが。

プリコンパイル済みヘッダ。自分で作ったヘッダをプリコンパイル済みヘッダに指定すると,ビルド中に「プリコンパイル済みヘッダー ファイルではありません。」と言われる。

で,StdAfx.h を使う版のものを参考にして,プロジェクト自体の設定も,「プリコンパイル済みヘッダファイル(.pch)を使用」にすることでコンパイルできた。

参考までにプリコンパイル済みヘッダの使用方法。

  • 変更されることが少ないヘッダを選び,そのヘッダを a.h,それをインクルードしてるファイルの代表 1 つを a.cpp とする。
  • プロジェクトの設定で,プロジェクトの [c/c++]-[プリコンパイル済みヘッダ] の設定を「プリコンパイル済みヘッダファイル(.pch)を使用」にし,a.h を指定。
  • a.cpp の設定を「プリコンパイル済みヘッダファイル(.pch)を作成」とし,a.h を指定。
  • a.cpp 以外で a.h をインクルードしている cpp ファイルの設定を「プリコンパイル済みヘッダファイル(.pch)を使用」とし,a.h を指定。

以上で完了し,a.cpp 以外のコンパイルが高速になる。

デバッグのアウトプットに例外が表示される。そこで

  • デバッグ中に [デバッグ]-[例外処理] でダイアログを出す
  • Microsoft C++ Exception を常に停止させる

として例外で停止させる。ここで原因は判明(単純な配列範囲例外)したのだが,その後のコードが

catch(...)

となっていた。結局,せっかく処理系が例外を発生させているのに全ての例外が catch されてしまって分からなくなっていたのだ。やはりめんどうでも exception の派生クラスを定義するなどした方がいいようだ。

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

strdup

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

Java で 4 バイトを数値として書き込もうとするが,どうもうまくいかない。入出力でエンコーディングを変換しているようなので,Writer を使ってバイナリを読み書きしようというのがまずいのか。OutputStream でやればいいのだが。

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

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

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

PostgreSQL ODBC ドライバ。VB からアクセスしているが,どうも書き込めない。Access でもできなかったのだが,ドライバの設定が読みとり専用になっていた。デフォルトが書き込み禁止なのか。覚えとかないと。

というかなんかぐちゃぐちゃな感じが。昨日 C で C++ っぽくプログラムを組んでそのソースを Perl で TeX 形式にして Pha-jtd 氏の HTML を添削したりして,今日 VB と SQL と C++ をやってるワケで,VB で ‘文字列’ とかやるし。ワケ分からん。


DLL の登録場所。

HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsCurrentVersionSharedDLLs

VB で小数点以下 n 桁四捨五入。なんか方法があるのかも知れないが,ヘルプで見つからなかったので。

Int(a * 100 + 0.5) / 100

#その後の情報によると Format を使うらしい。

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

if(a = 1)

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

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

そういえば書いてなかったような気がするが,HTML Help Workshop は起動するたびに *.log を関連づけてくれる。こんな拡張子をしかもほとんど関係ないソフトがとるってのはどうだろう。Word の Doc 以上ですな。

Pha-jtd 氏から,CGI が動かないとメールが届く。原因は#!/usr/local/bin/perl#!/user/local/bin/perlと書いていたから。

#ってここで書いていいですか > Pha-jtd 様

大学のレポートプログラムでコンパイルエラー。何故か for を使うとエラーになるというものだが,for 文の中でローカル変数を使っていたからだった。

課題自体は「C 言語」で簡単な計算機を作るというものだが,スタック作るのに template を使えないので

#define name2(x, y) x##y
#define declare(x, y) name2(x, declare)(y)
#define implement(x, y) name2(x, implement)(y)

とかやって実装したりして。ていうか C++ か Java くらい教えるようにして欲しいけど > 某大学情報工学科。

Perl。引数を処理しようとしてうまく行かない。

if(@argv < 5)
{
}

とかやっていたが,引数を表すのは @ARGV つまり大文字だった。

ふとある .cpp を見てみると,

#ifndef XXX_CPP
#define XXX_CPP

とか書いてあった。いつの間にこんなの書いたのか。


TeX で表に番号とキャプションを付ける。tabular を begin{table} end{table} で囲み,label と caption を付ける。はずだが,うまく出ない。参照すると節番号になってしまう。でネットをあさって原因が判明。label は caption の後に書くらしい。確かに既にうまくいってる「図」はそうなっている。で,そのようにして XXX.aux を消し,再コンパイルすると見事に正常に表示された。

# でも TeX でこういう順番気にしなきゃいけない事なんてなかったからなあ。普通気にしないような。

ある 2 つのビューがあり,A はマウスのクリックでマウスをキャプチャーする。B はフォーカスがなくなった時にダイアログを出すとする。(B にエディットボックスがあり,そこで編集を行うが,フォーカスがなくなったら値チェックを行う,など。もっと分かりやすい例は後で。)

ここで,B で編集をし,そのまま A をクリックするとどうなるかという問題である。この場合,

  • B のフォーカスが失われる
  • ダイアログが出現し,OK ボタンを押す
  • A に WM_LBUTTONDOWN メッセージが来て,マウスがキャプチャーされる

という具合になるのだが,ここで問題が生じる。A の WM_LBUTTONUP が呼ばれないのだ。よってマウスのリリースができず,困った状況になる。で,解決するために結構悩んだんだが,そういえばエクスプローラも同じ事をやっている。以下の手順で試してみて欲しい。

  • ツリービューでフォルダ名を編集し,* とかにする
  • 編集中のままリストビューをクリック
  • エラーが出るので,OK。

さて,どうなっただろうか。答えは「マウスボタンは離しているのにラバーバンドが現れる」だ。なあんだ。エクスプローラもできてないのか。

もう 1 つエクスプローラの問題。同様に

  • リストビューでファイル名を編集し,* とかにする
  • 編集中のまま真ん中の境界線をクリックし,素早くサイズ変更
  • エラーが出るので,OK。

さて,今度はどうなっただろうか。境界を右に動かした方はリストビューが欠けてしまい,逆に左に動かした方は白い変な領域ができたと思う。これは誰も気付かなかったバグなのか,それとも対応不可能なバグなのか。ちなみに私が試したのは Win98 だけなので他ではどうなるか分からない。

#後で試したところ,NT も同様だった。

Install Sield,ファイルコピー中のファイル名で,カタカナのア・カ行辺りが文字化けする。

Enable(INDVFILESTATUS);

を消し,表示そのものを行わないようにする。

いつか 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

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

修正追加。内部コードを EUC に変更したので,ismbblead,ismbbtrail(自作)を EUC にした。

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 が付けられてた。「登録されている拡張子は表示しない」になっていたのだ。うぅ。