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

         

Без окон, без дверей...


Следующий способ уменьшить размер программы заключается в ответе на вопрос: "Из-за чего программа, созданная в Visual C++, получается большой?" Ответ очень прост, C++ является объектным языком. В нем каждый элемент представляет собой объект, который обладает своими свойствами, методами и событиями. Любой объект вполне автономен и многое умеет делать без ваших указаний. Это значит, что вам нужно только подключить его к своей форме, изменить нужным образом свойства, и приложение готово! И оно будет работать без какого-либо прописывания его деятельности.

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

Но как же тогда создать компактный код, чтобы программа занимала минимум места на винчестере и в оперативной памяти? Тут есть несколько вариантов.

Не использовать библиотеку MFC (для любителей Borland Delphi это библиотека VCL ), которая упрощает программирование. В этом случае придется весь код набирать вручную и работать только с WinAPI ( Win dows Application Programming Interface , прикладной программный интерфейс). Программа получается очень маленькой и быстрой. Результирующий код будет меньше, чем при использовании MFC в сочетании с самым большим сжатием. Но таким образом вы лишаетесь простоты визуального программирования и можете ощутить все неудобства программирования с помощью чистого WinAPI.

Для большей оптимизации размера файла можно использовать Assembler , но он слишком сложен, да и писать программу на нем намного дольше, чем даже на чистом С. Именно поэтому данная тема не рассматривается в этой книге.

Сжимать готовые программы с помощью компрессоров. Объектный код сжимается в несколько раз, и программа, созданная с использованием MFC , может превратиться из монстра в 300 Кбайт в скромного по размерам "зверя", занимающего на диске всего 30—50 Кбайт. Главное преимущество состоит в том, что вы не лишаетесь возможностей объектного программирования и можете спокойно забыть про неудобства WinAPI.


Второй метод мы уже обсудили (см. разд. 1.1), поэтому остается только описать первый вариант.

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

Теперь переходим к практическим занятиям. Запустите Visual C++ и создайте новый проект. Для этого нужно выбрать команду меню File / New / Project (Файл/Новый/Проект). Перед вами откроется окно создания нового проекта ( 1.5). Слева расположено дерево типов проектов. Нас интересует C++, поэтому выберите пункт Visual C++ Projects . Этот пункт мы будем выбирать при написании абсолютно всех примеров из данной книги. С правой стороны в списке Templates (Шаблоны) появятся иконки для создания различных проектов на основе мастеров. Выберете пункт MFC Application (Приложение MFC).

Внизу окна расположены две строки ввода. В первой вы должны указать имя создаваемого проекта. Оно будет использоваться в качестве имени запускаемого файла и имени файла, который вы будете в дальнейшем открывать для редактирования. Давайте здесь укажем TestMFC.



1.5. Настройки программы



1.6. Окно Мастера создания нового проекта

В строке Location (Расположение) нужно указать путь к папке, в которой среда разработки создаст необходимые проекту файлы. Я рекомендую завести свою папку с именем, например, My C++ Projects , в которой будут размещаться все проекты. Выберете эту папку и нажмите ОК. По окончании работы мастера у вас в папке My C++ Projects появится еще одна папка с именем TestMFC , в которой и будут находиться все файлы данного проекта.

Как только вы нажали O К в окне создания нового проекта (см. 1.5), перед вами откроется окно Мастера создания нового MFC -приложения ( 1.6).

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



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



Application Type (Тип приложения). В этом разделе мы задаем тип создаваемого приложения. Давайте укажем здесь следующие значения:

Single Document (Документ с одним окном) — нам достаточно будет только одного окна. Многодокументные приложения мы не рассматриваем, поэтому большинство примеров с использованием MFC будет основываться на этом типе приложений или на основе диалоговых окон (Dialog based);

Project style (Стиль проекта) — во всех приложениях будем использовать стиль по умолчанию (MFC стандарт);

Document / View architecture support (Поддержка архитектуры Документ/ Просмотр) — это значение нас пока не интересует, поэтому оставим установленное по умолчанию;

Advanced Features (Дополнительные возможности). В этом разделе в будущем нас будет интересовать только параметр Windows socket (поддержка сокетов Windows), который позволит нам писать примеры для работы с сетью.

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

Нажмите кнопку Finish , чтобы завершить работу мастера. По завершении его работы вы увидите главное окно среды разработки Microsoft Visual C++, представленное на 1.7. Это окно мы будем использовать достаточно часто, поэтому в процессе работы уточним все детали.



1.7. Главное окно среды разработки Microsoft Visual C++

Сейчас нас интересуют параметры проекта. Мы должны отключить все, что нам будет мешать. При сборке проекта в Visual C++ по умолчанию могут использоваться два вида настроек: debug и release . Первый необходим на этапе разработки, и в этом режиме Visual C++ создает запускаемый файл, который содержит слишком много дополнительной информации. Она будет необходима вам в среде разработки в дальнейшем при отладке программы и поиске ошибок. Во втором режиме эта информация отключается, и запускаемый файл будет меньшего размера.



В верхней части окна на панели с кнопками найдите выпадающий список, в котором написано Debug . Измените это значение на Release .

