Пример внедрения DLL
Допустим, Вы хотите создать подкласс от экземпляра окна, порожденного другим процессом. Это, как Вы помните, позволит изменять поведение окна Все, что от Вас для этого требуется, — вызвать функцию SetWindowLongPtr, чтобы заменить адрес оконной процедуры в блоке памяти, принадлежащем окну, новым — указывающим на Вашу функцию WndProc. В документации Platform SDK утверждастся, что приложение
не может создать подкласс окна другого процесса Это не совсем верно Проблема создания подкласса окна из другого процесса па самом деле сводится к преодолению границ адресного пространства
Вызывая SetWindowLongPtr для создания подкласса окна (как показано ниже), Вы говорите системе, что все сообщения окну, на которое указывает hwnd, следует направлять не обычной оконной процедуре, а функции MySubclassProc
SetWindowLongPtr(hwnd, GWLP_WNDPROC, MySubclassProc);
Иными словами, когда системе надо передать сообщение процедуре WndProc указанного окна, она находит ее адрес и вызывает напрямую В нашем примере система видит, что с окном сопоставлен адрес функции MySubclassProc, и поэтому вызывает именно ее, а нс исходную оконную процедуру
Проблема с созданием подкласса окна, принадлежащего другому процессу, состоит в том, что процедура подкласса находится в чужом адресном пространстве Упрощенная схема приема сообщений оконной процедурой представлена па рис 22.1 Процесс А создает окно На адресное пространство этого процесса проецируется файл User32.dlI Эта проекция User32.dll О1вечает за прием и диспетчеризацию сообщений (синхронных и асинхронных), направляемых любому из окон, созданных потоками процесса А Обнаружив кякое-то сообщение, она определяет адрес процедуры WndProc окна и вызывает ее, передавая описатель окна, сообщение и параметры wParam и lParam Когда WndProc обработает сообщение, Uscr32 dll вернется в начало цикла и будет ждать следующее оконное сообщение
Рис. 22-1. Поток процесса В пытается создать подкласс окна, сформированного потоком процесса А
Теперь допустим, что процесс В хочет создать подкласс окна, порожденного одним из потоков процесса А Сначала код процесса В должен определить описатель этого окна, что можно сделать самыми рязными способами В примере на рис 22-1 поток процесса В просто вызывает FindWindow, затем — SetWtndowLongPtr, пытаясь изменить адрес процедуры WndProc окна Обратитевниманис пытаясь Этот вызов не дает ничего, кроме NULL Функция SetWindowLongPtr просто проверяет, не хочет
ли процесс изменить адрес WndProc окна, созданного другим процессом, и,если да, игнорирует вызов
А если бы функция SetWindowLongPtr могла изменить адрес WndProc? Система тогда связала бы адрес процедуры MySubclassProc с указанным окном. Затем при посылке сообщения этому окну код User32 в процессе А извлек бы данное сообщение, получил адрес MySubclassProc и попытался бы вызвать процедуру по этому адресу. Но это привело бы к крупным неприятностям, так как MySubclassProc находится в адресном пространстве процесса В, а активен — процесс А. Очевидно, если бы User32 обратился по данному адресу, то на самом деле он обратился бы к какому-то участку памяти в адресном пространстве процесса А, что, естественно, привело бы к нарушению доступа к памяти
Чтобы избежать этого, было бы неплохо сообщить системе, что MySubclassProc находится в адресном пространстве процесса В, и тогда она переключила бы контекст перед вызовом процедуры подкласса Увы, по ряду причин такая функциональность в системе не реализована.
Поскольку удачных решений этих проблем нет, Microsoft предпочла запретить функции SeiWindowLongPtr замену процедуры окна, созданного другим процессом.
Тем не менее порождение подкласса окна, созданного чужим процессом, возможно: нужно просто пойти другим путем. Ведь на самом деле проблема не столько в создании подкласса, сколько в закрытости адресного пространства процесса. Если бы Вы могли как-то поместить код своей оконной процедуры в адресное пространство процесса А, это позволило бы вызвать SetWindowLongPtr и передать ей адрес MySubclassProс, в процессе А. Я называю такой прием внедрением (injecting) DLL в адресное пространство процесса. Мне известно несколько способов подобного внедрения Рассмотрим их по порядку, начиняя с простейшего.