May 27, 2004

日付てすと

peke2のとこで曜日がずれてるらしいので、ちとテスト

と思ったら うちでは曜日表示してなかった。
けど、それっぽいのがぐぐったら出てきたので適用してみます。

http://cankoro.com/mt/archives/000031.html

May 20, 2004

軽井沢

明日から毎年恒例の軽井沢合宿に行ってまいります。
今年はAOMTT、カタン、麻雀合宿になる見込み。

一番楽しみなのは足のばして温泉に入れることだったりしますが。

VCアイコン

Win32アプリのデフォルトアイコンを設定するときは、リソースに IDR_MAINFRAME という名の*.icoファイルを追加する。32*32と16*16分けるときはどうすればいいんかいな。

May 13, 2004

アライメントの罠

久々に C でハマってしまった事例があるので、紹介。
もちろんハマるの意味は熱中するほうではなく、どつぼにはまる *1 状態を指しています。

データをファイルに出力するとき、よくあるケースとして構造体をそのまま書き込むことがあると思います。

// ビットマップヘッダを書き込む
WriteFile(hF, (BITMAPFILEHEADER*)&bmFH, sizeof(BITMAPFILEHEADER), &dwResult, NULL);

この例だと、BITMAPFILEHEADER構造体をそのままメモリからファイルへ出力しています。
通常、この構造体は PlatformSDK に含まれる WinGDI.h に定義されていて、実際には Windows.h をインクルードした時点で参照されています。

これを使う分には問題は発生しないのですが、先日ある理由により上記ヘッダファイルをインクルードせずに自分でBITMAPFILEHEADER構造体を定義して使っていたのです。具体的にはこんな感じ。

typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;

(MSDN に記載されているそのままです)

さて、このとき何が起こるのか予想できますか?

実はこの方法でBMPファイルを作成したところ、ペイント等でファイルを開こうとしてもエラーが出るだけです。不思議に思ってファイルをダンプしてみたところ、先頭部分(この構造体がまるまる入っている)がこんな感じになっていました。

42 4D 00 00 38 10 0E 00 00 00 00 00 38 00 00 00

(本当は ↓ みたいになるはず)
42 4D 36 10 0E 00 00 00 00 00 36 00 00 00 28 00

BITMAPFILEHEADER構造体の使い方を知らない方のために説明しておくと、通常のBMPでは bfType には常に 0x4d42 が入れてあり、bfReserved1 と bfReserved2 には 0 が入ってなくてはなりません。

しかし実際には上記のとおり、bfReserved1 に 0x000E が入ってしまっていました。
何度プログラムを見直しても、単に構造体をぶち込んでいるだけなのでミスは見当たりません。
よーく考えてみると、そもそも 3バイト目と4バイト目が0になっているのもおかしい。

はい。結論をいいますと、構造体では WORD bfType; となっているのに、実際のメモリ上にはWORD(2バイト)+パディング(2バイト)が入っていたのです。
通常のコンパイラだとデフォルト設定で構造体のアライメントが4バイトになっているのをすっかり忘れておりました。

これは 通常通りにWindows.hをインクルードして使っている分には問題は発生しません。期待通りに構造体がそのまま出力されます。WinGDI.hを よくよく見てみると

#include <pshpack2.h>
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
#include <poppack.h>

このように定義されているじゃありませんか。pshpack2の中にアライメントを2バイトに設定する指定が入っていて、poppackで再度設定を戻すようになっています。

原因がわかったので自前の構造体定義にも 同様の設定になるようにします。

#pragma pack(push, 2)

typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;

#pragma pack(pop)

これが正解。アライメント指定の記述方法がちと違いますが、これもアリです。
これによって構造体が2バイトアライメントされるようになり、構造体メンバはすべて2バイトと4バイトのものしかありませんので、ファイルに出力した場合でも余計なパディングはできません。

あーまた変なとこで時間くっちゃったい。

*1 : どつぼ=肥溜め の意味らしいです。関係ないけど