2015-09-12 8 views
2

Я пишу на C++. Большинство ошибок моего кода генерируют исключения при сбое. Иногда мне приходится иметь дело с API-интерфейсом Window, который является C-ориентированным и не бросает. Поэтому каждый раз, когда я вызываю функцию WINAPI, я проверяю возвращаемое значение, и если он указывает на ошибку, я использую GetLastError() для повторного восстановления кода ошибки конкретизации. Затем я конвертирую этот код ошибки в строку ошибки и создаю исключение на основе этого. Например:об обработке ошибок WINAPI

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

мне было интересно, если это уместно, чтобы написать общий wraper, что вызывать функцию 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; 
} 

Преимущество этого метода заключается в том, что я не должен проверять возвращаемое значение после каждого вызова WINAPI, при условии, что результат является правильным, если функция не бросает.

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

Но, боюсь, я что-то пропустил. Например, всегда ли верно, что если функция WINAPI устанавливает код ошибки, который отличается от ERROR_SUCCESS, это означает, что функция не выполнена?

+0

вы можете проверить код ошибки и отличаются уважение потока к коду ошибки, который легче, чем сравнение строк. вы можете наследовать исключение с атрибутом errcode. – ataman

+0

@ataman, также можно будет адаптировать 'std :: system_error', который включает коды ошибок. – chris

+0

@chris О, я не знал об этом. спасибо;) – ataman

ответ

1

Вы добираетесь туда с последней частью. Если функция не документирует, что вы можете использовать GetLastError, НЕ используйте ее. К сожалению, вам нужно отдельно проверить документацию каждой функции. Для целей этого вопроса это означает, что оболочка может использоваться только для функций, которые определяют использование GetLastError, и вы должны убедиться, что возвращаемое значение функции представляет собой ошибку, прежде чем получить дополнительную информацию об ошибке.

Чтобы проиллюстрировать пример, RegisterClass возвращает 0 при сбое и сообщает больше информации об ошибке через GetLastError. С другой стороны, RegSetValueEx возвращает ERROR_SUCCESS (0) на успех и не рекламирует то же самое, потому что он возвращает код ошибки напрямую. Между тем, есть WinExec, который возвращает значение, превышающее 31 при успехе, и один из нескольких кодов ошибок при сбое. Последние по-прежнему могут вызывать еще одну функцию, которая происходит с ошибкой, и вызывать SetLastError, даже несмотря на то, что вызов, который вы делаете, преуспевает.

Еще одна небольшая проблема заключается в том, что функции, возвращающие void, не будут работать, но это может быть исправлено довольно легко со специализацией или предложением, позволяющим использовать значения void, которые ничего не делают.


Для дополнительной иллюстрации, здесь является частью документации для GetLastError:

раздел Возвращаемого значения документации для каждой функции, которая устанавливает код последней ошибки отмечает, что условия, при которых функции устанавливает код последней ошибки. Большинство функций, которые устанавливают код последней ошибки потока, устанавливают его, когда они терпят неудачу. Однако некоторые функции также устанавливают код последней ошибки, когда они преуспевают. Если функция не документирована для установки кода последней ошибки, значение, возвращаемое этой функцией, является просто самым последним последним кодом ошибки, который был установлен; некоторые функции устанавливают код последней ошибки в 0 при успешном запуске, а другие - нет.

+1

И не забывайте о функциях, которые возвращают коды ошибок 'HRESULT' вместо использования' GetLastError() '(многие/большинство функций Shell и т. Д.). А также невоидные функции возврата, которые вообще не обеспечивают никакой обработки ошибок (некоторые функции GDI и т. Д.) –

+0

@RemyLebeau, Yikes, COM полностью соскользнул с моей головы вместе с этой другой категорией. – chris

+0

COM не является строго частью Windows API. Тем не менее, COM указывает стандартные сообщения об ошибках (['SUCCEEDED'] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms687197.aspx)/[' FAILED'] (https: // msdn.microsoft.com/en-us/library/windows/desktop/ms693474.aspx) и [Интерфейс IErrorInfo] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms221233 .aspx)). COM предоставляет гораздо больше возможностей для реализации обобщенной обертки обработки ошибок. – IInspectable

4

Эта функция, в ее нынешнем виде, бесполезна. Функции Win32, как правило, не указывают на ошибку, устанавливая код ошибки. Они указывают на отказ через их возвращаемое значение. A BOOL, который является ложным при сбое. Ручка, которая равна NULL или INVALID_HANDLE_VALUE. И так далее.

Имеются ошибочные функции, которые не работают, и указывают, что в возвращаемом значении, но не устанавливают код ошибки. Ваш подход не будет правильно их обрабатывать. Есть функции, которые преуспевают, и установите код ошибки. Опять же, ваша функция будет неправильно обрабатывать их.

Каждая функция имеет свои собственные правила обработки ошибок. Вы должны относиться к каждому из них по существу. Проверьте возвращаемое значение, как описано в документации этой функции.

Возможно, самое лучшее, что вы можете сделать, это написать функцию, которая принимает логическое значение, указывающее на успех, и выдает ошибку при сбое.

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

Зов это следующим образом:

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

Или

// CreateFile returns a sentinel to indicate failure 
HANDLE hfile = CreateFile(...); 
Win32Check(hfile != INVALID_HANDLE_VALUE); 
Смежные вопросы