SetWindowLongPtr возвращает ERROR_ACCESS_DENIED

Я все еще борюсь с крючками.

Моя цель:

  • Установить хук в notepad.exe
  • Подкласс его (моя конечная цель - создать подкласс класса Edit и показать содержимое в моем собственном окне)

Отказ от ответственности: я знаю, что есть более простые способы получить текст/содержимое из блокнота, но это способ для меня изучить C, winapi, подклассы и хуки.

Моя проблема в том, что SetWindowLongPtr всегда возвращать ошибку ERROR_ACCESS_DENIED (код 5).

22 мая 2013 г.: Исправлено! Проблема заключалась в том, что SetWindowLongPtr находился не в том месте. Он должен находиться внутри функции GetMsgProc.

Вопрос стал немного длинным и запутанным, поэтому я переписал вопрос (с обновленным кодом)

Теперь проблема заключается в том, что GetMsgProc НЕ вызывается, когда целью является notepad.exe. Если я изменю target на simple.exe, вызовет GetMsgProc и сработает!

(Simple.exe — это простой графический интерфейс):

Simple.exe

Код выглядит следующим образом:

exe.cpp

#include <windows.h>
#include "Resource.h"
#include <stdlib.h>
#include "stdafx.h"
#include <strsafe.h>

#include "C:\Users\Kristensen\Documents\Visual Studio 2012\Projects\Win32D\dll\dllHeader.h"

//---------------------------------------------------------------------------
HWND hWnd;

LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
//---------------------------------------------------------------------------
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLGFIRST),
        hWnd, reinterpret_cast<DLGPROC>(DlgProc));

    return FALSE;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        switch(wParam)
        {
        case IDOK:
            hookNotepad();
            return TRUE;
        case IDCANCEL:
            removeHook();
            EndDialog(hWndDlg, 0);
        }
        break;
    }

    return FALSE;
}
//---------------------------------------------------------------------------

dllHeader.h

    #ifdef DLLAPI
    #else
    #define DLLAPI extern "C" __declspec(dllimport)
    #endif
    DLLAPI bool hookNotepad();
    DLLAPI bool removeHook();

dll.cpp:

#include "stdafx.h"
#include <windows.h>
#define DLLAPI extern "C" __declspec(dllexport)
#include "dllHeader.h"

// shared variables
#pragma data_seg("Shared")
HHOOK g_hHook = NULL; // Hook for Notepad 
HWND npHWND = NULL; // Notepad handle
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")

// Forward references
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) ;
LRESULT CALLBACK NewWndProc(HWND Hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
//LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) ;


LONG OldWndProc; 
DWORD pid;
HINSTANCE g_hInstDll = NULL; // DllMain entry (DLL_PROCESS_ATTACH)
DWORD npThreadId = NULL; // Notepad thread ID

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) //Testing with CBTProc - same issues as with GetMsgProc.
{
    //If I hook notepad.exe, I never get called. (silence)

    //If I hook simple.exe, I get called (Beep beep!)

    // make some noise
    static DWORD dwTickKeep = 0;
    if ((GetTickCount()-dwTickKeep)>300)
    {   dwTickKeep = GetTickCount();
    Beep(2000, 100);
    }

    //Subclassing......
    //For simple.exe: (working)
    //HWND hwndEdit = ::FindWindowEx(npHWND,NULL,TEXT("WindowsForms10.RichEdit20W.app.0.2bf8098_r14_ad1"), NULL);
    //For notepad.exe: (not working)
    HWND hwndEdit = ::FindWindowEx(npHWND,NULL,TEXT("Edit"), NULL); 

    if (hwndEdit)
    {
        //Subclass it
        OldWndProc = GetWindowLongPtr(hwndEdit, GWLP_WNDPROC);
        SetWindowLongPtr(hwndEdit, GWL_WNDPROC, (LONG_PTR)NewWndProc);
    }
    return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}

BOOL APIENTRY DllMain( HMODULE hModule,  DWORD  ul_reason_for_call,   LPVOID lpReserved  )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hInstDll = hModule;
        break;

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

bool hookNotepad ()
{
    // If target is running
    // if (npHWND = FindWindow(NULL, TEXT("simpleGUI")))
    if (npHWND = FindWindow(TEXT("Notepad"), NULL))
    {
        // Finds the ThreadID for target. We use this in SetWindowsHookEx   
        npThreadId = GetWindowThreadProcessId(npHWND, &pid); 

        // Sets the hook in target
        g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInstDll, npThreadId); 
        //g_hHook = SetWindowsHookEx(WH_CBT, CBTProc, g_hInstDll, npThreadId); 

        // If the hook succesed
        if (g_hHook) 
        { 
            ////Add a menu in the notepad.exe, but not relevant for subclassing notepads edit class...
            //HMENU hCurrent = GetMenu(npHWND); //Get the CURRENT menu of the window.
            //HMENU hNew = CreateMenu(); //Create a new one. 
            //AppendMenu(hCurrent, MF_STRING | MF_POPUP, (unsigned int)hNew, TEXT("myMenu")); 
            //AppendMenu(hNew, MF_STRING, 2000, L"myButton"); //2000 is the ID of the new button. 
            //DrawMenuBar(npHWND); //redraw the Menu.

            //Force a msg to the messagequeue, so that the hook function(GetMsgProc) gets called
            PostThreadMessage(npThreadId, WM_NULL, 0, 0);
            return 1;
        }
        return 0;
    }
    else
        //Notepad is not running
        return 0;
}

