‘Win32’のエントリ

フォーカス矩形,どうしてもうまい具合に表示されなかったんだが,スクロールを CScrollView に任せずに自分で処理したらうまく表示された。CScrollView がなんか処理しているらしい。

VC6.0 を入れてからだが,どうもデバッグしてるとエクスプローラ関連が落ちる。デバッグ中のツールバーにマウスカーソルが重なると落ちるし,エクスプローラのメニューに触れると落ちる。何故だろう。


オフスクリーンバッファ,バッファ再作成の時にビットマップの Detach を行っていたが,リソースがどんどんなくなっていく。結局バッファの初期化,解放時に new,delete し,転送時にデバイスコンテキストに選択するようにした。

アイテムなどを選択したときの矩形の描画。通常の方法では細かい点線が書けないので悩んだが,DrawFocusRect で解決。

システム色の取得方法

::GetSysColor();

ビューの背景を変える方法。OnDraw で描画してしまうとその前に白で描画されるのでちらついてしまう。これを回避するには WM_ERASEBKGND メッセージで背景を描画し,TRUE を返す。

エディタ,マウスがクライアント外にある時のスクロールを実装する。メッセージは送られてこないのでタイマーで実装する。OnLButtonDown でタイマーを発生させ,その中で WM_MOUSE_MOVE メッセージを送る(クライアント座標で送ることに注意)。で OnLbuttonUp でタイマーを解放して,完成。

hr みたいな線をダイアログに置く方法。static コントロールに置いて,スタイルで「くぼみ」をチェックする。

日記 2000/01/05 にもう少しいい方法が。


ボタンのオーナードローをする方法。ボタンのオーナー描画をチェックし,ダイアログの WM_DRAWITEM をハンドルする。で nIDCtl がオーナードローしたいコントロールだったら lpDrawItemStruct->rcItem に,pDrawItemStruct->hDC を使って描画する。


メモ帳のバージョン情報のようにフリーリソースを表示しようとしてハマる。Windows3.1 のときは GetFreeSystemResources というのがあったようだが,Windows95 ではなくなっている。よってサンクを使って 16 ビットのコードを呼び出さなくてはならないらしいが,サンクコンパイラが必要だったりアセンブラで記述しなければならないなど非常に面倒。しかもできたとしても WindowsNT では動かない(メモ帳でも表示されない)。で手間に比べて得るものが少ないので中止。

クリップボードからのコピー。他のアプリからのペーストができなくて困ったが,FORMATETC を作って GetData に渡すことで解決。

STGMEDIUM stg;
FORMATETC fmt =
{
CF_TEXT,
NULL, 
DVASPECT_CONTENT,
-1,
TYMED_HGLOBAL,
};

if(!pDataObject->IsDataAvailable(CF_TEXT))
{
return false;
}

pDataObject->GetData(CF_TEXT, &stg, &fmt);
HGLOBAL hData = stg.hGlobal;
char *pText = (char *)::GlobalLock(hData);
…
::GlobalUnlock(hData);

VC,コンパイル中にプロジェクトのフォルダを新規作成すると落ちるのを発見。

ワイルドカード *.* をログで検索したら (*.*) という顔文字がヒット。ウーム。


カレットについて。カレットが突然消えるという現象が起こった。調べると,カレットは通常 WM_SETFOCUS で作成し,WM_KILLFOCUS で破棄するらしいので,そうやってみたら解決した。

string の == が使えなくて困る。でいろいろ調べると,

#include <string>

#include <string.h>

と書いていた。何故か string は使えたが,オペレータが見つからなかったようだ。

OLE ドラッグアンドドロップの実装方法。くまのおえかきアルバムでもやったが,ここにやり方を簡単に書いておく。

OLE の初期化のために CWinApp::InitInstance に AfxOleInit() を加える。

View に COleDropTarget メンバ変数を加え,OnInitialUpdate に COleDropTarget::Register(this) を加える。

OnLButtonDown

