• ベストアンサー

動的メモリの確保の仕方がわからなくて困っています

バージョンリソースの情報を取得しようと以下の関数をつくったのですがバージョンがうまく読み出せません。環境はVC6.0++です。 /*--------------------------------------------------------*/ char *get_version() { #pragma comment (lib, "version.lib") char szFileName[MAX_PATH]; DWORD dwZero = 0, dwVerInfoSize; unsigned char *szBufferBlock; void *szBufferVersion; UINT VersionLen; GetModuleFileName( NULL, szFileName, sizeof( szFileName)); dwVerInfoSize = GetFileVersionInfoSize( szFileName, &dwZero); if (dwVerInfoSize){ szBufferBlock = (unsigned char *)calloc( dwVerInfoSize, sizeof(char)); GetFileVersionInfo( szFileName, dwZero, dwVerInfoSize, szBufferBlock); if(!szBufferBlock){ } VerQueryValue( szBufferBlock, TEXT("\\StringFileInfo\\041104b0\\ProductVersion"), &szBufferVersion, &VersionLen); } free((void *)szBufferBlock); return (char *)szBufferVersion; }/*--------------------------------------------------------*/ unsigned char *szBufferBlock;を unsigned char szBufferBlock[1604];のようにして 静的に確保するとバージョンをちゃんと返すのですが、 上記のようにcallocをやnew演算子を使うとソソソソソソソソソソソソのような文字列しか返さなくなります。 原因がわかりません。教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

主旨はANo.1の方と同じですが、すこし詳細に回答してみます。 1)VerQueryValueの仕様 BOOL VerQueryValue(  const LPVOID pBlock, // address of buffer for version resource  LPTSTR lpSubBlock, // address of value to retrieve  LPVOID *lplpBuffer, // address of buffer for version value pointer  PUINT puLen // address of length buffer ); GetFileVersionInfoで取得した情報領域を指定して、検索するバージョン情報の指定をすると、 第3パラメータに取得対象の領域のポインタを返却、第4ポインタにはその長さを返却します。 この時、第3パラメータのポインタは、第1パラメータの領域内のアドレスを指しています。 2)get_version関数の返り値 MSDNに以下のような記述があります。(左記のVerQueryValueの仕様を参照してください)  The memory pointed to by *lplpBuffer is freed when the associated pBlock memory is freed. (第1パラメータの関連したメモリが開放された時に、第3パラメータが指示するメモリは開放される) return (char *)szBufferVersion; の前に、free((void *)szBufferBlock); がありますから、 1)で説明した第3パラメータであるポインタはこの時点で無効アドレスになります。 従ってどこを指しているかは不明であり、なにが返ってくるかわかりません。 結果として質問文にあったような状況が発生します。 3)その他 ANo.1の方が、動的メモリの開放を外に出すことを推奨していますが、 char* pVer = get_version(); puts(pVer); free(pVer); 現在のget_version関数の仕様は、動的確保メモリで確保した領域にGetFileVersionInfo関数で情報を 取得し、その中からVerQueryValueで対象の情報のアドレスを取得してそれを返却しています。 つまり、動的確保メモリの一部を返却するのであって、先頭アドレスを返す保障がありません。 従って妥当な手段とは思えません。 それと、返却するszBufferVersion[void *]を、char*にキャストしていますが、VerQueryValueから 返ってくるタイミングで、szBufferVersionの終端が'\0'になっている保障はありません。 つまり文字列である保障がありません。ですから、長さを別途返すのだ思います。 この点も、現在のget_version関数の仕様として問題があると思います。 むしろ、calloc/freeの対はget_version関数の中に閉じさせて、この返却値=文字列ポインタの 指す領域を別途動的メモリ確保の上で、VerQueryValue関数の返却値をコピーし'\0'の終端加工して 返却する方が良いのではないでしょうか?当然この領域は通常はfreeしませんが、最終的get_version 関数内でfreeさせます。 最終的に返却域をfreeするかどうかはget_versionのパラメータでコントロールすれば良いかと思い ます。

supernoob
質問者

お礼

よくわかりました。 VerQueryValueの解説は、英語がわからないので、ずっと日本語の解説を見ていました。日本語の解説にはこう書かれています。 lplpBuffer 変数へのポインタを指定します。関数から制御が返ると、この変数に、pBlockパラメータが指すバッファから取得された指定のバージョン情報が格納されます。関連するメモリが解放された時点で、*lplpBufferが指すメモリも解放されます。 バージョン情報が格納されますと書いてあるんで、てっきり文字列が格納されるのかと誤解していました。関連するメモリというのもよくわからなくて、dennou2000さんの説明を見て理解できました。 よく考えればメモリを確保してない変数に文字列が入るというのもおかしな話しで・・・ これですっきりしました。ありがとうございました。

その他の回答 (1)

  • tettsu
  • ベストアンサー率30% (4/13)
回答No.1

最後の部分 > free((void *)szBufferBlock); > return (char *)szBufferVersion; ここに問題があります。 freeでメモリ開放したポインタを戻り値としています。 どうしても動的確保したいのであれば、get_versionの中でfree関数を呼び出さずにおいて、 char* pVer = get_version(); puts(pVer); free(pVer); というように、get_versionの外でfree関数を呼ぶとよいと思います。 それと、 >unsigned char szBufferBlock[1604]; このように書くと、ちゃんとバージョンを返すとありますが、この書き方だと、 スタック上にメモリを一時的に確保してあるだけで、実際にプログラムを実行してうまく言ったとしても、たまたま動いているだけなので、 static unsigned char szBufferBlock[1604]; このようにstatic宣言しておかないと安定した動作はしないと思います。

supernoob
質問者

お礼

理解できました。 VerQueryValue関数の理解が足らなかったみたいです。 とりあえずget_version()関数を呼び出す関数の中でメモリを確保して、それをget_version()に渡してみようと思います。 ありがとうございました。

関連するQ&A