I'm reusing the same exact same source file (for command-line parsing), in different executables. I don't use a library, and instead build that same source directly into each exe.
Recently I added an option to list all shared-libraries used by the executable. This worked fine in the exe I tested that on, but when I tried it on a different exe, it compiled but worked incorrectly, print addresses instead of the paths of shared libraries. I'm (vaguely) aware of TCHAR. Somehow, these two exes, which have different dependencies (brought in via CMake build), end up having different TCHAR types (char vs wchat_t). Both exes accepted os << me32.szExePath fine, but the first was calling op<<(..., const char*) while the second was calling op<<(..., const void*), thus printing addresses.
OK I thought, I'll just use a compile-time if (i.e. if constexpr) since I'm in C++17, and use me32.szExePath as-is in the char case, and explicitly convert it from wchar_t to char otherwise. The code below fixed the second exe, so it printed paths (instead of addresses). But then it broke the first exe!!!
1>...\CliContext.cpp(1703,81): error C2664: 'errno_t wcstombs_s(size_t *,char *,size_t,const wchar_t *,size_t)': cannot convert argument 4 from 'char [260]' to 'const wchar_t *'
1>...\CliContext.cpp(1703,55): message : Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.17763.0\ucrt\stdlib.h(999,26): message : see declaration of 'wcstombs_s'
Which makes no sense to me, since the first exe shouldn't compile both if constexpr branches. After all, it can't have two values for the same sizeof(*me32.szExePath), can it?
I'm obvious missing something... Anyone can help?
Is there a way to write this code so it works in both TCHAR cases, for printing to an std::ostream?
PS: I tried earlier an if constexpr (std::is_same_v<wchar_t, *me32.szExePath>) alternate compile-time if, and it similarly failed.
PPS: These are cross-platform exes. I'm not showing the linux side. I do want to use an std::ostream and not something else to print.
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
static void sharedLibraries(std::ostream& os) {
auto dwPID = GetCurrentProcessId();
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == INVALID_HANDLE_VALUE) {
raise("CreateToolhelp32Snapshot (of modules)");
}
using Handle_uptr = std::unique_ptr<void, decltype(&CloseHandle)>;
Handle_uptr h(hModuleSnap, &CloseHandle);
MODULEENTRY32 me32;
me32.dwSize = sizeof(me32);
if (!Module32First(hModuleSnap, &me32)) {
raise("Module32First");
}
do {
assert(me32.th32ProcessID == dwPID);
if constexpr (sizeof(*me32.szExePath) == 1) {
os << me32.szExePath << std::endl;
}
if constexpr (sizeof(*me32.szExePath) == 2) {
size_t size = 0; char path[260*4];
wcstombs_s(&size, path, sizeof(path), me32.szExePath, sizeof(path)-1);
os << path << std::endl;
}
} while (Module32Next(hModuleSnap, &me32));
}
Using this worked for me:
I'd still like to understand why the
if constexprfailed though. FWIW.