Чтение онлайн

на главную - закладки

Жанры

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:
ВОПРОС-ОТВЕТ

Q. Можно ли из моей программы управлять окном которое создано другим приложением (закрывать, сворачивать, нажимать в нем кнопки и т.д.), если да то как?

Alhim

A. Выполнение этой задачи распадается на два этапа.

Сначала нужно каким-то образом определить хэндл окна, которым мы собираемся манипулировать. Основным инструментом здесь являются функции FindWindow(Ex), которые ищут окно по заданному классу и/или заголовку. В определении и того, и другого сильно помогает программа Spy++. Рассмотрим пример поиска HWND стандартной кнопки "Пуск". Сначала используем Spy++, чтобы определить классы панели задач и самой кнопки; оказывается, их имена "Shell_TrayWnd" и "Button" соответственно. Затем используем FindWindow(Ex).

HWND hWnd;

hWnd = FindWindow("Shell_TrayWnd", NULL);

hWnd = FindWindowEx(hWnd, NULL, "Button", NULL);

if (IsWindow(hWnd)) {

 // Кнопка найдена, работаем с ней

}

Ещё один набор функций, которые могут помочь в поиске хэндла чужого окна – это EnumChildWindows, EnumThreadWindows и EnumWindows, перечисляющие все окна, принадлежащие заданному

окну, все окна заданного потока и все окна в системе соответственно. За описанием этих функций следует обратиться к документации.

Кроме перечисленного можно упомянуть случай, когда приложение специально проектируется для взаимодействие с другим посредством обмена сообщениями. Например, одно приложение запускает другое, а затем обменивается с ним данными посредством WM_COPYDATA. В этом случае вполне уместно передать хэндл окна (это 4-хбайтовое целое) как параметр командной строки.

После того, как хэндл окна определён, можно переходить ко второму этапу – управлению окном. Многие функции позволяют работать с окном, вне зависимости от того, какому процессу оно принадлежит. Характерные примеры таких функций – ShowWindow и SetForegroundWindow. Для примера рассмотрим, как спрятать кнопку "Пуск", получать хэндл которой мы уже научились.

HWND hWnd;

hWnd = FindWindow("Shell_TrayWnd", NULL);

hWnd = FindWindowEx(hWnd, NULL, "Button", NULL);

if (IsWindow(hWnd)) {

 ShowWindow(hWnd, SW_HIDE);

 Sleep(5000);

 ShowWindow(hWnd, SW_SHOW); // Показываем обратно

}

Кроме использования подобных функций, можно посылать окну сообщения. Например, послав кнопке BM_CLICK (с помощью PostMessage), мы как бы нажимаем на неё.

Проблемы возникают с функциями, которые позволяют работать только с окнами, созданными в том же потоке, в котором вызывается функция. В качестве примера приведу функцию DestroyWindow. Похожая проблема возникает, когда нужно "сабкласить" окно чужого процесса. В этих случаях необходимо внедрить свой код в чужой процесс и выполнить его в чужом потоке; удобнее всего сделать эт о, если код оформлен в виде DLL.

Существует несколько способов внедрить DLL в чужой процесс. Я покажу один из них; он достаточно прост и работает на всех Win32-платформах (Windows 9x, Windows NT), но в некоторых случаях недостаточно точен. Этот способ подразумевает установку хука на поток, создавший интересующее нас окно. При этом DLL, содержащая функцию хука, загружается системой в адресное пространство чужого процесса. Это как раз то, что нам нужно. А функцию хука вполне можно оставить пустой.

Рассмотрим пример DLL, которая уничтожает окно чужого процесса. (Такие вещи нужно делать, только полностью отдавая себе отчёт о возможных последствиях. Процесс, оставленный без окна, имеет хорошие шансы "рухнуть").

// _KillDll.cpp : Defines the entry point for the DLL application.

//

#include <windows.h>

// Создаём переменную в разделяемом сегменте,

// чтобы передать HWND из программы в DLL в чужом процессе.

#pragma comment(linker, "/SECTION:SHARED,RWS")

#pragma data_seg("SHARED")

__declspec(allocate("SHARED")) HWND hWndToKill = NULL;

#pragma data_seg

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {

 if (ul_reason_for_call == DLL_PROCESS_ATTACH &&

IsWindow(hWndToKill) &&

GetWindowThreadProcessId(hWndToKill, NULL) == GetCurrentThreadId) {

// Если окно существует и принадлежит текущему потоку, убиваем его.

HANDLE hEvent = OpenEvent(NULL, FALSE, "{1F6C5480-155E-11d5-93A8-444553540000}");

DestroyWindow(hWndToKill);

SetEvent(hEvent);

CloseHandle(hEvent);

 }

 return TRUE;

}

// Пустая функция хука.

LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {

 return 1;

}

extern "C" __declspec(dllexport) void KillWndNow(HWND hWnd) {

 if (!IsWindow(hWnd)) return;

 hWndToKill = hWnd;

 HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, "{1F6C5480-155E-11d5-93A8-444553540000}");

 DWORD dwThread = GetWindowThreadProcessId(hWnd, NULL);

 HHOOK hHook =

SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, GetModuleHandle("_KillDll.dll"), dwThread);

 PostThreadMessage(dwThread, WM_NULL, 0, 0);

 WaitForSingleObject(hEvent, INFINITE);

 CloseHandle(hEvent);

 UnhookWindowsHookEx(hHook);

}

Чтобы использовать эту DLL, просто подключите её к программе (проще всего сделать это неявным методом), а затем выполните код:

extern "C" void KillWndNow(HWND hWnd);

HWND hWnd;

// Ищем окно

KillWndNow(hWnd);

Хотя код DLL сам по себе и небольшой, в нём есть несколько тонкостей, на которые я хотел бы обратить ваше внимание. Во-первых, я поместил переменную hWndToKill в разделяемый сегмент. Поскольку функция DestroyWindow вызывается в потоке чужого процесса, необходимо предусмотреть некоторый способ передачи хэндла окна через границы процессов. Разделяемая переменная – наиболее простое средство достичь цели. Во-вторых, DLL, содержащая функцию хука, не будет спроектирована на адресное пространство чужого процесса, пока функция хука реально не понадобится. В нашем случае хук имеет тип WH_GETMESSAGE, а значит DLL не загрузится, пока поток не получит какое-либо сообщение. Поэтому я посылаю ему сообщение WM_NULL (с кодом 0), чтобы вынудить ОС загрузить DLL. В-третьих, обратите внимание на применение события для синхронизации потоков в нашем и целевом процессах. Разумеется, для этой цели можно использовать и любой другой механизм синхронизации потоков.

Александр Шаргин ([email protected])
 ПОИСКАХ
ИСТИНЫ

Q. Хотелось бы побольше узнать о предварительном просмотре. В русской программе он смотрится инородным телом на своем иностранном языке. Можно ли его как-то настраивать под себя?

В этой же связи: не могу решить проблему.

В программе 3 меню и, соответственно, 3 панели инструментов, которые создал в Create. Переключая меню, вызываю ShowControlBar – прячу ненужные панели и показываю необходимую. Но после вызова PRINT PREVIEW, в окне появляются сразу все 3 панели инструментов.

Попутно: что означает AFX_IDS_PREVIEW_CLOSE в String Table?

Serg Petukhov

Успехов!

Алекс Jenter [email protected] Красноярск, 2001.

Программирование на Visual C++

Выпуск №37 от 18 марта 2001 г.

Приветствую, уважаемые подписчики!

Сегодня нас ждет новая статья нашего постоянного автора Александра Шаргина, на этот раз посвященная стандартной библиотеке шаблонов C++.

СТАТЬЯ

Введение в STL

Часть 1

Автор: Александр Шаргин

[email protected] 

Стандартная библиотека шаблонов (Standard Template Library, STL) входит в стандартную библиотеку языка "C++". В неё включены реализации наиболее часто используемых контейнеров и алгоритмов, что избавляет программистов от рутинного переписывания их снова и снова. При разработке контейнеров и применяемых к ним алгоритмов (таких как удаление одинаковых элементов, сортировка, поиск и т. д.) часто приходится приносить в жертву либо универсальность, либо быстродействие. Однако разработчики STL поставили перед собой сверхзадачу – сделать библиотеку одновременно эффективной и универсальной. Надо признать, что им удалось достичь цели, хотя для этого и пришлось использовать наиболее продвинутые возможности языка C++, такие как шаблоны и перегрузка операторов.

В этой статье мы рассмотрим основные концепции, которые легли в основу STL. Я не буду приводить подробных примеров использования векторов, списков и ассоциативных массивов, так как их хватает в каждом учебнике. Вместо этого я постараюсь показать, как устроена STL, для чего нужны её основные компоненты и как они взаимодействуют друг с другом, а также как расширить стандартную библиотеку, добавив в неё новые контейнеры и алгоритмы.

Стандарт языка C++ не регламетнирует реализацию контейнеров и алгоритмов STL. Поэтому с каждым компилятором поставляется своя реализация этой библиотеки. В последующем изложении я буду опираться на реализацию, поставляемую фирмой Microsoft вместе с компилятором Visual C++ 6.0. Тем не менее, большая часть сказанного будет справедлива и для других реализаций STL.

Основные концепции STL

Краеугольными камнями STL являются понятия контейнера (container), алгоритма (algorithm) и итератора (iterator).

