【问题标题】:Function returns std::wstring = NULL;函数返回 std::wstring = NULL;
【发布时间】:2011-08-16 05:08:32
【问题描述】:

我正在尝试为 winapi 函数 GetWindowText 制作包装器。 函数返回 std::wstring 但我不知道如何处理发生错误的位置。我返回 NULL 但我知道这是错误的。

std::wstring GetWindowText(HWND handle)
{
    const int size = 1024;
    TCHAR wnd_text[size] = {0};

    HRESULT hr = ::GetWindowText(handle,
                    wnd_text, size);
    if(SUCCEEDED(hr))
        return std::wstring(wnd_text);
    else
        return NULL;    
}

【问题讨论】:

    标签: c++ exception-handling error-handling


    【解决方案1】:

    根据您的应用,有几种合适的 解决方案。第一个是在发生异常的情况下抛出异常 错误:如果你走这条路,你应该定义一个 WindowsError 异常(源自标准异常之一),其中 包括GetLastError 提供的所有信息, 和可能的附加信息(函数的名称 失败等),以易于解析的格式。另一种是 返回某种Fallible;在这种情况下,您可能想要 扩展经典的Fallible 成语,使其可以包含 有关错误原因的附加信息。仍然 另一种可能性是通过 out 参数返回值, 并使用返回码(同样,可能带有额外的 信息,并可能添加代码以确保它具有 在被破坏之前经过测试)。

    【讨论】:

      【解决方案2】:

      首先 GetWindowText() 不返回 HRESULT 所以你的代码在这方面是错误的。

      其次,GetWindowTextW 对任何错误或字符数返回 0(如果确定)。 所以只返回一个空字符串:

      std::wstring GetWindowText(HWND handle)
      {
          const int size = 1024;
          TCHAR wnd_text[size] = {0};
      
          INT n = ::GetWindowTextW(handle,
                          wnd_text, size);
          if(n > 0)
              return std::wstring(wnd_text,n);
          else
              return std::wstring();
      }
      

      【讨论】:

        【解决方案3】:

        作为异常的替代方法,您还可以通过参数列表中的引用返回字符串,并通过返回 true 或 false 来指示成功,即

        bool GetWindowText(HWND handle, std::wstring& windowText)
        {
            const int size = 1024;
            TCHAR wnd_text[size] = {0};
        
            HRESULT hr = ::GetWindowText(handle,
                            wnd_text, size);
            if(SUCCEEDED(hr))
            {
                windowText = wnd_text;
                return true;
            }
            else
                return false;    
        }
        

        另一个避免引用参数的替代方法是返回一个类的实例,该实例包装了一个值,但也让你知道一个值是否存在,例如

        class ValueWrapper
        {
        public:
            ValueWrapper() : present( false ) {}
            ValueWrapper( const std::wstring& s ) : value( s ), present( true ) {}
        
            bool isPresent() const { return present; }
            const std::wstring& getValue() const { return value; }
        
        private:
            std::wstring value;
            bool present;
        };
        

        请注意,您可以很容易地制作此包装器的模板。那么你的功能就是

        ValueWrapper GetWindowText(HWND handle)
        {
            const int size = 1024;
            TCHAR wnd_text[size] = {0};
        
            HRESULT hr = ::GetWindowText(handle,
                            wnd_text, size);
            if(SUCCEEDED(hr))
                return ValueWrapper( wnd_text );
            else
                return ValueWrapper();
        }
        

        【讨论】:

        • 请注意,boost::optional<>ValueWrapper 的预先存在的、经过测试的实现。
        • @ildjarn:谢谢。我不知道。
        • 请注意,boost::optional<> 在这种情况下具有误导性的名称——这里的常规名称是 Fallible。 (此外,可以扩展 Fallible 以便它包含有关错误的其他信息。)
        【解决方案4】:

        另一种解决方案(不抛出异常):使用Boost.Optional 库。

        【讨论】:

          【解决方案5】:

          WinApi 设计为永远不会抛出异常。并且要获得某些函数返回失败的原因,在大多数情况下,您必须通过GetLastError() 获取最后一个错误。据我了解,您的功能将成为 WinApi 的一些补充,易于使用。所以我建议保留他们的设计。即如果失败返回空字符串,如果你的函数返回它,检查最后一个错误是什么。

          【讨论】:

            【解决方案6】:

            NULL 对于字符串绝对是可以的,特别不允许将空指针传递给字符串构造函数。

            如果不想抛出异常,可以返回空字符串return std::wstring();

            【讨论】:

            • 但是如果有问题的字符串实际上是空的,就会导致假错误。
            • @Grigory - 可能,是的,取决于接口的定义。但是,std::wstring(NULL) 总是是一个错误,因为它违反了语言标准!
            【解决方案7】:

            改为使用exception

            std::wstring GetWindowText(HWND handle)
            {
                const int size = 1024;
                TCHAR wnd_text[size] = {0};
            
                HRESULT hr = ::GetWindowText(handle,
                                wnd_text, size);
                if(SUCCEEDED(hr))
                    return std::wstring(wnd_text);
                else
                    throw std::runtime_error("insert error message here");    
            }
            

            【讨论】:

            • 不要认为这是个好主意。一般来说,winapi 函数不会抛出异常。最好通过SetLastError()msdn.microsoft.com/en-us/library/ms680627(v=vs.85).aspx设置最后一个错误
            • @Mihran:这是一个 C++ 包装器,因此它将 WinAPI 成语转换为 C++ 成语是正确的。如果将GetLastError值封装在特定的异常类型中会更好。
            • 这种情况下,他还是要返回一个值。它应该是什么价值?此外,在每次调用此函数后调用 GetLastError 比仅仅捕获异常要丑陋得多。
            • 我会从故障代码中提取代码以生成错误消息,但是为了响应错误情况而抛出异常完全是正确的(因为它不是执行此操作的操作系统接口本身;操作系统本身不抛出异常有一些很好的理由)。
            猜你喜欢
            • 1970-01-01
            • 2014-01-05
            • 2012-11-24
            • 1970-01-01
            • 2014-08-14
            • 2021-05-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多