c++ 如何正确使用GetModuleFileName?

58wvjzkj  于 2023-06-25  发布在  其他
关注(0)|答案(6)|浏览(134)

以下代码:

  1. #include <iostream>
  2. #include <Windows.h>
  3. using namespace std;
  4. int main ()
  5. { LPWSTR buffer; //or wchar_t * buffer;
  6. GetModuleFileName(NULL, buffer, MAX_PATH) ;
  7. cout<<buffer;
  8. cin.get();
  9. cin.get();
  10. }

应该显示程序执行的完整路径。但是在VS 2012中,我得到了错误:
使用了未初始化的局部变量“buffer”
代码有什么问题?

cunj1qz1

cunj1qz11#

你需要给予它一个缓冲区,可以容纳一些字符;

  1. wchar_t buffer[MAX_PATH];

例如。

vof42yt1

vof42yt12#

VS正确地指出你正在使用一个未初始化的缓冲区-buffer var是一个指向WSTR的指针,但是它没有被静态缓冲区初始化,也没有被分配。另外,您应该记住MAX_PATH通常是不够的,特别是在具有长路径名的现代系统上。
既然你正在使用C++,那么使用它的特性将是一个很好的实践。我可以假设以下代码:

  1. vector<wchar_t> pathBuf;
  2. DWORD copied = 0;
  3. do {
  4. pathBuf.resize(pathBuf.size()+MAX_PATH);
  5. copied = GetModuleFileName(0, &pathBuf.at(0), pathBuf.size());
  6. } while( copied >= pathBuf.size() );
  7. pathBuf.resize(copied);
  8. wstring path(pathBuf.begin(),pathBuf.end());
  9. cout << path;
  • 注意:在C++11之前,std::stringstd::wstring的标准不保证具有连续的数据,并且可能不安全地用作缓冲区。但是,它们在所有主要标准库中都是连续的。
展开查看全部
vktxenjb

vktxenjb3#

这是Win32 API的一个普遍问题,函数将字符串返回到一个有限大小的缓冲区,你永远不知道你的缓冲区是否足够大来容纳整个字符串。正如kingsb提到的,即使MAX_PATH现在也不是一个足够好的路径常量。
我倾向于使用一个通用的helper函数来实现这个目的:

  1. template <typename TChar, typename TStringGetterFunc>
  2. std::basic_string<TChar> GetStringFromWindowsApi( TStringGetterFunc stringGetter, int initialSize = 0 )
  3. {
  4. if( initialSize <= 0 )
  5. {
  6. initialSize = MAX_PATH;
  7. }
  8. std::basic_string<TChar> result( initialSize, 0 );
  9. for(;;)
  10. {
  11. auto length = stringGetter( &result[0], result.length() );
  12. if( length == 0 )
  13. {
  14. return std::basic_string<TChar>();
  15. }
  16. if( length < result.length() - 1 )
  17. {
  18. result.resize( length );
  19. result.shrink_to_fit();
  20. return result;
  21. }
  22. result.resize( result.length() * 2 );
  23. }
  24. }

对于GetModuleFileName,可以这样使用:

  1. extern HINSTANCE hInstance;
  2. auto moduleName = GetStringFromWindowsApi<TCHAR>( []( TCHAR* buffer, int size )
  3. {
  4. return GetModuleFileName( hInstance, buffer, size );
  5. } );

对于LoadString,像这样:

  1. std::basic_string<TCHAR> LoadResourceString( int id )
  2. {
  3. return GetStringFromWindowsApi<TCHAR>( [id]( TCHAR* buffer, int size )
  4. {
  5. return LoadString( hInstance, id, buffer, size );
  6. } );
  7. }
展开查看全部
zengzsys

zengzsys4#

而且,在我下面的答案的旁注/补充:有时候,你想访问一个函数size_t GetString(char* buf = NULL, size_t bufsize = 0);,如果你不带任何参数调用它,它将返回必要的缓冲区大小,如果你正常调用它,最多写入bufsize字符到buf(或者直到它想要返回的字符串的末尾,无论先到的),返回实际写入的字符数。一个实现可能看起来像这样:

  1. class GetterClass
  2. {
  3. private:
  4. size_t String2Buffer(const std::string& string, char* const pBuffer = NULL, size_t size = 0)
  5. {
  6. size_t s, length = string.length() + 1;
  7. if (!pBuffer) return length;
  8. s = std::min<>(length, size);
  9. memcpy(pBuffer, string.c_str(), s);
  10. return s;
  11. }
  12. public:
  13. size_t GetterFunc(char* p, size_t len)
  14. {
  15. const static std::string s("Hello World!");
  16. return String2Buffer(s, p, len);
  17. }
  18. };