// データをキャッシュ
COleDataSource *pData = CacheData();
if(pData != NULL)
{
    // ドラッグアンドドロップ
    DROPEFFECT dropeffect = pData->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE);

    delete pData;

    if(dropeffect == DROPEFFECT_MOVE)
    {
        // カット
    }
}

CacheData

COleDataSource *pData = new COleDataSource;

// グローバルメモリ
HGLOBAL hData = ::GlobalAlloc(GMEM_SHARE, 4);

// メモリにコピー
char *pText = (char *)::GlobalLock(hData);
strcpy(pText, "abc");
::GlobalUnlock(hData);

pData->CacheGlobalData(CF_TEXT, hData);

return pData;

なお,くまのおえかきアルバムではユーザー定義のフォーマットも利用したが,今回はやってないので割愛。

# ちなみにファイルをショートカットにドロップして開く,というのをやりたいが,未だにやり方が分かってない。というかシェルを勉強しなきゃならないのだがなかなか本がなくて…という感じ。

# ショートカットへのドロップはその後成功。2000/05/03 参照。

スクロールバーを自分でくっつける方法。

CRect rect;
GetClientRect(&rect);
m_scrollBar.Create(WS_VISIBLE | WS_CHILD | SBS_RIGHTALIGN | SBS_VERT, rect, this, 0);
m_scrollBar.SetScrollRange(0,100,FALSE);
m_scrollBar.SetScrollPos(0,TRUE);

だが,CView::OnInitialUpdate でやるとステータスバー領域とかが反映されない。これはステータスバーが作成される前に行われるからで,OnSize で設定し直せばよい。


スクロールバーのつまみのサイズが一定になっている。ファイルが小さいときはつまみが大きくなって欲しいのだ。かなり悩んだが,CWnd::SetScrollInfo の SCROLLINFO::nPos で設定できた。

おシゴトでエディタを作り始める。

スプリッターウィンドウを作る。3 つに分割されているが,VIEWX サンプルで簡単にできた。その際ハマった点。OnCreateClient でスプリッターを作るワケだが,

return CFrameWnd::OnCreateClient(lpcs, pContext);

をつけてはいけない。true でそのまま返す。

exe ファイルをデバッガで追いかけた場合のアドレス。0x400000 が加えられた値となるようだ。


デバッガで配列を見たとき,表示を 16 進にしておくと配列の添字が何故か 16 進として評価される。バグ?


mid を聞きつつ Web ページを見る。すると Web ページの mid が聞こえてきた。のはいいけど 2 重に聞こえる。ちょっとびっくり。

コンソールアプリを,DOS 窓なしで GUI から呼び出す。

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

// パイプの作成
HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
::CreatePipe(&hReadPipe, &hWritePipe, &sa, 0);

// STARTUPINFO
static STARTUPINFO StartupInfo;
static PROCESS_INFORMATION ProcessInfo;

::ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_HIDE;
StartupInfo.hStdOutput = hWritePipe;

::CreateProcess(
    NULL,
    lpCommandLine,
    NULL,
    NULL,
    FALSE,
    0,
    NULL,
    NULL,
    &StartupInfo, &ProcessInfo);

char buf[255];
unsigned long num;
::ReadFile(hReadPipe, buf, 255, &num, NULL);
if(::WaitForSingleObject(ProcessInfo.hProcess, INFINITE) == WAIT_OBJECT_0)
{
    DWORD res;
    ::GetExitCodeProcess(ProcessInfo.hProcess, &res);
    CloseHandle(ProcessInfo.hProcess);
}
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);

これを使えば Java コンパイラを呼び出したりとかで統合環境を作ったりできるかな。いつかやってみたい。

# 2000.05にもう少しちゃんとしたものを載せた。

ILS2,タイマーを 5ms ごとにしたら,描画・通信の同期の関係で異常な重さとなった。

下のミス。リリースバージョンでしか出ないバグで,デバッグに時間がかかった。

if(p != NULL)
{
    p = new char[10];
}

これでは条件が逆で,正しくは

if(p == NULL)
{
    p = new char[10];
}

