Программирование на C++ глазами хакера

         

Безумная мышка


Как сделать мышку безумной? Очень даже просто:

for (int i=0; i20; i++) { SetCursorPos(rand()%640, rand()%480); Sleep (100); }

Здесь запускается цикл от 0 до 20, в котором указатель мышки переносится в случайную позицию с помощью функции SetCursorPos, которой нужно передать два параметра X и Y в виде целых чисел — координаты новой позиции курсора. Параметры формируются с помощью функции rand() в диапазоне от нуля до числа, указанного после знака %. В реальной программе желательно определять разрешение экрана (ширину и высоту) с помощью функции GetSystemMetrics с параметрами SM_CYSCREEN и SM_CXSCREEN, но в примередля наглядности я ограничился разрешением 640x480.

После каждого перемещения делается задержка в 20 секунд, чтобы пользователь успел заметить указатель в новом положении. Таким образом, мышка совершит 20 прыжков по экрану.



Блокировка Рабочего стола


Работа с Windows начинается с Рабочего стола, а это тоже окно со всеми вытекающими отсюда последствиями. Чтобы получить его указатель, надо воспользоваться функцией GetDesktopWindow. Рассмотрим несколько примеров, с помощью которых можно пошутить, используя Рабочий стол.

HWND h=GetDesktopWindow();

EnableWindow(h, FALSE);

В первой строке кода мы получаем указатель на окно, а во второй — делаем его неактивным. Попробуйте выполнить этот код в своей программе, и вы заблокируете Windows. Жаль, что блокировка не полная, и с помощью нажатия клавиш Ctrl+Alt+Del откроется Диспетчер задач, после чего блокировка исчезнет. Но если поместить этот код в бесконечный цикл или в цикл обработки сообщений, то Windows исчезнет "навсегда".

Примечание
Исходный код примера , описанного в этом разделе , вы можете найти на компакт - диске в каталоге \Demo\Chapter2\DesktopWindow.





Исчезновение чужой программы


Как работать с чужими окнами, мы еще подробно рассмотрим в следующих главах книги. Но все же я приведу вам один интересный пример с исчезновением чужих программ:

HWND Wnd; while(true) { Wnd=GetForegroundWindow(); if (Wnd0) ShowWindow(Wnd,SW_HIDE); Sleep (1000); };

В этом примере запускается бесконечный цикл while, внутри которого выполняются следующие шаги:

получаем идентификатор активного окна с помощью функции GetForegroundWindow;

прячем окно с помощью функции ShowWindow, если идентификатор "правильный" (больше нуля);

делаем задержку в 1 секунду на реакцию пользователя.

Если выполнить этот код, то любое активное окно исчезнет максимум через одну секунду. Даже если попытаться снять задачу, которая выполняет этот код, то за одну секунду вы не успеете вызвать Панель задач, найти программу и снять ее, т. к. уже исчезнет Диспетчер задач. Невозможно нажать на кнопку Пуск и завершить работу, потому что исчезнет сама Панель задач, которая в данный момент станет активной.

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



Изменчивый указатель


Есть такая интересная WinAPI-функция — SetSystemCursor. У нее есть два параметра:

курсор, который надо изменить. Чтобы восстановить системный курсор, можно использовать фуцию GetCursor;

вид курсора, который нужно установить. Здесь можно указать одно из следующих значений:

OCR_NORMAL — стандартный курсор (по умолчанию);

OCR_IBEAM — курсор, используемый для выделения текста;

OCR_WAIT — большие песочные часы (ожидание);

OCR_CROSS — фафическое выделение (крест);

OCR_UP — стрелка вверх;

OCR_SIZE — курсор изменения размера;

OCR_ICON — значок;

OCR_SIZENWSE или OCR_SIZENESW — курсор, используемый для растяги-вания объекта;

OCR_SIZEWE — курсор для горизонтального изменения размера;

OCR_SIZENS — курсор для вертикального изменения размера;

OCR_SIZEALL — курсор для одновременного изменения размера по горизонтали и вертикали;

OCR_SIZENO — интернациональный несимвольный курсор;

OCR_APPSTARTING — маленькие песочные часы (загрузка приложения).

И сразу приведу небольшой пример изменения текущего курсора:

SetSystemCursor(GetCursor(), OCR_CROSS);

Этот код изменяет текущий курсор на крестик, который используется при графическом выделении.

Примечание
Все примеры , описанные в этом разделе, вы можете найти на компакт - диске в каталоге \Demo\Chapter2\JokesWinMouse.



Как программно потушить монитор?


Не знаю, как программно, а огнетушителем тушиться за пять сек :). Я даже помню, как в детстве получил значок юного огнетушителя, тфу, пожарника :).

А если серьезно, то системная команда "на тушение" выглядит так:

SendMessage(hWnd, WM_SYSCOMMAND, SC_MONITORPOWER, 0);

Чтобы "зажечь", измените последний параметр на -1.



Летающие объекты


В разд. 2.2 мы рассматривали пример, в котором программно щелкали по кнопке Пуск. Тогда это было определенное окно, поэтому задача упрощалась. Поставим задачу шире — щелкнуть в любой области экрана. Для этого потребуется определить, какое в этом месте окно. Этот вопрос тоже решается достаточно просто:

for (int i=0; i20; i++) { POINT pt = {rand()%800, rand()%600}; SetCursorPos(pt.x, pt.y); Sleep(100);

HWND hPointWnd = WindowFromPoint(pt); SendMessage(hPointWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(pt.x,pt.y)); SendMessage(hPointWnd, WM_LBUTTONUP, 0, MAKELONG(pt.x,pt.y)); }

В этом примере, как и в задаче с мышкой, мы случайным образом генерируем две координаты и сохраняем их в параметрах х и у структуры pt. Потом изменяем положение курсора в соответствии с полученными координатами.

На следующем шаге определяем окно, которое находится в этой позиции. Для этого существует функция WindowFromPoint, которой нужно передать один параметр — структуру типа POINT с хранящимися в ней координатами искомой точки. Функция вернет указатель на это окно.

Теперь отправляем два сообщения уже знакомым способом. В первом случае второй параметр равен WM_LBUTTONDOWN (когда нажата левая кнопка мыши), а во втором — WM_LBUTTONUP (когда отпущена). Почему здесь два события? Если кнопка Пуск реагирует на нажатия, то программы чаще всего обрабатывают полное нажатие (Click) или событие, когда отпущена кнопка. Поэтому желательно отправлять оба события.

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

Можно сразу немного изменить пример:

