вторник, 11 мая 2010 г.

Обработка ошибок и GetLastError()

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


   Функция Windows, обнаружив ошибку, через механизм локальной памяти потока сопоставляет соответствующий код ошибки с вызывающим потоком. Это позволяет потокам работать независимо друг от друга, не вмешиваясь в чужие ошибки. Когда функция вернет Вам управление, ее возвращаемое значение будет указывать на то, что произошла какая-то ошибка. Какая именно — Вы узнаете, вызвав функцию GetLastError().
   (Дж. Рихтер, "Создание эффективных WIN32-приложений")

   И на самом деле, некоторые функции (например OpenProcess/OpenMutex), в случае успешного выполнения, не изменяют код последней ошибки.

   Псевдо-код фукнции OpenProcess
function OpenProcess(dwDesiredAccess: DWORD; 
  bInheritHandle: Boolean; dwProcessId: DWORD): THandle; stdcall;
var
  Status: NTSTATUS;
  hProcess: THandle;
begin
  Status := NtOpenProcess(@hProcess, ...);
  if not NT_SUCCESS(Status) then  // error
    begin
      SetLastError(NtStatusToDosError(Status));
      Result := 0
    end
  else
    Result := hProcess
end;

   Таким образом, если функция успешно выполнила свою работу, LastErrorValue не изменяется, и остается таким же, каким было до вызова функции. Поэтому ниже приведенный код не корректен, т.к. возможна ситуация, когда hProcess <> 0, а GetLastError() возвращает значение, отличное от ERROR_SUCCESS
procedure SomeProcedure(ProcessID: Cardinal;);
var
  hProcess: THandle;
  LastError: Integer;
begin
  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or SYNCHRONIZE, False, processID);
  LastError := GetLastError();
  if (hProcess <> 0) and (LastError = ERROR_SUCCESS) then  
    begin
      ...
      // Делаем что-то полезное
      ...
    end;

  if (hProcess <> 0) then
    CloseHandle(hProcess)
end;

   А вот CreateEvent код последней ошибки изменяет (устанавливает его либо в ERROR_SUCCESS, либо в ERROR_ALREADY_EXISTS).
   Псевдо-код фукнции CreateEventW
function CreateEventW(lpEventAttributes: PSecurityAttributes;
  bManualReset, bInitialState: BOOL; lpName: PWideChar): THandle; stdcall;
var
  Status: NTSTATUS;
  hEvent: THandle;
begin
  Status = NtCreateEvent(@hEvent, ...);
  if NT_SUCCESS(Status) then
    begin
      if (Status = STATUS_OBJECT_NAME_EXISTS) then
        SetLastError(ERROR_ALREADY_EXISTS)
      else
        SetLastError(ERROR_SUCCESS);
      Result := hEvent
    end
  else
    begin
      SetLastError(NtStatusToDosError(Status));
      Result := 0
    end
end;

   Мораль:
  • Сначала необходимо проверить, что вернула функция, и если это значение свидетельствует об ошибке, тогда можно использовать GetLastError()
  • Обнуляйте переменные (с)

Комментариев нет:

Отправить комментарий