VC のデバッガのクイックウォッチ等に独自文字列を入れる方法。

例えば CString では中身の文字列が,CPoint では x と y が表示されるが,これらは実は visual studio ディレクトリ内にある autoexp.dat に指定されている。ヘルプにも載っているが,これを編集すれば自分で作ったクラス等で好きな文字列を表示させられる。

これが java だったら toString メソッドを実行させて表示,とかもっときれいにできるんだろうなぁ。

ダイアログ内でのエディットボックスでの改行。そのままだとリターンが押された時点でデフォルトボタンが押されてしまうので,エディットボックスプロパティの [スタイル]-[改行を許可] をチェック。

ユーザー定義メッセージの作り方

  1. メッセージを必要とするクラスに const static UINT のメッセージ用メンバ変数を用意。
  2. メッセージ番号を ::RegisterWindowMessage(“XXX”); で初期化。
  3. LRESULT OnXXX(WPARAM wParam, LPARAM lParam); を .h のメッセージマップに追加。
  4. ON_REGISTERED_MESSAGE(XXX, OnFilterStart); を .cpp のメッセージマップに追加。

EUC 用漢字 1 バイト目 2 バイト目認識。

iskanji1(unsigned int c)
{
    return (c >= 0xa1 && c <= 0xfe);
}
iskanji2(unsigned int c)
{
    return (c >= 0xa1 && c <= 0xfe);
}

と書いていたが,これだと半角カナがきたときに,SS2 の次のバイトを漢字 1 バイト目だと認識してしまう。とりあえず SS2 を漢字 1 バイト目だと認識するように修正。

Win98,起動しなくなる。なんかスタンバイしたら復帰しなくなって,再起動かけるとレジストリが破壊されたみたいで,ログイン画面前で止まる。とりあえず Linux から最新のデータをバックアップして,再インストール。

GCC。string::compare だが,

int compare (const basic_string& str, size_type pos, size_type n) const;
int compare (const charT* s, size_type pos, size_type n) const;

の 2 つの挙動が違う。というか後者の場合うまく動かない。これも詳しく調べてないけど前者なら期待通りに動いてるのでまあいいか,とか。

GCC。B 様の調査によると,STL を使う場合,__STL_USE_NAMESPACES を指定した方がよい場合があるらしい。例えば iterator とかを単独で使う場合などに,これがないと iterator が定義されなくなってしまうらしいが…。

1999/09/14「コンソールアプリを,DOS 窓なしで GUI から呼び出す。」だが,この方法だとデータを得られない,という読者さんからのご指摘を頂く。読んでくれた人がいるというだけでもありがたいが,メールまで頂くと非常に嬉しい。

以下,頂いたサンプル。(見やすくするためと簡略化のため,改行や出力先等,多少変更してあります。)

// コマンドライン
LPTSTR lpCommandLine;
lpCommandLine = "xxx.exe";

// 標準出力用パイプ
HANDLE hReadPipe = NULL, hWritePipe = NULL;

// エラー出力用パイプ
HANDLE hErrReadPipe = NULL, hErrWritePipe = NULL;

// セキュリティ属性(ハンドル継承を指定)
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;

// 標準出力パイプの作成
::CreatePipe(&hReadPipe, &hWritePipe, &sa, 8192);

// エラー出力パイプの作成
::CreatePipe(&hErrReadPipe, &hErrWritePipe, &sa, 8192);

STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;

::ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);

// ハンドルの継承を指定
StartupInfo.dwFlags = STARTF_USESTDHANDLES;

// DOS窓を表示しない
StartupInfo.wShowWindow = SW_HIDE;

// 標準出力ハンドルとエラー出力ハンドルを設定
StartupInfo.hStdOutput = hWritePipe;
StartupInfo.hStdError  = hErrWritePipe;