这通常发生在类工厂中,这些工厂位于DLL中,并且由于内存管理的原因,不想交换复杂类型,如std::string。要使用接口,您通常会以丑陋的代码结束:

  1. GetterClass g;
  2. GetterClass* pg = &g;
  3. std::string s(pg->GetterFunc() - 1, '\0');
  4. pg->GetterFunc(&s[0], s.size());

这很糟糕,原因很明显,一个是你不能直接在流插入中使用它。经过一些拖延和撕扯我的头发,我想出了几乎色情的解决方案,至少在模板和成员函数指针的使用方面:

  1. template <typename tClass>
  2. struct TypeHelper
  3. {
  4. typedef size_t(tClass::* MyFunc)(char*, size_t);
  5. static std::string Get(MyFunc fn, tClass& c)
  6. {
  7. size_t len = (c.*fn)(NULL, 0);
  8. std::string s(len - 1, '\0');
  9. size_t act = (c.*fn)(&s[0], len - 1);
  10. assert(act == s.size());
  11. return s;
  12. }
  13. };

其然后可以以以下方式使用:

  1. GetterClass Foo;
  2. GetterClass* pFoo = &Foo;
  3. std::string s1 = TypeHelper<GetterClass>::Get(&GetterClass::GetterFunc, Foo); // Class version.
  4. std::string s2 = TypeHelper<GetterClass>::Get(&GetterClass::GetterFunc, *pFoo); // Pointer-to-Instance version.

仍然有点复杂,但大部分繁重的工作都隐藏在幕后。

展开查看全部
5m1hhzi4

5m1hhzi45#

我还遇到了信任char pszPath[_MAX_PATH]足以让::GetModuleFilename()正常工作的问题(事实并非如此!)。因此,我冒昧地对Ivan的建议进行了一点修改,得出了以下代码

  1. template <typename TChar, typename TStringGetterFunc>
  2. std::basic_string<TChar> GetStringFromWindowsApi(TStringGetterFunc stringGetter, typename std::basic_string<TChar>::size_type initialSize = _MAX_PATH)
  3. {
  4. std::basic_string<TChar> sResult(initialSize, 0);
  5. while(true)
  6. {
  7. auto len = stringGetter(&sResult[0], sResult.length());
  8. if (len == 0) return std::basic_string<TChar>();
  9. if (len < sResult.length() - 1)
  10. {
  11. sResult.resize(len);
  12. sResult.shrink_to_fit();
  13. return sResult;
  14. }
  15. sResult.resize(sResult.length() * 2);
  16. }
  17. }

你可以用多种方式来使用,e。例如:

  1. std::string sModuleFileName = GetStringFromWindowsApi<char>([](char* buffer, int size)
  2. {
  3. return ::GetModuleFileNameA(NULL, buffer, size);
  4. });

如果你对lambda表达式没问题的话。如果成员变量是必需的,因为在DLL中经常会将HMODULE存储在DllMain(HMODULE hm, DWORD ul_reason_for_call, LPVOID)中,则必须添加this指针,如下所示:

  1. std::string sModuleFileName = GetStringFromWindowsApi<char>([this](char* buffer, int size)
  2. {
  3. return ::GetModuleFileNameA(m_MyModuleHandleHere, buffer, size);
  4. });

当然,“老式的方式”也可以像这个测试用例中所示的那样工作:

  1. typedef int (PGetterFunc)(char*, int);
  2. int MyGetterFunc(char* pszBuf, int len)
  3. {
  4. static const char psz[] = "**All your base are belong to us!**";
  5. if (len < strlen(psz) + 1)
  6. {
  7. strncpy(pszBuf, psz, len - 1);
  8. return len;
  9. }
  10. strcpy(pszBuf, psz);
  11. return static_cast<int>(strlen(psz));
  12. }
  13. void foo(void)
  14. {
  15. std::string s = GetStringFromWindowsApi<char, PGetterFunc>(MyGetterFunc, _MAX_PATH);
  16. }

你甚至可以使用TCHAR进行ANSI/UNICODE抽象,类似于这样:

  1. std::basic_string<TCHAR> sModuleFileName = GetStringFromWindowsApi<TCHAR>([](TCHAR* buffer, int size)
  2. {
  3. return ::GetModuleFileName(NULL, buffer, size);
  4. });

