Why use
__stdcall
???
IMHO, there are only two reasons to use
__stdcall
calling convention: When you need to
call an
existing third-party DLL (e.g. Win32 API) that uses
__stdcall
calling convention; or if you need to
build a DLL that will be called by an
existing third-party App that is known to use
__stdcall
calling convention when calling into your DLL. If
all is under
your control, simply stick with
__cdecl
calling convention.
Note that it is not necessary to explicitly specify __cdecl , because that is the default 😏 |
Also, you should be using Unicode (wide) strings, i.e.
std::wstring
or
wchar_t*
(aka
LPCWSTR
),
all the way, whenever you are dealing with file names or paths! And then, of course, you should be calling the "Unicode" (wide-string) version of LoadLibrary, i.e.
LoadLibraryW()
.
The same holds true for any other file-related functions, e.g.
GetModuleFileNameW()
and so on.
That is because, on Windows, file names (paths) support the full Unicode character set, whereas an ANSI string, i.e.
std::string
or
char*
, can only represent characters that happen to exist in the local ANSI (8-Bit) Codepage. So, by using ANSI strings for file names or paths, your code is very likely to break as soon as some "
non US-English" characters happen to appear in the path! Be aware that the "ANSI" (narrow-string) versions of the Win32 API functions only exist for backward-compatibility to "legacy" code. Don't use them in new code.
Converting to UTF-8 does
not work, because the "ANSI" (narrow-string) versions of the Win32 API functions do
not support UTF-8, but instead assume that the given
char*
-based string is encoded in the local ANSI (8-Bit) Codepage! Actually, that is
not entirely true, because the latest builds of Windows 10/11 can enable
experimental UTF-8 support for "ANSI" functions, but you can
not rely on that!
IMHO, there is
no excuse to
not use wide-strings (
wchar_t*
or
std::wstring
) all the way.
A good test is to rename your DLL file to, e.g.,
Ölrückstoßabdämpfung-Григорий-Βαρουφάκης-서울특별시.dll
and see if it still loads 😏
BTW, a proper
GetBaseDirectory()
function would look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
/* search from MSB to LSB for the first set (1) bit */
#if defined(_WIN64)
# pragma intrinsic(_BitScanReverse64)
# define BITSCAN_REVERSE(X,Y) _BitScanReverse64((X),(Y))
#else
# pragma intrinsic(_BitScanReverse)
# define BITSCAN_REVERSE(X,Y) _BitScanReverse((X),(Y))
#endif
/* size of "size_t", in bits */
#define SIZE_BITS (sizeof(size_t) * 8U)
/* maximum length of a path, on modern Windows, including the terminating NULL char */
#define PATH_LIMIT ((size_t)32768U)
static std::wstring ToString(std::vector<WCHAR>& buffer, const size_t len = SIZE_MAX)
{
if (len != SIZE_MAX)
{
return std::wstring(buffer.data(), len);
}
buffer[buffer.size() - 1U] = L'\0';
return std::wstring(buffer.data());
}
static size_t NextPowerOf2(const size_t value)
{
unsigned long index;
if (BITSCAN_REVERSE(&index, value))
{
return (index < (SIZE_BITS - 1U)) ? (((size_t)1U) << (index + 1U)) : SIZE_MAX;
}
return 1U;
}
template<typename T>
static void IncreaseBufferSize(std::vector<T> &buffer, const size_t limit, const T value)
{
const size_t currentSize = buffer.size();
buffer.resize(std::min(NextPowerOf2(currentSize), limit), value);
}
std::wstring GetExecutableFileName(void)
{
// note: The module path can actually be longer than MAX_PATH characters!
std::vector<WCHAR> executablePath(MAX_PATH, L'\0');
for (;;)
{
const DWORD len = GetModuleFileNameW(NULL, executablePath.data(), (DWORD)executablePath.size());
if ((len > 0U) && (len < executablePath.size()))
{
return ToString(executablePath, len);
}
else if ((len == executablePath.size()) && (executablePath.size() < PATH_LIMIT))
{
IncreaseBufferSize(executablePath, PATH_LIMIT, L'\0');
}
else
{
return std::wstring(); /* GetModuleFileNameW() has failed */
}
}
}
std::wstring GetDirectoryPart(const std::wstring &fullPath)
{
// note: The "directory" part can actually be longer than _MAX_DIR characters!
std::vector<WCHAR> drive(_MAX_DRIVE, L'\0'), directory(_MAX_DIR, L'\0');
for (;;)
{
switch (_wsplitpath_s(fullPath.c_str(),
drive.data(), drive.size(), directory.data(), directory.size(), NULL, 0U, NULL, 0U))
{
case 0:
return ToString(drive) + ToString(directory);
case ERANGE:
if (directory.size() < PATH_LIMIT)
{
IncreaseBufferSize(directory, PATH_LIMIT, L'\0');
continue;
}
default:
return std::wstring();
}
}
}
std::wstring GetBaseDirectory(void)
{
const std::wstring executablePath = GetExecutableFileName();
if (!executablePath.empty())
{
return GetDirectoryPart(executablePath);
}
return std::wstring();
}
| |