for (int i =0; i 20; i ++) { // Устанавливаем случайную позицию курсора POINT pt = {rand()%800, rand()%600}; SetCursorPos(pt.x, pt.y); Sleep(100);

// Посылаем сообщение о нажатии кнопки мыши HWND hPointWnd = WindowFromPoint(pt); SendMessage(hPointWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(pt.x,pt.y));

// Изменение позиции курсора POINT pt1 = {rand()%800, rand()%600}; SetCursorPos(pt1.x, pt1.y);

SendMessage(hPointWnd, WM_MOUSEMOVE, 0, MAKELONG(pt1.x, pt1.y));

// Отпускаем кнопку мышки SendMessage(hPointWnd, WM_LBUTTONUP, 0, MAKELONG(pt1.x, pt1.y)); }

Здесь между событиями нажатия и отпускания кнопки мы генерируем новые координаты, перемещаем туда мышку и отпускаем ее. Перед тем как послать сообщение о том, что кнопка отпущена, мы отправляем сообщение WM_MOUSEMOVE, которое заставляет программу отработать перемещение указателя мыши. Таким образом, нажали в одном месте, а отпустили в другом. И если в месте нажатия был объект, который можно перетащить, то он перелетит в новое место (получается программный Drag Drop).



Летающий Пуск


Вспоминаю, как я первый раз увидел Windows 95. Мне так понравилась кнопка Пуск, что я полюбил ее до глубины Выключить компьютер. Вскоре в нашем институте обновили парк машин, и на них тоже поставили Windows 95. Мне так захотелось подшутить над своими однокурсниками, что я решил написать программку, которая подбрасывала бы кнопку Пуск.

Сказано — сделано, написал (на Delphi ) и запустил на всех машинах. С каждым взлетом кнопки Пуск ламеры испуганно взлетали вместе с ней. А через некоторое время я увидел и в Интернете подобный прикол.

Сейчас я повторю свой старый подвиг и покажу вам, как самому написать такую программу с использованием Visual C++. Так что усаживайтесь поудобнее, наша кнопка Пуск взлетает на высоту 100 пикселов!

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

Но прежде чем начать, нужно подготовить картинку с изображением кнопки Пуск. Для этого вы можете нарисовать ее своими руками в любом графическом редакторе. Ну, а если вы IBM-совместимый человек, то можете нажать клавишу PrintScrn (или PrintScreen), чтобы запомнить изображение экрана в буфере обмена, а потом выполнить вставку содержимого буфера в любом графическом редакторе. Далее простыми манипуляциями вырезать изображение кнопки и сохранить его в отдельном файле.

У меня получилась картинка размером 50x20, и вы найдете ее на компакт-диске в каталоге Demo/Chapter2/Start Button/Start.bmp. Можете воспользоваться этим файлом.

Создайте новый проект в Visual C++ типа Win32 Project с именем Start Button и добавьте в него нашу картинку. Для этого откройте ресурсы, дважды щелкнув по файлу Start Button.rc. Перед вами откроется окно с деревом ресурсов ( 2.1).

Рис. 2.1. Окно просмотра ресурсов

Щелкните в этом окне правой кнопкой мыши и в появившемся выпадающем меню выберите пункт Add resource . Вы должны увидеть окно добавления ресурсов с возможностью выбора типа создаваемого ресурса ( 2.2). В этом окне выберите пункт Bitmap и нажмите кнопку New.




Рис. 2.2. Окно выбора типа создаваемого ресурса

В этом разделе будет создан новый ресурс для хранения изображения. Под окном просмотра ресурсов вы можете видеть окно свойств изображения ( 2.3). Здесь нужно первым делом изменить свойство Colors (Количество цветов), установив значение Color или True Color. Ширину и высоту (свойства Width и Height) нужно указать в соответствии с вашей картинкой.

Откройте изображение кнопки Пуск в любом графическом редакторе (например, Paint) и скопируйте его в буфер обмена (чаще всего для этого нужно выделить изображение и выбрать меню Edit/Copy). Вернитесь в редактор Visual C++ и выполните команду Edit/Paste. Вы должны увидеть нечто похожее на 2.4.

Теперь переходим непосредственно к программированию. На этом примере мы рассмотрим некоторые приемы, которые будем использовать в дальнейшем достаточно часто.

Откройте файл Start Button.cpp. Для этого найдите его в окне Solution Explorer в разделе Source Files и дважды щелкните на строке с именем. В самом начале файла найдите раздел глобальных переменных, который начинается с комментария Global Variables: После этого комментария добавим две переменные:

// Global Variables:

HWND hWnd;

HBITMAP startBitmap;



Рис. 2.З. Окно свойств изображения



2.4. Изображение кнопки Пуск в редакторе ресурсов

Первая переменная — hWnd — имеет тип HWND, который используется для хранения указателей на окна. В ней мы и сохраним указатель на созданное в примере окно, чтобы в любой момент можно было получить к нему доступ. Вторая переменная — startBitmap — имеет тип HBITMAP, который используется для хранения картинок, и мы поместим сюда наше изображение кнопки Пуск.

Теперь переходим в функцию _tWinMain. В ней, после загрузки из ресурсов текста окна и имени класса окна, добавим следующую строчку кода:

startBitmap = (HBITMAP)::LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);

Здесь мы переменной startBitmap присваиваем загруженную из ресурсов картинку. Для этого вызывается функция LoadImage, которой (в скобках) нужно передать следующие параметры:



экземпляр приложения — переменная hInstance, которую мы получили в качестве первого параметра нашей функции _tWinMain, именно она содержит необходимое значение экземпляра;

имя ресурса — наша картинка сохранена под именем IDB_BITMAP1;

тип изображения — в нашем случае растровая картинка — IMAGE_BITMAP;

размеры (следующие два параметра) — мы указали значение 0, чтобы использовать текущие размеры картинки;

флаги — здесь указано LR_DEFAULTCOLOR, что означает использование цветов по умолчанию.

Больше в этой функции мы пока ничего изменять не будем. Чуть позже мы еще сюда вернемся и добавим пару строчек, а сейчас переходим в функцию Initlnstance. Она будет выглядеть как в листинге 2.1.

Листинг 2.1. Обновленная функция InitInstance
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd) { return FALSE; } // Следующие строки добавлены нами int Style; Style = GetWindowLong(hWnd, GWL_STYLE); Style=Style || WS_CAPTION; Style=Style || WS_SYSMENU; SetWindowLong(hWnd, GWL_STYLE, Style);

return TRUE; }

Код, добавленный нами, начинается с объявления переменной Style, которая будет иметь тип int (целое число). В следующей строке этой переменной присваивается результат выполнения функции GetWindowLong. Она возвращает настройки окна и в скобках нужно передать два значения:

окно, параметры которого необходимо узнать — мы указываем только что созданное нами окно;

тип параметров — нас будет интересовать стиль окна, поэтому указана константа GWL_STYLE.

Зачем нам нужен стиль? Просто окно по умолчанию имеет заголовок, кнопки максимизации и минимизации, а нам все это не нужно. Для этого из полученного стиля в следующих двух строках удаляется заголовок окна и системное меню, которое содержит кнопки.

Теперь выполняем функцию SetWindowLong, которая записывает значения обратно в настройки окна. Если сейчас запустить программу, то вы увидите только клиентскую часть — серый квадрат без заголовка, кнопок и обрамления.



Переходим в функцию WndProc, где у нас обрабатываются все события. Нас будет интересовать рисование, поэтому добавим следующий обработчик события:

case WM_PAINT: hdc = BeginPaint(hWnd, ps); // TODO: Add any drawing code here... Rectangle(hdc, 1,1,10,10); hdcBits=::CreateCompatibleDC(hdc); SelectObject(hdcBits,startBitmap); BitBlt(hdc, 0, 0, 50, 20, hdcBits, 0, 0, SRCCOPY); DeleteDC(hdcBits); EndPaint(hWnd, ps); break;