デバッグでは値が NULL 以外に割り当てられるが,リリースでは値が NULL になったりならなかったりで動作がおかしくなる。

リリースのデバッグ情報を生成し,やっと見つけた。

同じ値の char 同士を比較したのに値が違う,と言われる。signed と unsigned を比較していた。

ILS2 DirectPlay のイベントスレッド,終了のためにイベントをもう 1 つ用意する。

while(1)
{
    // イベントを待つ
    HANDLE hHandles[2];
    hHandles[0] = m_hDPEvent;
    hHandles[1] = m_hDPKillEvent;
    if(::WaitForMultipleObjects(2, hHandles, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
    {
        // 終了イベント
        return;
    }
    else
    {
        処理
    }
}

のようにするとスッキリする。


VB から VC++ DLL を使う。VC++ で,MFC 拡張 DLL を作成し,

"func.h"
#ifdef __cplusplus
extern "C" {
#endif
int APIENTRY func();
#ifdef __cplusplus
}
#endif
"func.cpp"
int APIENTRY func()
{
    return 0;
}

で公開する関数を作成。この中からはクラスも呼び出せる。

次に def ファイル。

"library.def"
LIBRARY      "DialUpCommon"
DESCRIPTION  'DialUpCommon Windows Dynamic Link Library'

EXPORTS
    func=XXXXXXXX

XXXXXXXX の部分は内部名。@ がたくさん付いてるやつ。

VB 側

Private Declare Function func Lib "library.dll" () As Long

Private Sub Command1_Click()
func
End Sub

とする。その際,開発環境からは DLL をフルパスにするか,DLL をパスの通っている場所に置かなくてはダメだった。

DLL を作成。inline を書くのを忘れ,メソッドが 2 回定義され,DLL リンケージが違うと言われる。


実行ファイル名の取得方法。グローバル変数 _pgmptr を参照する。


プロセス ID の取得方法。_getpid() を使用する。

VC。アプリケーションの拡張子を後から変える方法

String Table の IDR_MAINFRAME を

xxxnnxxxnxxx File (*.xxx)n.xnxxx.Documentnxxx Document

のようにする。(xxx が拡張子)

スタティックライブラリにリソースを埋め込む方法。[挿入] – [リソース] でリソースを作成,その後プロジェクトに *.rc を追加する。

Direct3D,X ファイルをリソースからロードしようとして失敗する。見つからないと言われる。D3DRMLOADRESOURCE の loadResource.lpName が LPCTSTR なのが不思議に思ったのだが,結局リソース ID を MAKEINTRESOURCE で変換することで解決。

D3DRMLOADRESOURCE loadResource;
loadResource.hModule = AfxGetInstanceHandle();
loadResource.lpName = MAKEINTRESOURCE(RESOURCE_ID);
loadResource.lpType = "X";
pMeshBuilder->Load(&loadResource, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL);

VisualC++6.0 の,Doc-View を使わない設定だと,AppWizard で LoadStdProfileSettings が生成されない。ILS2 に以下の部分を追加

LoadStdProfileSettings();

ILS2 で使用する Sound ライブラリ。なんとか wav ファイルの再生が完成。これで Direct Sound もいけるだろう。


ウィンドウのサイズを固定にする方法。例えば AppWizard で作ったものなら,

cs.style &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);

としておけば ok。

大学の課題の n 次元女王支配問題が完成。とりあえずプロファイルをとるか,と思ったが,そういえば VC6.0(だけ?)ではプロファイルのチェックボックスがインクリメンタルリンクとごっちゃになって指定できないバグがあったのを思い出す。

プロジェクトオプションに手動で

/profile

を付ければいいだけだが。ていうかサービスパック 3 でもなおっていないとは。

VC,Doc-View なしのプロジェクトで,ツールバーとかを作成しない場合,CMainFrame::OnCreate を作成してくれない。バグだろう。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    // フレームのクライアント領域全体を占めるビューを作成します。
    if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
        CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))
    {
        TRACE0("Failed to create view windown");
        return -1;
    }

    return 0;
}

を追加。これがないとビューがなくて落ちる。