Среда разработки Visual C++ может создавать запускаемые файлы, использующие MFC -библиотеки, двух типов: статические и динамические. По умолчанию используется динамическая сборка. В таком режиме запускаемый файл получается меньшего размера, но он не будет работать без динамических библиотек, таких как mfcXXX.dll, где XXX — это номер версии среды разработки.

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

Чтобы изменить тип использования MFC , в окне Solution Explorer сначала выберите имя вашего проекта, а затем меню команду Project/Properties. На 1.8 представлено окно свойств проекта.



1.8. Окно свойств проекта

Слева в окне расположены разделы свойств. Нас будет интересовать раздел General (Основные). Выделите его, и в основном окне появится список соответствующих свойств. Найдите свойство Use of MFC и измените его значение на Use of MFC in a Static Library. Нажмите кнопку OK , чтобы закрыть окно и сохранить изменения.

Теперь соберем наш проект в готовый исполняемый файл. Для этого нужно выбрать команду меню Build/Build solution (Построить/Построить проект). Внизу главного окна (см. 1.7), в панели Output (Вывод) будет появляться информация о ходе сборки. Дождитесь, пока не появится сообщение типа:

---------------------- Done ----------------------
Build : 1 succeeded , 0 failed , 0 skipped

Теперь перейдите в папку, которую вы выделили под хранение проектов, и найдите там папку TestMFC . В ней расположены файлы с исходным кодом нашего проекта, сгенерированные мастером. Тут же должна быть папка Release , в которой среда разработки создала во время компиляции промежуточные и исполняемый файлы. Выделите файл TestMFC . exe и посмотрите его свойства (надо щелкнуть правой кнопкой мыши и выбрать в появившемся меню пункт Свойства). Размер нашего пустого проекта составляет 386 Кбайт. Это очень много.



Попробуйте открыть его в программе ASPack и сжать. У меня сжатый исполняемый файл составил 187 Кбайт. Сжатие составило практически 50%, и это уже более или менее приемлемый размер для шуточной программы.

Примечание
Пример этой программы вы можете увидеть на компакт-диске в папке /Demo/Chapter1/TestMFC. Чтобы открыть этот пример, выберите команду меню File/Open solution. Перед вами появиться стандартное окно открытия файлов. Перейдите в нужную директорию и выберите файл с именем проекта и расширением vcproj.
Чтобы сделать программу еще меньше, необходимо отказаться от MFC и писать на чистом С. Это немного сложнее и не так удобно, но для небольших проектов вполне приемлемо.

Для того чтобы создать маленькую программу без использования MFC, нужно снова использовать меню File/New/Project и здесь выбрать уже тип создаваемого проекта Win32 Project. В качестве имени давайте укажем ctest, a путь оставим тот же.

Если у вас все еще открыт предыдущий проект, то под строкой ввода пути для проекта есть переключатели: Add to solution (Добавить в решение) и Close solution (Закрыть решение). Если вы выберете первый из них, то текущий проект будет добавлен в уже открытый. Если выбрать закрытие, то текущий проект будет закрыт и для вас будет создано новое рабочее поле.

После нажатия кнопки O К перед вами откроется окно мастера. Первый шаг чисто информационный, поэтому выберете раздел Application Settings (Настройки приложения). Перед вами откроется окно как на 1.9.

Нас интересует простое приложение Windows , поэтому вы должны выбрать в разделе Application type (Тип приложения) переключатель Windows application. Больше нигде не надо ставить галочки, чтобы мастер не добавлял ничего лишнего. Нам необходим только самый минимум. Нажмите кнопку Finish, и будет создан новый проект.

Здесь также нужно изменить Debug на Release, чтобы создать проект без дополнительной информации. В настройках проекта ничего менять не надо, потому что созданный мастером шаблон не использует MFC и ему не нужны динамические библиотеки. Можете зайти в свойства проекта и убедиться, что в свойстве Use of MFC стоит Standard Windows Libraries (использовать стандартные библиотеки Windows). Это значит, что нет MFC, и ничего дополнительного программе не надо, только стандартные библиотеки Windows.





1.9. Окно настроек приложения

Попробуйте откомпилировать проект. Для этого выберете команду меню Build/Build solution. По окончании сборки перейдите в папку ctest/Release каталога, где вы создавали проекты, и посмотрите на размер результирующего файла. У меня получился 81 Кбайт. Если сжать файл, то получится менее 70 Кбайт. Вот такая программа уже скопируется по сети достаточно быстро.

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


Диалоговые окна


Отдельного разговора заслуживают диалоговые окна. С их помощью пользователи вводят информацию в программу и получают ответы. Если что-то окажется неудобным или раздражающим, то щелчок по uninstall.exe не заставит себя ждать. К дизайну каждого элемента нужно подходить с особой тщательностью.

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

Рис. 1.15. Окно "О программе" в The Bat!

Это окно оформлено нестандартно, красиво, но с большой ошибкой — даже в окне "О программе" должна быть кнопка Закрыть или OК , потому что трудно догадаться, что форма закрывается только по щелчку в определенной области.

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

Посмотрите на окно создания резервной копии в программе The Bat! ( 1.16). Оно по вертикали больше, и из-за этого смотрится не очень хорошо. Я понимаю, что разработчики постарались встроить максимум возможностей, но само окно и неровности в компонентах немного раздражают.

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

