BugHunterUK
BugHunterUK

Reputation: 8928

Writing wchar_t to file

I'm attempting to write a wchar_t character (wide character) to file. Currently it writes the hex code to file, and not the character.

Here's the part in the application where it's failing

if(capslockKey || shiftKey)
{
    // When debugging and I press 's' this becomes 'S'. It's visible in the deugging locals window.
    wchar_t upper = towupper((wchar_t)*buffer);

    if(upper != *buffer)
        // "Testing " gets written to file, but upper gets written as a hex value
        logFile << L"Testing " << upper << logFile.flush();

    return CallNextHookEx(hHook, nCode, wParam, lParam);
}

I've commented the code. When debugging upper contains the correct character. If a lowercase s is pressed, it becomes S if shift or capslock is pressed. This works fine.

But, when writing to file upper gets written as a hex value. The string Testing gets written as normal. Outputting to stdout using std::wout prints the characters just fine.

How can I save the actual character to file, and not the hex value?

Thanks to Remy, I ended up with the following which works fine:

/*
 * Is the keypress an alpha key? This limits everything except a-z and A-Z
 */
if((p->vkCode > 64) && (p->vkCode < 91) || (p->vkCode > 96) && (p->vkCode < 123))
{
    /*
     * Are we dealing with capital letters?
     */
    if(capslockKey || shiftKey)
    {
        wchar_t upper = towupper((wchar_t)*buffer);

        if(upper != *buffer)
            logFile << (wchar_t)upper << std::flush;
    }       
    else
    {
        logFile << buffer << std::flush;
    }

    return CallNextHookEx(hHook, nCode, wParam, lParam);
}

... Although WM_CHAR might be a better option for my needs. Saves having to deal with issues such as whether or not shift is pressed. Or converting VK_RETURN to \n.

Edit

Above is pointless (lowercase to uppercase part). GetKeyboardState and toUnicodeEx does it perfectly with much less hassle.

BYTE keyState[256] = {0};
bool keyStateResult = GetKeyboardState(keyState);
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
wchar_t buffer[5];

int toUnicodeResult = ToUnicodeEx(
    p->vkCode,
    p->scanCode,
    keyState,
    buffer,
    _countof(buffer),
    0,
    NULL
);

Upvotes: 0

Views: 2595

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595339

On this line:

logFile << L"Testing " << upper << logFile.flush();

You are not passing flush() itself to operator<<. You are actually calling flush() first and then passing its return value (a basic_ostream& reference) to operator<<, which is causing the hex output you see.

To properly flush the stream using operator<<, you need to use the std::flush stream manipulator instead, which will (indirectly) call logFile.flush() for you without sending its returned reference to the stream output:

logFile << L"Testing " << upper << std::flush;

Upvotes: 4

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145204

Wide streams translate to and from a narrow character external encoding. To directly save wide text (UTF-16 encoded in Windows) you can use a narrow stream opened in binary mode. Or you can use Microsoft extension _setmode to set _O_U16-something mode on the stream, but that's non-portable, and doesn't seem to work with newer versions of MinGW g++, although it did work earlier.


A good alternative is to use the standard library facilities to translate the wide text to UTF-8 encoded narrow text, and save that to a narrow stream opened in binary mode. Since this is Windows it would be a good idea to save a BOM (byte order mark) at the beginning. Conversely, in Unixland it's a good idea to not do that.


I'm unable to reproduce the hex digits effect – and you don't include code to reproduce it – but the above advice is what you need to do anyway, assuming that the wide text isn't restricted to plain ASCII.

Upvotes: 1

Related Questions