Delphi GetLastError с выполнением пакетов

Когда я выполняю этот код без пакетов среды выполнения, у меня возникает ошибка кода 32, это правильно. Но когда я активирую пакеты среды выполнения (например, только с помощью «FireDACASADriver; YmagControlDB»), код ошибки всегда равен «0».

procedure TForm1.Button1Click(Sender: TObject);
Var
   Stream: TStream;
   iError : integer;
begin
   Stream := nil;
   iError := -1;
   try
      try
         Stream := TFileStream.Create('d:\toto.docx', fmOpenRead);
      except
         begin
            iError := GetLastError;
         end;
      end;
   finally
      if Assigned(Stream) then
         Stream.Free;
   end;
   showmessage('Erreur : ' + inttostr(iError));
end;

Как я могу исправить GetLastError с помощью пакетов времени выполнения?


person Joc02    schedule 08.04.2016    source источник


Ответы (2)


Здесь просто неуместно вызывать GetLastError. Вы смешиваете две разные модели обработки ошибок.

Вызовите GetLastError сразу же после сбоя вызова API, если это указано в документации. Когда вы ее вызываете, какая-то другая функция вполне могла бы вызвать SetLastError и сбросить значение.

Поэтому неправильно вызывать GetLastError, поскольку вы не используете функции Win32, и вам следует удалить вызов GetLastError. Ваш код должен быть:

procedure TForm1.Button1Click(Sender: TObject);
var
  Stream: TStream;
begin
  Stream := TFileStream.Create('d:\toto.docx', fmOpenRead);
  try
    // ....    
  finally
    Stream.Free;
  end;
end;

В случае ошибки будет возбуждено исключение, о котором сообщит обработчик исключений верхнего уровня.

Пакеты среды выполнения не должны влиять на то, как выполняется этот код.

Возможные причины ошибки: файл не существует или заблокирован.

Вы написали:

if Assigned(Stream) then
  Stream.Free;

Это всегда бессмысленно, так как метод Free также проверяет, является ли ссылка на объект nil. Фактически ваш код эквивалентен:

if Assigned(Stream) then
  if Assigned(Stream) then
    Stream.Destroy;

Так что лучше положиться на тест внутри Free и просто написать:

Stream.Free;

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

  • Позвоните CreateFile, чтобы открыть файл.
  • Проверьте возвращенный дескриптор на INVALID_HANDLE_VALUE, чтобы обнаружить ошибку.
  • В случае ошибки используйте GetLastError, чтобы узнать причину ошибки.
  • В противном случае закройте дескриптор с помощью CloseHandle.

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

Как правило, лучше просить прощения, чем разрешения.

person David Heffernan    schedule 08.04.2016
comment
Хорошо, но я пишу этот код для обнаружения заблокированного документа. getLastError без runtimepackge имел код 32. Как лучше всего определить, что файл заблокирован? - person Joc02; 08.04.2016

Действие возбуждения исключения может сбросить код ошибки вызывающего потока. Просто неуместно вызывать GetLastError() внутри обработчика исключений.

При этом, если TFileStream не удается открыть файл, возникает исключение, содержащее предоставленное системой сообщение об ошибке (но не фактический код ошибки), например:

procedure TForm1.Button1Click(Sender: TObject);
var
  Stream: TStream;
begin
  try
    Stream := TFileStream.Create('d:\toto.docx', fmOpenRead);
    try
      // use Stream as needed
    finally
      Stream.Free;
    end;
  except
    on E: Exception do
      ShowMessage('Erreur : ' + E.Message);
  end;
end;

Если вам нужен доступ к коду ошибки, вы не можете использовать TFileStream, вместо этого вам придется использовать CreateFile() напрямую:

procedure TForm1.Button1Click(Sender: TObject);
var
  hFile: THandle;
  iError: DWORD;
begin
  hFile := CreateFile('d:\toto.docx', GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    try
      // use hFile as needed...
      //
      // if you need to access the file as a TStream, you can
      // instantiate a THandleStream passing hFile to its constructor...
      //
    finally
      CloseHandle(hFile);
    end;
  end else
  begin
    iError := GetLastError;
    ShowMessage('Erreur : ' + IntToStr(iError));
    if iError = ERROR_SHARING_VIOLATION then
    begin
      // do something...
    end;
  end;
end;

В качестве альтернативы:

procedure TForm1.Button1Click(Sender: TObject);
var
  hFile: THandle;
begin
  hFile := CreateFile('d:\toto.docx', GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);
  try
    Win32Check(hFile <> INVALID_HANDLE_VALUE);
    try
      // use hFile as needed...
    finally
      CloseHandle(hFile);
    end;
  except
    on E: EOSError do
    begin
      ShowMessage('Erreur : ' + IntToStr(E.ErrorCode));
      if E.ErrorCode = ERROR_SHARING_VIOLATION then
      begin
        // do something...
      end;
    end;
  end;
end;
person Remy Lebeau    schedule 08.04.2016