1.16. Окно создания резервной копии в The Bat!, вытянутое по вертикали

Единственное, что здесь хорошо сделано — все компоненты удачно сгруппированы. Элементы выбора Check Box собраны В одну группу, a Radio Button — в другую.


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

Если нужно отображать свойства какого-то объекта (документа, файла), то его желательно делать вытянутым по вертикали. Это исключение из правил, которое надо запомнить, и ему, желательно, следовать всегда.



Рис. 1.17. Окно создания резервной копии в The Bat! после небольших манипуляций



1.18. Окно для отображения свойств

Всю информацию нужно разложить по тематическим разделам в виде отдельных вкладок. Ярким примером такого окна является окно свойств в проводнике Windows ( 1.18) или окна свойств документа в MS Word (меню Файл/Свойства).

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

Если в программе много параметров, то можно соорудить что-то а-ля MS Word (меню Сервис/Параметры) из множества вкладок ( 1.19). Если настроек слишком много, то используем стиль Netscape Navigator, где слева построено дерево разделов, и при выборе нужного пункта в центре окна отображаются соответствующие параметры.



1.19. Окно для настройки параметров

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

Посмотрите на 1.20, где показано окно настроек программы mIRC. Оно смотрится просто ужасно. Дерево разделов слишком узкое, окно недостаточно широкое, элементы управления в разделах не упорядочены и содержат абсолютно ненужные кнопки, которые можно куда-нибудь вынести. Кнопка Sort отделена от других и пропадает в бездне, а раскрывающиеся списки имеют разную длину.





Рис. 1.20. Окно свойств mIRC

Теперь посмотрите на 1.21, где я немного причесал это окно в графическом редакторе MS Paint. Теперь оно стало более широким, и элементы встали стройными рядами, потому что выровнены по левому краю и имеют одинаковую ширину. Кнопка с изображением солнца не имела подсказки, и ее предназначение вообще не понятно (просто перебрасывает нас на настройки раздела Connect), поэтому была удалена.

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



Рис. 1.21. Улучшенное окно свойств mIRC



1.22. Перегруженный интерфейс Feurio

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

Я вспоминаю, как однажды описывал программу Feurio для журнала "Хакер", которая была мощнейшей для записи музыкальных дисков. Пришлось потратить неделю на разборки с интерфейсом, а вывод был один — программа обладает большими возможностями и должна присутствовать в арсенале любого меломана. Но я не до такой степени страстный любитель музыки, чтобы мириться с неудобствами ( 1.22). Лучше использовать простой, но удобный WinOnCD, чем мощный, но некрасивый Feurio. Вот если бы я писал диски каждый день, то, может быть, использовал что-то сложное.

Помните, если не знаете, как что-то сделать, посмотрите у конкурентов!!!


Интерфейс главного окна


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

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

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

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

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

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

Панель инструментов тоже должна быть максимально приближена к стандартному виду. Панель, содержащая основные команды (создать, открыть, печать и т. д.), должна быть вверху окна. Нельзя располагать ее по краям или внизу. Картинки лучше всего использовать стандартные, которые применяются в таких программах, как MS Office или других программах корпорации Microsoft. Пользователи сроднились с ними и быстрее смогут привыкнуть к вашему интерфейсу.


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

Рисунки должны быть информативными и вызывать ассоциации с выполняемой командой. Если под кнопкой с рисунком бегемота спрятаны настройки цветовой палитры, то об этом не догадается даже Настродамус.

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

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


Если вы прочитали этот раздел


Если вы прочитали этот раздел внимательно, то можете считать, что с азбукой оптимизации вы уже знакомы. Но это только основы, и тут есть куда развиваться. Я бы мог рассказать больше, но не вижу особого смысла, потому что оптимизация — это процесс творческий, и в каждой конкретной ситуации к нему можно подойти с разных сторон. И все же те законы, которые мы сегодня рассмотрели, действуют в 99,9% случаев.
Если вы хотите познать теорию оптимизации в программировании более глубоко, то вам нужно больше изучать принципы работы процессора и операционных систем. Главное, что законы вы уже знаете, а остальное придет со временем.

Элементы управления


Все элементы управления в окнах должны быть из состава Windows. Н e надо создавать кнопки неправильной формы только из-за того, что вы это умеете делать. Я тоже когда-то делал круглые кнопки и плоские поля ввода. Это плохо влияет на продажи, и очень сложно таким образом сделать что-то гармоничное.

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



Код программы


Теперь познакомимся с кодом программы, который нам сгенерировал мастер. Он находится в файле ctest.cpp , и вы можете его увидеть в листинге 1.1. Весь код мы, конечно же, рассматривать не будем. Данная книга не является самоучителем по языку C++, хотя необходимые вещи рассматриваются очень подробно. Если вы уже знакомы с этим языком программирования, то для вас код файла должен быть понятен. Если нет, то достаточно того, что мы сейчас рассмотрим.

