函数返回值为std::wstring = NULL;

4

我试图为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;    
}
7个回答

7

可以抛出一个异常

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()设置上一个错误。http://msdn.microsoft.com/en-us/library/ms680627(v=vs.85).aspx - Mihran Hovsepyan
5
@Mihran:这是一个*C ++*包装器,因此将WinAPI的习语转换为C ++习语是正确的。如果它将GetLastError值封装在特定的异常类型中,那将更好。 - Matteo Italia
在这种情况下,他仍然必须返回一个值。应该是什么值?此外,在每次调用此函数后调用GetLastError比捕获异常更为丑陋。 - user500944
我会将生成错误消息的代码从失败代码中分离出来,但对于错误情况抛出异常是完全正确的(前提是不是操作系统接口本身在执行此操作;操作系统不抛出异常有一些非常好的原因)。 - Donal Fellows

5
作为异常的替代方案,您还可以在参数列表中返回字符串引用,并通过返回 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();
}

2
请注意,boost::optional<> 是一个现成的、经过测试的 ValueWrapper 实现。 - ildjarn
请注意,boost::optional<>在这种情况下有一个具有误导性的名称——常规名称是Fallible。(此外,可以扩展Fallible以包含有关错误的其他信息。) - James Kanze

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

0
首先,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();
}

0

NULL绝对不适用于字符串,您明确不能将空指针传递给字符串构造函数。

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


2
但是,如果所讨论的字符串实际上为空,则会导致虚假错误。 - user500944
1
@Grigory - 可能会,取决于接口的定义。然而,std::wstring(NULL) 始终是一个错误,因为它违反了语言标准! - Bo Persson

0
WinApi 的设计是不会抛出异常的。而且,如果某个函数返回不成功,你在大多数情况下必须通过 GetLastError() 获取最后的错误原因。据我所知,你的函数将成为 WinApi 的一部分并且易于使用。因此,我建议保持它们的设计。即,在失败的情况下返回空字符串,并检查你的函数是否返回了最后的错误。

0
根据您的应用程序,有几种适当的解决方案。第一种是在出现错误时抛出异常:如果您选择这种方法,应该定义一个 WindowsError 异常(从标准异常派生),其中包括从 GetLastError 获得的所有可用信息,以及可能的其他信息(失败的函数名称等),以易于解析的格式呈现。另一种是返回某种类型的 Fallible;在这种情况下,您可能需要扩展经典的 Fallible 习惯用法,以便它可以包含有关错误原因的其他信息。还有一种可能性是通过 out 参数返回值,并使用返回代码(同样可能带有附加信息,并可能添加代码以确保在销毁之前已进行测试)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接