1. ホーム

CrtIsValidHeapPointerのエラーの解決法

2022-02-12 20:32:52

いくつかの時間前、私はVS2010カプセル化を開始し、ライブラリをカプセル化し、デモを書き、全く問題なくテストし、その後私の同僚は私のライブラリを呼び出すために必要な、私は彼にdllライブラリのVS2003バージョンをコンパイルする必要があります、私は再びライブラリをカプセル化して、デモを書き、テスト、実行は問題ありません、終わり近くに、常に_ CrtIsValidHeapPointerエラーが報告されます。バージョン10の初めに問題がないため、問題の前にカプセル化されたバージョン03は、常に彼らは、スタックにスタックの問題で、その結果、問題を記述するライブラリインターフェイスではないと思う、半日を試して、問題はまだ解決することはできません。とし、インターネット上での検索は、元のランタイムライブラリの問題の選択であることを見つけるために、ライブラリのカプセル化 03シングルスレッドデバッグ(/ MLd)のデフォルトの選択と私はデモで、同様に、後で呼び出し、マルチスレッドデバッグDLL(/ MDd)を使用している。変更後、最終的に問題はありません。私は分析の問題のこの作品のネット友達はかなり徹底していると思う、それを共有する


この問題は、異なるモジュールに割り当てられたメモリを解放する際に発生し、この問題はDebugモードでのみプロンプトが表示され、Releaseモードでは表示されません。しかし、プロンプトが出ないからといって、エラーがないわけではありませんので、修正する必要があります。解決策は、メモリを割り当てたモジュールでメモリを解放することです。

このエラーには2つの可能性があります。

i. リリースの問題点

1、モジュール間でメモリの確保と解放ができない。モジュールによって割り当てられたメモリは、そのモジュール自身によって解放されなければなりません。DLL には、メインプログラムがメモリを解放するために呼び出す、MemRelease などの別のメソッドがあるはずです。

2を回避する方法である、新規と 削除 GlobalAlloc() と GlobalFree() メソッドを使用します。
<スパン II. <スパン ダイナミックリンクライブラリ の問題点

この質問には2つのシナリオがあります。

1. 外部 lib のリンクエラー: 現在の デバッグ モードであるにもかかわらず、プロジェクトのプロパティ->設定のプロパティ->リンカー->入力->追加の依存関係のリンクライブラリは、リリースバージョンを使用しているので、これを慎重に確認する必要があります。また、Releaseモードで間違ってDebugのリンクライブラリを使用してしまうという問題もあります。

2.ruantime Linbraryモードエラー。プロジェクトのプロパティ->設定のプロパティ->C/C++->コード生成->ランタイムライブラリのオプションは、現在のコンパイルモードと一致していない可能性があります。例えば、現在のモードがReleaseの場合はMulti-threaded Debug DLL (/MDd)が選択され、現在のモードがDebugの場合はMulti-threaded DLL (/MD)が選択されています。

今日はこのアサートが発生し、DLLのローカルベクトルは、リリースが報告されると、ベクトルは、DLLで定義されているし、実際に空間の割り当てでexe(参照によってパラメータを渡すと、push_backメソッドを呼び出します)ことがわかった。ベクトルは、それがDLLは、DLLによってではないメモリを解放していることがわかりますリリースされるように、それはこのアサーションのうち、一時的な解決策は、DLLの割り当てに、ベクトルの予約メソッドを介して達成することができますもちろん、より良い解決策は、上位層のこの使用を回避することです。

また、mfcでもこの問題が発生しました。

mfcのメインプログラムは、DLLを参照して、DLLで動的にオブジェクトを割り当て、その後、メインプログラムでリリースされ、問題が発生します、プログラムが静的にmfcランタイムライブラリに依存しているため、リリースでは、唯一のメインプログラムを探してヒープ空間に位置している、アプリケーションがアドレスのヒープリストでは、もはやのリリースと考え、mfcの動的依存性に変更すると、プログラムは唯一の依存性を探しますアプリケーションのみ依存DLLのヒープのリストを探して、問題はないだろうようになります

CrtIsValidHeapPointerの実装コードから、この関数がNULLポインタをチェックするだけでなく、より重要なのはポインタアドレスの有効性をチェックすることであることがわかります。

私が遭遇した問題
メモリを解放するときにアサートがポップアップし、エラーを報告する関数が上記のこれです。CrtIsValidHeapPointerです。
CrtIsValidHeapPointerに関するコメントで状況を説明しています。解放されるメモリアドレスが現在制御されているヒープのアドレス範囲にない場合にもこのエラーが報告されます。