bool removeHook()
{
    // Removes the hook
    if (g_hHook != NULL)
    {
        UnhookWindowsHookEx(g_hHook);
        g_hHook = NULL;
    }
    return 0;
}



LRESULT CALLBACK NewWndProc(HWND Hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{ 
    //We should come here and should be able to read the text from the Edit class...
    return CallWindowProc((WNDPROC)OldWndProc, Hwnd, Message, wParam, lParam); 
}

Любые подсказки, комментарии или советы высоко ценятся ...


person Theis Kristensen    schedule 19.05.2013    source источник
comment
Я не уверен, что это проблема, но я не думаю, что вам следует использовать общий раздел в вашей DLL. Успешен ли вызов SetWindowsHookEx?   -  person Harry Johnston    schedule 22.05.2013
comment
@HarryJohnston: Да, хук получает возвращаемое значение. Все работает, за исключением того, что GetMsgProc не вызывается (и здесь мне нужно установить SetWindowLongPtr для подкласса элемента управления). Таким образом, функция GetMsgProc должна быть частью адресного пространства noptepads...   -  person Theis Kristensen    schedule 22.05.2013
comment
Возможно, блокнот делает что-то неожиданное? Вы пробовали это в каких-либо других приложениях, например, в своей простой программе?   -  person Harry Johnston    schedule 22.05.2013
comment
@HarryJohnston: я пробовал это на Calc (та же проблема), но позже я попробую простую программу. Спасибо за ваш вклад до сих пор. Я вернусь :-)   -  person Theis Kristensen    schedule 22.05.2013
comment
@HarryJohnston: я попробовал это на простом графическом интерфейсе .exe. Здесь я отлично работаю, за исключением SetWindowLongPtr. Это дает необработанное исключение по адресу 0x76881F45 (usp10.dll) в simple.exe: 0xC0000005: место записи нарушения прав доступа 0x001C0FFC   -  person Theis Kristensen    schedule 22.05.2013


Ответы (2)


В вашем коде 3 проблемы:

(1) Переменная HWND npHWND предназначена для совместного использования между host-exe и notepad-exe, поэтому ее необходимо поместить в блок сегмента общих данных. Это значение в настоящее время оценивается внутри вызова «hookNotepad» и существует только в host-exe. Эта проблема привела к тому, что дескриптор npHWND был нулевым в notepad-exe, поэтому вызов SetWindowLongPtr не удался.

(2) Есть 2 вызова SetWindowLongPtr, один из них неправильный. Тот, что внутри GetMsgProc, правильный, потому что он будет выполняться внутри контекста notepad-exe, когда хук установлен. Удалите другой неправильный внутри hookNotepad.

(3) Даже если (1) и (2) разрешены, окончательное поведение SetWindowLongPtr может быть не таким, как вы ожидали, потому что основным интерактивным элементом пользовательского интерфейса notepad-exe является встроенный элемент управления редактированием, а не окно основного фрейма. Вы должны перечислить дочерние окна рамки блокнота и подкласса единственного дочернего окна с классом Edit.

Редактировать №1 — добавить код звукового индикатора для проверки активности ------------------------------------

Добавьте этот блок кода внутрь GetMsgProc