Полный вариант функции WndProc вы можете увидеть в листинге 2.2.

Листинг 2.2. Функция WndProc с обработчиком для рисования
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; HDC hdcBits;

switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, ps); // TODO: Add any drawing code here... Rectangle(hdc, 1,1,10,10); hdcBits=::CreateCompatibleDC(hdc); SelectObject(hdcBits,startBitmap); BitBlt(hdc, 0, 0, 50, 20, hdcBits, 0, 0, SRCCOPY); DeleteDC(hdcBits); EndPaint(hWnd, ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }

Чтобы начать рисование, надо знать, где мы будем это делать. У каждого окна есть контекст, в котором можно рисовать средствами Windows. Чтобы получить его для текущего окна, нужно вызвать функцию BeginPaint. Эта функция как раз и вернет нам указатель на контекст окна, указанного в качестве первого параметра.

Но чтобы отобразить изображение нашей кнопки Пуск, надо еще подготовить картинку. В WinAPI нет готовой функции для рисования растрового изображения, но есть возможность выбрать изображение в контекст и возможность копирования между контекстами. Для этого сначала надо создать контекст рисования, совместимый с тем, что использует окно, чтобы можно было без проблем производить копирование. Воспользуемся функцией CreateCompatibleDC, которой нужно передать контекст окна, а она нам вернет новый контекст, совместимый с указанным.



Следующим шагом мы должны выбрать в новый контекст нашу картинку. Для этого можно вызвать функцию SelectObject, у которой два параметра:

контекст, в который нужно выбрать объект, — указываем созданный нами контекст, на основе оконного;

объект, который надо выбрать, — указываем картинку.

Вот теперь можно производить копирование с помощью функции BitBlt. У нее в скобках нужно указать следующие параметры:

контекст рисования, в который надо копировать (приемник), — указываем контекст окна;

следующие четыре параметра являются левой верхней координатой, шириной и высотой прямоугольника, в который надо скопировать изображение (целые числа). В данном случае левая и верхняя позиции будут равны нулю, чтобы картинка располагалась в левом верхнем углу окна. Ширина и высота равны размеру картинки (50x20);

источник копирования — указываем контекст hdcBits, в котором находится наша картинка;

следующие два параметра задают левую верхнюю координату прямоугольника в контексте-источнике (именно от этой точки будет взято изображение для копирования) — указываем нули, т. к. нас интересует вся кнопка;

последний параметр указывает на тип копирования — используем флаг SRC_COPY, т. к. будем создавать копию источника в приемнике.

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

Завершаем рисование вызовом метода EndPaint. Таким образом мы ставим точку в начатое функцией BeginPaint рисование.

Теперь в нашем окне в левом верхнем углу будет рисоваться изображение кнопки Пуск. Остается сделать самую малость — уменьшить размер окна до размеров изображения, чтобы пользователь видел только картинку, и заставить окно двигаться. Для этого мы должны написать функцию DrawStartButton (листинг 2.3), Желательно, до функци _tWinMain.