<スパン 問題の分析
1. 私の実装は、exeでdllのメソッドを呼び出し、このメソッド内でいくつかのメモリを確保し、新しく確保したメモリにデータをコピーして渡し、その後exeでこのメモリの部分を解放します。
2、同じプロセスで、ヒープが2つある?あなたのプロジェクトのランタイムライブラリがMT型(アプリケーションがランタイムライブラリを使用できるようにするマルチスレッドの静的バージョン)であれば、はい、確かに、dllとexeは別々の2つのヒープです。
3. つまり、DLLに割り当てられたメモリはDLL内でしか解放できないので、そうしないとCrtIsValidHeapPointer関数がエラーを報告することになります

<スパン 最初は、どうしてこうなるんだろうと思いました。しかし、DLLの実装者が異なり、さらにその人が選んだランタイムライブラリが異なる形でリンクされている可能性を考えると、それらをすべて1つのヒープに置かなければならないのは事態を複雑化させることになります。


解決策
誰が確保して誰が解放するのか、確保したいメモリのサイズはDLL自身にしか分からないので、メモリの確保をDLLに入れ、そのDLLがメモリを解放するメソッドを提供する必要があったのです。みたいな感じ。
1. allocAndGetData(CData** ppData);   // 関数名が気に入らない
2. freeData(*pData);

<スパン 第2条

_CrtIsValidHeapPointer()は、デバッグモードでローカルヒープメモリのアドレスのテストを行います。Cランタイムライブラリが共有でリンクされている場合は問題ありませんが、Cランタイムライブラリが静的でリンクされている場合は、DLL内の関数の呼び出しに問題がある可能性があります。エラーが発生します。

<スパン 例 2つのDLLファイルa.dll ,b.dllがあり、a.dllはCランタイムライブラリへのスタティックリンクを使用しています。

<スパン a.dllで関数CString aFun()がエクスポートされている場合、その値として文字列を返す。

b.dllでCString str=aFun()として関数が呼び出された場合。

b.dllは、デバッグ時に_CrtIsValidHeapPointer()が間違っていることを示すエラーで実行されます。

MSDNでこの関数を調べると、DLLがCランタイムライブラリに静的にリンクされている場合は、DLLと相対的にヒープ(Heap)が作成され、Cランタイムライブラリに共有でリンクされている場合は、アプリケーションのヒープメモリが使用されることがわかります。そして、DEBugモードの_CrtIsValidHeapPointer()は、入力されたアドレスがローカルヒープメモリにあることを確認するので、問題は明白になります:。

a.dll は静的にリンクされた C ランタイムライブラリを使用しているので、その DLL に相対するヒープメモリを作成し、a.dll で割り当てられた一時変数がそのヒープに割り当てられるようにします。

<スパン a.dllの関数aFun()が文字列を値として返すところを見てください。return文の実行により、戻り値として一時変数が作成されますが、この一時変数のメモリはa.dllのヒープメモリにあり、bで呼び出されると、b.dllのヒープメモリにある一時変数になります。 dllの中で、CString str = aFun();という文を実行し、一時変数関数のCStringデストラクタが終了すると、メモリ空間を解放しますが、この時_CrtIsValidHeapPointer()はメモリアドレスがローカルヒープメモリにないことを検出しエラーを発生させます。しかし、このエラーを無視した場合、プログラムが正しく実行されることがあります。

回避策としては、a.ll を C ランタイムライブラリに共有アプローチでリンクすることです。

このエラーは見つけるのに時間がかかりました、ハハ。

第3条

これは、DLLは、アプリケーション(それを呼び出すexe)の独立したローカルヒープを持っているため、呼び出し後にDLLが占有する空間は、無効である以前の新しいメモリアドレス、その後、この問題を解決するには、DLLファイルが動的にメモリを適用するときにHeapCreate関数を使用して、新しい演算子を使用できない、詳細はMSDN参照。 以下のURLを参照することは可能です。

問題は赤字で、カスタムクラスで

上記の文を次のように置き換えます。

コルヒストグラム   pMyColHist;
pMyColHist = new ColHistogram;

動作はするのですが、今ひとつ理由がわかりません

<スパン この引用は (MSDN) から

CrtIsValidHeapPointer関数は、特定のメモリアドレスがローカルヒープ内にあることを確認するために使用されます。ヒープとは、Cランタイムライブラリの特定のインスタンスによって作成および管理されるヒープのことを指します。動的にリンクされたライブラリ(DLL)がランタイムライブラリへの静的リンクを含む場合、ランタイムヒープの独自のインスタンスを持ち、したがってアプリケーションのヒープとは独立した独自のヒープを持ちます。 DEBUGが定義されていない場合、プリプロセス中に_CrtIsValidHeapPointerへのコールが削除されます。

