关于WINAPI错误处理

dra*_*m17 4 c++ error-handling winapi

我正在写C++.我的大多数代码在失败时抛出异常.有时我必须处理面向C的Window API并且不抛出.因此,每次调用WINAPI函数时,我都会检查返回值,如果它指示错误,我使用GetLastError()来重新编译concreate错误代码.然后我将该错误代码转换为错误字符串并基于它抛出异常.例如:

HANDLE ph = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!ph) {
    throw std::runtime_error(win_error_to_string(GetLastError()));
}
Run Code Online (Sandbox Code Playgroud)

我想知道如果在调用期间设置了错误,是否适合编写调用WINAPI函数的泛型包装器抛出异常.像这样的东西:

template <typename R, typename... Args>
decltype(auto) call_winapi(R(WINAPI*func)(Args...), Args... &&args)
{
    SetLastError(ERROR_SUCCESS);
    const R result = func(args);
    const DWORD error = GetLastError();
    if (error != ERROR_SUCCESS) {
        throw std::runtime_error(win_error_to_string(error));
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

该技术的优点是我不必在每次WINAPI调用后检查返回值,假设如果函数没有抛出结果是正确的.

HANDLE ph = call_winapi(OpenProcess, PROCESS_QUERY_INFORMATION, FASLE, pid);
Run Code Online (Sandbox Code Playgroud)

但我害怕错过了什么.就像,如果WINAPI函数设置一个与ERROR_SUCCESS不同的错误代码,这意味着该函数失败了吗?

Dav*_*nan 6

目前这个功能是没用的.通常,Win32函数不会通过设置错误代码来指示失败.它们通过返回值表示失败.一个BOOL是失败假的.手柄是NULLINVALID_HANDLE_VALUE.等等.

有失败的错误行为函数,并指示在返回值中,但不设置错误代码.您的方法无法正确处理这些问题.有些功能成功,并设置错误代码.同样,你的功能会误导他们.

每个函数都有自己的错误处理规则.你必须根据自己的优点对待每一个.按照该功能文档中的说明检查返回值.

也许你能做的最好的事情就是编写一个接受表示成功的布尔值的函数,并在失败时抛出错误.

void Win32Check(bool success)
{
    if (!success)
        throw std::runtime_error(win_error_to_string(GetLastError()));
}
Run Code Online (Sandbox Code Playgroud)

像这样称呼它:

// DeleteFile returns returns BOOL indicating success
Win32Check(DeleteFile(...));
Run Code Online (Sandbox Code Playgroud)

要么

// CreateFile returns a sentinel to indicate failure
HANDLE hfile = CreateFile(...);
Win32Check(hfile != INVALID_HANDLE_VALUE);
Run Code Online (Sandbox Code Playgroud)


chr*_*ris 1

你已经完成最后一部分了。如果某个函数没有记录您可以使用GetLastError,那么请勿使用它。不幸的是,您必须单独检查每个函数的文档才能看到这一点。就本问题而言,这意味着包装器只能用于指定使用 的函数GetLastError,并且您应该在获取更多错误信息之前检查函数的返回值是否表示失败。

举个例子来说明,RegisterClass失败时返回0,并通过 通告更多错误信息GetLastError。另一方面,成功时RegSetValueEx返回ERROR_SUCCESS(0),并且不做广告,因为它直接返回错误代码。同时,还有WinExec,它在成功时返回大于 31 的值,在失败时返回列出的几个错误代码之一。后者仍然可以自由地调用另一个恰好失败的函数并调用SetLastError,即使您的调用成功了。

另一个小问题是函数返回void不起作用,但是可以通过专门化或允许void不执行任何操作的值的提案来轻松修复。


为了进一步说明,这里是文档的一部分GetLastError

每个设置最后一个错误代码的函数的文档的“返回值”部分注明了该函数设置最后一个错误代码的条件。大多数设置线程最后错误代码的函数都会在失败时设置它。但是,某些函数在成功时也会设置最后一个错误代码。如果没有记录该函数来设置最后一个错误代码,则该函数返回的值只是最近设置的最后一个错误代码;某些函数在成功时将最后一个错误代码设置为 0,而其他函数则不会。