Листинг 2.3. Функция , заставляющая окно двигаться
void DrawStartButton() { int i; HANDLE h; int toppos=GetSystemMetrics(SM_CYSCREEN)-23;



//Отображаем окно ShowWindow(hWnd, SW_SHOW); //Установить верхнюю позицию окна в левый нижний угол экрана. SetWindowPos(hWnd, HWND_TOPMOST, 4, toppos, 50, 20, SWP_SHOWWINDOW); UpdateWindow(hWnd); //Создаем пустой указатель h, который будем использовать для задержки. h=CreateEvent(0, true, false, "et");

// Сейчас будем поднимать кнопку // От 1 до 50 выполнять действия для изменения положения окна for (i=0; i0; i--) { toppos=toppos+4; SetWindowPos(hWnd, HWND_TOPMOST, 4, toppos, 50, 20, SWP_SHOWWINDOW); WaitForSingleObject(h,15);//Задержка в 5 миллисекунд } }

Чтобы правильно расположить окно с нашей кнопкой на экране компьютера, мы должны знать его разрешение. Для этого выполняется следующая строка кода:

int toppos = GetSystemMetrics(SM_CYSCREEN)-23;

Здесь вызывается функция GetSystemMetrics, которая возвращает значение определенного системного параметра. В скобках указывается параметр, который нас интересует (в данном случае SM_CYSCREEN, высота экрана). Из результата вычитаем число 23 (высота картинки + еще 3 пиксела) и сохраняем результат в переменной toppos.

Таким образом, мы вычислили верхнюю позицию окна с изображением кнопки, и можем его туда переместить. Еще необходимо, чтобы наше окно всегда было поверх остальных. Обе эти операции можно сделать, вызвав только одну функцию SetWindowPos. У нее 7 параметров:

окно, которое надо переместить, — указываем наше окошко;

место размещения (после какого окна нужно расположить указанное) — устанавливаем флаг HWND_TOPMOST (поверх всех);

следующие четыре параметра определяют прямоугольник, в котором должно располагаться окно. Левую позицию задаем равной 4. Верхнюю — равной переменной toppos. Ширина и высота окна должны определяться размерами картинки. Возможно, что после запуска программы вам придется подкорректировать левую верхнюю позицию в зависимости от подготовленной вами картинки;

последний параметр задает режим отображения окна — устанавливаем флаг SWP_SHOWWINDOW (просто отобразить).

После этого прорисовываем окно в новой позиции с помощью вызова функции UpdateWindow(hWnd). В скобках указано окно, которое надо отобразить.



Последний штрих — создание пустого события с помощью вызова функции СreateEvent. Это событие мы будем использовать чуть позже, и нас устраивает, что оно пустое.

Теперь наше окно расположено в нужном месте, и можно приступить к его анимации (движению по экрану). Для этого запускаем цикл от 0 до 50, внутри которого выполняются следующие действия:

for (i=0; i

Сначала уменьшается значение переменной toppos на четыре пиксела. Таким образом, окно будет ползти вверх по экрану. Потом перемещаем это окно в новую позицию.

Самое интересное здесь — это последняя строчка кода, в которой выполняется функция WaitForSingleObject. Она ожидает наступления события, определенного первым параметром. Второй параметр указывает количество миллисекунд, которые надо ожидать. Так как событие пустое, оно никогда не наступит, и функция прождет его ровно указанное во втором параметре время. Таким образом, мы делаем задержку между движениями окна, которая не загружает систему. Некоторые любят для задержек использовать циклы с математическими операциями, но это нагружает процессор бесполезной работой, что является плохим тоном. По моим наблюдениям, использование WaitForSingleObject в наименьшей степени нагружает компьютер и отлично работает.

Итак, наш цикл двигает кнопку вверх по экрану. После этого мы должны вернуть кнопку на место, для чего запускается еще один цикл, в котором тем же способом кнопка движется в обратном направлении.

Теперь у нас все готово, и мы должны вернуться в функцию _tWinMain и написать там вызов функции DrawStartButton. Я рекомендую сделать этот вызов перед циклом обработки сообщений и внутри него:

DrawStartButton();

// Main message loop: while (GetMessage(msg, NULL, 0, 0)) { DrawStartButton(); if (!TranslateAccelerator(msg.hwnd, hAccelTable, msg)) { TranslateMessage(msg); DispatchMessage(msg); } }

Теперь при старте программы наша лже-кнопка будет взлетать и возвращаться на место поверх настоящей кнопки. Если вы попытаетесь навести на настоящую кнопку мышку, то она пройдет поверх окна лже-кнопки, и наша программа получит сообщение от мышки, а значит, выполнится цикл обработки событий, в котором функция DrawStartButton снова подбросит нашу кнопку на некоторую высоту.



Эффект получается красивым, и обязательно посмотрите на результат ( 2.5). Ничего разрушительного в этом нет, но вашим друзьям понравится.



2.5. Пример работы программы

Примечание
Исходный код и запускаемый файл этого примера вы можете найти на компакт - диске в каталоге \Demo\Chapter2\Start Button. Чтобы запустить программу, выберите меню Debug/Start.
Очень много шуток могут основываться на эффекте "подставки", когда мы рисуем или двигаем не сам объект, а совершенно другое окно с изображением нужного объекта. Таким образом можно даже просто вывести на экран диалоговое окно с сообщением "Форматировать диск С:", и пользователь может не на шутку испугаться. Ну, а если вывести десять сообщений с надписью "Virus Alert!!!", то даже профессионал может поверить.

Вспоминаются времена 90-х годов, когда даже вирусы были веселыми. Все их действия заключались в том, что они выводили какие-то веселые сообщения, играли музыку через PC Speaker или выводили на экран какую-то ASCII-графику. При этом самое страшное, что они делали, — копировались без спроса между компьютерами. Конечно же, даже эти вирусы нельзя считать хорошими, но они, по крайней мере, были с изюминкой. Нынешние вирусы не несут в себе вообще ничего пристойного и интересного.


Маленькие шутки


Рассмотрим несколько маленьких приколов. Это небольшие задачи, ради которых нет смысла писать самостоятельные примеры, поэтому в целях экономии места я объединил различные шутки в одну программу. Вы можете использовать эту заготовку в своих невидимых шуточных приложениях или реальных программах. Некоторые используемые функции могут пригодиться и в коммерческих проектах.



Мышка в клетке


Очень интересным примером является ограничение свободы перемещения мышки. Посмотрите на следующий код:

RECT r;

r.left=10;

r.top=10;

r.bottom=100;

r.right=100;

CiipCursor(r);

Определим переменную r типа RECT. Это структура, которая состоит из четырех числовых переменных, описывающих прямоугольник. Переменные структуры имеют следующие имена: left, top, bottom и right (левая, верхняя, нижняя и правая координаты прямоугольника).

В следующих четырех строчках мы присваиваем этим переменным значения, определяя тем самым прямоугольную область. Затем вызываем функцию ClipCursor, которая и офаничивает движение курсора мышки указанным прямоугольником.

Попробуйте выполнить следующий код:

RECT r;

r.left=0;

r.top=0;

r.bottom=1;

r.right=1;

CiipCursor(r);

Здесь размер области передвижения равен 1 пикселу по горизонтали и вертикали, поэтому мышка окажется запертой в клетке.



Начните работу с кнопки Пуск


Если вы сами устанавливали Windows, то после первого запуска, неверно, видели сообщение ОС типа "Начните работу с этой кнопки" и стрелку, указывающую на кнопку Пуск. Я достаточно долго работал администратором сети, и мне наскучило отвечать пользователям на вопрос: "А где у меня программа XXX". После очередного вопроса я написал программу, которая постоянно открывает меню, появляющееся по нажатию кнопки Пуск. Сейчас нам предстоит написать подобный пример.

Создайте новое приложение Win32 Project. Я назвал новый проект CrazyStart, но вы можете назвать и по-другому. В данном примере имя проекта не будет использоваться, и путаницы в понимании не будет.

Откройте файл с кодом вашего проекта, он должен иметь имя вашего проекта и расширение срр (у меня это CrazyStart.cpp). Найдите функцию _tWinMain и доведите ее до вида как в листинге 2.4. По комментариям, которые указаны в листинге, вы легко можете определить, что нужно добавить.

Листинг 2.4. Функция _tWinMain
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. MSG msg; HACCEL hAccelTable;

// Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_CRAZYSTART, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance);

// Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; }

hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_CRAZYSTART);

// Main message loop: // Необходимо добавить в свой код следующие три строки: HWND hTaskBar, hButton;

hTaskBar= FindWindow("Shell_TrayWnd",NULL); hButton= GetWindow(hTaskBar, GW_CHILD);

while (GetMessage(msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, msg)) { TranslateMessage(msg); DispatchMessage(msg); } // Hажать кнопку "Пуск" // Необходимо добавить в свой код следующие две строки: SendMessage(hButton, WM_LBUTTONDOWN, 0, 0); Sleep(1000); }


return (int) msg.wParam; }

Сначала мы объявляем две переменные hTaskBar и hButton типа HWND. Это уже знакомый нам тип, который используется для ссылки на окна. Потом мы выполняем функцию FindWindow, которая ищет окно по заданным двум параметрам:

имя класса окна — это имя используется при регистрации окна в системе;

имя заголовка окна — текст, который указан в заголовке.

Кнопка Пуск расположена на Панели задач, которая является окном, и именно его мы хотим найти. Класс этого окна — Shell_TrayWnd, что мы и указываем в первом параметре. Заголовка нет, поэтому и имени окна не будет, так что второй параметр пустой и равен NULL.

На Панели задач есть только одна кнопка — Пуск, поэтому мы можем получить на нее ссылку с помощью вызова функции GetWindow. Эта функция имеет два параметра:

указатель на окно;

"родственные связи" искомого окна и указанного. У нас кнопка находится на окне, поэтому окно является для нее родителем, а сама кнопка — подчиненным, и мы должны указать флаг GW_CHILD.

Таким образом, мы получим указатель на кнопку и сохраним его в переменной hButton. В цикле обработчика сообщений мы посылаем кнопке Пуск сообщение с помощью функции SendMessage со следующими параметрами:

окно, сообщение которому мы хотим послать, — указатель на кнопку Пуск;

сообщение, которое надо послать, — отсылаем WM_LBUTTONDOWN, что равносильно нажатию левой кнопки мыши.

Кнопка Пуск, получив наше сообщение, будет думать, что по ней щелкнули левой кнопкой мыши, и отобразит меню.

После этого вызывается функция Sleep, которая делает задержку в заданное количество миллисекунд. У нас указано 1000, что равносильно одной секунде. Эта функция останавливает выполнение программы, но, в отличие от использованного ранее метода с функцией WaitForsingleObject, эта помимо задержки больше загружает систему. Таким образом, когда пользователь захочет закрыть наше окно, он поведет мышкой по окну, и в этот момент будет сгенерировано множество сообщений от мышки. Задержка от функции Sleep будет настолько большой, что закрыть окно будет сложно.