<スパン それはcのランタイムライブラリに対応するヒープを持っていないので、この段落を読んだ後、私はプログラム内のローカルヒープに自分自身を適用したが、また、動的にcランタイムライブラリに接続するDIBクラスを生成する必要があり、その後ColHistogramの私のインスタンスが、動的に生成されなければなりません。例えば、Cstring strを追加しても問題はないのですが、CStringがシステム定義であることだけは知っていて、cランタイムライブラリとの関係はよくわかりません。Cランタイムライブラリが静的にリンクされている場合、dllはアプリケーション(それを呼び出すexe)とは独立したローカルヒープを持っていなければならず(しかし私のプログラムはそうではない)、もし_DEBUGが定義されていなければ、_CrtIsValidHeapPointerはプリプロセッサによって削除されます。そんな感じです。上に書いてあることはよくわからないことが多いので、とりあえずの説明です。

また、dbgheap.cファイルはdllの中にあるようで、まだ見る方法はないようです

呼び出し側とライブラリの両方をリリースとしてコンパイルすれば、この問題は発生しません。
本質的なリファレンスです。[http://topic.csdn.net/u/20071116/09/cc675daa-0ab2-4952-99d0-98f3dd717439.html]
1、モジュール間でメモリの確保と解放を行うことはできません。モジュールで確保したメモリは、そのモジュール自身で解放する必要があります。DLL 内に MemRelease などの別のメソッドがあり、メインプログラムがそれを呼び出してメモリを解放する必要があります。
2, は、new と delete をバイパスして、GlobalAlloc() と GlobalFree() メソッドを使用することです。
自分:Debug版でopencv 2.0 peopledetect.cppを使用すると問題が発生し、C/C++設定のランタイムライブラリのマルチスレッドデバッグDLL (/MDd) を変更することで解決しました。
奇妙なエラー、その落とし穴とは! ネイティブリブを参照するC++/CLIプログラムが順調にコンパイルされると、以下のようなエラーでプログラムが開始されることがあります。

によるmsの既知のバグです。

このエラーが発生する理由は、winformsアプリケーションはマネージドエントリーポイントを持っているからです。ネイティブのグローバルオブジェクトの初期化はCRTによって行われます(この場合、CRTのスタートアップルーチンがないため、MyBoardグローバルオブジェクトは正しく初期化されません。

<スパン IDEでは、ManagedAppのエントリーを"main"として指定しています。しかし、"main"を使用すると、CRTのスタートアップの初期化の多くをバイパスすることができます。

<スパン 回避策

<スパン プロジェクト上で右クリック--->プロパティ--->リンク--->詳細設定

Entry Point を main から "?mainCRTStartupStrArray@@$FYMHP$01AP$AAVString@System@@@Z" に変更します。

原則です。

このシンボルは、実際には "int __clrcall mainCRTStartupStrArray(cli::array^)" のマングルドネームです。


マロク/フリー

malloc/freeは、CおよびC++の標準ライブラリ関数で、オーバーライド可能であり、ヘッダーライブラリ関数のサポートが必要です。
コンパイラの制御下にはないため、コンストラクタやデストラクタのタスクを実行できない

malloc標準ライブラリ関数が行うこと:動的ヒープメモリの確保
標準ライブラリ関数 free の役割:ダイナミックヒープメモリの解放

mallocで確保したメモリの一部をポインタでアクセスし、メモリデータにアクセスするためにポインタを移動させる必要があること

mallocはデータ型に関係なくバイトしか認識せず、void*しか返せないので、プログラマはそれを適切なポインタ型に強制変換しなければなりません。
また、if (NULL ! = ...) を使用して、メモリ領域の要求が成功したかどうかを判断します。


 新規/削除
new/deleteはC++の演算子で、オーバーライド可能な予約語であり、ヘッダのサポートは必要ありません。
コンパイラの制御の範囲内で、コンストラクタとデストラクタのタスクを実行することができます

new 演算子が行うこと:動的ヒープメモリの確保、オブジェクトの初期化(コンストラクタの呼び出し)。
delete演算子の役割:オブジェクトのクリーンアップ(デストラクタの呼び出し)、ダイナミックヒープメモリの解放

newで作成されたオブジェクトは、そのアドレス空間に直接アクセスするのではなく、メンバ関数でアクセスすることができます。

new は malloc にコンストラクタの実行を加えたものと考えることができ、new ポインタは強制的な型変換なしに直接型情報を持ちます。

 malloc/freeとnew/deleteの関連性と違いについて
基本データ型については、"object"のような構築と破壊の処理はないので、それらについては。
malloc/free と new/delete は等価です。

new/deleteはmalloc/freeを完全にオーバーライドするので、なぜC++はmalloc/freeを段階的に廃止しないのでしょうか?
C++プログラムはC関数を呼び出すことが多く、Cプログラムは動的ヒープメモリの確保と解放にのみmalloc/freeを使用できるため

メモリリークのチェックはmallocでもnewでも可能ですが、newはファイル内の行を指定できるのに対し、mallocにはそのような情報がないという違いがあります。

new" によって生成された "動的オブジェクトを free で解放すると、オブジェクトはデストラクタを実行できなくなり、プログラムエラーの原因となることがあります。
mallocで要求されたダイナミックメモリをdeleteで解放すると、理論上はエラーになりませんが、プログラムの可読性が悪くなります。
ですから、new/deleteはmalloc/freeと同様にペアで使用する必要があります。

 Visual C++ 6.0 の malloc/free、new/delete。

int* p = (int*)malloc(5 * sizeof(int))。

void* __cdecl malloc(size_t nSize)
{ <未定義
 void* res = _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);

 resを返します。
}

void* __cdecl _nh_malloc_dbg(size_t nSize, int nhFlag, int nBlockUse, const char * szFileName, int nLine)
{ <未定義
 void* pvBlk;

 for (;;)
 { <未定義
 _mlock(_HEAP_LOCK) です。

 __try
 { <未定義
 pvBlk = _heap_alloc_dbg(nSize, nBlockUse, szFileName, nLine);
 }
 _最終的に
 { <未定義
 _munlock(_HEAP_LOCK)です。
 }

 if (pvBlk || nhFlag == 0)
 pvBlk を返します。
 }
}

void* __cdecl _heap_alloc_dbg(size_t nSize, int nBlockUse, const char * szFileName, int nLine)
{ <未定義
 pHead = (_CrtMemBlockHeader*)_heap_alloc_base(blockSize);

// 割り当てたメモリに 4 x 256 (すなわち 4 FD) を追加し、メモリを解放する際にメモリ境界マーカーとして使用する。
// _bNoMansLandFillの値は253で、nNoMansLandSizeの値は4です。
 memset((void*)pHead->gap, _bNoMansLandFill, nNoMansLandSize);
 memset((void*)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);
 memset((void*)pbData(pHead), _bCleanLandFill, nSize);

 return (void*)pbData(pHead);
}

void* __cdecl _heap_alloc_base(size_t size)
{ <未定義
 return HeapAlloc(_crtheap, 0, size); // HeapAlloc 関数は、ヒープメモリを割り当てるための Microsoft 内部 API である。
}

free(p)です。

void __cdecl free(void * pUserData)
{ <未定義
 _free_dbg(pUserData, _NORMAL_BLOCK);
}

void __cdecl _free_dbg(void* pUserData, int nBlockUse)
{ <未定義
 _ASSERTE(_CrtIsValidHeapPointer(pUserData));

// _bNoMansLandFillの値は253、nNoMansLandSizeの値は4です。
CheckBytes(pHead->gap, _bNoMansLandFill, nNoMansLandSize)を使用します。
 CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill, nNoMansLandSize)とする。
<スパン
 memset(pHead, _bDeadLandFill, sizeof(_CrtMemBlockHeader) + pHead->nDataSize + nNoMansLandSize)を実行します。
 _free_base(pHead);
}

