Если вы внимательно посмотрите на различные функции, которые возвращают дескрипторы объектов ядра (THandle), то вы увидите, что какие-то их них возвращают 0 (например, CreateThread), а какие-то -1 - INVALID_HANDLE_VALUE (например, CreateFile). И вам каждый раз приходится заглядывать в документацию, чтобы проверить, что же возвращает вот эта конкретная функция при неудаче.
Почему же возвращаемые значения так отличаются?
Причины, как вы уже подозреваете, - исторические.
Эти значения выбирались, исходя из соображений совместимости с 16-ти битной Windows. 16-ти битные функции OpenFile, _lopen и _lcreat возвращали -1 при ошибке, поэтому 32-х разрядная функция CreateFile также возвращает -1 (INVALID_HANDLE_VALUE), чтобы упростить перенос приложений с Win16.
(Вооружённые этим знанием, вы уже можете ответить на следующий простой вопрос: почему, когда мне нужно вызывать CreateFile, хотя в действительности я не открываю файл? Разве не должна функция называться OpenFile? Ответ: да, OpenFile было бы более подходящим именем, но это имя уже занято.)
С другой стороны, в Win16 не было эквивалентов для CreateThread или CreateMutex, поэтому они возвращают 0.
Поскольку прецедент уже установил непоследовательность возвращаемых значений, то при добавлении новой функции всегда получается выбор между возвратом 0 или INVALID_HANDLE_VALUE.
Эта непоследовательность имеет несколько последствий.
По-первых, конечно же, вам нужно аккуратно проверять возвращаемые значения.
Во-вторых , это означает, что если вы хотите написать какую-то общую обёртку вокруг значения дескриптора - то вам надо различать два возможных значения "не дескриптор".
В-третьих, если вы хотите инициализировать значение дескриптора, то вам придётся делать это по-разному, в зависимости от функции, с которой вы собрались его использовать. Например, следующий код неверен:
varВ этом коде два бага. Во-первых, возвращаемое из CreateFile значение проверяется неверно. Код выше проверяет на 0 вместо INVALID_HANDLE_VALUE. Во-вторых, код неверно инициализирует переменную H. Вот исправленный вариант:
H: THandle;
begin
H := 0;
if UseLogFile then
H := CreateFile(...);
DoOtherStuff;
if H <> 0 then
Log(H);
DoOtherStuff;
if H <> 0 then
CloseHandle(H);
end;
varВ-четвёртых, вам нужно быть особенно внимательными со значением INVALID_HANDLE_VALUE: чисто случайно оно совпадает со значением псевдо-дескриптора, возвращаемым GetCurrentProcess. Многие функции ядра принимают такие псевдо-дескрипторы, поэтому, если вы, например, облажаетесь и случайно вызовите, скажем, WaitForSingleObject со значением INVALID_HANDLE_VALUE, в итоге вы обнаружите, что вы ждёте текущий процесс. Такое ожидание, конечно же, никогда не завершится, потому что процесс переходит в сигнальное состояние только при выходе, поэтому вы в итоге ждёте самого себя.
H: THandle;
begin
H := INVALID_HANDLE_VALUE;
if UseLogFile then
H := CreateFile(...);
DoOtherStuff;
if H <> INVALID_HANDLE_VALUE then
Log(H);
DoOtherStuff;
if H <> INVALID_HANDLE_VALUE then
CloseHandle(H);
end;
>>С другой стороны, в Win16 не было эквивалентов для CreateThread или CreateMutex, поэтому они возвращают 0.
ОтветитьУдалитьСтранное утверждение.
Почему бы в новых функциях не возвращать старое значение ошибки?
Я не уверен и могу страшно наврать (сильно не бейте). Но могу попробовать угадать: думаю, потому что функции, возвращающие 0, пришли из WinNT. WinNT изначально была отдельной веткой (NT OS/2), не связанной с Win3.0/Win9x. Только после выхода Win3.0 MS сделала обе ветки совместимыми (добавила в NT поддержку WinAPI) - появился Win NT 3.1 и путаница с 0/INVALID_HANDLE_VALUE. 0 - это от Native API, а INVALID_HANDLE_VALUE - от WinAPI.
ОтветитьУдалить