Примечание
Исходный код и запускаемый файл этого примера вы можете найти на компакт - диске в каталоге \Demo\Chapter2\CrazyStart.

Найти и уничтожить


Попробуем найти определенное окно и уничтожить его. Для этого создайте новое приложение Win32 Project, а также пункт меню, по нажатию которого будет выполняться наш код.

Теперь перейдите в функцию WndProc, в которой обрабатываются все события окна. В начало этой функции нужно добавить переменную h типа HWND, что будет выглядеть примерно так:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; HWND h;

А дальше добавим обработчик события нашего меню:

case ID_MYCOMMAND_FINDANDDESTROY: h=FindWindow(0, "1 — Блокнот "); if (h!=0) SendMessage(h, WM_DESTROY, 0, 0); break;

С помощью уже знакомой нам функции FindWindow мы ищем окно, у которого в заголовке есть текст "1 — Блокнот". Результат поиска сохраняем в переменной h. В качестве параметра поиска задаем только заголовок окна, а класс окна оставляем пустым (его не всегда можно определить). В этом и состоит тонкость программы, потому что большинство приложений с открытым документом имеет заголовок типа "Имя документа — Название приложения", например, "MyTestDoc — Microsoft Word". Если открытых документов нет, в заголовке остается название программы — "Microsoft Word". Только в этом случае функция FindWindow может однозначно определить окно, которое надо найти, иначе она возвратит 0. Если проверка переменной h показывает, что окно найдено, то посылается сообщение WM_DESTROY, чтобы уничтожить его.

Как видите, поиск окна по заголовку нельзя назвать точным и надежным.

В этом примере окно уничтожается только по выбору соответствующего пункта меню.

А что, если перенести этот код в цикл обработки сообщений следующим образом:

// Main message loop: while (GetMessage(msg, NULL, 0, 0)) { h=FindWindow(0, "1 - Блокнот"); if (h!=0) SendMessage(h, WM_DESTROY, 0,0);

if (!TranslateAccelerator(msg.hwnd, hAccelTable, msg)) { TranslateMessage(msg); DispatchMessage(msg); } }

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

Примечание
Исходный код примера , описанного в этом разделе , вы можете найти на компакт - диске в каталоге \Demo\Chapter2\FindAndDestroy.



Продолжаем шутить над Панелью задач


Как мы уже знаем, Панель задач — это такое же окно, и над ним можно жестоко издеваться всеми доступными функциями для работы с окном. Когда мы писали первый пример, который подбрасывал лже-кнопку, то мы поднимали свое окно с изображением кнопки Пуск. Кто нам теперь мешает модифицировать этот пример и подбросить реальную кнопку Пуск, когда мы уже знаем, как получить к ней доступ.

Но не все так просто, и сейчас мы рассмотрим пример, в котором увидим несколько интересных приемов, позволяющих шутить над реальной кнопкой Пуск.

Создайте новый проект StartEnable и добавьте в раздел глобальных переменных следующие строки:

HWND hWnd;

HWND hTaskBar, hButton;

HMENU MainMenu;

В этом месте мы объявляем три переменные, которые будут ссылаться на окна:

HWnd — будет хранить указатель на наше окно, чтобы мы могли его использовать в любой точке кода;

hTaskBar и hButton — будут хранить указатели на Панель задач и кнопку Пуск;

MainMenu — сюда мы загрузим меню нашей программы, чтобы в дальнейшем использовать его.

Теперь переходим в функцию _tWinMain и в ней добавляем код из листинга 2.7 до обработчика событий.

Листинг 2.7. Код , который нужно добавить в функцию _tWinMain

hTaskBar= FindWindow("Shell_TrayWnd",NULL); hButton= GetWindow(hTaskBar, GW_CHILD); MainMenu=LoadMenu(hInstance, (LPCTSTR)IDC_STARTENABLE);

SetParent(hButton, 0);

int i; HANDLE h; int toppos=GetSystemMetrics(SM_CYSCREEN)-23;

//Установить верхнюю позицию окна в левый нижний угол экрана. SetWindowPos(hButton, HWND_TOPMOST, 4, toppos, 50, 20, SWP_SHOWWINDOW); UpdateWindow(hButton); //Создаю пустой указатель h который буду использовать для задержки. h=CreateEvent(0, true, false, "et");

// Сейчас будем подымать кнопку // Цикл по изменению позиции кнопки for (i=0; i0; i--) { toppos=toppos+4; SetWindowPos(hButton, HWND_TOPMOST, 4, toppos, 50, 20, SWP_SHOWWINDOW); WaitForSingleObject(h,15);//Задержка в 5 миллисекунд } SetParent(hButton, hTaskBar);

Первые две строки нам уже знакомы. Здесь мы находим Панель задач и кнопку Пуск, сохраняя значения в глобальных переменных. Почему в глобальных, а не прямо в процедуре? Просто эти переменные мы будем использовать и дальше в этой программе (для других шуток). Поэтому, чтобы не выполнять один и тот же поиск, который всегда будет выдавать один и тот же результат, лучше сохраним один раз полученные значения в глобальной памяти программы.


В третьей строке загружается меню в переменную MainMenu с помощью функции LoadMenu. У этой функции два параметра — указатель на экземпляр и имя загружаемого меню. Это меню будет использоваться позже, а пока мы только подготовили переменную на будущее.

Теперь в этой функции будем подбрасывать кнопку Пуск. Но прежде, чем это сделать, надо вспомнить, где она находится. Кнопка принадлежит Панели задач (расположена на ней), а значит, если мы начнем двигать что-то сейчас, то кнопка будет стоять на месте. Почему? Потому что кнопка не сможет оторваться от своей панели. Первым делом необходимо разорвать связь между кнопкой и Панелью задач. Для этого выполняем функцию SetParent со следующими параметрами:

окно, родителя которого нужно изменить, — наша кнопка;

новый родитель указанного окна — указываем нуль.

Таким образом, у кнопки после выполнения этой функции будет "нулевой" родитель, т. е. связь будет разрушена.

Вот теперь можно двигать кнопку, как угодно. Поэтому следующий код будет вам знаком. Он практически не отличается от кода из листинга 2.3, где мы двигали окно, но в данном случае двигается кнопка, поэтому в функции SetWindowPos в качестве первого параметра используется указатель на кнопку, чтобы двигать именно ее.

После подъема и опускания кнопки Пуск мы должны вернуть ее на место, поэтому снова выполняем функцию SetParent, чтобы установить в качестве родителя для кнопки Панель задач.

На 2.6 можно увидеть результат работы программы, который вы получите, если уже сейчас запустите пример. Обратите внимание, что на том месте, где должна быть кнопка Пуск, — пустота.



2.6. Результат работы программы

Для реализации всех шуток в одной программе мы создадим несколько пунктов меню, с помощью которых будем вызывать разные команды. Для создания меню откройте ресурсы и выберите соответствующий пункт дерева ( 2.7).



2.7. Ресурсы и пункт дерева , под которым прячется меню

