Мета

Написати дуже швидку і маленьку програму, що приховує за CTRL + F12 задані
вікна. При натисканні комбінації CTRL + F10 вона повинна показати заховані вікна.
Вхідні дані:

 TXT Файл виду
————
Internet Explorer
The Bat!
Visual C++
911
————

Якщо будуть знайдені вікна, що містять у своєму заголовку зазначені рядки, вони
будуть заховані.
У зазначених вище прикладі будуть заховані всі вікна IE, вікно
Microsoft Visual C + +, вікно поштової програми "The Bat!" і всі вікна, в
заголовках яких міститься комбінація символів "911".

Отже, писати будемо на чистому Win32 API. Створимо вікно, прив'яжемо до нього
гарячі клавіші. На вимогу будемо здійснювати перебір видимих вікон у системі
і в заголовку кожного будемо шукати задані комбінації символів.


Опції лінкера

  

Якщо нічого не робити, то нам не вдасться отримати у результаті файл менш
32 КБ (приблизно). Тому пишемо:

#pragma comment(linker,"/MERGE:.rdata=.text")
# Pragma comment (linker, "/ FILEALIGN: 512 / SECTION:. Text, EWRX / IGNORE: 4078")
#pragma comment(linker,"/ENTRY:New_WinMain")
#pragma comment(linker,"/NODEFAULTLIB")

На що тепер варто звернути особливу увагу? Зазвичай, точка входу в програму
виглядає так:

 int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nCmdShow)

(До речі, для Win32 додатків другий параметр завжди NULL)

Але (!)… Так як ми відключили "Runtime library", нам тепер передається в цих
параметрах різне сміття. Тому називаємо точку входу не WinMain а New_WinMain,
яку оголосимо, як void New_WinMain (void), щоб не забути про те, що нам
нічого не передається. А параметр HINSTANCE отримуємо функцією
GetModuleHandle (NULL). Ах так, і виходити з програми будемо функцією
ExitProcess.

Тепер якщо зібрати нашу порожню програмку, яка нічого робити не буде,
розмір її буде 1 Кб. Але нам потрібно ще дописати 3 Кб коду. Продовжимо.

Щоб все подальше було зрозуміло навіть новачкові в програмуванні під
Windows, я прокоментую все.

Оголосимо деякі константи


Це знадобиться для реєстрації "гарячих" клавіш функцією RegisterHotKey.

#define HOTKEYHIDE 1
#define HOTKEYSHOW 2

Розмір буффера, куди буде зчитуватися заголовок вікна функцією
GetWindowText.

#define SSZZ 256

Розмір буфера, куди буде зчитуватися файл зі стоками фільтрації
(Використовується в оголошенні char FilterStrings [MAXFIL];)

#define MAXFIL 1024

(Примітка: При бажанні можна зробити і виділення пам'яті динамічно – знайти
файл, дізнатися його розмір і виділити блок. Приблизний приклад:

// …………………
WIN32_FIND_DATA FindData;
HANDLE hFind=FindFirstFile(szFilterStringsFile,&FindData);
if (hFind!=INVALID_HANDLE_VALUE)
{
i = (FindData.nFileSizeHigh * MAXDWORD) + FindData.nFileSizeLow;
HGLOBAL hGA=GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE,i+1);
// (+ end-ZERO)
if (hGA!=NULL)
{
LPVOID lpStrings=GlobalLock(hGA);
DWORD dw;
if (lpStrings! = NULL) ReadFile (hFile, lpStrings, i, & dw, NULL);
}

}
FindClose(hFind);
CloseHandle(hFile);
// ………………………….
/ / Але так як навряд чи файл настройок у нас буде більше одного
/ / Кілобайт, я залишив статичний масив.
)


Задамо глобальні змінні


Масив хендлов вікон (навряд чи буде у нас понад 300 вікон)

HWND aHwnd[300];

Кількість ініціалізованих елементів у цьому масиві

unsigned int cHwnd=0;

Дескриптори вікон – головне і два дочірніх – кнопка "Hide" і кнопка "Edit
filter strings”

HWND hwndMain, hwndButtonHide, hwndButtonEditFilter;

Тут буде щось типу "c: programswinhiderwinhider.settings.txt"

char szFilterStringsFile [MAX_PATH] = "(з) 2002 KMiNT21";