// コンソールアプリ起動
if (::CreateProcess(
    NULL,
    lpCommandLine,
    NULL,
    NULL,
    TRUE,    // ハンドルの継承
    DETACHED_PROCESS, // DOS窓を表示しないための指定
    NULL,
    NULL,
    &StartupInfo, &ProcessInfo))
{
    // パイプ内容受け取り用バッファ
    char bufStdOut[8192], bufErrOut[8192];
    DWORD dwStdOut = 0, dwErrOut = 0;
    DWORD dwRet;

    // プロセス起動中のパイプ内容受け取り処理
    while ( (dwRet = ::WaitForSingleObject(ProcessInfo.hProcess, 0)) !=
        WAIT_ABANDONED)
    {
        memset(bufStdOut, 0, sizeof(bufStdOut));
        memset(bufErrOut, 0, sizeof(bufErrOut));

        // 標準出力パイプの内容を調べる
        ::PeekNamedPipe(hReadPipe, NULL, 0, NULL, &dwStdOut, NULL);
        if (dwStdOut > 0)
        {
             // 内容が存在すれば、読み取る
             ::ReadFile(hReadPipe, bufStdOut, sizeof(bufStdOut) - 1, &dwStdOut,
                NULL);
             // bufStdOut にパイプ出力が入る
        }

        // 同様にエラー出力の処理
        ::PeekNamedPipe(hErrReadPipe, NULL, 0, NULL, &dwErrOut, NULL);
        if (dwErrOut > 0)
        {
             ::ReadFile(hErrReadPipe, bufErrOut, sizeof(bufErrOut) - 1, &dwErrOut,
                NULL);
             // bufErrOut にエラー出力が入る
        }

        // メッセージキューを取得し、存在すれば、処理を促す
        MSG msg;
        if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
             ::TranslateMessage(&msg);
             ::DispatchMessage(&msg);
        }
        // プロセス終了なら、ループを抜ける
        if (dwRet == WAIT_OBJECT_0)
             break;
    }

    // プロセスハンドルとスレッドハンドルを閉じる
    DWORD res;
    ::GetExitCodeProcess(ProcessInfo.hProcess, &res);
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
}

// すべてのパイプを閉じる
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);
::CloseHandle(hErrWritePipe);
::CloseHandle(hErrReadPipe);

修正個所としては,

  • CreateProcess の bInheritHandles,dwCreationFlags の値。
  • 全体的なエラーチェック。

というところ。

# ちなみに 1999/09/14 では Win98 で動いているのを確認しているので,多分 NT 等では動かなかったのでは…と思う。いずれにしろ上のコードならば大丈夫でしょう。

# 2006/07/10 CreateProcess第3引数にsaを渡さないようにした。将来プロセスハンドルを子に継承させるならsaを渡すのだが,この目的では不要。


その後読者さんとは話がスクロールバーの話題へ。スクロールバーを MFC で使っていると,OnVScroll() の SB_THUMBPOSITION,SB_THUMBTRACK において,nPos が 16bit を越えると負の値となる。また,MFC のヘルプには

nMinPos と nMaxPos で指定された値の差は、32、767以下でなければいけません。

ということが書いてあったので,てっきりスクロールバーは 16bit しか扱えないんだと思っていたが,これは WM_VSCROLL が nPos を 16bit しか渡してくれないから,ということだ。(nPos = (short int) HIWORD(wParam); となっている。)

読者さんの指摘通り,OnVScroll() 内で GetScrollInfo() を直接呼び出し,スクロール位置を得ることで 32bit の値をとることができた。


いや,いつも身内だけでやってるのでこういうのはいいですね。上記以外でも,もしこの日記を読んだ方がいて,何か変だと思うことがあったら是非知らせて欲しいです。

TeX を消してしまったので,再インストール。その際フォントを作成するのが面倒(1999/10/27 日記参照)なので,どうにかして自動生成させようとする。

DVIOUT に指定する template に以下を記述。

%1st
set PATH=%PATH%;c:texDVIOUT;c:texbin
set TEXMF=c:texsharetexmf
set TEXMFCNF=.;c:texsharetexmfweb2c
c:
cd c:texsharetexmffontspkcxpubliccm
%2nd
MakeTeXPK ^s ^d ^D ^d/^D canonbjc
%3rd
copy c:texsharetexmffontspkcxpubliccm*.tfm c:texsharetexmffontstfmptex
del c:texsharetexmffontspkcxpubliccm*.tfm
del c:texsharetexmffontspkcxpubliccm*.log
del c:texsharetexmffontspkcxpubliccm*.gf

