c++unicodemultibyte

Correct way to use WideCharToMultiByte when using unicode


I asked a question recently about using unicode and the problems that arose here:

argument of type "WCHAR *" is incompatible with parameter of type "LPCSTR" in c++

In solving one problem, I encountered another one that has taken me now a literal rabbit hole of differences between ansi and unicode. I have learnt a lot but I still cannot seem to solve this problem after almost a week of trying.

I looked at How do you properly use WideCharToMultiByte as I think this is what I need to convert the find_pid from unicode wchar back to char otherwise I get erros but to no avail.

All help little or small is appreciated as this is driving me mad.

Here is what I am trying to solve:

 DWORD find_pid(const wchar_t* procname) {
// Dynamically resolve some functions
HMODULE kernel32 = GetModuleHandleA("Kernel32.dll");

using CreateToolhelp32SnapshotPrototype = HANDLE(WINAPI *)(DWORD, DWORD);
CreateToolhelp32SnapshotPrototype CreateToolhelp32Snapshot = (CreateToolhelp32SnapshotPrototype)GetProcAddress(kernel32, "CreateToolhelp32Snapshot");

using Process32FirstPrototype = BOOL(WINAPI *)(HANDLE, LPPROCESSENTRY32);
Process32FirstPrototype Process32First = (Process32FirstPrototype)GetProcAddress(kernel32, "Process32First");

using Process32NextPrototype = BOOL(WINAPI *)(HANDLE, LPPROCESSENTRY32);
Process32NextPrototype Process32Next = (Process32NextPrototype)GetProcAddress(kernel32, "Process32Next");

// Init some important local variables
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
DWORD pid = 0;
pe32.dwSize = sizeof(PROCESSENTRY32);

// Find the PID now by enumerating a snapshot of all the running processes
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap)
    return 0;

if (!Process32First(hProcSnap, &pe32)) {
    CloseHandle(hProcSnap);
    return 0;
}

while (Process32Next(hProcSnap, &pe32)) {
    if (lstrcmp(procname, pe32.szExeFile) == 0) {
        pid = pe32.th32ProcessID;
        break;
    }
}

// Cleanup
CloseHandle(hProcSnap);

return pid;
  }

//changing to std::wstring does not work, already tried
std::string parentProcess = "C:\\hello.exe"
DWORD pid = find_pid(parentProcess.c_str());

Solution

  • Most likely your problem is that you're compiling with UNICODE defined. In this case PROCESSENTRY32 will actually be PROCESSENTRY32W.

    But you're calling the ASCII-Version of Process32First instead of the unicode-version Process32FirstW.

    Most of the winapi functions that accept both ascii & unicode arguments have 2 separate versions:

    In your case that would be:

    #ifdef UNICODE
    #define Process32First Process32FirstW
    #define Process32Next Process32NextW
    #define PROCESSENTRY32 PROCESSENTRY32W
    #define PPROCESSENTRY32 PPROCESSENTRY32W
    #define LPPROCESSENTRY32 LPPROCESSENTRY32W
    #endif  // !UNICODE
    

    Also keep in mind that Process32First will also populate your PROCESSENTRY32 (with the first found entry). So with your current implementation you'll always skip over the first process.


    If you're building a windows app it's best to decide from the start if you want to use ascii or unicode.
    (there's also the option to make both compile with TCHAR & friends)

    Mixing them within a single app will lead to a lot of conversion problems (since not every unicode character can be represented in your ascii code page)

    Also it'll make your life a lot easier if you just rely on the linker to import the functions instead of using GetProcAddress().

    If you want to stick with unicode (the default for new projects), you could write your function like this:

    #include <windows.h>
    #include <tlhelp32.h>
    
    DWORD find_pid(const wchar_t* procname) {
        // Init some important local variables
        HANDLE hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(PROCESSENTRY32);
    
        // Find the PID now by enumerating a snapshot of all the running processes
        if (hProcSnap == INVALID_HANDLE_VALUE)
            return 0;
    
        if (!Process32First(hProcSnap, &pe32)) {
            CloseHandle(hProcSnap);
            return 0;
        }
    
        do {
            if (lstrcmp(procname, pe32.szExeFile) == 0) {
                CloseHandle(hProcSnap);
                return pe32.th32ProcessID;
            }
        } while (Process32Next(hProcSnap, &pe32));
    
        // not found
        CloseHandle(hProcSnap);
        return 0;
    }
    

    and call it like this:

    std::wstring parentProcess = L"C:\\hello.exe";
    DWORD pid = find_pid(parentProcess.c_str());
    
    // or just:
    DWORD pid = find_pid(L"C:\\hello.exe");
    

    If you want your application to be able to compile for both unicode & ascii, you'll have to use TCHAR:

    #include <windows.h>
    #include <tlhelp32.h>
    #include <tchar.h>
    
    DWORD find_pid(const TCHAR* procname) {
        // Init some important local variables
        HANDLE hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(PROCESSENTRY32);
    
        // Find the PID now by enumerating a snapshot of all the running processes
        if (hProcSnap == INVALID_HANDLE_VALUE)
            return 0;
    
        if (!Process32First(hProcSnap, &pe32)) {
            CloseHandle(hProcSnap);
            return 0;
        }
    
        do {
            if (lstrcmp(procname, pe32.szExeFile) == 0) {
                CloseHandle(hProcSnap);
                return pe32.th32ProcessID;
            }
        } while (Process32Next(hProcSnap, &pe32));
    
        // not found
        CloseHandle(hProcSnap);
        return 0;
    }
    

    and call it like this:

    DWORD pid = find_pid(_T("C:\\hello.exe"));