sp00n
sp00n

Reputation: 1098

Is there a way to pass a file path with unicode characters to a function that only takes ANSI characters in C++?

TL;DR
I'm trying to pass the path to a file (e.g. DLL), which contains unicode (UTF-16) characters, to a C++ function, which only supports the "A" variant, so it takes only ANSI characters.

More precisely it's DetourCreateProcessWithDllEx resp. DetourCreateProcessWithDlls from the Microsoft Detours library.

Their lpDllName resp. *rlpDlls parameters are of the type LPCSTR, so no wide characters can be used. According to this issue, this seems to be because the "import table does not have a wide character version". The issue has been locked in the meantime, so maybe someone here has an idea on how I could still pass the path with unicode characters to the function.

When looking at the withdll sample code, you probably don't even need to load the Detours library, as the point of failure is already at the LoadLibraryExA call, which checks if the DLL has the correct exports.

Yes, using the unicode variant LoadLibraryExW would work for this, but the point of failure is then just delayed until the DetourCreateProcessWithDllEx call, so I guess Detours itself isn't necessary to test this. If the call to LoadLibraryExA fails, then the Detours functions would probably fail as well.

So I'm looking for ways to make this work. I'm afraid there isn't any, but maybe somebody knows one. It does actually already work with "simple" unicode directories like "E:\test\Chäröcter Teßt" or "E:\test\Bjørn". But it fails for e.g. "E:\test\Bölükbaşı". With this it throws a "126" error for LoadLibraryExA ("The specified module could not be found") or "3" for the Detours call ("The system cannot find the path specified").

Here's some stripped down code taken from the withdll sample as a test case:

(As explained, for an even more simple test case you could probably remove the Detours part and only check with the LoadLibraryExA call.)

#include <windows.h>
#include <detours.h>
#include <strsafe.h>

int CDECL main(int argc, char** argv)
{
    LPCSTR rpszDllRaw = "detoursSimpleDll64.dll"; // Exists in the same directory, along with its 32bit counterpart
    LPCSTR szExe = "HelloWorld.exe";              // Exists in the same directory

    LPCSTR rpszDllOut;
    CHAR szDllPath[1024];
    PCHAR pszFilePart = NULL;

    if (!GetFullPathNameA(rpszDllRaw, ARRAYSIZE(szDllPath), szDllPath, &pszFilePart)) {
        printf("Error - GetFullPathNameA: %s is not a valid path name\n", rpszDllRaw);
        return 9002;
    }

    DWORD c = (DWORD)strlen(szDllPath) + 1;
    PCHAR psz = new CHAR[c];
    StringCchCopyA(psz, c, szDllPath);
    rpszDllOut = psz;

    HMODULE hDll = LoadLibraryExA(rpszDllOut, NULL, DONT_RESOLVE_DLL_REFERENCES);

    if (hDll == NULL) {
        printf("Error - LoadLibraryExA: %s failed to load (error %ld).\n", rpszDllOut, GetLastError());
        return 9003;
    }
    else {
        printf("LoadLibraryExA succeeded for %s\n", rpszDllOut);
    }


    // Here begins the Detours code
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;
    CHAR szCommand[2048];
    CHAR szFullExe[1024] = "\0";
    PCHAR pszFileExe = NULL;

    DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
    ZeroMemory(&si, sizeof(si));
    ZeroMemory(&pi, sizeof(pi));
    si.cb = sizeof(si);

    szCommand[0] = L'\0';

    SetLastError(0);
    SearchPathA(NULL, szExe, ".exe", ARRAYSIZE(szFullExe), szFullExe, &pszFileExe);


    if (!DetourCreateProcessWithDllExA(szFullExe[0] ? szFullExe : NULL, szCommand,
        NULL, NULL, TRUE, dwFlags, NULL, NULL,
        &si, &pi, rpszDllOut, NULL)) {
        DWORD dwError = GetLastError();
        printf("DetourCreateProcessWithDllEx failed: %ld\n", dwError);
        ExitProcess(9009);
    }

    printf("DetourCreateProcessWithDllEx succeeded for %s and %s\n", szFullExe, rpszDllOut);


    ResumeThread(pi.hThread);

    WaitForSingleObject(pi.hProcess, INFINITE);

    DWORD dwResult = 0;
    if (!GetExitCodeProcess(pi.hProcess, &dwResult)) {
        printf("GetExitCodeProcess failed: %ld\n", GetLastError());
        return 9010;
    }
    dllPathFinal = NULL;
    delete dllPathFinal;


    return dwResult;
}

Upvotes: 1

Views: 463

Answers (0)

Related Questions