Листинг 1.1. Исходный код файла ctest.cpp
#include "stdafx.h" #include "ctest.h" #define MAX_LOADSTRING 100 // Global Variables: // (Глобальные переменные): HINSTANCE hInst; // current instance // (текущий интерфейс)
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text // (Заголовок окна) TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name // (Имя класса главного окна) // Forward declarations of functions included in this code module: // Описание процедур, используемых в этом модуле: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);

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_CTEST, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance);

// Perform application initialization: // (Инициализация приложения:) if (!InitInstance (hInstance, nCmdShow)) { return FALSE; }

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

// Main message loop: //(Главный цикл обработки сообщений:) while (GetMessage(msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, msg)) { TranslateMessage(msg); DispatchMessage(msg); } }


return (int) msg.wParam; }

// FUNCTION (Функция): MyRegisterClass() // PURPOSE (Предназначение): Registers the window class // (Регистрация класса окна) // COMMENTS (Комментарии): // This function and its usage are only necessary if you want this code // to be compatible with Win32 systems prior to the 'RegisterClassEx' // function that was added to Windows 95. // It is important to call this function so that the application // will get 'well formed' small icons associated with it. // (Эта функция и ее использование необходимы, только если вы хотите, // чтобы этот код был совместим с системой Win32 до функции // 'RegisterClassEx', которая была добавлена в Windows 95. // Это важно, вызвать эту функцию так, чтобы приложение получило // 'хорошо отфарматированную' маленькую иконку, ассоциированную с ним.)

ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_CTEST); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCTSTR)IDC_CTEST; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

return RegisterClassEx(wcex); }

// FUNCTION (Функция): InitInstance(HANDLE, int) // PURPOSE (Предназначение): Saves instance handle and creates main // window // (Функция сохраняет указатель экземпляра и создает окно) // COMMENTS (Комментарии): // In this function, we save the instance handle in a global variable // and create and display the main program window. // (В этой функции мы сохраняем указатель экземпляра в глобальной // переменной, создаем и отобажаем главное окно.) BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd;

hInst = hInstance; // Store instance handle in our global variable // (Сохраняем указатель экземпляра в глобальной переменной)



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

if (!hWnd) { return FALSE; }

ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);

return TRUE; }

// FUNCTION (Функция): WndProc(HWND, unsigned, WORD, LONG) // PURPOSE (Предназначение): Processes messages for the main window // (Обработка сообщений главного окна) // WM_COMMAND — process the application menu // (обработка меню приложения) // WM_PAINT - Paint the main window // (Прорисовка окна) // WM_DESTROY — post a quit message and return // (отправка сообщения о выходе из программы) 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) { 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; }

// Message handler for about box // (Обработчик сообщения для окна "О программе") // Мы окно о программе удалили, поэтому следующий код можно удалять LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE;

case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; }

Мы уже знаем, что у нашей программы не должно быть никаких окон. Из проекта в разделе Dialog окно О программе мы уже удалили (см. разд. 1.3.1). Но в коде еще остались ссылки на него, поэтому вы не сможете выполнить программу. Чтобы проект запустился, удалите все, что находится после следующей строки:



// Мы окно о программе удалили, поэтому следующий код можно удалять

Этот код отображает окно О программе, и его можно удалять полностью.

Теперь перейдите в процедуру wndProc и удалите здесь вызов процедуры About . Для этого нужно найти и удалить следующие три строчки:

case IDM_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;

Это обработчик события для пункта меню Help/About нашей программы. Все обработчики находятся в функции WndProc и имеют следующую структуру:

case Идентификатор Действия break;

Здесь Идентификатор — это константа, которая назначена элементу управления (например, пункту меню). Оператор case проверяет, если пришло событие от элемента управления с указанным идентификатором, то выполняется последующий код до оператора break. Чуть позже мы познакомимся с событиями на практике.

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

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

ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);

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

