【问题标题】:C++ swallowing errorsC++ 吞咽错误
【发布时间】:2011-12-24 15:35:33
【问题描述】:

我正在编写一个应用程序,它通过 Windows RPC 机制与另一个进程中的托管接口进行通信。

编组是通过标准的内置 NDR 处理的。该接口公开了几个函数,其中一个通过参数返回一个字符串(UNICODE_STRING)。

函数成功返回 0,否则返回错误代码。现在,当我调用 RPC 函数时,它会成功并填充 UNICODE_STRING 参数。

问题是 C++ 或运行时在尝试读取函数返回的字符串时吞下了一个错误。 为了节省您阅读大量代码,这里是我的代码的缩写版本:

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;
    }
}

执行这段代码后,输出为:

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: 

然后返回。 我已经通过这个函数使用了一个调试器,它正确地遍历了整个 68 个字符(不管缓冲区中有什么 - 它是垃圾数据)并正确地进入最后一个 I won't be written wcout 指令。我想知道问题是否与控制台无法处理的不可读字符有关,这些字符可能会改变下一个字符的行为或控制台光标 - 这会使输出不可见,但不是 - 我已经交换了输出流并设置它到一个文件 (wofstream) 并且文件的大小是 47 字节 - 这与写入控制台流的大小完全相同。

我没有收到任何访问冲突、任何异常、任何日志条目,什么都没有。即使调试器(VS2010)在选择中断每种可能的异常类型后也没有注意到任何异常 - 更令人惊讶的是,使用调试器单步执行代码并观察那里的本地人会产生预期值(我从 0到 68 并且缓冲区中的当前 wchar_t 有一个值(垃圾 unicode char)。

谁能解释一下这种行为?

编辑:

将打印循环替换为:

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;
}

正确打印temp 的值,最高可达 67。

但是,为什么打印这些字符的 wchar_t 版本会阻止输出流接受任何内容?

【问题讨论】:

  • 您是否尝试过检查流是否有错误?我敢打赌,你正在尝试写一些无效的东西,而 failbit 正在被设置。

标签: c++ string file-io buffer wofstream


【解决方案1】:

您确定unicode_str 包含有效的Unicode 数据吗?

我对@9​​87654322@(和一般的std::wostreams)的经验是,每当他们收到无效的Unicode 数据时,他们就会陷入糟糕的状态。默认情况下,发生这种情况时它们不会抛出异常:它们只是设置内部故障位或坏位并停止工作。您可以通过调用std::wcout.fail()std::wcout.bad() 来检查流是否处于错误状态,并且可以通过调用std::wcout.clear() 来清除错误状态标志。

另外,如果您希望在设置了 failbit 或 badbit 时引发异常,您可以调用 std::wcout.exceptions(std::wostream::failbit | std::wostream::badbit)

所以底线是我建议不要为此使用std::wostreams。我希望这可以帮助您解决问题。

【讨论】:

  • 不,unicode_str 包含无效的 unicode 数据 - 我在问题中提到它包含垃圾数据。
【解决方案2】:

实际上,我有足够的信心,我将把这个问题作为答案,而不是在 cmets 中提问。

大多数时候,iostream 的行为令人费解,这是因为failbiteofbit(或有时badbit)被设置了。当处于其中一种不良状态时,I/O 操作通常(总是?)会立即返回而不做任何事情。

我认为错误的 unicode 数据会导致 failbitbadbit 被设置。

作为一般的调试原则,当我看到iostream 的行为异常时,我检查的第一件事就是确保流仍然是好的。几乎每次,我发现流实际上并不好,完全解释了神秘的行为。

一旦您知道流不好,您就可以决定采取一些措施。对于坏的 unicode 数据,也许只需将流的状态重置回一个好的状态就足够了——我对 unicode I/O 的了解不够,无法确定。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-29
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-21
    • 2014-07-03
    • 1970-01-01
    相关资源
    最近更新 更多