Вы увидите свое меню, по которому можно двигаться. В конце каждого списка меню есть пустой пункт с именем Tуре Hеrе, в котором имя написано на белом фоне. Выберите самый правый пункт с именем Tуре Hеrе и введите новое имя Our menu . Название этого пункта и цвет изменятся, и таким способом мы получим новое меню, а справа от него, под ним появится новый подпункт с именем Tуре Hеrе. Таким образом мы создаем новые пункты (подпункты) меню, которые будут видны в программе.



Выберите пункт Our menu и перетащите его мышкой, чтобы он оказался перед пунктом Help , как на 2.8.



2.8. В центре окна редактор меню

Теперь выделите пункт Our menu . Перейдите на пункт меню Tуре Hеrе, который находится ниже, и наберите новое имя Move window to System Tray . Имя изменится, а строчкой ниже появится новый пункт Tуре Hеrе. Выделите его и создайте новый пункт Enable System Tray . Таким же образом добавьте еще два пункта: Disable System Tray и Insert menu. В результате ваше меню должно выглядеть, как на 2.9.



2.9. Результат создания меню

Теперь приступаем к программированию. Перейдите в исходный код программы и найдите функцию WndProc. Когда пользователь выбирает какой-то пункт меню, то генерируется событие, и мы должны его обрабатывать в этой функции.

Полный код функции WndProc вы можете увидеть в листинге 2.8. Просмотрите его и приведите свою функцию к такому же виду.

Листинг 2.8. Функция обработки сообщений WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;

switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { //Обрабатываем меню Move window to System Tray case ID_OURMENU_MOVEWINDOWTOSYSTEMTRAY: SetParent(hWnd, hTaskBar); break; //Пункт меню Enable System Tray case ID_OURMENU_ENABLESYSTEMTRAY133: EnableWindow(hTaskBar, true); break; //Пункт меню Disable System Tray case ID_OURMENU_DISABLESYSTEMTRAY: EnableWindow(hTaskBar, false); //Пункт меню Insert menu break; case ID_OURMENU_INSERTMENU: SetMenu(hTaskBar, MainMenu); break; case IDM_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, ps); // TODO: Add any drawing code here... EndPaint(hWnd, ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }



Большая часть из приведенного в листинге 2. 8 кода была сгенерирована мастером при создании проекта. Нами добавлено не так уж и много, и сейчас нам предстоит это разобрать по частям.

Для первого пункта меню (Move window to System Tray) в данной функции выполняется следующий код:

// Пункт меню Move window to System Tray case ID_OURMENU_MOVEWINDOWTOSYSTEMTRAY: SetParent(hWnd, hTaskBar); break;

Оператор case проверяет пришедшее сообщение с константой ID_OURMENU_MOVEWINDOWTOSYSTEMTRAY. Если вы перейдете в редактор ресурсов и выберете созданный нами пункт меню Move window to System Tray, то в окне свойств (справа внизу) в свойстве ID увидите именно эту константу. Если она отличается, то для вас Visual C++ сгенерировал другое имя (у нас может отличаться версия среды разработки), и вы должны подкорректировать исходный код. Если проверка прошла успешно, то выполнится весь код до оператора break. Здесь идет вызов только одной функции SetParent. Мы уже знаем, что эта функция изменяет родителя окна, указанного в качестве первого параметра, делая его подчиненным по отношению к окну, заданному вторым параметром. Первым у нас указано главное окно, а вторым — Панель задач. Получается, что наше окно становится подчиненным по отношению к Панели задач.

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



2.10. Главное окно нашей программы стало подчиненным по отношению к Панели задач

При выборе пункта меню Disable System Tray выполняется следующий код:

//Пункт меню Disable System Tray case ID_OURMENU_DISABLESYSTEMTRAY1333: EnableWindow(hTaskBar, false); break;

Здесь мы выполняем функцию EnableWindow, которая делает доступным или недоступным какое-то окно. В качестве первого параметра мы передаем указатель на окно, а второй параметр равен true (сделать доступным) или false (сделать недоступным). В данном случае мы делаем недоступной Панель задач. Вы сколько угодно можете нажимать на кнопки панели, но ничего кроме звукового сигнала об ошибке не услышите. Но можно было бы указать и hButton , чтобы заблокировать только кнопку Пуск, а не всю Панель задач.



Если выбрать пункт меню Disable System Tray, выполнится та же функция EnableWndow, только теперь мы сделаем окно доступным.

Выбор пункта меню Insert menu активизирует функцию SetMenu, которая устанавливает меню для окна. Первым параметром определяется окно, а во втором параметре указывается загруженное меню. Вот и пригодилась переменная MainMenu, в которую мы в самом начале загрузили меню.



2.11. Меню в Панели задач — нонсенс или реальность?

Посмотрите на 2.11, там показан результат работы программы после выбора этого пункта меню. Самое интересное, что мышкой вы его выбрать не можете. Единственный вариант войти в него — это клавиатура. Чтобы выбрать меню с помощью клавиатуры, нужно сделать активным окно (щелкните на Панели задач) и нажать кнопку Alt. Первый пункт меню должен подсветиться, и теперь вы можете перемещаться по пунктам меню с помощью клавиатуры и мышки.

Примечание
Исходный код и запускаемый файл этого примера вы можете найти на компакт - диске в каталоге \Demo\Chapter2\StartEnable.

Программное управление CD-ROM


Очень хорошая шутка — открытие и закрытие лотка CD-ROM. Вы можете организовать цикл и бесконечно открывать и закрывать дверцу. Мы же рассмотрим пример единичного открытия.

Итак, нужно добавить заголовочный файл mmsystem.h. Это можно сделать в начале файла или в заголовочном файле stdafx.h следующим образом:

#include mmsystem.h

Теперь в окне Solution Explorer переместите указатель на строку с именем вашего проекта и выберите меню Project/Properties. В открывшемся окне ( 2.12) в дереве слева установите Configuration Properties/Linker/Command Line. Функции, которые мы сейчас будем использовать, расположены в библиотеке winmm.lib, а она при сборке проекта по умолчанию не подключается к запускаемому файлу. Поэтому мы должны подключить эту библиотеку вручную. Для этого в поле Additional Options напишите имя библиотеки winmm.lib.

Для работы нам понадобятся следующие переменные:

MCI_OPEN_PARMS OpenParm;

MCI_SET_PARMS SetParm;

MCIDEVICEID dID;

Сам код открытия и закрытия CD-ROM будет выглядеть следующим образом:

OpenParm.lpstrDeviceType="CDAudio";

mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE, (DWORD_PTR)OpenParm);

dID = OpenParm.wDeviceID;

mciSendCommand(dID, MCI_SET, MCI_SET_DOOR_OPEN,(DWORD_PTR)SetParm);

mciSendCommand(dID,MCI_SET,MCI_SET_DOOR_CLOSED,(DWORD_PTR)SetParm);

mciSendCommand(dID, MCI_CLOSE, MCI_NOTIFY, (DWORD_PTR)SetParm);

Сначала мы должны определить параметр lpstrDeviceType структуры OpenParm. Ему нужно присвоить значение строки "CDAudio", что и будет указывать на необходимость работы с CD-ROM .

2.12. Настройки командной строки сборщика проекта

