如何正确使用 GetModuleFileName



以下代码:

#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::stringstd::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);
}

相关内容

  • 没有找到相关文章

最新更新