• Контейнер – это хранилище объектов (как встроенных, так и определённых пользователем типов). Простейшие виды контейнеров (статические и динамические массивы) встроены непосредственно в язык C++. Кроме того, стандартная библиотека включает в себя реализации таких контейнеров, как вектор (vector), список (list), очередь (deque), ассоциативный массив (map), множество (set), и некоторых других.

• Алгоритм – это функция для манипулирования объектами, содержащимися в контейнере. Типичные примеры алгоритмов – сортировка и поиск. В STL реализовано порядка 60 алгоритмов, которые можно применять к различным контейнерам, в том числе к массивам, встроенным в язык C++.

• Итератор – это абстракция указателя, то есть объект, который может ссылаться на другие объекты, содержащиеся в контейнере. Основные функции итератора – обеспечение доступа к объекту, на который он ссылается (разыменование), и переход от одного элемента контейнера к другому (итерация, отсюда и название итератора). Для встроенных контейнеров в качестве итераторов используются обычные указатели. В случае с более сложными контейнерами итераторы реализуются в виде классов с набором перегруженных операторов.

Рассмотрим эти концепции более подробно.

Итераторы

Итераторы используются для доступа к элементам контейнера так же, как указатели – для доступа к элементам обычного массива. Как мы знаем, в языке C++ над указателями можно выполнять следующий набор операций: разыменование, инкремент/декремент, сложение/вычитание и сравнение. Соответственно, любой итератор реализует все эти операции или некоторое их подмножество. Кроме того, некоторые итераторы позволяют работать с объектами в режиме "только чтение" или "только запись", тогда как другие предоставляют доступ и на чтение, и на запись. В зависимости от набора поддерживаемых операций различают 5 типов итераторов, которые приведены в следующей таблице.

Тип итератора Доступ Разыменование Итерация Сравнение
Итератор вывода (output iterator) Только запись * ++  
Итератор ввода (input iterator) Только чтение *, –> ++ ==, !=
Прямой итератор (Forward iterator) Чтение и запись *, –> ++ ==, !=
Двунаправленный итератор (bidirectional iterator) Чтение и запись *, –> ++, -- ==, !=
Итератор с произвольным доступом (random-access iterator) Чтение и запись *, –>, [] ++, --, +, –, +=, –= ==, !=, <, <=, >, >=

Итератор с произвольным доступом реализует полный набор операций, применимых к обычным указателям.

Контейнеры

Как мы уже знаем, контейнер предназначен для хранения объектов. Хотя внутреннее устройство контейнеров очень сильно различается, каждый контейнер обязан предоставить строго определённый интерфейс, через который с ним будут взаимодействовать алгоритмы. Этот интерфейс обеспечивают итераторы. Каждый контейнер обязан иметь соответствующий ему итератор (и только итератор). Важно подчеркнуть, что никакие дополнительные функции-члены для взаимодействия алгоритмов и контейнеров не используются. Это сделано потому, что стандартные алгоритмы должны работать в том числе со встроенными контейнерами языка C++, у которых есть итераторы (указатели), но нет ничего, кроме них. Таким образом при написании собственного контейнера реализация итератора – необходимый минимум.

Поделиться:
Популярные книги

Газлайтер. Том 8

Володин Григорий
8. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 8

На Ларэде

Кронос Александр
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
На Ларэде

Охота на попаданку. Бракованная жена

Герр Ольга
Любовные романы:
любовно-фантастические романы
5.60
рейтинг книги
Охота на попаданку. Бракованная жена

Кай из рода красных драконов

Бэд Кристиан
1. Красная кость
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Кай из рода красных драконов

Хозяйка Проклятой Пустоши. Книга 2

Белецкая Наталья
2. Хозяйка Проклятой Пустоши
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Хозяйка Проклятой Пустоши. Книга 2

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Потусторонний. Книга 2

Погуляй Юрий Александрович
2. Господин Артемьев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Потусторонний. Книга 2

Чапаев и пустота

Пелевин Виктор Олегович
Проза:
современная проза
8.39
рейтинг книги
Чапаев и пустота

Солнечный корт

Сакавич Нора
4. Все ради игры
Фантастика:
зарубежная фантастика
5.00
рейтинг книги
Солнечный корт

Лютая

Шёпот Светлана Богдановна
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Лютая

Ведьмак (большой сборник)

Сапковский Анджей
Ведьмак
Фантастика:
фэнтези
9.29
рейтинг книги
Ведьмак (большой сборник)

Наследие Маозари 4

Панежин Евгений
4. Наследие Маозари
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Наследие Маозари 4

Ученик

Губарев Алексей
1. Тай Фун
Фантастика:
фэнтези
5.00
рейтинг книги
Ученик

Начальник милиции. Книга 5

Дамиров Рафаэль
5. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции. Книга 5