Для работы с мультимедийными устройствами, к которым относится и CD - ROM , используется функция mciSendCommand. Она отправляет устройству сообщение и передает следующие параметры:

идентификатор устройства, которое должно получить сообщение, — значение получаем при открытии устройства, поэтому, если в качестве второго параметра указан флаг MCI_OPEN, т o параметр игнорируется, т. к. устройство еще не открыто;

команда сообщения;

флаг для сообщения, которое должно быть послано устройству;

указатель на структуру, которая содержит параметры для команды сообщения.

В первый раз мы посылаем сообщение mci _ open , чтобы открыть устройство. После этого в параметре wDeviceID структуры OpenParm будет находиться идентификатор открытого устройства. Именно его мы будем использовать в качестве первого параметра для отправки сообщений.

Чтобы открыть дверцу CD-ROM, нужно отправить сообщение, в котором второй параметр равен MCI_SET, а третий — MSI_SET_DOOR_OPEN. Последний параметр нас не интересует. Закрытие дверцы похоже на открытие, только третий параметр равен MSI_SET_DOOR_CLOSED.

После завершения работы с устройством мы должны его закрыть. Для этого отправляем сообщение, в котором второй параметр равен MCI_CLOSE, а третий — MCI_NOTIFY.



Простые шутки


Теперь можно приступать к написанию простых программ-приколов в Windows. Так как эта ОС самая распространенная, то и шутки в ней самые интересные. Думаю, что любой компьютерщик с удовольствием подкинет своему другу какую-нибудь веселую программку, которая введет жертву в легкий шок. В каждом из нас заложено еще при рождении стремление к превосходству. Все мы хотим быть лучшими, и программисты часто доказывают свое первенство с помощью написания чего-то уникального, интересного и вызывающего. Чаще всего в виде самовыражения выступают программы-шутки.

Хотя мои программы не будут вредоносными, но все же они должны быть кому-нибудь подброшены. Поэтому человека, которому должна быть подкинута программа, будем называть жертвой.

Большинство приколов этой главы основаны на простых функциях WinAPI. Хоть я и сказал, что начальные знания программирования желательны, но все же весь используемый в книге код я постараюсь расписывать очень подробно. Особый упор сделан на используемые в примерах WinAPI -функции. Если некоторые возможности Visual C++ вы используете каждый день, то функции WinAPI можете использовать достаточно редко, поэтому я даже не надеюсь, что вы знаете их все.

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



Сетевая бомба


В Windows NT-системах (NT/2000/XP/2003) появилась очень интересная и удобная команда NET SEND, которая позволяет отправить на другой компьютер сообщение из командной строки. Вы пишете команду, адрес получателя и текст сообщение. А после выполнения инструкции на компьютере адресата появляется окно с текстом сообщения. Пример такого окна вы можете увидеть на 2.13.

2.13. Сообщение, посланное командой NET SEND

Сообщение отправляется командой следующего вида:

NET SEND Адрес Текст

В качестве адреса можно указывать как NETBios-имя компьютера, так и IP -адрес. Вот пример, который посылает сообщение "Hi, Dany" на компьютер Dany :

NET SEND Dany Hi, Dany

Самое интересное, что Windows 2000 и Windows XP абсолютно не защищены от бомбардировки командой NET SEND. Вы можете очень быстро послать хоть сто команд на компьютер вашего друга с любыми сообщениями, и все они дойдут. Но отправлять руками — утомительно, поэтому напишем небольшую программу.

Создайте новый проект типа Win32 Project в Visual C++, и в функции _tWinMain напишите следующий код до цикла обработки сообщений:

for (int i=0; i10; i++) { WinExec("NET SEND 192.168.1.121 You will be cry by me", SW_SHOW); Sleep(1000); }

Здесь мы запускаем цикл, в котором функция WinExec будет 10 раз выполнять код, указанный в качестве первого параметра в командной строке Windows . В данном примере это текст "NET SEND 192.168.1.121 You will be cry by me". Если выполнить этот код в командной строке, то вы отправите сообщение "You will be cry by me" на компьютер с адресом 192.168.1.121.

2.14. Окно управления службами

На каждом шаге цикла мы делаем задержку в 1 секунду с помощью функции Sleep, чтобы между сообщениями была хоть какая-то пауза.

Если вас начали бомбить сообщениями NET SEND, то даже не пытайтесь успеть закрыть все окна. Выполните следующие действия:

Выдерните сетевой кабель, который связывает вас с сетью, из которой идет бомбардировка. Если это для вас неприемлемо, и связь нельзя выключать, то в любом случае переходите к следующему пункту.


Выполните последовательно Пуск/Настройка/Панель управления/Администрирование/Службы и в появившемся окне найдите строку "Служба сообщений" ( 2.14). Щелкните по ней правой кнопкой мыши и в появившемся меню выберите пункт Стоп.

Если вы не пользуетесь сообщениями, то лучше эту службу отключить заранее (по умолчанию включена), чтобы не было даже потенциальной возможности такой атаки на ваш компьютер.

Примечание
Исходный код примера , описанного в этом разделе , вы можете найти на компакт - диске в каталоге \Demo\Chapter2\NetBomb.

Шутки с мышкой


Мышка тоже может быть объектом насмешек. Например, можно заставить ее беспорядочно бегать по монитору или ограничить движение маленьким квадратом. А можно вообще остановить указатель в определенной точке, создав видимость его зависания. Как показывает практика, игры с мышкой производят на пользователя большее впечатление, особенно на начинающего, потому что он больше работает с ней, а не с клавиатурой.



Светомузыка над кнопкой Пуск


Над кнопкой Пуск можно издеваться достаточно долго. Еще одна шутка, которую можно сделать с этой кнопкой, — спрятать ее.

Для следующей задачи вы можете создать новое приложение или воспользоваться кодом из предыдущего примера, немного подкорректировав функцию _tWinMain как в листинге 2.5.

Листинг 2.5. Обновленная функция _tWinMain
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. MSG msg; HACCEL hAccelTable;

// Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_CRAZYSTART, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance);

// Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; }

hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_CRAZYSTART);

// Main message loop: HWND hTaskBar, hButton;

hTaskBar= FindWindow("Shell_TrayWnd",NULL); hButton= FindWindowEx(hTaskBar, 0,"Button", NULL);

while (GetMessage(msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, msg)) { TranslateMessage(msg); DispatchMessage(msg); } // Спрятать кнопку "Пуск" ShowWindow(hButton, SW_HIDE); // Насладимся эрелищем 2 секунды Sleep(50); // Показать кнопку "Пуск" ShowWindow(hButton, SW_SHOW); Sleep(50); }

return (int) msg.wParam; }

В этом примере мы точно так же ищем окно Панели задач и кнопку Пуск на ней. Отличие от предыдущего примера скрыто внутри обработчика событий. Здесь мы используем функцию ShowWindow. В главе 1 мы уже рассматривали эту функцию и знаем, что она предназначена для отображения окна. Но она может быть использована и для того, чтобы максимизировать, минимизировать или спрятать окно.