Первая строка кода показывает созданное в данной программе окно, а вторая — обновляет его содержимое. Вы можете закомментировать строки, поставив перед ними две косые черты (//). Попробуйте теперь скомпилировать и запустить программу. Вы ничего не увидите ни на экране, ни по нажатию клавиш Ctrl+Alt+Del. Если у вас Windows 2000/XP , то только на вкладке Процессы окна Диспетчер задач Windows вы сможете найти в списке свою программу ( 1.12).

Если вы не имели опыта программирования на Visual C++ и сейчас чего-то не поняли, не расстраивайтесь. Постепенно все встанет на свои места. В дальнейшем мы рассмотрим достаточно много из того, что вы видите в исходном коде.



Рассмотрим подробнее некоторые части представленного кода. Программа начинает выполнение с функции _tWinMain (листинг 1.2).



1.12. Программа ctest среди процессов

Листинг 1.2. Стартовая функция
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // Дальше идет объявление двух переменных MSG msg; HACCEL hAccelTable;

// Инициализация строковых переменных LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hlnstance, IDC_CTEST, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // Выполнение инициализации приложения if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_CTEST); // Главный цикл обработки сообщений Windows while (GetMessage(msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, msg)) { TranslateMessage(msg); DispatchMessage(msg); } } return (int) msg.wParam; }

Мы уже много раз использовали слово "функция", но не дали ей определение. Если у вас есть опыт программирования в C++, то вы должны быть знакомы с этим понятием. Некоторые вещи выходят за рамки данной книги, и я их буду опускать. Более подробную информации о языке C++ вы можете узнать в специализированной литературе. И все же мы рассмотрим максимальное количество информации, необходимой для понимания примеров.

Все функции в C++ объявляются следующим образом:

Тип Имя (Параметры) { }

Тип — тип возвращаемого значения. Если используется int, это указывает на число целого типа.

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

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

У нашей главной функции после возвращаемого типа стоит ключевое слово APIENTRY, которое указывает на точку входа программы.

Теперь посмотрите на листинг 1.1. Здесь я расставил комментарии, чтобы вы понимали, что происходит. Как мы уже знаем, комментарии начинаются с двойной косой черты (//). Текст, который стоит после этих черточек, не влияет на работу программы, а только поясняет код.



В самом начале функции объявляются две переменные:

MSG msg; HACCEL hAccelTable ;

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

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

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

Второй способ — использование глобальных переменных, что не рекомендуется делать. Глобальные переменные видны в любой функции. Их принято определять в заголовочном файле (файл с расширением h) или до описания функций, в самом начале файла.

Локальные переменные объявляются внутри функции, и к ним можно обратиться только в ней. Такие переменные автоматически создаются при запуске функции в специальной области памяти (стеке) и автоматически уничтожаются при выходе из нее. Автоматическое создание/удаление относится только к простым переменным, но не к указателям, которые желательно освобождать вручную.

После объявления переменных идут следующие две строки:

LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_CTEST, szWindowClass, MAX_LOADSTRING);



Эти две функции с именем LoadString загружают текст из ресурсов строк. Функция — это часть кода, которая имеет имя и может вызываться из других мест программы. В данном случае выполнится код загрузки ресурса.

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

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

IDS_APP_TITLE — имя ресурса, который надо загрузить. Если вы сейчас дважды щелкните в ресурсах на разделе String Table, то перед вами откроется таблица строк, в которой в одной колонке будет имя строки, а во второй текст. Вот именно это имя и нужно указывать в этом параметре.

szTitle — переменная, в которую должно поместиться значение. В начале файла у нас объявлено две переменные с именами szTitie и szWindowClass:

TCHAR szTitie[MAX_LOADSTRING]; // Текст заголовка окна

TCHAR szWindowClass[MAX_LOADSTRING]; // Имя класса главного окна
Как мы уже знаем, при объявлении переменных вначале идет тип. В данном случае указан tchar , что означает строку. Далее идет имя переменной. А для строк еще надо указать в квадратных скобках размер (максимальную длину в символах). В качестве размера указано MAX_LOADSTRING. Это константа, которая равна максимальному размеру загружаемых символов. Можно было бы указать в квадратных скобках и реальное число, но если есть возможность, то лучше использовать предопределенные константы.

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

После этого идет вызов функции MyRegisterClass(hInstance). В ней происходит заполнение структуры WNDCLASSEX. Что такое структура? Это особая переменная, которая хранит в себе набор переменных любого типа. Например, структура может хранить одну переменную с именем Age числового типа и одну строкового — с именем Name. Чтобы прочитать или изменить значение этих переменных, нужно написать Структура.Переменная. Структура — это имя структурной переменной, а переменная — это имя переменной.



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

style — стиль окна;

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

Hinstance — манипулятор, который мы получили при запуске программы в процедуре _tWinMain;

HbrBackground — цвет фона (в принципе, он необязателен, но по умолчанию используется цвет окна);

LpszClassName — имя создаваемого класса;

Hcursor — курсор. Сюда загружается стандартный курсор-стрелка.

Все, структура готова, и мы можем зарегистрировать новый класс будущего окна. Для этого вызывается функция WinAPI RegisterClassEx(wcex). После этого в системе есть описание вашего будущего окна. Почему будущего? Да потому, что само окно мы еще не создали. Для этого нужно еще вызвать функцию CreateWindow (это происходит в функции InitInstance, которая в свою очередь вызывается в _tWinMain после вызова MyRegisterClass):

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

У нее достаточно много параметров, и давайте посмотрим на них внимательнее.

Имя класса. Мы зарегистрировали класс и сохранили имя в переменной szWindowClass, значит и здесь мы должны указать именно этот класс.

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

Стиль окна. Нас интересует простейшее WS_OVERLAPPEDWINDOW окно.

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

Главное окно по отношению к создаваемому. Наше окно само по себе главное, поэтому указываем NULL, что соответствует нулю.

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



созданное окно;

параметры отображения окна.

Здесь указано nCmdShow, значение, которое передается программе в зависимости от параметров, указанных в свойстве ярлыка, вызывающего программу. Остальные значения параметра можно посмотреть в файле справки по WinAPI-функциям.

И последняя подготовительная функция — UpdateWindow. Это просто отрисовка созданного окна.

Теперь разберемся с циклом обработки сообщений. Функция GetMessage ожидает пользовательского или системного сообщения, и как только оно наступает, возвращает true (истина). Полученное сообщение преобразуется

В необходимый вид с помощью TranslateMessage и отправляется обработчику сообщений с помощью вызова функции DispatchMessage.

В каждой программе должна быть процедура обработки сообщений. Какая именно? Мы указали ее при создании класса окна в свойстве WindowClass.Lpfnwndproc. В Visual C++ принято называть ее wndProc - стандартное имя, используемое по умолчанию. Сама же процедура должна выглядеть приблизительно как в листинге 1.1.

В процедуре-обработчике событий желательно всегда делать вызов функции defwindowproc. Эта функция ищет в системе обработчик полученного сообщения, установленный по умолчанию. Это очень важно, тогда вам не придется без особой необходимости самому писать то, что может сделать ОС. Обработка полученного сообщения происходит с помощью сравнения параметра message со стандартными событиями. Например, если message равен wm_destroy, то это значит, что программа хочет уничтожиться, и тогда в обработчике можно освободить выделенную под программу память.

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

Если вы захотите сделать это окно невидимым, то просто уберите из кода функцию ShowWindow, которая отображает окно на экране. Ваша программа сразу же станет невидимой в системе. Второй способ — изменить второй параметр этой процедуры на SW_HIDE (внешне равносильно отсутствию вызова процедуры). Функцию ShowWindow используют с параметром SW_HIDE, когда нужно спрятать окно в процессе выполнения программы без его уничтожения из памяти компьютера.



Чуть позже мы еще встретимся с процедурой ShowWindow, и не один раз.

Для компиляции проекта выберите команду меню Build/Build Solution. Таким образом, вы соберете проект и создадите запускаемый файл. Чтобы запустить программу, выберите команду меню Debug/Start.

Примечание
На компакт-диске в папке \Demo\Chapter1\empt\ вы можете увидеть исходный код этого примера.

Минимизация и невидимость


Что самое главное при написании программ-приколов? Ну, конечно же, невидимость. Программы, созданные в этой и следующих главах, будут незаметно сидеть в системе и выполнять нужные действия при наступлении определенного события. Это значит, что программа не должна отображаться на Панели задач или в списке запущенных программ, в окне, появляющемся при нажатии Ctrl+Alt+Del. Поэтому прежде чем начать что-то писать, нужно узнать, как спрятать свое творение от чужого глаза.

Кроме этого, программы-приколы должны иметь маленький размер. Приложения, создаваемые Visual C++ с использованием современных технологий MFC (Microsoft Foundation Classes, базовые классы от Microsoft), достаточно "весомые". Даже простейшая программа, выводящая одно окно, отнимет достаточно много места на диске. Если вы захотите отослать такую шутку по электронной почте, то отправка и получение письма с вашей программой отнимут лишнее время у вас и получателя. Это не очень приятно, поэтому в этой главе мы познакомимся с тем, как можно уменьшить размер программ, создаваемых в Visual C++.



Оптимизация программ


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

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

Даже для того, чтобы написать текст из двух строк, мы включаем свой компьютер и загружаем MS Word, тратя на это драгоценное время. А может, легче было бы написать этот текст вручную? Я вас понимаю — не солидно!!!

Программисты — так это вообще "полное бесстыдство", как говорил один из моих преподавателей: "Тра-та-та". Они считают, что раз их творение (в виде исходного кода) никто не увидит, то можно писать что угодно. Так это они ошибаются. С этой точки зрения программы с открытым исходным кодом имеют большое преимущество, потому что намного чище и быстрей. Создавая код, мы ленимся его оптимизировать не только с точки зрения размера, но и с точки зрения скорости. Глядя на такие вещи, хочется ругаться непристойными словами, только программа от этого лучше не станет.

Хакеры далеко не ушли. Если раньше, глядя на программиста или хакера, создавался образ прокуренного, заросшего и немытого молодого человека, то сейчас это цифровое существо, залитое пивом "Балтика" по самые уши, за которое все выполняют машины. Вам медсестра в поликлинике не говорила, что у вас вместо крови одно только пиво льется? Я ничего против пива не имею, я и сам его люблю, но надо же и меру знать.

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


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

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

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

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


Правильное оформление окон


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

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

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

Ярким примером маленькой утилиты с незамысловатыми возможностями, покорившей весь мир, является WinAMP. Программа простая, и какое бы ни было ее главное окно, пользователь всегда сможет разобраться, как запустить воспроизведение музыки. В данном случае именно нестандартное, но красивое решение является залогом победы в своей рыночной нише. А если еще и добавить возможность легкой смены внешнего вида (поддержка скинов), то можно считать, что на 50% победа обеспечена. После этого можно снабжать оригинальный интерфейс солидными возможностями. На 1.13 можно увидеть пример интерфейса такой программы (InterVideo DVD 4).


Если вы разрабатываете программу с множеством возможностей и с разветвленной структурой, то главное окно должно быть выполнено в стандартных цветах Windows и быть прямоугольным. Представьте себе, если бы главное окно MS Word было бы овальным или круглым. Возможно, что это было бы красиво и интересно, но я бы удалил такое через секунду после начала знакомства.

Обязательно придерживайтесь стандартов, сформировавшихся в сфере софта похожего направления. Например, стандартом оформления интерфейса для графического редактора стал Photoshop . Раньше все софтверные компании пытались придумывать что-то свое, но потом смирились с тем, что в данном направлении законодателем моды является фирма Adobe, и стали следовать ей.



Рис. 1.13. Внешний вид программы для просмотра DVD - дисков в ОС Windows



1.14. Интерфейс программы Macromedia Flash MX

Когда появился Flash 5, то разработчики Macromedia постарались максимально приблизить его интерфейс к Photoshop . Несмотря на то, что одна программа для работы с растровой графикой, а другая — для векторной, они в управлении стали похожими. Даже панель инструментов сверху окна убрали, хотя для повышения "юзабилити" панель нужна. Благодаря этому Macromedia Flash 5 получил невероятную популярность, особенно среди профессионалов-художников. А ведь в 5-й версии графические возможности не сильно изменились, главными нововведениями стали расширенный ActionScript и измененный интерфейс. Художники не программируют, поэтому ActionScript их не волнует, а вот интерфейс Adobe Photoshop пришелся по вкусу, потому что все стало знакомым, и не нужно тратить долгие месяцы на переобучение и привыкание. На 1.14 посмотрите образец интерфейса программы Flash MX в исполнении Macromedia .

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



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

Я видел несколько проигрывателей, которые могли бы обойти WinAMP по многим показателям, но именно он стал первопроходцем и завоевал сердца большинства меломанов. Остальные же стали подражателями, и теперь рынок плееров захлестнули проигрыватели с нестандартными окнами и поддержкой скинов.

Отрицательным примером можно назвать программу 3D FTP. Разработчикам понравился успех WinAMP, и они сделали поддержку скинов в FTP -клиенте. Такой ужасной программы я еще не видел!!! Вы представляете Adobe Photoshop или MS Word с поддержкой скинов? Или программисты слишком много выпили, или еще ходят в детский сад и не знают о стандартизации. Клиент 3D FTP был очень мощным, с громадным количеством возможностей, превосходящих многих конкурентов, но благодаря глупому дизайну умер в самом расцвете сил. А надо было всего лишь посмотреть на Cute FTP или CyD FTP Client, привести все окна к его виду и убрать скины и нестандартные элементы управления.

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

Разработка интерфейса — целая наука, и по ней пишут книги, но если знать хотя бы основы, то можно добиться невероятного успеха.


Простейшая программа


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

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

Итак, откройте проект ctest и обратите внимание на панель Solution Explorer . Здесь расположено дерево, в котором по разделам сгруппировано все, что входит в проект. Мое дерево вы можете увидеть на 1.10, и у вас должно быть что-то подобное.

1.10. Дерево проекта

В нашем проекте есть следующие разделы:

Source Files (Файлы исходного кода) — здесь хранятся файлы с исходным кодом программы. Основной код расположен в файле с именем как у проекта и расширением срр. В данном случае это ctest.cpp;

Header Files (Заголовочные файлы) — здесь хранятся файлы описания, содержащие различную вспомогательную информацию, подключаемые модули, описания классов;

Resource Files (Файлы ресурсов) — здесь сейчас хранятся файлы иконок и файл ресурсов ctest.rc.



Ресурсы проекта


Давайте посмотрим содержание ресурсов. Щелкните дважды по имени файла ctest.rc, и в этой же панели появится вкладка Resource View . В ней в виде дерева представлены ресурсы, разбитые по разделам.

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

Раздел Dialog содержит диалоговые окна. Если программа будет невидима, то и никакие окна ей не нужны. В нашем шаблоне, по умолчанию находится окно IDD_ABOUTBOX — окно с информацией о программе. Для удаления окна нужно также щелкнуть правой кнопкой и выбрать в меню пункт Delete.

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

Чтобы пользователь запустил программу, иконка должна быть знакома и не вызывать подозрений. Например, для шуточной программы можно назначить иконку программы MS Word . Если в системе у пользователя прячутся расширения для стандартных файлов (используется по умолчанию), то он подумает, что перед ним Word -документ. А если еще и название файла заманчивое, то он обязательно его запустит.

Чтобы изменить иконку и нарисовать нечто оригинальное, вы должны дважды щелкнуть на ее названии, и в главном окне откроется простой графический редактор ( 1.11), в котором можно изменить изображение. Ничего серьезного в нем нарисовать невозможно, поэтому лучше вставить уже готовое изображение (например, через буфер обмена). Что-то подходящее всегда можно найти в Интернете.

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

В разделе String Table можно хранить строки в виде констант. По умолчанию там находятся названия заголовков окон. Эти строки много места не занимают, поэтому их можно и оставить.

1.11. Редактор иконки



Сжатие запускаемых файлов


Самый простой способ уменьшить размер приложения — использование программы для сжатия файлов. Лично я очень люблю ASPack , которую вы можете скачать в Интернете по адресу http://www.aspack.com или скопировать с компакт-диска из директории Programs (файл установки называется ASPack.exe). Она прекрасно сжимает исполняемые файлы E ХЕ и динамические библиотеки DLL.

Запустите программу ASPack . exe , и перед вами откроется окно ( 1.1) приглашения к установке.

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

Рис. 1.1. Окно установки программы ASPack

Рис. 1.2. Главное окно программы ASPack

Главное окно программы ( 1.2) имеет несколько вкладок:

Open File;

Compress;

Options;

About;

Help .

На вкладке Open File есть только одна кнопка — Open . Нажмите на нее и выберите файл, который вы хотите сжать. Как только выбор сделан, программа перейдет на вкладку Compress ( 1.3) и начнет сжатие.

1.З. Сжатие файла

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

Настроек у ASPack не так уж много и расположены они на вкладке Options ( 1.4).

Давайте рассмотрим, для чего они нужны.

Compress resources (Сжимать ресурсы) — если вы используете при написании программ MFC и создаете в ресурсах диалоговые окна программ или храните там растровые картинки, то в исполняемом файле будет соответствующая секция достаточно большого размера. Из моей практики могу сказать, что больше всего места отнимают именно картинки, потому что они хранятся без сжатия. Если установить этот параметр (поставить галочку), то программа будет сжимать эту секцию.

Create backup copy (Создавать резервную копию) — перед началом процесса сжатия программа будет создавать резервную копию. Старое содержимое будет располагаться в файле с таким же именем и в той же папке, но с расширением bak. Например, если вы сжимали файл mypro - gram.exe, то резервная копия будет иметь имя myprogram.bak.
Я рекомендую вам всегда ставить этот флажок, потому что ASPack иногда ведет себя нестабильно, и программа может быть испорчена. В этом случае вы можете восстановить старый файл из резервной копии с помощью обратной замены расширения bak на ехе. Если вы не ставите флажок, то я рекомендую самостоятельно делать резервную копию файла.
Если у вас есть полный исходный код программы, то испорченный файл можно перекомпилировать. Но если вы сжимаете чужую программу, то без резервной копии восстановление невозможно, поэтому не надо создавать себе лишние проблемы.
После того как программа произвела сжатие, проверьте ее на работоспособность. Чаще всего, если программа запустилась, то в дальнейшей работе проблем не будет. Очень редко происходят ошибки при открытии определенных окон, но они бывают. Перед поставкой клиенту окончательной версии тщательно протестируйте каждую возможность вашего продукта. Если вы создаете коммерческий проект, то вряд ли кому-то понравится сообщение об ошибке.




1.4. Настройки программы

Auto run after loading (Автоматический запуск после загрузки) — как только вы открыли файл на вкладке Open File, программа может автоматически начать сжатие, если вы поставили галочку в этом параметре.

Exit when done (Выйти по завершению) — закрыть программу по окончании процесса сжатия.

Max compression (Максимальное сжатие) — вероятность неправильной работы программы при использовании этого параметра увеличивается, но и файл может стать меньше. Можете протестировать программу при максимальном сжатии, и если возникнут проблемы, то убрать галочку с этого параметра.

use Windows DLL loader (Использовать загрузчик Windows ) — существует два загрузчика динамических библиотек: стандартный Windows и оптимизированный для старых компиляторов Borland C++. Мы будем использовать для написания программ Visual C++, поэтому вы должны поставить галочку на этом параметре.

Preserve extra data (Игнорировать дополнительные данные) — в некоторых программах в конце запускаемого файла могут быть какие-то дополнительные данные. Если ASPack попытается их сжать, то эти данные могут стать недоступными. Примером такого файла может быть инсталлятор в виде одного запускаемого файла. В нем вначале идет исполняемый код программы, а в конце — добавленные файлы, которые инсталлятор должен скопировать на компьютер. Именно эти данные иногда необходимо игнорировать и не сжимать.

Теперь давайте разберемся, как работает сжатие. Сначала весь код программы сжимается архиватором. Если вы думаете, что он какой-то "навороченный", то сильно ошибаетесь. Для сжатия используется обычный архиватор, только оптимизированный для сжатия двоичного кода. В конец сжатого кода добавляется код разархиватора, который будет во время выполнения разжимать программу в первоначальное состояние. И в самом конце ASPack изменяет заголовок исполняемого файла так, чтобы при старте сначала запускался разархиватор.

В ASPack алгоритм сжатия очень хороший, а разархиватор достаточно маленький (меньше 1 Кбайт), поэтому сжатие происходит очень сильно, а к результирующему файлу добавляется только один килобайт. Таким образом, программа может сжать файл размером в 1,5 Мбайт в 300—400 Кбайт.



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

Некоторые считают, что из-за расходов на распаковку программа будет работать медленней!!! Я бы сказал, что вы не заметите разницу. Даже если и будут какие-то потери, то они будут неощутимы (по крайней мере, на современных компьютерах). Это происходит потому, что архивация хорошо оптимизирована под двоичный код. И по сути дела, распаковка происходит только один раз и в дальнейшем никакого влияния на работу программы не оказывает. В результате потери в скорости из-за сжатия будут неощутимы.

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

При нормальном программировании с использованием всех "навороченных" возможностей типа визуальности и объектного программирования код получается большим, но его можно сжать специальным архиватором на 60 - 70%. А писать такой код намного легче и быстрее.

Еще одно "за" использование сжатия — заархивированный код труднее взломать, потому что не каждый Disassembler сможет прочитать упакованные команды. Так что помимо уменьшения размера вы получаете защиту, способную остановить большинство взломщиков. Конечно же, профессионала этим не отпугнешь. Но взломщик средней руки не будет мучиться со сжатым двоичным кодом.

Примечание
На компакт - диске в папке \ Demo \ Chapter 1\ Screens 1 вы можете найти файлы приведенных цветных рисунков.