いや,絶対もっといい方法があるはずなんだけど。

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

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

某ウィルスチェッカ問題(2000/02/03 参照)だが,ようやくメーカーさんから再登録のメールが来た。早速該当ページへ行き,登録。が,以下のエラーが出た。

Microsoft OLE DB Provider for ODBC Drivers エラー '80040e31' 

[Microsoft][ODBC SQL Server Driver]時間切れになりました。 

/vbr/process.asp, 行 110 

時間切れのようである。結局何度かやってうまくいったのだが,相変わらずエラーの意味が専門家以外には分かりづらい。まあ登録はできたからよしとしよう。

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 以外のコンパイルが高速になる。

Mebius BIOS,ディスプレイドライバをアップグレード。SHARP のページに結構前から置いてあったが,やっと入れてみる。ちなみに今ダウンロードしようとするとディスプレイドライバは途中で応答がなくなるのだが,どうなのか。

まあそれはおいといて,前にダウンロードしてあったものを入れてみる。インストール自体はすんなりいく。で再起動。

…おお。Visual Studio のツリーが変にならない。前はツリーのところが変な風にゴミが表示されていたのだが,それがなくなった。そして,私が主に使っている Photo Crew というフォト・レタッチソフトも動作がよくなった。これは前は編集していると絵がどうしようもなくグチャグチャになっていってたのだが,大丈夫になったようだ。BIOS,ドライバどっちのおかげかは分からないが,入れて正解な感じ。

ついでにこの BIOS で NT + SP4 以上での問題も解決されないかな。

#その後,ダメなことを確認。

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

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

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

catch(...)

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

都合により NT をセットアップ。とりあえずある社長様から頂いたサービスパック 3 を用意し,WindowsNT Workstation4.0 を入れる。途中までは前もやったので ok。前はここでサービスパック 4 を入れてディスプレイドライバを入れて落ちたのである。とりあえずサービスパックなしの状態でディスプレイドライバを入れる。で,サービスパック 3 をインストール。

無事起動。IE5.0 も入るし快適快適。そうそう。社長様が「Back Office Server には NT Server が入ってる」って言ってたな。ということで Microsoft さんが(無料で)送ってきた Back Office Server から NT Server を入れる。実行ファイルをダブルクリック。インストールディスクを作ってアップグレード。

無事起動。おお。サーバーだ。ノートでサーバーってのもいいですな。あとはサービスパック 4 以上じゃなきゃインストールできない SQL Server が入って PCIMCA の MIDI カードが使えて DirectX も使えれば Win98 から乗り換えられるのだが…。って 2000 入れろって感じでしょうか。

ちなみに使い勝手は…エクスプローラがローカルでも早い! ってのが感動した。(日記 1999/12/02 参照)

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

strdup

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

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

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

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

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

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

某ソフトのユーザー登録。オンライン登録はいいんだけど,以下の(見慣れた)エラーが出た。

データベースが実行できませんでした。
-2147217900
[Microsoft][ODBC SQL Server Driver][SQL Server]PRIMARY KEY 違反、制約 ‘XXX’: オフ゛シ゛ェクト ‘XXX’ に重複したキーを挿入しようとしました。

要するに他の人と主キーがかぶってるワケだが,まさか氏名だけで主キーにしてるワケではないだろう,と,ふとハガキの方を見ると,「ユーザー登録をデータベース化する上で、同姓同名の場合の判断を生年月日で行いますので、ご記入をお願いしております。」とか書いてあった。この世界に同じ氏名で同じ生年月日の人は絶対いないと思ってるのだろうか。

あとエラーって出るのはいいけど SQL Server のエラーメッセージを一般人に見せられても困るし。ユーザーへの簡単なエラーメッセージとどうすればいいかを書いて,詳しいエラーメッセージはサーバーのログに残すようにするって仕様にはできなかったのか。

# 実は某ウィルスチェッカソフトなんですが,いまいち信用がおけないなぁ,と思う昨今でした。まあパターンファイルはダウンロードできるしいいか。データベースの整合性はおかしくなるかも知れないけど私の知ったこっちゃない。

というかなんかぐちゃぐちゃな感じが。昨日 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 くらい教えるようにして欲しいけど > 某大学情報工学科。