以下代码:
#include <iostream>
#include <Windows.h>
using namespace std;
int main ()
{ LPWSTR buffer; //or wchar_t * buffer;
GetModuleFileName(NULL, buffer, MAX_PATH) ;
cout<<buffer;
cin.get();
cin.get();
}
应显示程序执行的完整路径。但是在VS 2012中,我收到错误:
使用未初始化的局部变量"缓冲区"
代码出了什么问题?
你需要给它一个可以容纳一些字符的缓冲区;
wchar_t buffer[MAX_PATH];
例如。
VS正确地指出您正在使用未初始化的缓冲区 - buffer
var是指向WSTR
的指针,但它尚未使用静态缓冲区初始化,也没有分配。此外,您应该记住,MAX_PATH
通常是不够的,尤其是在具有长路径名的现代系统上。
由于您使用的是C++,因此最好使用它的功能。我可以假设以下代码:
vector<wchar_t> pathBuf;
DWORD copied = 0;
do {
pathBuf.resize(pathBuf.size()+MAX_PATH);
copied = GetModuleFileName(0, &pathBuf.at(0), pathBuf.size());
} while( copied >= pathBuf.size() );
pathBuf.resize(copied);
wstring path(pathBuf.begin(),pathBuf.end());
cout << path;
注意:在 C++11 之前,标准不保证std::string
和std::wstring
具有连续的数据,并且可能无法安全地用作缓冲区。但是,它们在所有主要标准库中都是连续的。
这是 Win32 API 的普遍问题,函数将字符串返回到有限大小的缓冲区中,并且您永远不确定缓冲区是否足够大以容纳整个字符串。正如 kingsb 所提到的,即使是 MAX_PATH 对于当今的路径来说也不够好。
我倾向于为此目的使用通用的辅助函数:
template <typename TChar, typename TStringGetterFunc>
std::basic_string<TChar> GetStringFromWindowsApi( TStringGetterFunc stringGetter, int initialSize = 0 )
{
if( initialSize <= 0 )
{
initialSize = MAX_PATH;
}
std::basic_string<TChar> result( initialSize, 0 );
for(;;)
{
auto length = stringGetter( &result[0], result.length() );
if( length == 0 )
{
return std::basic_string<TChar>();
}
if( length < result.length() - 1 )
{
result.resize( length );
result.shrink_to_fit();
return result;
}
result.resize( result.length() * 2 );
}
}
对于GetModule文件名,可以像这样使用:
extern HINSTANCE hInstance;
auto moduleName = GetStringFromWindowsApi<TCHAR>( []( TCHAR* buffer, int size )
{
return GetModuleFileName( hInstance, buffer, size );
} );
或者对于像这样的负载字符串:
std::basic_string<TCHAR> LoadResourceString( int id )
{
return GetStringFromWindowsApi<TCHAR>( [id]( TCHAR* buffer, int size )
{
return LoadString( hInstance, id, buffer, size );
} );
}
而且,在下面我回答的旁注/补充: 有时,您想访问一个函数 size_t GetString(char* buf = NULL, size_t bufsize = 0);
如果您调用它而不带任何参数,它将返回必要的缓冲区大小,并且 - 如果您正常调用它 - 将最多 bufsize 字符写入 buf(或直到它想要返回的字符串末尾,无论先到什么),返回实际写入的字符数。实现可能如下所示:
class GetterClass
{
private:
size_t String2Buffer(const std::string& string, char* const pBuffer = NULL, size_t size = 0)
{
size_t s, length = string.length() + 1;
if (!pBuffer) return length;
s = std::min<>(length, size);
memcpy(pBuffer, string.c_str(), s);
return s;
}
public:
size_t GetterFunc(char* p, size_t len)
{
const static std::string s("Hello World!");
return String2Buffer(s, p, len);
}
};
这通常发生在驻留在 DLL 中的类工厂中,并且由于内存管理而不想交换复杂类型(如 std::string)。要使用接口,您经常会得到丑陋的代码:
GetterClass g;
GetterClass* pg = &g;
std::string s(pg->GetterFunc() - 1, ' ');
pg->GetterFunc(&s[0], s.size());
这很糟糕,原因很明显,一个是你不能在流插入中直接使用它。经过一番拖延和扯头发,我想出了那个近乎色情的解决方案,至少在模板和指向成员函数的指针用法方面:
template <typename tClass>
struct TypeHelper
{
typedef size_t(tClass::* MyFunc)(char*, size_t);
static std::string Get(MyFunc fn, tClass& c)
{
size_t len = (c.*fn)(NULL, 0);
std::string s(len - 1, ' ');
size_t act = (c.*fn)(&s[0], len - 1);
assert(act == s.size());
return s;
}
};
然后可以通过以下方式使用:
GetterClass Foo;
GetterClass* pFoo = &Foo;
std::string s1 = TypeHelper<GetterClass>::Get(&GetterClass::GetterFunc, Foo); // Class version.
std::string s2 = TypeHelper<GetterClass>::Get(&GetterClass::GetterFunc, *pFoo); // Pointer-to-Instance version.
仍然有点复杂,但大部分繁重的工作都隐藏在幕后。
我也遇到了这个问题,相信char pszPath[_MAX_PATH]
足以让::GetModuleFilename()
正常工作(它没有!所以我冒昧地摆弄了一下伊万相当整洁的建议,并想出了以下代码
template <typename TChar, typename TStringGetterFunc>
std::basic_string<TChar> GetStringFromWindowsApi(TStringGetterFunc stringGetter, typename std::basic_string<TChar>::size_type initialSize = _MAX_PATH)
{
std::basic_string<TChar> sResult(initialSize, 0);
while(true)
{
auto len = stringGetter(&sResult[0], sResult.length());
if (len == 0) return std::basic_string<TChar>();
if (len < sResult.length() - 1)
{
sResult.resize(len);
sResult.shrink_to_fit();
return sResult;
}
sResult.resize(sResult.length() * 2);
}
}
您可以通过多种方式使用,例如:
std::string sModuleFileName = GetStringFromWindowsApi<char>([](char* buffer, int size)
{
return ::GetModuleFileNameA(NULL, buffer, size);
});
如果您可以使用 lambda 表达式。如果需要成员变量,因为在 DLL 中通常会出现这种情况,您将 HMODULE 存储在 DllMain(HMODULE hm, DWORD ul_reason_for_call, LPVOID)
中,则必须添加 this 指针,如下所示:
std::string sModuleFileName = GetStringFromWindowsApi<char>([this](char* buffer, int size)
{
return ::GetModuleFileNameA(m_MyModuleHandleHere, buffer, size);
});
当然,"老式方式"也如以下测试用例所示:
typedef int (PGetterFunc)(char*, int);
int MyGetterFunc(char* pszBuf, int len)
{
static const char psz[] = "**All your base are belong to us!**";
if (len < strlen(psz) + 1)
{
strncpy(pszBuf, psz, len - 1);
return len;
}
strcpy(pszBuf, psz);
return static_cast<int>(strlen(psz));
}
void foo(void)
{
std::string s = GetStringFromWindowsApi<char, PGetterFunc>(MyGetterFunc, _MAX_PATH);
}
你甚至可以使用 TCHAR 进行类似于这样的 ANSI/UNICODE 抽象:
std::basic_string<TCHAR> sModuleFileName = GetStringFromWindowsApi<TCHAR>([](TCHAR* buffer, int size)
{
return ::GetModuleFileName(NULL, buffer, size);
});
或者我从上帝知道的地方得到的#include <tstdlib.h>
:
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <assert.h>
#ifdef _WIN32
#include <tchar.h> // For _T() macro!
#else // Non Windows Platform!
#ifdef _UNICODE
typedef wchar_t TCHAR;
#ifndef _T
#define _T(s) L##s
#endif
#ifndef _TSTR
#define _TSTR(s) L##s
#endif
#else
typedef wchar_t TCHAR;
#ifndef _T
#define _T(s) s
#endif
#ifndef _TSTR
#define _TSTR(s) s
#endif
#endif
#endif
/// <summary>
/// The tstd namespace contains the STL equivalents to TCHAR as defined in <tchar.h> to allow
/// all the Unicode magic happen with strings and streams of the STL.
/// Just use tstd::tstring instead of std::string etc. and the correct types will automatically be selected
/// depending on the _UNICODE preprocessor flag set or not.
/// E. g.
/// tstd::tstring will resolve to std::string if _UNICODE is NOT defined and
/// tstd::tstring will resolve to std::wstring _UNICODE IS defined.
/// </summary>
namespace tstd
{
#ifdef _UNICODE
// Set the wide character versions.
typedef std::wstring tstring;
typedef std::wostream tostream;
typedef std::wistream tistream;
typedef std::wiostream tiostream;
typedef std::wistringstream tistringstream;
typedef std::wostringstream tostringstream;
typedef std::wstringstream tstringstream;
typedef std::wifstream tifstream;
typedef std::wofstream tofstream;
typedef std::wfstream tfstream;
typedef std::wfilebuf tfilebuf;
typedef std::wios tios;
typedef std::wstreambuf tstreambuf;
typedef std::wstreampos tstreampos;
typedef std::wstringbuf tstringbuf;
// declare an unnamed namespace as a superior alternative to the use of global static variable declarations.
namespace
{
tostream& tcout = std::wcout;
tostream& tcerr = std::wcerr;
tostream& tclog = std::wclog;
tistream& tcin = std::wcin;
/// <summary>
/// Unicode implementation for std::endl.
/// </summary>
/// <param name="output">Output character stream.</param>
/// <returns>Output character stream.</returns>
std::wostream& tendl(std::wostream& output)
{
output << std::endl;
return output;
}
/// <summary>
/// wstr to tstr conversion for Unicode. Nothing to do here but copying things.
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from std::wstring to tstd::tstring.</returns>
tstring wstr_to_tstr(const std::wstring& arg)
{
return arg;
}
/// <summary>
/// str to tstr conversion for Unicode. Internally calls mbstowcs() for conversion..
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from std::string to tstd::tstring.</returns>
tstring str_to_tstr(const std::string& arg)
{
tstring res(arg.length() + 1, L' '); // +1 because wcstombs_s() always wants to write a terminating null character.
size_t converted;
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.
assert(converted - 1 == arg.length()); // Sanity test.
res.resize(res.size() - 1); // Remove ' '.
return res;
}
/// <summary>
/// tstr to wstr conversion for Unicode. Nothing to do here but copying things.
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from tstd::tstring to std::wstring.</returns>
std::wstring tstr_to_wstr(const tstring& arg)
{
return arg;
}
/// <summary>
/// tstr to str conversion for Unicode. Internally calls wcstombs() for conversion.
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from tstd::tstring to std::string.</returns>
std::string tstr_to_str(const tstring& arg)
{
std::string res(arg.length() + 1, ' '); // +1 because wcstombs_s() always wants to write a terminating null character.
size_t converted;
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.
assert(converted - 1 == arg.length()); // Sanity test.
res.resize(res.size() - 1); // Remove ' '.
return res;
}
}
#else
// Set the multibyte versions.
typedef std::string tstring;
typedef std::ostream tostream;
typedef std::istream tistream;
typedef std::iostream tiostream;
typedef std::istringstream tistringstream;
typedef std::ostringstream tostringstream;
typedef std::stringstream tstringstream;
typedef std::ifstream tifstream;
typedef std::ofstream tofstream;
typedef std::fstream tfstream;
typedef std::filebuf tfilebuf;
typedef std::ios tios;
typedef std::streambuf tstreambuf;
typedef std::streampos tstreampos;
typedef std::stringbuf tstringbuf;
// declare an unnamed namespace as a superior alternative to the use of global static variable declarations.
namespace
{
tostream& tcout = std::cout;
tostream& tcerr = std::cerr;
tostream& tclog = std::clog;
tistream& tcin = std::cin;
/// <summary>
/// Multibyte implementation for std::endl.
/// </summary>
/// <param name="output">Output character stream.</param>
/// <returns>Output character stream.</returns>
std::ostream& tendl(std::ostream& output)
{
output << std::endl;
return output;
}
/// <summary>
/// wstr to tstr conversion for multibyte. Internally calls wcstombs() for conversion.
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from std::wstring to tstd::tstring.</returns>
tstring wstr_to_tstr(const std::wstring& arg)
{
tstring res(arg.length()+1, ' '); // +1 because wcstombs_s() always wants to write a terminating null character.
size_t converted;
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.
assert(converted-1 == arg.length()); // Sanity test.
res.resize(res.size() - 1); // Remove ' '.
return res;
}
/// <summary>
/// str to tstr conversion for multibyte. Nothing to do here but copying things.
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from std::string to tstd::tstring.</returns>
tstring str_to_tstr(const std::string& arg)
{
return arg;
}
/// <summary>
/// tstr to wstr conversion for multibyte. Internally calls mbstowcs() for conversion..
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from tstd::tstring to std::wstring.</returns>
std::wstring tstr_to_wstr(const tstring& arg)
{
std::wstring res(arg.length()+1, L' '); // +1 because wcstombs_s() always wants to write a terminating null character.
size_t converted;
mbstowcs_s(&converted, const_cast<wchar_t*>(res.data()), res.length(), arg.c_str(), arg.length());
assert(converted-1 == arg.length()); // Sanity test.
res.resize(res.size() - 1); // Remove ' '.
return res;
}
/// <summary>
/// tstr to str conversion for multibyte. Nothing to do here but copying things.
/// </summary>
/// <param name="arg">Input string.</param>
/// <returns>Output string conversted from tstd::tstring to std::string.</returns>
std::string tstr_to_str(const tstring& arg)
{
return arg;
}
}
#endif
}
试试这个:
{
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL, szPath, MAX_PATH);
}