int __cdecl _CrtIsValidHeapPointer(const void* pUserData)
{ <未定義
return HeapValidate(_crtheap, 0, pHdr(pUserData)); // HeapValidate 関数は、与えられたヒープの有効性を検証する Microsoft 内部 API である。
}

static int __cdecl CheckBytes(unsigned char* pb, unsigned char bCheck, size_t nSize) // bCheckの値は253、nSizeの値は4である。
{ <未定義
 int bOkay = TRUE。

 while (nSize --)
 { <未定義
 if (*pb++ ! = bCheck) // 割り当てたメモリの後に256が4つあるかどうか(つまり、4FD)、ない場合はエラー
 bOkay = FALSE。
 }

 return bOkay; // エラーをチェックし、TRUE を返します。
}

void __cdecl _free_base(void* pBlock)
{ <未定義
 PHEADER ペーダー。

 if (pBlock == NULL)
 を返します。

 HeapFree(_crtheap, 0, pBlock); // HeapAlloc または HeapReAlloc で割り当てられたメモリブロックを解放します。
}

int* a = new int[6];

void* operator new(unsigned int cb)
{ <未定義
 void* res = _nh_malloc(cb, 1);

 resを返します。
}

void* __cdecl _nh_malloc(size_t nSize, int nhFlag)