ドナドナされるプログラマのメモ

Windows用アプリのプログラミングメモ

Critical error detected c0000374

昔作ったソフトの改造をしていたら、突然「Critical error detected c0000374」というエラーが出て止まるようになった。実行継続して例外を処理させてみたら、

「ハンドルされない例外が 0x00007FFB1E01E6FC (ntdll.dll) で発生しました(伏せ字.exe 内): 0xC0000374: ヒープは壊れています。 (パラメーター:0x00007FFB1E0722B0)。」

なんてダイアログが出てきた。ヒープ破損!?メモリ関係でやっちゃったかなあ・・・。とりあえず、stdafx.hに

#include <crtdbg.h>

を追加。そして、プログラムが停止したあたりに

_ASSERT(_CrtCheckMemory());

を仕込み、ヒープを破壊した箇所を絞り込む。・・・?なんでこんなコードで破壊が起きているんだ?

 ちょっとだけコード解説。このコードは、CImageクラスの画像をOpenCVのMatクラスにコピーしている。最初の数行で、それぞれに対し同一サイズの画像用メモリを確保し、memcpyでコピーしている。GetPitch()でmemcpyの仕方を場合分けしているのは、CImage型の変数が有する画像イメージへの生ポインタを取得するGetBits()関数が2通りのポインタを返すためだ。

CImageは、画像左上を原点とするフォーマットと左下を原点とするフォーマットをサポートする。GetPitch()やGetBits()は、ユーザーがその違いを気にしなくても済むよう、左下原点の場合はPitchとして負の値を、GetBitsの結果として左下の点に相当するメモリアドレスを返す。これにより、たとえばforループを書くときに場合分けをせずに済むようになる。しかし、この機能は今回のコードでは邪魔で、それぞれの場合用に書き分ける必要が生じる。それが、GetPitch()でmemcpyの仕方を場合分けしている理由だ。

さて、本題。同一サイズの画像をコピーしているだけなのに、なぜヒープが壊れるのだ?・・・もしかして、画像サイズは同一でも、メモリサイズは同一ではない?試しに、1ラインあたりのメモリサイズを見てみよう。CImageもcv::Matも、構造体内にデータとして持っていたはず・・・

  • CImage
    img.m_nPitch : -408
  • cv::Mat
    *tmp.step.p: 405

ビンゴでした。1ラインあたりのメモリサイズが異なっていました。CImage::GetPitch()のドキュメントにも、「The pitch can also include additional memory, reserved for the bitmap.」なんて書いてありました。これを直せば、治るかな?