或者#include <tstdlib.h>,我不知道从哪里弄来的:

  1. #include <string>
  2. #include <sstream>
  3. #include <iostream>
  4. #include <fstream>
  5. #include <stdlib.h>
  6. #include <assert.h>
  7. #ifdef _WIN32
  8. #include <tchar.h> // For _T() macro!
  9. #else // Non Windows Platform!
  10. #ifdef _UNICODE
  11. typedef wchar_t TCHAR;
  12. #ifndef _T
  13. #define _T(s) L##s
  14. #endif
  15. #ifndef _TSTR
  16. #define _TSTR(s) L##s
  17. #endif
  18. #else
  19. typedef wchar_t TCHAR;
  20. #ifndef _T
  21. #define _T(s) s
  22. #endif
  23. #ifndef _TSTR
  24. #define _TSTR(s) s
  25. #endif
  26. #endif
  27. #endif
  28. /// <summary>
  29. /// The tstd namespace contains the STL equivalents to TCHAR as defined in <tchar.h> to allow
  30. /// all the Unicode magic happen with strings and streams of the STL.
  31. /// Just use tstd::tstring instead of std::string etc. and the correct types will automatically be selected
  32. /// depending on the _UNICODE preprocessor flag set or not.
  33. /// E. g.
  34. /// tstd::tstring will resolve to std::string if _UNICODE is NOT defined and
  35. /// tstd::tstring will resolve to std::wstring _UNICODE IS defined.
  36. /// </summary>
  37. namespace tstd
  38. {
  39. #ifdef _UNICODE
  40. // Set the wide character versions.
  41. typedef std::wstring tstring;
  42. typedef std::wostream tostream;
  43. typedef std::wistream tistream;
  44. typedef std::wiostream tiostream;
  45. typedef std::wistringstream tistringstream;
  46. typedef std::wostringstream tostringstream;
  47. typedef std::wstringstream tstringstream;
  48. typedef std::wifstream tifstream;
  49. typedef std::wofstream tofstream;
  50. typedef std::wfstream tfstream;
  51. typedef std::wfilebuf tfilebuf;
  52. typedef std::wios tios;
  53. typedef std::wstreambuf tstreambuf;
  54. typedef std::wstreampos tstreampos;
  55. typedef std::wstringbuf tstringbuf;
  56. // declare an unnamed namespace as a superior alternative to the use of global static variable declarations.
  57. namespace
  58. {
  59. tostream& tcout = std::wcout;
  60. tostream& tcerr = std::wcerr;
  61. tostream& tclog = std::wclog;
  62. tistream& tcin = std::wcin;
  63. /// <summary>
  64. /// Unicode implementation for std::endl.
  65. /// </summary>
  66. /// <param name="output">Output character stream.</param>
  67. /// <returns>Output character stream.</returns>
  68. std::wostream& tendl(std::wostream& output)
  69. {
  70. output << std::endl;
  71. return output;
  72. }
  73. /// <summary>
  74. /// wstr to tstr conversion for Unicode. Nothing to do here but copying things.
  75. /// </summary>
  76. /// <param name="arg">Input string.</param>
  77. /// <returns>Output string conversted from std::wstring to tstd::tstring.</returns>
  78. tstring wstr_to_tstr(const std::wstring& arg)
  79. {
  80. return arg;
  81. }
  82. /// <summary>
  83. /// str to tstr conversion for Unicode. Internally calls mbstowcs() for conversion..
  84. /// </summary>
  85. /// <param name="arg">Input string.</param>
  86. /// <returns>Output string conversted from std::string to tstd::tstring.</returns>
  87. tstring str_to_tstr(const std::string& arg)
  88. {
  89. tstring res(arg.length() + 1, L'\0'); // +1 because wcstombs_s() always wants to write a terminating null character.
  90. size_t converted;
  91. mbstowcs_s(&converted, const_cast<wchar_t*>(res.data()), res.length(), arg.c_str(), arg.length()); // Using the safer version of mbstowcs() here even though res is always defined adequately.
  92. assert(converted - 1 == arg.length()); // Sanity test.
  93. res.resize(res.size() - 1); // Remove '\0'.
  94. return res;
  95. }
  96. /// <summary>
  97. /// tstr to wstr conversion for Unicode. Nothing to do here but copying things.
  98. /// </summary>
  99. /// <param name="arg">Input string.</param>
  100. /// <returns>Output string conversted from tstd::tstring to std::wstring.</returns>
  101. std::wstring tstr_to_wstr(const tstring& arg)
  102. {
  103. return arg;
  104. }
  105. /// <summary>
  106. /// tstr to str conversion for Unicode. Internally calls wcstombs() for conversion.
  107. /// </summary>
  108. /// <param name="arg">Input string.</param>
  109. /// <returns>Output string conversted from tstd::tstring to std::string.</returns>
  110. std::string tstr_to_str(const tstring& arg)
  111. {
  112. std::string res(arg.length() + 1, '\0'); // +1 because wcstombs_s() always wants to write a terminating null character.
  113. size_t converted;
  114. wcstombs_s(&converted, const_cast<char*>(res.data()), res.length(), arg.c_str(), arg.length()); // Using the safer version of wcstombs() here even though res is always defined adequately.
  115. assert(converted - 1 == arg.length()); // Sanity test.
  116. res.resize(res.size() - 1); // Remove '\0'.
  117. return res;
  118. }
  119. }
  120. #else
  121. // Set the multibyte versions.
  122. typedef std::string tstring;
  123. typedef std::ostream tostream;
  124. typedef std::istream tistream;
  125. typedef std::iostream tiostream;
  126. typedef std::istringstream tistringstream;
  127. typedef std::ostringstream tostringstream;
  128. typedef std::stringstream tstringstream;
  129. typedef std::ifstream tifstream;
  130. typedef std::ofstream tofstream;
  131. typedef std::fstream tfstream;
  132. typedef std::filebuf tfilebuf;
  133. typedef std::ios tios;
  134. typedef std::streambuf tstreambuf;
  135. typedef std::streampos tstreampos;
  136. typedef std::stringbuf tstringbuf;
  137. // declare an unnamed namespace as a superior alternative to the use of global static variable declarations.
  138. namespace
  139. {
  140. tostream& tcout = std::cout;
  141. tostream& tcerr = std::cerr;
  142. tostream& tclog = std::clog;
  143. tistream& tcin = std::cin;
  144. /// <summary>
  145. /// Multibyte implementation for std::endl.
  146. /// </summary>
  147. /// <param name="output">Output character stream.</param>
  148. /// <returns>Output character stream.</returns>
  149. std::ostream& tendl(std::ostream& output)
  150. {
  151. output << std::endl;
  152. return output;
  153. }
  154. /// <summary>
  155. /// wstr to tstr conversion for multibyte. Internally calls wcstombs() for conversion.
  156. /// </summary>
  157. /// <param name="arg">Input string.</param>
  158. /// <returns>Output string conversted from std::wstring to tstd::tstring.</returns>
  159. tstring wstr_to_tstr(const std::wstring& arg)
  160. {
  161. tstring res(arg.length()+1, '\0'); // +1 because wcstombs_s() always wants to write a terminating null character.
  162. size_t converted;
  163. wcstombs_s(&converted, const_cast<char*>(res.data()), res.length(), arg.c_str(), arg.length()); // Using the safer version of wcstombs() here even though res is always defined adequately.
  164. assert(converted-1 == arg.length()); // Sanity test.
  165. res.resize(res.size() - 1); // Remove '\0'.
  166. return res;
  167. }
  168. /// <summary>
  169. /// str to tstr conversion for multibyte. Nothing to do here but copying things.
  170. /// </summary>
  171. /// <param name="arg">Input string.</param>
  172. /// <returns>Output string conversted from std::string to tstd::tstring.</returns>
  173. tstring str_to_tstr(const std::string& arg)
  174. {
  175. return arg;
  176. }
  177. /// <summary>
  178. /// tstr to wstr conversion for multibyte. Internally calls mbstowcs() for conversion..
  179. /// </summary>
  180. /// <param name="arg">Input string.</param>
  181. /// <returns>Output string conversted from tstd::tstring to std::wstring.</returns>
  182. std::wstring tstr_to_wstr(const tstring& arg)
  183. {
  184. std::wstring res(arg.length()+1, L'\0'); // +1 because wcstombs_s() always wants to write a terminating null character.
  185. size_t converted;
  186. mbstowcs_s(&converted, const_cast<wchar_t*>(res.data()), res.length(), arg.c_str(), arg.length());
  187. assert(converted-1 == arg.length()); // Sanity test.
  188. res.resize(res.size() - 1); // Remove '\0'.
  189. return res;
  190. }
  191. /// <summary>
  192. /// tstr to str conversion for multibyte. Nothing to do here but copying things.
  193. /// </summary>
  194. /// <param name="arg">Input string.</param>
  195. /// <returns>Output string conversted from tstd::tstring to std::string.</returns>
  196. std::string tstr_to_str(const tstring& arg)
  197. {
  198. return arg;
  199. }
  200. }
  201. #endif
  202. }
展开查看全部
3j86kqsm

3j86kqsm6#

你可以试试这个:

  1. {
  2. TCHAR szPath[MAX_PATH];
  3. GetModuleFileName(NULL, szPath, MAX_PATH);
  4. }

相关问题