Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

about WINAPI errors handling

I'm writing on C++. Most of my code throw exceptions when fail. Sometimes I have to deal with the Window API that is C-oriented and does not throw. So, every time I call a WINAPI function I check the return value and if it indicate an error I use GetLastError() to retrieve the concrete error code. Then I convert that error code in to an error string and throw an exception based on it.

For example:

HANDLE ph = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);

if (!ph) {
    throw std::runtime_error(win_error_to_string(GetLastError()));
}

I was wondering if it is appropriate to write an generic wrapper that invoke a WINAPI function an throw exception if an error was set during the call. Something like that:

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

The advantage of that technique is that I do not have to check the return value after each WINAPI call, assuming that the result is correct if the function does not throw.

HANDLE ph = call_winapi(OpenProcess, PROCESS_QUERY_INFORMATION, FASLE, pid);

But I'm afraid I missed something. Like, is it always true that if the WINAPI function set an error code that differ from ERROR_SUCCESS that means that the function failed?

like image 430
draganm17 Avatar asked Sep 12 '15 22:09

draganm17


People also ask

What is API error handling?

Generally speaking, it means one of two things — something was so wrong in your request or your handling that the API simply couldn't parse the passed data, or the API itself has so many problems that even the most well-formed request is going to fail.

How do you handle a flutter error?

Errors that don't occur within Flutter's callbacks can't be caught by the framework, but you can handle them by setting up a Zone . All errors caught by Flutter are routed to the FlutterError. onError handler. By default, this calls FlutterError.

What is Golang error handling?

In summary, here's the gist of what was covered here: Errors in Go are just lightweight pieces of data that implement the Error interface. Predefined errors will improve signaling, allowing us to check which error occurred. Wrap errors to add enough context to trace through function calls (similar to a stack trace)


2 Answers

This function, as it stands, is of no use. Win32 functions do not, as a rule, indicate failure by setting the error code. They indicate failure via their return value. A BOOL that is false on failure. A handle that is NULL or INVALID_HANDLE_VALUE. And so on.

There are mis-behaving functions that fail, and indicate that in the return value, but do not set the error code. Your approach won't handle those correctly. There are functions that succeed, and set the error code. Again, your function will mis-treat them.

Each function has its own error handling rules. You have to treat each one on it own merits. Check the return value as described in that function's documentation.

Perhaps the best you can do is write a function that accepts a boolean indicating success, and throws an error on failure.

void Win32Check(bool success)
{
    if (!success)
        throw std::runtime_error(win_error_to_string(GetLastError()));
}

Call it like this:

// DeleteFile returns returns BOOL indicating success
Win32Check(DeleteFile(...));

Or

// CreateFile returns a sentinel to indicate failure
HANDLE hfile = CreateFile(...);
Win32Check(hfile != INVALID_HANDLE_VALUE);
like image 124
David Heffernan Avatar answered Sep 20 '22 15:09

David Heffernan


You're getting there with the last part. If a function does not document that you can use GetLastError, then DO NOT use it. Unfortunately, you have to check each function's documentation separately to see this. For the purposes of this question, this means that the wrapper may only be used for functions that do specify the use of GetLastError, and you should check that the return value of the function represents failure before getting more error info.

To illustrate with an example, RegisterClass returns 0 on failure and advertises more error information through GetLastError. On the other hand, RegSetValueEx returns ERROR_SUCCESS (0) on success and does not advertise the same because it returns an error code directly. Meanwhile, there's WinExec, which returns a value greater than 31 on success and one of several listed error codes on failure. The latter are still free to call another function that happens to fail and call SetLastError, even though the call you make succeeds.

Another slight problem is that functions returning void will not work, but that can be fixed rather easily with a specialization, or a proposal going through for allowing void values that do nothing.


To further illustrate, here is part of the documentation for GetLastError:

The Return Value section of the documentation for each function that sets the last-error code notes the conditions under which the function sets the last-error code. Most functions that set the thread's last-error code set it when they fail. However, some functions also set the last-error code when they succeed. If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not.

like image 35
chris Avatar answered Sep 22 '22 15:09

chris