Кнопки в Windows — это те же окна, поэтому мы можем использовать эту функцию для нашей кнопки Пуск. Функция ShowWindow вызывается два раза, и оба раза первый параметр передается в виде указателя на найденную кнопку. В качестве второго параметра первый раз передаем флаг SW_HIDE, который заставляет кнопку спрятаться, а во второй раз — SW_SHOW, чтобы отобразить кнопку. Между вызовами функции ShowWindow стоит функция Sleep, которая выполняет задержку для того, чтобы пользователь успел увидеть панель с кнопкой и без нее.


Запустите программу, и она будет в бесконечном цикле прятать и отображать кнопку Пуск. Теперь вы можете без проблем написать код, который просто прячет главную кнопку Windows, и пользователь больше не сможет на нее нажать.

Еще одно отличие этого примера, здесь кнопка на Панели задач ищется иначе. Если раньше мы использовали GetWindow, то в этом примере используется функция FindWindowEx. Она схожа с FindWindow, но позволяет производить более точный поиск не только главных окон, но и дочерних, принадлежащих другим окнам, потому что содержит следующие параметры:

окно, на котором нужно искать элемент управления, — благодаря этому параметру мы можем искать кнопку внутри окна;

элемент управления на этом окне, с которого нужно начинать поиск, — если здесь указать 0, то поиск будет начинаться с самого первого элемента управления;

класс элемента управления — в нашем случае это кнопка, значит, нужно

указать Button;

имя — если указать нуль (NULL), т o будет происходить поиск всех элементов подобного класса.

Примечание
Исходный код и запускаемый файл этого примера вы можете найти на компакт - диске в каталоге \Demo\Chapter2\StartMusic.
Немного изменив код, можно сделать светомузыку из всей Панели задач. Необходимый код вы можете увидеть в листинге 2.6. Правки минимальны, просто вместо кнопки в функцию ShowWindow отправляется указатель на Панель задач.

Листинг 2.6. Светомузыка для Панели задач
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ... ...

HWND hTaskBar;

hTaskBar= FindWindow("Shell_TrayWnd",NULL);

// Main message loop: while (GetMessage(msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, msg)) { TranslateMessage(msg); DispatchMessage(msg); } // Спрятать задачи ShowWindow(hTaskBar, SW_HIDE); // Насладимся эрелищем 2 секунды Sleep(100); // Показать задачи ShowWindow(hTaskBar, SW_SHOW); Sleep(100); }

return (int) msg.wParam; }

Примечание
Исходный код и запускаемый файл этого примера вы можете найти на компакт - диске в каталоге \Demo\Chapter2\Tasks.

Удаление часов из Панели задач


Это выполняется почти так же, как при работе с кнопкой Пуск. Нужно сначала найти окно Панели задач. Потом на нем найти окно TгауВаr, на котором уже найти часы. После этого часики легко убираются функцией ShowWindow, которой нужно передать в качестве первого параметра указатель на окно часов, а во втором параметре — указать SW_HIDE.

HWND Wnd;

Wnd = FindWindow("Shell_TrayWnd", NULL);

Wnd = FindWindowEx(Wnd, HWND(0), "TrayNotifyWnd", NULL);

Wnd = FindWindowEx(Wnd, HWND(0), "TrayClockWClass", NULL);

ShowWindow(Wnd, SW_HIDE);

Можно было бы спрятать всю панель с иконками, которая расположена в правом углу панели задач. Для этого достаточно не использовать строчку кода с параметром "TrayClockWClass".



Установка на Рабочий стол собственных обоев


Задача — проще некуда:

SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, "с:\\1.bmp", SPIF_UPDATEINIFILE);

Функция SystemParametersInfo имеет следующие параметры:

действие, которое надо выполнить — этих действий очень много и описывать все нереально, привожу самые интересные:

SPI_SETDESKWALLPAPER — установить собственные обои. Путь к файлу с обоями должен быть передан в третьем параметре;

SPI_SETDOUBLECLICKTIME — время двойного щелчка. Количество миллисекунд между первым и вторым щелчком мышкой нужно указать во втором параметре. Попробуйте указать здесь число меньше 10, и я думаю, что вы никогда не успеете за это время "кликнуть" дважды. Таким образом, практически отключается функция двойного щелчка;

SPI_SETKEYBOARDDELAY — во втором параметре устанавливается задержка между нажатиями клавиш на клавиатуре при удерживании кнопки;

SPI_SETMOUSEBUTTONSWAP — если во втором параметре 0, то кнопки мышки используются стандартно, иначе кнопки меняются местами, как для левши;

второй параметр зависит от состояния первого;

третий параметр зависит от состояния первого;

четвертым параметром устанавливаются флаги, в которых указывается, что надо делать после выполнения действия. Возможны следующие варианты:

SPIF_UPDATEINIFILE — обновить пользовательский профиль;

SPIF_SENDCHANGE — сгенерировать WM_SETTINGCHANGE-сообщeниe;

SPIF_SENDWININICHANGE — то же, что и предыдущий параметр.

Еслифункция выполнилась удачно, то она вернет любое число, не равное нулю, иначе функция вернет ноль. Пример кода, который меняет клавиши мышки местами:

// Установить мыть для левши

SystemParametersInfo(SPI_SETMOUSEBUTTONSWAP, 1, 0, SPIF_SENDWININICHANGE);

// Вернуть на родину

SystemParametersInfo(SPI_SETMOUSEBUTTONSWAP, 0, 0, SPIF_SENDWININICHANGE);

Примечание
Все примеры, описанные в этом разделе, вы можете найти на компакт - диске в папке \Demo\Chapter2\SmallCh.



Запуск системных CPL - файлов


Добавьте в начало файла модуль shellapi.h, чтобы вы могли использовать функцию ShellExecute:

#include shellapi.h

Теперь напишите следующий код :

ShellExecute(hWnd, "Open", "Rundll32.exe", "shell32,Control_RunDLL filename.cpl", "", SW_SHOWNORMAL);

Функция ShellExecute запускает указанную программу. У нее есть следующие параметры:

окно, из которого запускается программа, — можно указать хоть 0, для нас это не важно;

действие, которое надо выполнить, — для запуска программы указываем "Open";

имя запускаемой программы;

команды, которые надо передать в командной строке;

каталог по умолчанию, с которого будет работать запущенная программа, — при задании пустой строки будет использоваться путь по умолчанию, что нас вполне устраивает;

тип запуска — параметр, который указывает, как запустить программу, — указываем SW_SHOWNORMAL, что означает запуск программы в нормальном режиме (флаг идентичен параметру у функции ShowWindow).

Например, нам нужно запустить Rundll32.exe (умеет выполнять DLL - и CPL -файлы). В качестве команды нужно передать текст вот такого вида: shell32, Control_RunDLL filename.cpl.

Тогда вот такой код отобразит окно настроек сети Интернет:

ShellExecute(hWnd, "Open", "Rundll32.exe", "shell32,Control_RunDLL inetcpl.cpl", "", SW_SHOWNORMAL);

А такой код отобразит окно настроек экрана:

ShellExecute(hWnd, "Open", "Rundll32.exe", "shell32,Control_RunDLL desk.cpl", "", SW_SHOWNORMAL);