Відповідно, хендл файлу з ім'ям "щось типу"

HANDLE hFile;

А це місце, куди будемо зчитувати всі з цього файлу

char FilterStrings[MAXFIL];

Функції


Обробка повідомлень головного вікна

 LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

Функція, яка буде викликатися для кожного вікна при переборі всіх вікон

static BOOL FAR PASCAL my_EnumWindowsProc (HWND hwnd, DWORD lParam);

Перевірка наявності рядка str2 в str1

BOOL Contain(char* str1, char* str2);

Переховування з екрану чергового вікна

inline void HideNext (HWND hwnd) {ShowWindow (aHwnd [cHwnd + +] = hwnd, SW_HIDE);}

Повернення всіх захованих вікон на екран

inline void ShowAll (void) {while (cHwnd) ShowWindow (aHwnd [- cHwnd], SW_SHOW);}

Пройдемося по головним рядках функції NewWinMain


* Отримаємо INSTANCE модуля. Це нам потрібно для реєстрації віконного класу

HINSTANCE hInst=GetModuleHandle(NULL);

* Зареєструємо віконний клас

 WNDCLASS wc;
wc.style = CS_HREDRAW|CS_VREDRAW ;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
wc.hInstance = hInst;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wc.lpszClassName = "CKMINT21WINDOWSHIDERPRO";
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wc.lpszMenuName=NULL;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
if (! RegisterClass (& wc)) MessageBox (0, "I can" t register window
class. "," Error: ", 0), ExitProcess (0);


Створюємо головне вікно програми

hwndMain = CreateWindow (wc.lpszClassName, "Small windows hider!",
WS_BORDER|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
CW_USEDEFAULT,0,291,180, NULL, NULL, hInst, NULL);

І поміщаємо на нього дві кнопки. Як бачимо, кнопки мають клас "BUTTON". Вони
є дочірніми вікна hwndMain.

hwndButtonHide = CreateWindow ("BUTTON", "Hide!", WS_VISIBLE | WS_CHILD,
10,10,261,90, hwndMain, NULL, hInst, NULL);
ShowWindow (hwndButtonHide, SW_SHOW), UpdateWindow (hwndButtonHide);
hwndButtonEditFilter = CreateWindow ("BUTTON", "Edit filters",
WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP, 10,110,261,30,
hwndMain, NULL, hInst, NULL);
ShowWindow (hwndButtonEditFilter, SW_SHOW), UpdateWindow (hwndButtonEditFilter);

Нарешті, показуємо головне вікно

ShowWindow(hwndMain,SW_SHOW), UpdateWindow(hwndMain);

Примітка: Так як хтось цього може не знати, хочу відзначити, що в
мові С + + є "операція слідування" – кома. Тобто просто послідовно
виконаються обидві функції ShowWindow і UpdateWindow (як окремий блок). У
вищевказаної рядку можна було б і просто поставити ";", а взагалі іноді це
допомагає позбутися від величезної кількості фігурних дужок {}, в тексті
програми.


* Потім реєструємо в системі HotKeys. Вони будуть прив'язані до головного вікна,
якому будуть передаватся повідомлення WM_HOTKEY.

RegisterHotKey(hwndMain,HOTKEYHIDE,MOD_CONTROL,VK_F12)
RegisterHotKey(hwndMain,HOTKEYSHOW,MOD_CONTROL,VK_F10)

Потім зчитуємо налаштування з файлу і запускаємо головний цикл обробки віконних
повідомлень для поточного процесу.

MSG  msg;
while (GetMessage (& msg, NULL, 0,0)) TranslateMessage (& msg),
DispatchMessage(&msg);

Віконна процедура

 / / Тут все досить стандартно. Робимо switch (msg).
// …
case WM_HOTKEY:
if (HOTKEYSHOW == (int)wParam)
/ / Показуємо все, що ми до цього ховали, а так само головне
/ / Вікно програми
ShowAll(), ShowWindow(hwnd,SW_SHOW);

if (HOTKEYHIDE == (int)wParam)
/ / Приховуємо наше головне вікно і запускаємо перебір всіх вікон у
/ / Системі – EnumWindows. Тепер буде викликатися функція
/ / My_EnumWindowsProc для кожного виявленого в системі вікна.
ShowWindow (hwnd, SW_HIDE), EnumWindows ((int (__stdcall *) (struct
HWND__ *, long)) my_EnumWindowsProc, 0);
break;
// …

/ / Якщо програму намагаються мінімізувати, просто приховуємо її
// …………………….
case WM_SYSCOMMAND:
if (SC_MINIMIZE == wParam) {ShowWindow (hwnd, SW_HIDE); return 0;}
break;
/ / Увага, після ShowWindow (hwnd, SW_HIDE) ми пишемо return 0,
/ / Замість break. Чому? Та тому що не хочемо, щоб це
/ / Повідомлення пішло далі у систему. Ми його вже обробили
/ / По-своєму.
// …
/ / А потім обробляємо натискання на кнопки.
case BN_CLICKED:
if (hwndButtonHide == (HWND) lParam) ShowWindow (hwndMain, SW_HIDE);
if (hwndButtonEditFilter == (HWND) lParam) ShellExecute (NULL, "open",
szFilterStringsFile, NULL, NULL, SW_SHOWMAXIMIZED);
break;


Розглянемо функцію my_EnumWindowsProc


Пропустимо всі невидимі вікна

if (!IsWindowVisible(hwnd)) return TRUE;

Отримаємо TITLE чергового вікна

GetWindowText(hwnd, szWindowsTitle, SSZZ)

Потім перебираємо всі стоки з файлу налаштувань

 for (i = 0; i / / якщо це початок рядка, то
{
if (Contain (szWindowsTitle, FilterStrings + i)) HideNext (hwnd);
/ / Затаїмо вікно, якщо цей рядок міститься в szWindowsTitle
while(FilterStrings[i]) i++;
/ / Змістимо покажчик на наступний 0
}

Продовжуємо подальше перебір вікон

return TRUE;

(Якби було return FALSE, перебір би закінчився.)

В інших функціях особливо описувати нічого.


FAQ, що виник в результаті безлічі заданих мені запитань.


Q: Чому програма не лінкуются?

A: Спробуйте зібрати не debug, а release версію. А якщо вам потрібна
можливість налагодження, скористайтеся звичайними # define. І все-таки є ще одні
варіант. У налагоджувальної версії лінкер не може зібрати файл тому, що не знаходить
"__chkesp", Яка міститься в "CHKESP.OBJ". Що ми можемо зробити? Та взяти і
замінити той obj на свій, який буде менше розміром і не буде містити
непотрібний нам код.

Q: Як тепер отримати передану командний рядок?

A: Ну тут все просто. Користуйтеся стандартними API. Те ж саме і для
Instance додатки. Ось вони – GetCommandLine, GetModuleHandle.

Q: А який мінімальний align можливий?

A: Для того, щоб ваша програма запускалася нормально в будь-якої версії
Windows, використовуйте 512 байт.

Q: А чи можна робити такими маленькими DLL?

A: Так. Призначте свою точку входу замість _DllMainCRTStartup. Ось приклад.

Q: А чому зникли функції strcmp, strlen і т.п.?

A: Так як вони були реалізовані в RTL, тепер ви не можете їх використовувати.
Але це не біда. У модулі kernel є відмінна заміна цих функцій. Назви ті
ж, але з буквою "l" спочатку. Наприклад – lstrlen, lstrcmp, lstrcat.

Q: А тепер стали недоступні функції роботи з пам'яттю – memset,
CopyMemory?

A: RTL сам надає інтефейс для роботи з пам'яттю. По-перше, щоб
дотримуватися снандарт, по-друге, щоб спростити роботу з пам'яттю в середовищі Win32.
Ось подивіться на функцію CopyMemory – вона насправді не є справжньою
API функцією. Спробуйте злінкувати проект без RTL, в якому використовується ця
функція. Результат – невдала спроба лінковки – посилання на _memcpy. Ще один
приклад – функція new. У середовищі Win32 ви повинен скористатися функціями
GlobalAlloc, GlobalLoc і т.п. Однак ви можете просто замінити RTL функції
своїми. У файлі add.txt ви можете взяти вже готові функції, якщо не хочете
писати їх самі.

Q: У мене є одне питання, якого немає в цьому FAQ, що робити?

A: У такому випадку ви можете задати питання на нашому форумі. www.uinc.ru/forum/


Links:


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*