// make some noise
static DWORD dwTickKeep = 0;
if ((GetTickCount()-dwTickKeep)>300)
{   dwTickKeep = GetTickCount();
    Beep(2000, 100);
}
person mfc    schedule 22.05.2013
comment
Спасибо за ваш ответ и ваши действительные баллы. GetMsgProc по-прежнему не работает. Я попытался установить крючок в моей собственной простой программе, и я продвинулся немного дальше. Если я пытаюсь перехватить simple.exe, то вызывается GetMsgProc! Но если я добавлю функцию SetWindowLongPtr в функцию GetMsgProc (этот: OldWndProc = SetWindowLongPtr(npHWND, GWLP_WNDPROC, (long)NewWndProc);) ............ продолжение следует..... - person Theis Kristensen; 22.05.2013
comment
............ Я получаю сообщение об ошибке, как только моя мышь наводится на мой простой GUI. Программа зависает, и я получаю: Необработанное исключение win32 произошло в simple.exe». Когда я отлаживаю эту ошибку, я получаю следующее: Необработанное исключение по адресу 0x76881F45 (usp10.dll) в simple.exe: 0xC0000005: Место записи нарушения прав доступа 0x001C0FFC. На данный момент статус таков: * Я все еще не могу заставить GetMsgProc работать при подключении notepad.exe * Он работает, если я подключаю программу simple.exe. * SetWindowLongPtr дает нарушение прав доступа (в simple.exe) - person Theis Kristensen; 22.05.2013
comment
Если dll появляется в памяти блокнота, как вы указали, GetMsgProc должен работать. GetMsgProc очень загруженное место, так как все сообщения проходят через него. Если вам нужно сделать индикацию активности, она должна быть простой и короткой, так как длительная операция, такая как MessageBox, может привести к застреванию цикла сообщений. Смотрите, как я редактирую №1 для звукового индикатора. - person mfc; 22.05.2013
comment
Хороший момент со звуковым индикатором. Мой исходный вопрос обновлен, как и код. Все та же проблема... - person Theis Kristensen; 23.05.2013
comment
@TheisKristensen - Попробуйте удалить 2 экспортных определения DLLAPI GetMsgProc и NewWndProc в dll.h, эти функции не следует экспортировать, нужно только пару функций подключения/отключения. - person mfc; 23.05.2013
comment
Сделал - та же проблема. Нет шума в GetMsgProc. - person Theis Kristensen; 23.05.2013
comment
Странно, могут быть новые средства безопасности, добавленные в более новую ОС. На самом деле я проверил ваш код и изменил его, пока он не заработает (появится сообщение о щелчке), прежде чем я отвечу на вопрос, но я использую WinXP. Последнее предложение, отладьте hookNotepad и посмотрите, успешен ли каждый вызов API. - person mfc; 23.05.2013
comment
Если отладили «hookNotepad» и все вызовы успешны. Одна вещь, о которой я думал, это то, что я использую 64-разрядную версию Windows 7, и я думаю, что notepad.exe также является x64. Я не вносил никаких изменений в конфигурацию Visual Studio, поэтому я предполагаю, что программа win32 выполнена в 32-битной версии?? Но если это так, то SetWindowsHookEx НЕ должен быть успешным (но это так). - person Theis Kristensen; 23.05.2013
comment
Я думаю, логичный ответ — нельзя смешивать 32-битные и 64-битные бинарные модули в памяти одного процесса, они совершенно разные по структуре памяти. API хука/подкласса на самом деле просто перенаправляет вызов из исходной процедуры в новую, предоставленную пользователем, без каких-либо изменений в вызывающем интерфейсе. И зная, что соглашение о вызовах 32/64 бит отличается, это просто невозможно. Вы также можете проверить, работает ли ваш код с апплетами Windows, скопировав файл notepad.exe из 32-разрядной версии Win7. - person mfc; 23.05.2013
comment
Ваше право - проблема решена. Стандартный notepad.exe в win 7 — это 64-битное приложение. В папке C:\Windows\SysWOW64 есть другая версия notepad.exe, она 32-битная. Это также отображается в диспетчере задач. После запуска 32-битной версии блокнота все работает! Спасибо большое за помощь и время! - person Theis Kristensen; 23.05.2013

Вы должны сделать это в контексте подключенного процесса. Ваша функция hookNotepad() выполняется в другом процессе, и поэтому ваша функция WndProc() находится в другом адресном пространстве.

person Luke    schedule 19.05.2013
comment
Спасибо, я думал, что уже делаю это (?). Когда я вводил свой файл dll (с функцией hookNotepad()), я думал, что файл dll находится в процессе блокнота. Скриншот, который я приложил, показывает, что файл dll загружается в блокнот, верно? Можете ли вы привести пример того, как внедрить функцию hookNotepad() в правильное адресное пространство? - person Theis Kristensen; 19.05.2013
comment
Он загружается в notepad.exe, но вы по-прежнему вызываете SetWindowLongPtr() в hookNotepad(), который работает в вашем исполняемом файле перехватчика. Вам нужно вызвать SetWindowLongPtr() в контексте notepad.exe, что в данном случае можно сделать в хуке (что, похоже, вы уже делаете в MsgProc). - person Luke; 20.05.2013
comment
Проблема в том, что функция GetMsgProc никогда не вызывается. Я имею в виду, что 2 параметра SetWindowsHook — это GetMsgProc, которые являются указателем на процедуру ловушки. Эта процедура/функция должна вызываться, когда блокнот получает сообщение, отправленное в очередь сообщений (очередь сообщений блокнота), верно? Таким образом, любое сообщение блокнота (например, WM_ACTIVATE) должно запускать функцию GetMsgProc........ но это не так. - person Theis Kristensen; 20.05.2013