25

以下代码:

#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 中,我得到了错误:

使用了未初始化的局部变量“缓冲区”

代码有什么问题?

4

5 回答 5

36

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

 wchar_t buffer[MAX_PATH]; 

例如。

于 2013-09-13T09:44:10.383 回答
8

VS 正确指出您正在使用未初始化的缓冲区 -缓冲区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;

不要直接使用 wstring 作为缓冲区:它没有被定义为在每个实现中都有一个连续的缓冲区(但通常是)

于 2015-11-09T16:06:01.890 回答
2

这是 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 );
    }
}

GetModuleFileName 可以这样使用:

extern HINSTANCE hInstance;

auto moduleName = GetStringFromWindowsApi<TCHAR>( []( TCHAR* buffer, int size )
{
    return GetModuleFileName( hInstance, buffer, size );
} );

或者像这样的 LoadString:

std::basic_string<TCHAR> LoadResourceString( int id )
{
    return GetStringFromWindowsApi<TCHAR>( [id]( TCHAR* buffer, int size )
    {
        return LoadString( hInstance, id, buffer, size );
    } );
}

于 2019-02-02T09:00:07.017 回答
1

而且,在我下面的答案的旁注/补充中:有时,您想要访问一个函数 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);
    }
};

This typically happens in class factories that live in DLLs and don't want to exchange complex types like std::string because of memory management. To use the interface, you often end up with ugly code:

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

which sucks for obvious reasons, one being that you can't directly use this in stream insertion. After some procrastination and tearing my hair out, I came up with that almost pornographic solution, at least in terms of template and pointer-to-member-function usage:

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, '\0');
        size_t act = (c.*fn)(&s[0], len - 1);
        assert(act == s.size());
        return s;
    }
};

which can then be used in the following way:

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.

Still a bit complicated but most of the heavy lifting is hidden behind the scenes.

于 2021-10-22T12:41:20.570 回答
0

我也遇到了这个问题,相信char pszPath[_MAX_PATH]足以::GetModuleFilename()正常工作(它没有!)。所以我冒昧地摆弄了 Ivan 相当巧妙的建议,并想出了以下代码

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'\0'); // +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 '\0'.
            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, '\0'); // +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 '\0'.
            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, '\0'); // +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 '\0'.
            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'\0'); // +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 '\0'.
            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
}
于 2021-08-19T14:45:30.890 回答