Мета, Програми та розповсюдженний програм, C / C + +, статті

Написати дуже швидку і маленьку програму, що приховує за 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>

*

*