I also ran across that issue with trusting char pszPath[_MAX_PATH]
being enough for ::GetModuleFilename()
to work properly (it didn't!). So I took the liberty to fiddle with Ivan's quite neat suggestion a bit and came up with the following code
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);
}
}
which you can use in a number of ways, e. g.:
std::string sModuleFileName = GetStringFromWindowsApi<char>([](char* buffer, int size)
{
return ::GetModuleFileNameA(NULL, buffer, size);
});
if you are OK with lambda expressions. If member variables are required as it is often the case in DLLs where you store away the HMODULE in DllMain(HMODULE hm, DWORD ul_reason_for_call, LPVOID)
, you must add the this pointer like below:
std::string sModuleFileName = GetStringFromWindowsApi<char>([this](char* buffer, int size)
{
return ::GetModuleFileNameA(m_MyModuleHandleHere, buffer, size);
});
Of course, the "old fashioned way" also works as shown in this test case:
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);
}
and you could even use TCHAR for ANSI/UNICODE abstraction similar to this:
std::basic_string<TCHAR> sModuleFileName = GetStringFromWindowsApi<TCHAR>([](TCHAR* buffer, int size)
{
return ::GetModuleFileName(NULL, buffer, size);
});
or #include <tstdlib.h>
that I got from god-knows-where:
#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
}
GetModuleFileName
can returnERROR_INSUFFICIENT_BUFFER
for long path. – Organism