Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Круглый стол
  
Правила КС
>> Настройки

Фильтр вопросов
>> Новые вопросы
отслеживать по
>> Новые ответы

Избранное

Страница вопросов
Поиск по КС


Специальные проекты:
>> К л ю к в а
>> Г о л о в о л о м к и

Вопрос №

Задать вопрос
Off-topic вопросы

Помощь

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  10:50[Войти] | [Зарегистрироваться]
Ответ на вопрос № 57810

Вопросы с аналогичными сообщениями об ошибках:
  • Access violation at address ... (776)

    15-12-2007 08:10
    Здравстуйте. У меня есть глобальный хук, но я столкнулся с такой проблемой: если мы запускаем хук, включаем какое-то приложение, набираем всякие символы, а потом закрываем приложение, то выскакивает ошибка инструкция по адресу "такому-то" обратилась к памяти по адресу "такому-то". Память не может быть "read".
    Привожу код библиотеки:

    library keyhook;

    uses
      SysUtils,
      Windows,
      Messages,
      Forms;

    const
      MMFName: PChar = 'KeyMMF';

    type
      PGlobalDLLData = ^TGlobalDLLData;
      TGlobalDLLData = packed record
        SysHook: HWND;
        MyAppWnd: HWND;
      end;

    var
      GlobalData: PGlobalDLLData;
      MMFHandle: THandle;
      WM_MYKEYHOOK: Cardinal;

    function KeyboardProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;
    var
        AppWnd: HWND; 
    begin

      if code < 0 then
      begin
        Result:= CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam);
        Exit;
      end;

      if (  ((lParam and KF_UP)=0) and (wParam>=65) and (wParam<=90)  ) OR (  ((lParam and KF_UP)=0) and (wParam=VK_SPACE)  ) then
      begin
        AppWnd:= GetForegroundWindow();
        SendMessage(GlobalData^.MyAppWnd, WM_MYKEYHOOK, wParam, AppWnd);
      end;

      CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam);
      Result:= 0;

    end;

    procedure hook(switch : Boolean; hMainProg: HWND) export; stdcall;
    begin
      if switch=true then
      begin
        GlobalData^.SysHook := SetWindowsHookEx(WH_KEYBOARD, @KeyboardProc, HInstance, 0);
        GlobalData^.MyAppWnd:= hMainProg;
        if GlobalData^.SysHook <> 0 then
            MessageBox(0, 'KEYBOARD HOOK установлен !', 'Message from keyhook.dll', 0)
          else
            MessageBox(0, 'HOOK установить не удалось !', 'Message from keyhook.dll', 0);

      end
      else
      begin

        if UnhookWindowsHookEx(GlobalData^.SysHook) then
          MessageBox(0, 'HOOK снят !', 'Message from keyhook.dll', 0)
        else
          MessageBox(0, 'HOOK снять не удалось !', 'Message from keyhook.dll', 0);

      end;
    end;

    procedure OpenGlobalData();
    begin
      WM_MYKEYHOOK:= RegisterWindowMessage('WM_MYKEYHOOK');

      MMFHandle:= CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TGlobalDLLData), MMFName);

      if MMFHandle = 0 then
        begin
          MessageBox(0, 'Can''t create FileMapping', 'Message from keyhook.dll', 0);
          Exit;
        end;

      GlobalData:= MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TGlobalDLLData));
      if GlobalData = nil then
        begin
          CloseHandle(MMFHandle);
          MessageBox(0, 'Can''t make MapViewOfFile', 'Message from keyhook.dll', 0);
          Exit;
        end;

    end;

    procedure CloseGlobalData();
    begin
      UnmapViewOfFile(GlobalData);
      CloseHandle(MMFHandle);
    end;

    procedure DLLEntryPoint(dwReason: DWord); stdcall;
    begin
      case dwReason of
        DLL_PROCESS_ATTACH: OpenGlobalData;
        DLL_PROCESS_DETACH: CloseGlobalData;
      end;
    end;

    exports hook;

    begin
      DLLProc:= @DLLEntryPoint;
      DLLEntryPoint(DLL_PROCESS_ATTACH);
    end.


    Откройте пожалуйста секрет, что не так!

    [+] Добавить в избранные вопросы

    Отслеживать ответы на этот вопрос по RSS

    Ответы:


    Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице.
    Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.

    28-11-2009 00:57 | Комментарий к предыдущим ответам
    Мне сейчас лень копаться.

    Проверьте, про при удалении ловушки (вызове Done) происходит Data^.Sign := 0;

    27-11-2009 15:06 | Комментарий к предыдущим ответам
    Уважаемый Александр Алексеев, вы могли бы дать пояснение? Возник вопрос. В вашем коде, в частности, написано

            begin
              // Здесь мы можем быть, только, если библиотека инициализируется дважды из разных приложений
              Done;
              SetLastError(ERROR_ALREADY_REGISTERED);
              Result := False;
              Exit;
            end;


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

    1. Если программа запускается впервые, на "чистой" машине - все отлично.
    2. Если Программа запускается, останавливается (Done отработал), и запускается повторно - возникает именно ERROR_ALREADY_REGISTERED.
    3. Если повторный запуск происходит через какое-то время - все опять работает.

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

    27-12-2007 01:24
    Заменить
    (wParam>=65) and (wParam<=90)
    на
    (((wParam >= 65) and (wParam <= 90)) or ((wParam >= 48) and (wParam <= 57)))

    Я рекомендую найти любую книжку по Delphi и начать изучать. На этом сайте писать за вас вашу программу не будут.

    26-12-2007 12:05
    Вы бы не могли подсказать какую нужно строчку добавить и куда, пожалуйста.

    26-12-2007 11:47
    Да

    26-12-2007 11:35
    Тоесть для того, чтобы отображались цифры, нужно добавить строчку по типу (wParam>=65) and (wParam<=90)?

    26-12-2007 02:43
    Вырезка из Windows.pas:

      VK_HELP = 47;
    { VK_0 thru VK_9 are the same as ASCII '0' thru '9' ($30 - $39) }
    { VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' ($41 - $5A) }
      {$EXTERNALSYM VK_LWIN}
      VK_LWIN = 91;



    (wParam>=65) and (wParam<=90)


    65 - это Ord('A') = VK_A
    90 - это Ord('Z') = VK_Z
    Для фильтрации цифр используйте Ord('0') = VK_0 и Ord('9') = VK_9

    Вы определяете нажатие кнопок на клавиатуре. Грубо говоря, никакого понятия "кнопка с русской буквой 'а'" для клавиатуры нет. Есть кнопка номер такой-то (VK_F). Всё остальное определяет ОС. Нужно руками определить раскладку, состояние спец. кнопок (Shift, Caps Lock) и перевести код клавиши в символ.
    Я бы рассмотрел вариант с установкой другого типа ловушки, например на получение сообщения, и отлавливал бы WM_CHAR.

    25-12-2007 12:57
    По ходу эксплуатации возник ещё один вопрос: как сделать, чтобы сия программа воспринимала цифры и русские символы?

    25-12-2007 12:08
    Вы были действительно правы. Всё прекрасно работает. Благодарю за поддержку и помощь.

    25-12-2007 06:06
    Ага, попался.

    procedure DLLEntryPoint(dwReason: DWord); stdcall;


    Нужно убрать stdcall;

    Суммируя, в коде вопроса нужно:
    1. Заменить

    function KeyboardProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;


    на

    function KeyboardProc(code : integer; wParam : longint; lParam : longint) : longint; stdcall;



    2. Заменить

    procedure DLLEntryPoint(dwReason: DWord); stdcall;


    на

    procedure DLLEntryPoint(dwReason: DWord);


    25-12-2007 05:43 | Комментарий к предыдущим ответам
    Описка: тип ловушки, разумеется, WH_KEYBOARD, а не WH_CALLWNDPROC

    25-12-2007 03:14
    Нашлось время попробовать ваш код.
    Действительно, вылетает AV. о_О
    Не смог пока найти, в чём проблема.
    Вот код, который использую я (переделан для вашего случая) - этот вариант работает:

    library KeyHook;

    uses
      Windows, Messages;

    type
      // Информация о ловушке (hook-е), размещаемая в разделяемой памяти для доступа к ней библиотек во всех процессах
      // Главное приложение создаёт эту область и записывает в неё параметры. Внедрённые библиотеки открывают и читают её.
      PHookSharedData = ^THookSharedData;
      THookSharedData = record
        Sign: Cardinal;                  // Сигнатура, должна быть = Sign (см. ниже)
        hHook: THandle;                  // Дескриптор ловушки, для вызова следующего hook-а в Win9x
        Wnd: HWND;
      end;

    const
      // Уникальное публичное имя ловушки, для открытия её по имени
      MappingName = 'C0D7DD8D-2B00-4242-8DE5-F93FA1DC142C';
      // Сигнатура ловушки, для проверки успешности установки ловушки
      Sign        = $2B746C41;

    var
      // Разделяемые данные ловушки
      Data: PHookSharedData;
      // Дескриптор разделяемых данных
      hData: THandle;
      WM_MYKEYHOOK: UINT;

    // Завершает работу, при необходимости - снимает ловушку, экспортируется из библиотеки
    procedure Done; stdcall;
    var
      Z: Integer;
      H: HResult;
    begin
      // Сохраним GetLastError, чтоюы не запортить его внутри процедуры
      H := GetLastError;
      try
        // Отключаем разделяемую память
        if Data <> nil then
        begin
          // Можно писать в память?
          if not IsBadWritePtr(Data, 8) then
          begin // Да => мы находимся в главном приложении и должны отключить ловушку
            // Читаем дескриптор ловушки, одновременно обнуляя его
            Z := InterlockedExchange(Integer(Data^.hHook), 0);
            // Снимаем ловушку, если она была
            if Z <> 0 then
              UnhookWindowsHookEx(Z);
            // Стираем сигнатуру, т.к. ловушки уже нет
            Data^.Sign := 0;
          end;
          // Отключить разделяемую память в любом случае
          UnmapViewOfFile(Data);
          Data := nil;
        end;
        // Закрываем объект разделяемой памяти
        if hData <> 0 then
        begin
          CloseHandle(hData);
          hData := 0;
        end;
      except
        // Игнорируем все ошибки
      end;
      SetLastError(H);
    end;

    // Подпрограмма ловушки
    function  Hook(nCode: Integer; wParam, lParam: Integer): LRESULT; stdcall;
    begin
      try
        // Если ловушка вызвана первый раз
        if Data = nil then
        begin
          // Открываем объект разделяемой памяти по имени
          hData := OpenFileMapping(FILE_MAP_READ, False, MappingName);
          if hData <> 0 then
            // Проецируем данные ловушки на наше адресное пространство
            Data := MapViewOfFile(hData, FILE_MAP_READ, 0, 0, 0);
          // Если данные инициализированы не были
          if (Data = nil) or (Data^.Sign <> Sign) then
          begin
          // Ничего не делаем и выходим
          Result := 0;
          Exit;
          end;
          // Данные ловушки инициализированы и мы готовы к работе
        end;
        // Если наша очередь обработать сообщение
        if nCode >= 0 then
        begin
          // Если мы должны обработать сообщение
          if nCode = HC_ACTION then
          begin
            if ((lParam and KF_UP) = 0) and (((wParam >= 65) and (wParam <= 90)) or (wParam = VK_SPACE)) then
              SendMessage(Data^.Wnd, WM_MYKEYHOOK, wParam, GetForegroundWindow);
          end;
          // Признак того, что мы обработали сообщение
          Result:=0;
        end
        else
          // Иначе сообщение обрабатывает следующий hook в очереди
          Result := CallNextHookEx(Data^.hHook, nCode, wParam, lParam);
      except
        // При ошибках - игнорируем их и ничего не делаем
        Result := 0;
      end;
    end;

    // Начинает работу, устанавливает ловушку, экспортируется из библиотеки
    function  Init(const Wnd: HWND): Bool; stdcall;
    begin
      try
        // Если инициализация уже была
        if hData <> 0 then
        begin
          SetLastError(ERROR_ALREADY_INITIALIZED);
          Result := False;
          Exit;
        end;
        // Создаём объект разделяемой памяти
        hData := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(THookSharedData), MappingName);
        Result := hData <> 0;
        if Result then
        begin
          // Проецируем данные ловушки на наше адресное пространство
          Data := MapViewOfFile(hData, FILE_MAP_ALL_ACCESS, 0, 0, 0);
          Result := Data <> nil;
          if Result then
          begin
            // Проверка сигнатуры
            if InterlockedExchange(Integer(Data^.Sign), Sign) = Sign then
            begin
              // Здесь мы можем быть, только, если библиотека инициализируется дважды из разных приложений
              Done;
              SetLastError(ERROR_ALREADY_REGISTERED);
              Result := False;
              Exit;
            end;
            // Ставим глобальную ловушку на вызов оконной процедуры
            Data^.hHook := SetWindowsHookEx(WH_CALLWNDPROC, @Hook, HInstance, 0);
            Result := Data^.hHook <> 0;
            if not Result then
              Done
            else
              Data^.Wnd := Wnd; 
          end
          else
            Done;
        end;
      except
        // При ошибках - завершаем работу и вернём ошибку
        Done;
        SetLastError(ERROR_GEN_FAILURE);
        Result := False;
      end;
    end;

    // Секция экспорта
    exports
      Init, Done;

    // Реализация initialization/finaliztion

    var
      // Старый DLLPRoc
      OldDLLProc: TDLLProc;

    // Наш DLLProc
    procedure CustomDLLProc(Reason: Integer);
    begin
      try
        // Если библиотека выгружается - завершим перед этим её работу
        if Reason = DLL_PROCESS_DETACH then
          Done;
        // Также вызовем старый DLLProc
        if Assigned (OldDLLProc) then
          OldDLLProc (Reason);
      except
      end;
    end;

    // Стартовый код библиотеки
    procedure InitDLL;
    begin
      try
        // Подмена DLLProc
        OldDLLProc:=DLLProc;
        DLLProc:=@CustomDLLProc;
        // Инициализация глобальных переменных
        Data := nil;
        hData := 0;
        WM_MYKEYHOOK := RegisterWindowMessage('WM_MYKEYHOOK');
      except
      end;
    end;

    begin
      InitDLL;
    end.


    function  Init(const Wnd: HWND): Bool; stdcall; external 'keyhook.dll';
    procedure Done; stdcall; external 'keyhook.dll';

    procedure TForm1.bt1Click(Sender: TObject);
    begin
      if not Init(Handle) then
        RaiseLastOSError;
    end;

    procedure TForm1.bt2Click(Sender: TObject);
    begin
      Done;
    end;



    Принципиальных отличий от вашего кода не вижу.
    Попробую в свободное время покопаться ещё.

    18-12-2007 10:02
    Сделал так, как Вы сказали. Теперь одна ошибка выскакивает при установке хука. Ну и как обычно при закрытии приложения, но уже с другим адресом.

    18-12-2007 06:51
    Как возникает ошибка: устанавливаю хук, запускаю программу, ввожу в ней символы, закрываю программу и тут и выскакивает ошибка. Причём до тех пор, пока я не снему хук.

    18-12-2007 06:45
    >>>Ошибка возникает в момент закрытия программы.
    Точнее. Вашей программы или любой другой типа Winamp?
    Т.е. вы закрыли _свою_ программу и теперь при закрытии любой программы вываливается AV или же вообще после установки хука при закрытии любого приложения вываливается AV?

    Попробуйте:
    1. Обернуть все функции в try/except и выводить MessageBox с указанием места возникновения ошибки.
    2. В KeyboardProc первым делом проверять if GlobalData = nil then begin Result := 0; Exit; end; Также в CloseGlobalData в конец вставить GlobalData := nil.

    Если ошибка продолжит возникать, посмотрите, не изменился ли адрес, по которому программа не может получить доступ на чтение, до и после выполнения пункта 2. Ну, типа, если это обращение по nil, то должно стоять что-то типа "Read address 00000000", если чтение по недействительному указателю, отличному от nil, то "Read address XXYYZZHH".

    18-12-2007 06:26
    Ошибка возникает в момент закрытия программы. Это ошибка выскакивает абсолютно во всех приложениях, кроме блокнота.

    18-12-2007 01:02 | Вопрос к автору: запрос дополнительной информации
    А в какой момент точно и в каком приложении возникает ошибка?
    Можно, конечно, и на главное приложение взглянуть, но навряд-ли там что-то не так.

    17-12-2007 11:17
    Я снова исправил, но всё осталось по-старому. Может проблема не в библиотеки, а в приложении, которое вызывает библиотеку?
    P.S. Стучал Вам в асю, но Вы отклонили.

    17-12-2007 09:37
    Странно, у меня почти ровно такой же код работает без проблем.

    Единственные различия:

    1. В конце KeyboardProc:
    >>> Result := CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam);

    2. Убрать модуль Forms из uses.

    17-12-2007 05:44
    Я поменял, как Вы сказали. Но ошибка всё-равно выскакивает.

    17-12-2007 04:47
    >> function KeyboardProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;

    С чего вы взяли что wParam - тип word (2 байта)? Ужас.

    Срочно замените на longint (4 байта), пока никто не увидел.

    16-12-2007 13:52
    Тоесть Вы предлогаете убрать или заменить wParam? Можно точнее, что ни так в этой библиотеки и как сделать, чтобы она нормально функционировала.

    16-12-2007 04:45
    wParam, вообще говоря, уже двенадцать лет как имеет тип LongInt.
    Вы портите вызывающей программе стэк.
     

    Добавьте свое cообщение

    Вашe имя:  [Войти]
    Ваш адрес (e-mail):На Королевстве все адреса защищаются от спам-роботов
    контрольный вопрос:
    Два кольца, два конца, посередине гвоздик.
    в качестве ответа на вопрос или загадку следует давать только одно слово в именительном падеже и именно в такой форме, как оно используется в оригинале.
    Надоело отвечать на странные вопросы? Зарегистрируйтесь на сайте.
    Тип сообщения:
    Текст:
    Жирный шрифт  Наклонный шрифт  Подчеркнутый шрифт  Выравнивание по центру  Список  Заголовок  Разделительная линия  Код  Маленький шрифт  Крупный шрифт  Цитирование блока текста  Строчное цитирование
  • вопрос Круглого стола № XXX

  • вопрос № YYY в тесте № XXX Рыцарской Квинтаны

  • сообщение № YYY в теме № XXX Базарной площади
  • обсуждение темы № YYY Базарной площади
  •  
     Правила оформления сообщений на Королевстве

    Вопросы с аналогичными сообщениями об ошибках:
  • Access violation at address ... (776)


    Страница избранных вопросов Круглого стола.
  •   
    Время на сайте: GMT минус 5 часов

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

    Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
    Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

     
    © При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
    Все используемые на сайте торговые марки являются собственностью их производителей.

    Яндекс цитирования