API は悪い設計の証拠ですが、解決策は実際には非常に単純です。シンプルですが、悲しいことに、このようにしなければなりません。複数のメモリ割り当てが必要になる可能性があるため、パフォーマンスがやや低下するからです。ソリューションのキーポイントを次に示します。
異なる Windows バージョン (XP など) では異なるセマンティクスを持つ可能性があるため、異なる Windows バージョン間の戻り値に実際に依存することはできません。
指定されたバッファが小さすぎて文字列を保持できない場合、戻り値は 0 ターミネータを含む文字数になります。
提供されたバッファが文字列を保持するのに十分な大きさである場合、戻り値は 0 ターミネータを除いた文字数です。
これは、返された値がバッファ サイズと正確に等しい場合でも、成功したかどうかはまだわからないことを意味します。もっとデータがあるかもしれません。か否か。最終的には、バッファー長が実際に必要な長さよりも大きい場合にのみ、成功を確信できます。悲しいことに...
したがって、解決策は小さなバッファから始めることです。次に、正確なバッファー長 (TCHAR 単位) を渡して GetModuleFileName を呼び出し、返された結果をそれと比較します。返された結果がバッファ長より小さければ成功です。返された結果がバッファ長以上の場合は、より大きなバッファで再試行する必要があります。すすぎ、完了するまで繰り返します。完了したら、バッファの文字列コピー (strdup/wcsdup/tcsdup) を作成し、クリーンアップして、文字列コピーを返します。この文字列は、一時バッファからのオーバーヘッドではなく、適切な割り当てサイズになります。呼び出し元は、返された文字列を解放する責任があることに注意してください (strdup/wcsdup/tcsdup mallocs メモリ)。
実装と使用コードの例については、以下を参照してください。私はこのコードを 10 年以上使用しており、非常に長いパスが多数存在する可能性があるエンタープライズ ドキュメント管理ソフトウェアも含まれています。もちろん、コードはさまざまな方法で最適化できます。たとえば、最初に返された文字列をローカル バッファ (TCHAR buf[256]) にロードします。そのバッファーが小さすぎる場合は、動的割り当てループを開始できます。他の最適化も可能ですが、ここでは説明しません。
実装と使用例:
/* Ensure Win32 API Unicode setting is in sync with CRT Unicode setting */
#if defined(_UNICODE) && !defined(UNICODE)
# define UNICODE
#elif defined(UNICODE) && !defined(_UNICODE)
# define _UNICODE
#endif
#include <stdio.h> /* not needed for our function, just for printf */
#include <tchar.h>
#include <windows.h>
LPCTSTR GetMainModulePath(void)
{
TCHAR* buf = NULL;
DWORD bufLen = 256;
DWORD retLen;
while (32768 >= bufLen)
{
if (!(buf = (TCHAR*)malloc(sizeof(TCHAR) * (size_t)bufLen))
{
/* Insufficient memory */
return NULL;
}
if (!(retLen = GetModuleFileName(NULL, buf, bufLen)))
{
/* GetModuleFileName failed */
free(buf);
return NULL;
}
else if (bufLen > retLen)
{
/* Success */
LPCTSTR result = _tcsdup(buf); /* Caller should free returned pointer */
free(buf);
return result;
}
free(buf);
bufLen <<= 1;
}
/* Path too long */
return NULL;
}
int main(int argc, char* argv[])
{
LPCTSTR path;
if (!(path = GetMainModulePath()))
{
/* Insufficient memory or path too long */
return 0;
}
_tprintf("%s\n", path);
free(path); /* GetMainModulePath malloced memory using _tcsdup */
return 0;
}
とはいえ、GetModuleFileName(Ex) に関するその他のさまざまな注意事項を十分に認識しておく必要があることを指摘したいと思います。32/64 ビット/WOW64 の間でさまざまな問題があります。また、出力は必ずしも完全な長いパスではありませんが、短いファイル名であるか、パスのエイリアシングの影響を受ける可能性があります。そのような関数を使用する場合、呼び出し元に使用可能で信頼性の高い完全な長いパスを提供することが目標であると期待しています。したがって、使用可能で信頼性の高い完全な長い絶対パスを確実に返すことをお勧めしますさまざまな Windows バージョンとアーキテクチャ間で移植可能です (これも 32/64 ビット/WOW64)。それを効率的に行う方法は、ここでは範囲を超えています。
これは現存する中で最悪の Win32 API の 1 つですが、コーディングを楽しんでいただけることを願っています。