Вызов TMouse.GetCursorPos иногда завершается с ошибкой "Сбой вызова функции ОС"

Иногда моя заявка получает ошибку ниже.

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

TMouse.GetCursorPostion не делает ничего, кроме вызова API-интерфейса Windows для GetCursorPosition. Затем он проверяет возвращаемое значение и вызывает GetLastError, если это не удалось.

"Ошибка вызова функции ОС" не очень помогает выявлять причину этого. Может ли вызывать эту ошибку экранную заставку или спящий режим? Я мог бы изменить компонент так, чтобы он просто перехватывал и игнорировал ошибку, но, если это возможно, я бы предпочел знать, что / почему это происходит в первую очередь.

Мое приложение использует Delphi 2007, и вызов выполняется из компонента Transfer@Once (v 1.7) Quasidata.

Вот стек вызовов:

операционная система: Windows XP с пакетом обновления 3, сборка 2600
номер исключения: 1
класс исключения: EOSError
сообщение об исключении: сбой вызова функции ОС.

основная тема ($d34):
0045e208 UaarSales.exe SysUtils       RaiseLastOSError
0045e191 UaarSales.exe SysUtils       RaiseLastOSError
0045e237 UaarSales.exe SysUtils       Win32Check
004c6de9 UaarSales.exe контролирует TMouse.GetCursorPos
00736d8b UaarSales.exe taoCntrr  3999 TtaoHoverTimer.Timer
004a1d27 UaarSales.exe ExtCtrls       TTimer.WndProc
0047a7a0 UaarSales.exe Классы StdWndProc
7e4196c2 USER32.dll                   DispatchMessageA
004da230 UaarSales.exe Формы TApplication.ProcessMessage
004da26a UaarSales.exe Формы TApplication.HandleMessage
004da55f UaarSales.exe Формы TApplication.Run
00b3ea76 UaarSales.exe UaarSales  117 инициализация

Вот процедура таймера


procedure TtaoHoverTimer.Timer;
var
  lPos: TPoint;
begin
  lPos := Mouse.CursorPos;   // this is line 3999 
  if (lPos.X = FMousePos.X) and (lPos.Y = FMousePos.Y) and
    not ((lPos.X = FOutdatedPos.X) and (lPos.Y = FOutdatedPos.Y)) then
  begin
    inherited Timer;
    FOutdatedPos := Point(MaxInt, MaxInt);
  end;
  Enabled := False;
end;

3 ответа

Решение

CursorPos использует метод Windows GetCursorPos. Замечания в MSDN говорят, что у него есть два требования:

  • "Вызывающий процесс должен иметь доступ WINSTA_READATTRIBUTES к оконной станции".
  • "Рабочий стол ввода должен быть текущим рабочим столом при вызове GetCursorPos. Вызовите OpenInputDesktop, чтобы определить, является ли текущий рабочий стол входным рабочим столом. Если это не так, вызовите SetThreadDesktop с HDESK, возвращаемым OpenInputDesktop, чтобы переключиться на этот рабочий стол".

Таким образом, есть вероятность, что заставка работает на другом рабочем столе. С другой стороны, если вы используете Vista, я уверен, что диалоговое окно ввода пароля (для разблокировки компьютера) работает и на другом рабочем столе.

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

Наконец, вы можете вызвать GetLastError, чтобы увидеть, какой была последняя ошибка Windows, после того, как было сгенерировано исключение. Это должно сказать вам наверняка, какова реальная проблема, с которой он сталкивается. Как и в комментарии (спасибо!), Вы уже столкнулись с сообщением об ошибке в сообщении об исключении.

Попробуйте вызвать GetCursorPos(cursorPos); метод в модуле Windows.

Что-то вроде этого:

var
   cursorPos       : TPoint;

begin
     GetCursorPos(cursorPos);
     cursorPos := ScreenToClient(cursorPos);

Работает без проблем во всех моих приложениях.

Не видя код и какую версию Windows, можно только догадываться. Я бы посмотрел код процедуры TtaoHoverTimer.Timer в модуле taoCntrr.

Другие вопросы по тегам