c++stringfile-iobufferwofstream

C++ swallowing errors


I'm writing an application that communicates through the Windows RPC mechanism with a hosted interface in another process.

The marshaling is handled through the standard built-in NDR. The interface exposes several functions, among them one that returns a string (UNICODE_STRING) through a parameter.

The function returns 0 on success or an error code otherwise. Now when I call the RPC function, it succeeds and the UNICODE_STRING parameter gets populated.

The problem is that either C++ or the runtime swallows an error that occurs while trying to read the string returned by the function. To save you reading lots of code, here is an abbreviated version of my code:

void func()
{
    UNICODE_STRING unicode_str;

    HMODULE hLib = LoadLibrary(L"Ntdll.dll");
    RtlInitUnicodeStringFn fn;

    fn = (RtlInitUnicodeStringFn) GetProcAddress(hLib, "RtlInitUnicodeString");
    fn(&unicode_str, NULL);

    std::cout << "Before calling RPC the buffer was pointing at: " << std::hex << unicode_str.Buffer << std::endl;

    if(call(binding.get_binding_handle(), &unicode_str))
    {
        std::cout << "len: " << unicode_str.Length << " maxlen: " << unicode_str.MaximumLength << std::endl;
        std::cout << "After calling RPC the buffer is pointing at: " << std::hex << unicode_str.Buffer << std::endl;
        try
        {
            for(int i = 0; i < unicode_str.Length; i++)
                std::wcout << i << ": " << unicode_str.Buffer[i] << std::endl;
        }
        catch(...)
        {
            std::cout << "Caught an exception";
        }
        std::wcout << "I won't be written" << std::endl;
    }
}

bool call(void* handle, PUNICODE_STRING str)
{
    __try
    {
        std::cout << "RPC call returned: " << RpcInterfaceFunction(handle, str) << std::endl;
        return true;
    }
    __except(1)
    {
        std::cout << "Error: " << std::dec << GetExceptionCode() << std::endl;
        return false;
    }
}

After executing this code, the output is:

Before calling RPC the buffer is pointing at : 000000000
RPC call returned: 0
len:68, maxlen: 70
After calling RPC the buffer is pointing at: 00746168
0: #
1: t
2: 

and returns. I've stepped with a debugger through this function and it correctly goes through the whole 68 characters (regardless of what is in the buffer - it's garbage data) and steps into the last I won't be written wcout instruction correctly. I wondered if the problem was with unreadable characters that the console can't handle and these characters could alter the next character's behavior or the console cursor - and that would make the output invisible, but no - I've swapped the output stream and set it to a file (wofstream) and the file's size was 47 bytes - which is exactly as much as is written to the console stream.

I'm not getting any access violation, any exception, any log entry, nothing. Even the debugger (VS2010) doesn't note any exception after choosing to break on every possible type of exception - what is yet more surprising is that stepping through the code with a debugger and watching the locals there yields expected values (i goes from 0 to 68 and the current wchar_t in the buffer has a value (garbage unicode char).

Could anybody please explain this behavior?

Edit:

swapping the printing loop with:

for(int i = 0; i < unicode_str.Length; i++)
{
    std::wcout << "I'm writing the " << i << "th character" << std::endl;
    unsigned short temp = unicode_str.Buffer[i];
    std::wcout << i << ": " << temp << std::endl;
}

Prints the values of temp correctly up to 67.

But still, why printing the wchar_t version of these characters stops the output stream from accepting anything?


Solution

  • Are you sure that unicode_str contains valid Unicode data?

    My experience with std::wcout (and std::wostreams in general) is that they get into a bad state whenever they are given invalid Unicode data. And by default, they don't throw exceptions when this happens: they just set their internal failbit or badbit and stop working. You can check if the stream is in a bad state by calling std::wcout.fail() or std::wcout.bad(), and you can clear the error state flags by calling std::wcout.clear().

    Also, if you would rather that an exception be thrown when either the failbit or badbit is set, you can call std::wcout.exceptions(std::wostream::failbit | std::wostream::badbit).

    So the bottom line is that I would recommend against using std::wostreams for this purpose. I hope this helps you with your problem.