VR-online JOURNAL Фленов Михаил and VR-Team VR-online для программистов №5 Delphi (Базы данных): Работа с ADO ..........................................................................................3 Delphi (OpenGL): Собственные примитивы ...............................................................................8 Delphi: Работа с системным реестром .......................................................................................13 Delphi: Создаём отчётность в Excel ...........................................................................................17 Delphi: Работа с файлами............................................................................................................20 Delphi (Графика): Работа с PaintBox..........................................................................................24 SQL: SQL калькулятор................................................................................................................26 SQL: Объединения.......................................................................................................................28 Java: Компоненты ........................................................................................................................31 OpenGL: Примитивы библиотеки GNU ....................................................................................34 Sockets: Компоненты ТServerSocket и ТClientSocket...............................................................37
Copyright: VR-online Journal http://www.vr-online.ru
VR-online Journal (Horrific and VR-Team)
Только что закончился матч Россия-Грузия. Я просто в шоке, первый раз я вижу осмысленную игру нашей сборной, отличную игру наших нападающих, отличную защиту и созидательное нападение. Я ярый болельщик сборной и за такую команду мне не будет стыдно, если она поедет на чемпионат Европы. Первое, что бросается в глаза – вратарь перестал выносить мяч. За две последние игры не было ни одного выноса на чужую половину. Этого и не должно быть. Вратарь должен защищать ворота и давать точный пас своим защитникам и полузащитникам, а их задача уже растягивать оборону и создавать моменты. Защита справляется со своей задачей на 99%. Мы пропускаем, но это рикошеты или авто голы. Реально, наши соперники ничего сделать не могут. Полузащита – полностью блокирует созидание соперника, контролирует мяч и создаёт моменты. Я рад за возвращение Титова, который после долго отсутствия забил очень важный мяч. Единственный, кто меня не очень устраивает – Гусев. Это отличный игрок, но он игрок удара, штрафного, углового. Но при наличии такого игрока как Мостовой, Гусев уходит в тень. Мостовой отлично играет в полузащите на любой позиции и при этом имеет великолепный удар. Нападение. Все ругали нашу сборную за отсутствие нападения, но сейчас я уверен, что оно есть. Кержаков, Булыкин и Сычёв – это действительно нападающие. Они очень хорошо разрывают защиту соперника, создают моменты, а главное - забивают. Единственный, кто пока не забил – это Кержаков. Но как много работы он выполняет, какие выдаёт пасы. Ведь именно с его паса Булыкин забивал один из своих голов Швейцарцам, и именно с его скидки головой Титов рвал сетку в воротах сборной Грузии. А ведь он мог и сам попытаться пробить, но не пожадничал и выдал великолепные пасы. Мне даже жалко, что его удары пока не достигают цели. У нас есть хорошие шансы выйти в финальную стадию чемпионата, если команда будет играть так и дальше и настраиваться на игру с первой секунды, а не после забитого мяча.
Для программистов №5
VR-online JOURNAL Horrific aka Фленов Михаил
INFO: ИДЕЯ И РЕАЛИЗАЦИЯ: Флёнов Михаил (Horrific) ГРАФИКА: Фленов Михаил, tr4sh
VR-Team: Crazy_Script, Del, Demogorgon, Fighter, Mish!, Negus, Spider NET, tr4sh INTERNET: WWW: http://www.vr-online.ru E-MAIL: horrific@vr-online.com ДИЗАЙН САЙТА: tr4sh КОДИНГ САЙТА: Mish!
Данный журнал распространяется в виде PDF файлов. Вы можете выкладывать номера на любые носители без изменения внешнего вида журнала, без перевода в другие форматы, без изменения самого файла. В журнал запрещается вносить изменения. Перепечатка материалов запрещена. Журнал распространяется бесплатно, и ты можешь скачать его с нашего сайта, поэтому мы не видим смысла в перепечатывании материалов. Если ты хочешь стать автором журнала, то присылай свою статью на наш e-mail и мы обязательно включим её в очередной номер.
Фленов Михаил aka Horrific
http://www.vr-online.ru
2
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Delphi (Базы данных): Работа с ADO Сегодня ты познакомишься с технологией ADO. Она позволяет эффективно соединяться с таблицами Access, Excel и другими произведениями от Microsoft. То же самое можно было бы сделать и через BDE (как мы делали раньше), но с базами данных от Microsoft лучше работать через ADO (меньше глюков:). Для начала немного теории. Что такое АDO и зачем она нужна? АDO - это, грубо говоря, та же BDE, только реализация Microsoft. Как ты понимаешь, icrosoft не устроила BDE, поэтому они решили сделать свою библиотеку универсального доступа к базам данных. Я заметил, что Microsoft всегда что-то не устраивает и им обязательно нужно влезть в каждую дырку. Через ADO, как и через BDE можно подсоединиться к любой базе данных, достаточно иметь соответствующий драйвер. В этом случае твоя программа общается с базой данных через ADO. В этом есть свои преимущества и недостатки. Основное преимущество - к любой базе можно подключиться, используя универсальные функции ADO. Поэтому, если в процессе разработки выясниться, что требуется другое хранилище данных, то не придётся переделывать всю прогу. То же самое относиться к BDE, с которым мы до сих пор работали. Основной недостаток - теряются некоторые специфические возможности базы данных, потому что ADO не может реализовать все особенности Access, Excel, и других баз данных. Ещё один недостаток - с помощью него тяжело создавать новые базы данных или изменять их поля. Иногда это просто невозможно. У ADO есть одно преимущество перед BDE - драйверы доступа к базам от Microsoft установлены на большинстве компьютеров с Windows, особенно если установлен любой Office, а драйверы и сам BDE встречается очень редко. Перейдём непосредственно к реализации ADO в Delphi. Для этого нам нужна любая база данных на Access. Я создал такую базу, и её ты сможешь забрать вместе с исходниками сегодняшнего примера для своих извращений в конце статьи.
Рис 1
http://www.vr-online.ru
3
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Устанавливаем на форму ТADOConnection из закладки ADO. Выделяем этот компонент и дважды щёлкаем по свойству ConnectionString. Должна быть выбрана опция "Use connection string" как на рисунке 1. Теперь жми на пимпу "Build" и перед тобой откроется окно выбора драйвера, (рис 2) в котором показаны все установленные в твоём компьютере. Я буду коннектится с базой Access, поэтому я выбираю драйвер "Microsoft Jet 4.0 OLE DB Provider". Если у тебя установлен Office, то этот драйвер у тебя обязан присутствовать.
Рис 2. Окно выбора драйвера После выбора драйвера можно смело давить на кнопку "Next" или выбирать закладку "Connection". Перед тобой открывается простенькое окошко, как на рисунке 3. Если ты выбрал другой драйвер, то окошко может отличаться.
http://www.vr-online.ru
4
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Рис 3. Окно выбора файла В окошке "Select or enter a database name" введи полный путь к базе данных. Если твоя база будет расположена в той же директории, что и прога, то желательно ввести только имя базы, без полного пути. В этом случае ты избавишься от болезни выходного отверстия при переносе проги в другую директорию или другой комп. Иначе, тебе придется перекомпилировать прогу или программно корректировать строку коннекта. Больше ничего не надо менять, а просто нажать ОК. Для любопытства можно полазить по другим закладкам этого окна, но я не думаю, что ты найдёшь что-нибудь полезное. Поэтому закрывай его и давай двигаться дальше. Теперь поставим на форму ТADOTable и старый - добрый ТDataSource. У ТADOTable в строке Connection укажем созданный нами ADOConnection1. Можно и не указывать, а просто ещё раз заполнить строку ConnectionString у ТADOTable, но это не желательно, потому что имя базы может измениться и тебе придётся у всех компонентов менять свойство ConnectionString. А так нужно поменять только у ADOConnection1. У DataSource1 свойство DataSet установим в ADOTable1. Теперь возвращаемся в ADOConnection1 и меняем свойство LoginPrompt в FALSE. Это нужно, чтобы не было занудного окна о вводе пароля. После этого в ADOTable1 выбираем в свойство TableName имя таблицы внутри базы данных. Если ты уже работал с Access, то знаешь, что внутри одного файла моет находиться несколько таблиц. До сих пор мы указали только имя файла, а теперь указывает имя таблицы на которую будет ссылаться ADOTable1. Теперь можно устанавливать свойство Active у ADOTable1 в TRUE.
http://www.vr-online.ru
5
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Всё!!! Остальная работа будет происходить также как и с простыми таблицами, с которыми мы уже работали. Чем мне нравится Delphi, так это тем, что не надо особо переучиваться. ТADOTable имеет похожие свойства и методы как и у простого ТTable. Всё, качай исходник и выделывайся над ним как хочешь. Для удобства я поставил на него DBGrid, так что тебе будет над чем пострадать этой ночью.
Исходники примера находятся в файле database.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
6
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Тебя мучает одна проблема, которую ты не можешь решить? Заходи на форум на нашем сайте, подумаем вместе!!! На нашем форуме ты можешь задать любой вопрос и получить ответ по следующим темам: • Программирование (Delphi, JBuilder, C++ Builder, Kylix, Visual C++Visual Basic и другие); • Технологии программирования (сети, мультимедиа, DirectX, OpenGL); • Администрирование; • Операционные системы; • Базы данных (SQL Server, язык SQL и другие); • Железо; • Internet технологии (Perl, PHP, Flash, XML); • Софт; • Сети; Адрес сайта http://www.vr-online.ru
http://www.vr-online.ru
7
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Delphi (OpenGL): Собственные примитивы Представим, что нам надо нарисовать стол. В этом случае нам надо нарисовать четыре ножки, которые выглядят как кубы. Вроде ничего сложного, но куба среди примитивов OpenGL нет. Поэтому придёться каждый куб рисовать из шести квадратов. И так четыре раза. Не легче ли один раз создать ножку и использовать её в своей проге сколько угодно? Вот именно этому мы и научимся. Для реализации задуманного я использовал пример из июльского номера. Посмотри на рисунок 1, чтобы освежить его в памяти. Я этот пример сейчас немного изнасилую.
Рис 1. Форма На событие OnCreate добавим вот такую конструкцию: glNewList(1,GL_COMPILE); glColor3f (0.0, 1.0, 0.0); glBegin(GL_QUAD_STRIP); glVertex3f(1.25,1.25,1); glVertex3f(1.25,-1.0,1); glVertex3f(-1,1.25,0); glVertex3f(-1,-1.0,0); glEnd; glColor3f (1.0, 1.0, 0.0); glBegin(GL_QUAD_STRIP); glVertex3f(1.0,1.0,0); glVertex3f(1.0,-1.25,0); glVertex3f(-1.25,1.0,1); glVertex3f(-1.25,-1.25,1); glEnd; glEndList;
http://www.vr-online.ru
8
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Функция glNewList создаёт новый список (объект). Он работает как Begin, а концом для него служит glEndList. Всё, что находиться между этими двумя функциями - это вершины, или стороны создаваемого нового объекта. В моём случае - это простые два квадрата, которые пересекают друг друга (см рисунок 1). Теперь разберёмся с параметрами функции glNewList. В качестве первого параметра выступает цифра, которая идентифицирует новый объект. Она изменяется от 1 до 255 и по этой цифре мы будем в дальнейшем обращаться к этому объекту. Второй параметр константа, которая указывает, как будет создаваться объект. Она может быть только GL_COMPILE - создать объект или GL_COMPILE_AND_EXECUTE - создать объект и вывести на экран. Я в своём примере только создаю новый объект. Теперь корректируем событие OnPaint
procedure TForm1.FormPaint(Sender: TObject); var ps:TPaintStruct; begin BeginPaint(Handle,ps); glClearColor(1,0.5,0.5,1); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glRotated(45,1,0,0); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glCallList(1); glTranslatef(2,2,0); glCallList(1); glTranslatef(-4,-4,2); glCallList(1); glFlush(); swapBuffers(dc); EndPaint(Handle,ps); end; Здесь я теперь вместо рисования пересекающихся квадратов, просто вызываю созданный объект с помощью glCallList . В качестве параметра выступает номер объекта (я создавал объект 1, значит и здесь должна быть 1). Я делаю это три раза в разных позициях, чтобы пример был более наглядным. Пример готов, можно запустить его и посмотреть на результат. Теперь я хочу немного улучшить наш пример. В разделе private главной формы TForm я объявил несколько переменных:
private X,Y,Z:real; RX,RY,RZ:real; В событии OnCreate я присваиваю всем этим переменным 0. После этого создадим новое событие OnKeyDown и напишем в нём:
http://www.vr-online.ru
9
VR-online Journal (Horrific and VR-Team)
Для программистов №5
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key =VK_RIGHT then X:=X+0.1; if Key =VK_LEFT then X:=X-0.1; if Key =VK_UP then Y:=Y+0.1; if Key =VK_DOWN then Y:=Y-0.1; if Key =VK_INSERT then RY:=RY-2; if Key =VK_DELETE then RY:=RY+2; if Key =VK_HOME then RX:=RX-2; if Key =VK_END then RX:=RX+2; FormPaint(nil); end; И наконец событие OnPaint корректирую так:
procedure TForm1.FormPaint(Sender: TObject); var ps:TPaintStruct; begin BeginPaint(Handle,ps); glClearColor(1,0.5,0.5,1); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glPushMatrix; glRotated(45,1,0,0); glTranslatef(X,Y,Z); glRotated(RX,1,0,0); glRotated(RY,0,1,0); glRotated(RZ,0,0,1); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glCallList(1); glTranslatef(2,2,0); glCallList(1); glTranslatef(-4,-4,2); glCallList(1); glPopMatrix; glFlush(); swapBuffers(dc); EndPaint(Handle,ps); end; Можно запустить программу и посмотреть на результат. Попробуй понажимать на клавиши стрелок, Insert, Home, Delete, End. Если результат удовлетворил, то давай
http://www.vr-online.ru
10
VR-online Journal (Horrific and VR-Team)
Для программистов №5
перейдём к рассмотрению новых функций. С функцией glRotated мы уже знакомы, это поворот сцены. GlTranslatef - перемещает сцену по координатам указанным в скобках (X,Y,Z). На функциях glPushMatrix и glPopMatrix я остановлюсь подольше. Они очень даже интересны и очень полезны. Их назначение - сохранить и восстановить текущую матрицу перемещений. Найди пример из июльского номера и запусти. Теперь попробуй полностью перекрыть окно и снова показать его. Сцена изменилась. Это произошло из-за того, что снова произошла прорисовка экрана, а мы использовали функцию поворота glRotated. Когда программа запустилась, то вызвалась функция glRotated и сцена повернулась на 45 градусов. После того, как ты перекрыл окно и снова показал, сцена перерисовалась и повернулась ещё на 45 градусов (всего уже 90). Как избавится от этого эффекта? Легко. Заключить все повороты (glRotated), перемещения (glTranslatef) и масштабирование (clScale) и рисование соответствующих объектов между glPushMatrix и glPopMatrix. Первая их них сохраняет текущую матрицу поворотов, перемещений и масштабирование. После этого можно смело рисовать. После вызова glPopMatrix, матрица восстановиться в состояние, которое было до вызова glPushMatrix. В нашем случае, я сохраняю нулевую матрицу (потому что я ещё не производил никаких действий до вызова glPushMatrix). Потом рисую объекты и в конце восстанавливаю нулевую матрицу. Если перед вызовом glPushMatrix поместить glRotate(45,1,0,0) (поворот на 45 градусов), то сохраниться матрица, в которой есть только этот поворот. После восстановления в матрице опять будет только этот поворот. Все действия между glPushMatrix и glPopMatrix будут отменены. Вроде всё. Мы сегодня и так поработали слишком хорошо. Продолжим наши занятия в следующий раз, а пока играйся с новым примером. До встречи. Исходники примера находятся в файле opengl.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
11
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Реклама в журнале VR-online Почему вы обязаны разместить рекламу на страницах VRonline:
4. 5. 6. 7.
1. Таких низких цен вы не видели ни где. 2. У нас располагается нестареющая информация. Которая будет актуальна всегда. 3. Вы имеете возможность пожизненно расположить свой банер на наших страницах по самым низким ценам. Пожизненность гарантируется в не зависимости от роста числа посещаемости. У нас есть потенциал для роста, как в объемах страниц, так и в посещаемости. Наши материалы очень часто сохраняются на дисках посетителей. Ваша реклама будет доступна в любых вариантах журнала. Журнал распространяется не только с сайта VR-online, но и другими сайтами и даже на CD, поэтому тираж огромен.
Если вы собираетесь рекламировать не просто сайт в интернете, а компанию, которая занимается информационными технологиями, то ваша дорога лежит сюда. Это лучшее рекламное место, которое можно найти в сети. Торопитесь такие цены не надолго. Расценки на размещение рекламы на страницах VR-online: • Банер 100х100 на главной странице сайта в течении месяца-$25 • Банер 468х60 на главной странице сайта в течении месяца-$30 • Банер 100х100 на странице оглавления на сайте 1-го номера (пожизненно)-$25 • Банер 468х60 на странице оглавления на сайте 1-го номера (пожизненно)-$30 • Страница в журнале-$200 • Половина страницы в журнале-$100 • Банер на странице статьи журнала.-$50
http://www.vr-online.ru
12
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Delphi: Работа с системным реестром Мне пришло уже несколько писем с просьбой объяснить, как работать с системным реестром. Так как это достаточно большой вопрос, то выносить его в раздел "Каком к верху" не возможно, поэтому я решил посвятить этому отдельную статью. Работы будет много, так что запасайся валерьянкой. На рисунке 1 ты можешь увидеть форму, которая нам понадобиться. Если ты любишь выполнять примеры самостоятельно, а не использовать мои готовые исходники, то нарисуй нечто подобное.
Рис 1. Пример формы Прежде чем приступать к программированию необходимо в разделе uses добавить модуль registry. Теперь всё готово к рукоблудству. Приступим!!! Начнём с записи в реестр. Создай событие OnClick для первой кнопки (у меня она с именем "Записать") и напиши там следующее: procedure TForm1.Button1Click(Sender: TObject); var RegIni:TRegIniFile; begin RegIni:=TRegIniFile.Create('Software'); RegIni.OpenKey('VR-online', true); RegIni.WriteString('Razd', 'Param', Edit1.Text); RegIni.Free; end; Первая строка (RegIni:=TRegIniFile.Create('Software')) создаёт переменную RegIni типа TRegIniFile, через которую мы будем работать с реестром. В этом объекте (TRegIniFile) реализованы все необходимые нам функции, которые позволяют получить очень простой доступ к реестру. В скобках я указываю имя подраздела, с которым я собираюсь работать. Сразу возникает вопрос: а внутри какого основного раздела Delphi будет искать подраздел "Software"? Ответ: HKEY_CURRENT_USER. Этот раздел используется по умолчанию.
http://www.vr-online.ru
13
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Итак, первая строка инициализирует реестр и текущем разделом становиться HKEY_CURRENT_USER\ Software. Если бы мы написали нашу первую строку так: RegIni:=TRegIniFile.Create(' Network') то текущим разделом был бы уже: HKEY_CURRENT_USER\ Network. В принципе, мы уже можем приступать к записи, но я решил открыть ещё подраздел: RegIni.OpenKey('VR-online', true). OpenKey - открывает следующий подраздел, имя которого указано в качестве первого параметра в скобках. Второй параметр означает: создавать ли раздел если его нет?. У меня стоит значение true - это значит, что если такого подраздела нет, то он будет создан. Если поставить false, то ничего создаваться не будет. Итак, теперь текущий раздел у нас HKEY_CURRENT_USER\ Software\ VR-online. Давай уже что-нибудь запишем: RegIni.WriteString('Razd', 'Param', Edit1.Text). Функция WriteString записывает строку в текущий раздел реестра. Первый параметр - имя подраздела в текущем разделе, т.е. наше значение будет записываться в HKEY_CURRENT_USER\ Software\ VR-online\ Razd. Обязательно учитывай это. Второй параметр - имя записываемого параметра, а третий - значение параметра. В качестве значения я использую строку записанную в Edit1. Последняя строка RegIni.Free уничтожает созданную нами переменную. Вздохнём с облегчением. Запись в реестре мы сделали. Можно запустить прогу и проверить на практике, как произошла эта запись. Когда наиграешься возвращайся сюда, и мы продолжим изучение реестра. Для записи в реестр мы использовали процедуру WriteString. Но она не единственная, есть ещё WriteBool (для записи булево значения) и WriteInteger (для записи целого значения). У всех у них одинаковое количество параметров и все они похожи: первый означает имя подраздела, второй - имя параметра, третий - значение. Вот пример их использования. RegIni.WriteInteger('Razd', 'Param', 1); RegIni.WriteBool('Razd', 'Param', true); Теперь перейдём к чтению параметра. Создай событие OnClick для второй кнопки (у меня она с именем "Считать") и напиши там: procedure TForm1.Button2Click(Sender: TObject); var RegIni:TRegIniFile; begin RegIni:=TRegIniFile.Create('Software'); RegIni.OpenKey('VR-online', true); Edit1.Text:=RegIni.ReadString('Razd', 'Param', 'Default'); RegIni.Free; end; Практически всё здесь, как и при записи. Точно так же создаётся переменная и открывается раздел. Затем идёт функция RegIni.ReadString, которая непосредственно читает значения. В качестве первого параметра выступает имя раздела, второй - имя параметра, и третий - значение по умолчанию. Функция возвращает считанное значение, а если его нет, то функция вернёт значение по умолчанию (то, что ты указал в третьем параметре 'Default'). Для чтения значений из реестра есть ещё две функции: ReadBool (читать булево значение) и ReadInteger (читать число). Вот пример их использования:
http://www.vr-online.ru
14
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Edit1.Text:=RegIni.ReadInteger('Razd', 'Param', 1); Edit1.Text:=RegIni.ReadBool('Razd', 'Param', true); Дальше я не буду приводить полные процедуры, а только познакомлю тебя с функциями объекта TRegIniFile, с которым мы работаем. DeleteValue - удаляет параметр. В скобках ты должен указать имя параметра. Например, для того чтобы удалить созданный нами параметр (Param), мы должны написать: RegIni.OpenKey('VR-online', true); RegIni.OpenKey('Razd', true); RegIni.DeleteValue('Param'); DeleteKey - удаляет раздел. В скобках указывается имя раздела для удаления. У объекта TRegIniFile эта функция имеет два параметра, но я тебе советую использовать эту же функцию его предка TRegistry. Для этого нужно написать TRegistry(RegIni). DeleteKey( 'Razd' );. RootKey - это свойство объекта TRegIniFile, которое указывает на головной раздел, который сейчас используется. Как я уже говорил, по умолчанию это HKEY_CURRENT_USER. Чтобы изменить это значение, нужно просто присвоить другое. Нарпимер: procedure TForm1.Button5Click(Sender: TObject); var RegIni:TRegIniFile; begin RegIni:=TRegIniFile.Create('Software'); RegIni.RootKey:=HKEY_LOCAL_MACHINE; RegIni.OpenKey('VR-online', true); RegIni.WriteString('Razd', 'Param', Edit1.Text); RegIni.Free; end; Вот и всё. Это основные функции, которых тебе хватит надолго. С помощью их можно сделать практически всё. Теперь ты можешь использовать системный реестр в своих корыстных целях. Удачи!!! Исходники примера находятся в файле delphi.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
15
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Программирование в Delphi глазами хакера Автор: Фленов Михаил aka Horrific Из книги ты узнаешь: • Кто такой Хакер и как им стать; • Как создавать программы маленького размера; • Как оптимизировать код программы; • Как заставить летать кнопку «Пуск»; • Научишься контролировать системную палитру; • Научишься изменять разрешение экрана из своих программ; • Увидишь множество шуточного кода; • Узнаешь, как подсматриваем пароли, спрятанные под звездочками; • Напишешь программу мониторинга запускных файлов и клавиатурный шпион; • Сможешь портить окна чужих программ; • Как создавать окна неправильной формы; • Научишься работать с сетью через компоненты Delphi и увидишь как создаются сканеры портов, утилиты ping и др. • Узнаешь, как работать с сетью на уровне библиотеки WinSock; • Узнаешь, как работать с железом И многое другое. Посмотри на содержимое диска, и ты поймёшь, что он стоит того, чтобы купить эту книгу с диском: \Headers - Все необходимые заголовочные файлы, которые нужно будет подключать к Delphi для компиляции некоторых примеров \Source - Исходные коды своих простых программ, чтобы вы могли ознакомиться с реальными приложениями. Их немного, но посмотреть стоит. \Soft - Инсталляционный пакет программы Adobe Acrobat Reader v5.0. Если у вас нет этой программы, то вы должны её установить, чтобы можно было читать документацию, расположенную на диске. \Vr-online - Полная копия сайта автора, а это 100 мегабайт документации, полезной информации, исходных кодов и компонентов. Здесь же вы можете найти мою книгу "Библия Delphi" - в электронном виде. В ней вы найдёте все необходимые для понимания этого материала основы и если вы ещё ни разу не видели Delphi, то после прочтения этой книги вы сможете понять всё описанное здесь. \Документация - Дополнительная документация, которая может понадобиться для понимания каких-то глав. \Иконки - В этой директории вы найдёте большую коллекцию иконок, которые вы можете использовать в своих программах. Эту коллекцию я подбирал достаточно долго и все иконки хорошего качества. \Компоненты - Дополнительные компоненты, которые будут использоваться в примерах книги. \Программы - Программы, которые пригодятся в программировании. Среди них Header Convert - программа, которая конвертирует заголовочные файлы с языка С на Delphi и ASPack - программа сжатия запускных файлов. Спрашивай книгу в книжных магазинах своего города!!!
http://www.vr-online.ru
16
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Delphi: Создаём отчётность в Excel Сегодня мы не будем непосредственно программировать базы данных. Наша задача создать отчётность в Excel. Эта потребность может возникнуть у каждого программиста баз данных, ведь Office установлен в нашей стране практически на каждом компьютере. А это значит, что вашу отчётность можно смело переносить между компьютерами и быть уверенным, что её смогут прочитать. Сначала я покажу полностью процедуру, потому что исходников в конце статьи не будет, а потом мы разберём её по косточкам: procedure TForm1.Button1Click(Sender: TObject); var XLApp,Sheet,Colum:Variant; index,i:Integer; begin XLApp:= CreateOleObject('Excel.Application'); XLApp.Visible:=true; XLApp.Workbooks.Add(-4167); XLApp.Workbooks[1].WorkSheets[1].Name:='Отчёт'; Colum:=XLApp.Workbooks[1].WorkSheets['Отчёт'].Columns; Colum.Columns[1].ColumnWidth:=40; Colum.Columns[2].ColumnWidth:=10; Colum.Columns[3].ColumnWidth:=30; Colum.Columns[4].ColumnWidth:=10; Colum:=XLApp.Workbooks[1].WorkSheets['Отчёт'].Rows; Colum.Rows[2].Font.Bold:=true; Colum.Rows[1].Font.Bold:=true; Colum.Rows[1].Font.Color:=clBlue; Colum.Rows[1].Font.Size:=14; Sheet:=XLApp.Workbooks[1].WorkSheets['Отчёт']; Sheet.Cells[1,2]:='Отчёт из Delphi'; Sheet.Cells[2,1]:='Колонка 1'; Sheet.Cells[2,2]:='Колонка 2'; Sheet.Cells[2,3]:='Колонка 3'; Sheet.Cells[2,4]:='Колонка 4'; index:=3; for i:=0 to 2 do begin Sheet.Rows[index].Font.Color:=clGreen; Sheet.Cells[index,1]:=random(100); Sheet.Cells[index,2]:=random(100); Sheet.Cells[index,3]:=random(100); Sheet.Cells[index,4].Font.Color:=clRed; Sheet.Cells[index,4]:=random(100);
http://www.vr-online.ru
17
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Inc(index); end; end; Для того, чтобы всё это работало, нужно подключить в раздел Uses модуль ComObj . А теперь начнём всё с самого начала. Первая строка создаёт объект Excel (XLApp:= CreateOleObject('Excel.Application')), и записывает его в переменную XLApp . Эта переменная типа Variant . Variant - это тип, который может принимать любые значения: строки, числа, указатели и др. Вторая строка (XLApp.Visible:=true) заставляет запустить сам Excel. Потом я добавляю новую рабочую книгу (XLApp.Workbooks.Add(-4167)). Число в скобках - это константа, которая означает создание книги и её изменять нельзя. Дальше я даю название созданной книге XLApp.Workbooks[1].WorkSheets[1].Name:='Отчёт'. Это действие не обязательно, но я всегда это делаю, потому что меня бесит название по умолчанию "Лист 1". Теперь у нас Excel запущен и создана новая книга. Можно переходить к впечатыванию данных. Но прежде чем это сделать я отформатирую колонки и строки. Для этого я получаю указатель на колонки рабочей книги (Colum:= XLApp. Workbooks[1]. WorkSheets['Отчёт']. Columns), и записываю результат в переменную Colum типа Variant . Теперь последовательно изменяю ширину колонок (Colum. Columns[1]. ColumnWidth := 40). На русском эта команда будет звучать так: Колонки. Колонка[1]. ШиринаКолонки:=40. После этого я в ту же переменную записываю указатель на строки рабочей книги (Colum := XLApp. Workbooks[1]. WorkSheets['Отчёт']. Rows). Для украшения строк нашего отчёта, я устанавливаю у первых двух строк жирный шрифт (Colum. Rows[1]. Font. Bold := true). В квадратных скобках теперь порядковый номер строки. Далее идут две строки, в которых я устанавливаю цвет первой строки в синий и размер шрифта равный 14. Форматирование окончено, теперь можно выводить данные. Для этого я получаю указатель на лист (Sheet:=XLApp.Workbooks[1].WorkSheets['Отчёт']). Для того, чтобы вывести данные, нужно просто присвоить значение в Sheet.Cells[строка, колонки]. В процессе вывода данных можно изменять цвет строк Sheet. Rows[строка]. Font. Color или колонок Sheet. Columns[колонка]. Font. Color, простым присваиванием (как мы это делали при форматировании). Если нужно изменить цвет отдельной ячейки, то это можно сделать, присвоив новое значение в Sheet.Cells[строка, колонка].Font.Color. Вот ещё некоторые параметры, которые ты можешь изменить: Sheet.Cells[строка,колонка].Font.Italic - курсивный шрифт Sheet.Cells[строка,колонка].Font.Bold - жирный шрифт Sheet.Cells[строка,колонка].Font.Underline - подчёркнутый шрифт Sheet.Cells[строка,колонка].Font.Size - размер шрифта С помощью всего этого, ты сможешь создавать простые, но эффективные отчёты. В Delphi, в директории Lib есть файлик excel97.pas, в нём ты найдёшь все доступные функции Excel. Если у тебя есть достаточные навыки, чтобы разобраться с ним, то в перёд. А начинающему программисту там без бутылки не разобраться. Если ты относишься ко второй категории, то жди, возможно я ещё вернусь к этой теме. Исходники находятся в файле delphi2.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
18
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Ты ищешь хорошую книгу по Delphi? Зайди на www.vr-online.ru и скачай полный электронный вариант Библии Delphi от Фленова Михаила абсолютно бесплатно. Эта книгу научит тебя программировать, даже если ты никогда в жизни не написал ни строчки кода. В ней описано всё, начиная от основ программирования и заканчивая реальными примерами программ и задач, которые программисты решают каждый день. Библия Delphi – самая иллюстрированная и самая бесплатная книга. По ней научились программировать множество людей и ты тоже сможешь.
http://www.vr-online.ru
19
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Delphi: Работа с файлами Два месяца назад я научил тебя работать с дисками. Теперь пора заняться файловой системой. Основное, что мы сегодня изучим - организация поиска файлов и получение некоторой информации о них. Для этого есть два способа: использование функций WinAPI и встроенного в окна COM объекта. Сегодня нас интересует первый из них. Исходники сегодняшнего примера, как всегда доступны в конце статьи. Можешь их скачать и проверить программу в работе, а на все возникающие вопросы ты получишь ответы в этой статье.
Рис 1. Пример формы На рисунке 1, как всегда ты видишь пример рабочей проги, которая будет сегодня создана. Рассмотри рисунок. Если ты это сделал хорошо, то поймёшь, что для работы нам понадобиться один TListView, одна кнопка TButton, одна строка ввода TEdit и один TLabel для отображения текста везде строки ввода. Если ты любишь создавать примеры сам, а не использовать мои исходники, то расположи эти компоненты, как оказано на рисунке. Для работы примера нужен модуль shellapi , добавь его в раздел uses. При рассмотрении примеров, обращай внимание на комментарии, они помогут тебе разобраться с происходящим в проге. А я буду расписывать только наиболее интересные моменты. Как всегда, начнём писать прогу с процедуры FormCreate. Вот как она выглядит:
http://www.vr-online.ru
20
VR-online Journal (Horrific and VR-Team)
Для программистов №5
procedure TForm1.FormCreate(Sender: TObject); var SysImageList: uint; SFI: TSHFileInfo; begin //Создаю списки маленьких и больших иконок. ListView1.Largeimages:=TImageList.Create(self); ListView1.Smallimages:=TImageList.Create(self); //Запрашиваю большие иконки SysImageList := SHGetFileInfo('', 0, SFI, SizeOf(TSHFileInfo), SHGFI_SYSICONINDEX or SHGFI_LARGEICON); if SysImageList <> 0 then begin //Присваиваю системные иконки в ListView1 ListView1.Largeimages.Handle := SysImageList; ListView1.Largeimages.ShareImages := TRUE; end; //Запрашиваю маленькие иконки SysImageList := SHGetFileInfo('', 0, SFI, SizeOf(TSHFileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON); if SysImageList <> 0 then begin ListView1.Smallimages.Handle := SysImageList; ListView1.Smallimages.ShareImages := TRUE; end; end; Функция SHGetFileInfo возвращает информацию о файле, директории или диске. Первый параметр - путь к файлу. Второй - атрибуты. Третий - указатель на ТSHFILEINFO. Четвертый - размер ТSHFILEINFO. Последний - флаги, указывающие на тип информации запрашиваемый у системы. Теперь о том, как выглядит функция в моём примере. Первые два параметра пустые, это означает, что нам нужны глобальные данные. Если ты укажешь существующий файл, то ты получишь информацию по нему. В качестве флагов я указываю SHGFI_SYSICONINDEX и SHGFI_LARGEICON. SHGFI_SYSICONINDEX означает, что я запрашиваю указатель на системный список иконок (ImageList). Второй флаг говорит, что мне нужны большие иконки. При втором вызове этой функции я запрашиваю маленькие иконки (SHGFI_SMALLICON). Функция возвращает мне указатель на соответствующий системе SysImageList, который я в последствии присваиваю в ListView1.Largeimages.Handle. Системный список иконок (ImageList) содержит все иконки, установленные в системе и ассоциированные с разными типами файлов. Эти иконки ты можешь видеть в Windows Explorer у вайлов doc, txt, ini, zip и др. Теперь о процедуре FormShow. В ней я вызываю другую процедуру AddFile, которая считывает все файлы из текущей директории. procedure TForm1.FormShow(Sender: TObject); begin AddFile(Edit1.Text+'*.*',faAnyFile) end; Процедуру AddFile я не буду приводить, потому что она слишком большая. Я лишь расскажу как она действует. Внутри процедуры запускается поиск файлов в текущей директории.
http://www.vr-online.ru
21
VR-online Journal (Horrific and VR-Team)
Для программистов №5
//Запускаю поиск hFindFile := FindFirst(Маска поиска, Аттрибуты , Информация); //Проверяю корректность найденного файла if hFindFile <> INVALID_HANDLE_VALUE then //Если корректно, то запускаеться цикл repeat - until. repeat //Здесь вписаны операторы, которые нужно выпонять. until (FindNext(SearchRec) <> 0); FindClose(SearchRec); FindFirst - открывает поиск. В качестве первого параметра выступает маска поиска. Если ты укажешь конкретный файл, то система найдёт его. Но это не серьёзно, лучше искать более серьёзные вещи. Например, ты можешь запустить поиск всех файлов в корне диска С. Для этого первый параметр должен быть 'C:\*.*'. Для поиска только файлов ЕХЕ, в папке Fold ты должен указать 'C:\Fold\*.exe'. Второй параметр - атрибуты включаемых в поиск файлов. Я использую faAnyFile, чтобы искать любые файлы. Тебе доступны faReadOnly - искать файлы с атрибутом ReadOnly. faHidden - искать скрытые файлы. faSysFile - искать системные файлы. faArchive - искать архивные файлы. faDirectory - искать директории. Паследний параметр - это структура в которой нам вернёться информация о поиске, а именно имя найденного файла, размер, время создания и т.д. После вызова этой процедуры, я проверяю на корректность найденного файла. Если всё в норме, то запускаеться цикл Repeat - Until. Этот цикл выполняет операторы расположенные между repeat и until, пока условие расположенное после слова until являеться верным. Как только условие нарушается, цикл прерывается. Этот цикл похож на while, но с одним отличием. Если в цикле while условие заведомо не верно, то операторы внутри цикла не выпоняться. А в Repeat-Until выполнятся, потому что сначала происходит выполнение операторов, а лишь затем проверка Until. Рассмотрим пример: index:=1; while index=0 do Param:=0; В этом примере оператор Param:=0; не будет выполнен, потому что index=1 и условие заведомо не верно. index:=1; repeat Param:=0; until index=0; В этом примере Param:=0 выполнится, Потому что сначала выполняется этот оператор, а лишь потом проверка на равенство index нулю. Хочу предупредить, что функция поиска, может возвращать в качестве найденного имени в структуре SearchRec (параметр Name) точку или две точки. Если ты посмотришь на директорию, то таких файлов не будет. Откуда берутся эти имена? Имя файла в виде точки указывает на текущую директорию, а имя файла из двух точек указывает на директорию верхнего уровня. Если я встречаю такие имена, то я их просто отбрасываю.
http://www.vr-online.ru
22
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Принцип ясен? А с самой работой разберёшься по исходнику, ты уже не маленький. До встречи!! Исходники примера находятся в файле Delphi1.zip
Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
23
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Delphi (Графика): Работа с PaintBox Совсем недавно, ко мне пришло письмо, с просьбой рассказать о TPaintBox. В двух словах такое не расскажешь, поэтому пришлось писать статью. Я думаю, что она будет полезна интересна многим моим читателям, потому что этот компонент имеет свои особенности. TPaintBox чем-то похож на TBitmap, и на первый взгляд эта похожесть очень большая, но это не так. У него нет большинства нужных возможностей. Единственное, что у него есть для рисования - это Canvas. В принципе этого достаточно, но есть одна проблема TPaintBox не умеет сохранять образы рисунка как TImage. TImage - это картинка, которая выводится на экран, поэтому она хранится памяти и показывается простым копированием. TPaintBox - это поле для рисования, и изображение не сохраняется в памяти. Чем всё это грозит? Если TImage перекрыть другим окном, то ничего страшного не произойдёт. После открытия картинка снова будет показываться. Если перекрыть TPaintBox, то информация бесследно уничтожится. Мы уже встречались с такой проблемой, когда рисовали на форме (форма также не умеет сохранять изображения). Вывод - с тем же успехом, можно рисовать прямо на форме, без использования TPaintBox. Но иногда, всё же удобно его использовать, для ограничения поля ввода. Например, чтобы отследить, где нажать мышь. Например, когда тебе нужно рисовать только в определённом участке на окне. В этом случае, ты в этот участок помещаешь TPaintBox и отслеживаешь события, нужные тебе для рисования, только для этого участка. Пора перейти от слов к делу. На рисунке 1, ты можешь видеть мой пример формы. С левой стороны находится TPaintBox (его не видно, на этом месте только рисунок), с правой стороны кнопка.
Рис 1. Главная форма
http://www.vr-online.ru
24
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Я создал два события OnMouseDown и OnMouseMove, и для обоих написал один и тот же текст: PaintBox1.Canvas.Pixels[X,Y]:=clRed; Теперь, если ты будешь вести мышкой над TPaintBox или давить на него, то будут появляться красные точки. Эти события не будут происходить вне TPaintBox, т.е. мы ограничили область рисования только этим компонентом. Для кнопки я написал следующее событие: procedure TForm1.Button1Click(Sender: TObject); begin PaintBox1.Canvas.Pen.Color:=clGreen; PaintBox1.Canvas.Pen.Width:=3; PaintBox1.Canvas.MoveTo(Random(300),Random(200)); PaintBox1.Canvas.LineTo(Random(300),Random(200)); end; Здесь нет ничего нового, и ты можешь разобраться со всеми операторами без меня. Первая строка задаёт цвет карандаша у Canvas компонента PaintBox1. Вторая строка задаёт ширину карандаша. Третья строка перемещает текущую точку в случайное место. Последняя строка рисует линию из текущей точки в новую случайную точку. Я больше не стал усложнять пример. Как видишь, рисование происходит так же, как мы рисовали на форме. Только тогда мы писали просто Canvas. LineTo(Random(300), Random(200)), а теперь в начале добавляется имя компонента, Canvas которого мы хотим использовать. Запусти пример и попробуй с ним поиграть. Когда наиграешься, попробуй свернуть окно или перекрыть его другим. Все твои извращения исчезнут. Удачи. Занятие получилось маленьким, но полезным для закрепления материала. Исходники примера можете взять в файле delphi3.zip.
Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
25
VR-online Journal (Horrific and VR-Team)
Для программистов №5
SQL: SQL калькулятор До сих пор мы просто выводили данные из таблицы, но сегодня ты увидишь, что SQL может производить некоторые математические действия. Эти действия очень слабенькие, но в большинстве случаев даже этого бывает достаточно. Если тебе нужны более сложные расчёты, то придётся их выполнять вручную. Зачем нужно вставлять математику в запросы, ведь это можно сделать программно? Ответ в этой статье. Ты можешь получить данные из базы, и потом выполнять математику, но если есть возможность вставить эту математику в запрос, то лучше это сделать. В этом случае ты освободишь клиентскую машину от лишней загрузки, и тем более сервер будет выполнять эти расчёты намного быстрее. Когда ты делаешь расчёты программно, то выполняется два просмотра всей базы. Во время первого выбираются данные, а во время второго идёт расчёт. Когда математика вставлена в запрос, то все действия выполняются за один проход - выбираются данные и одновременно происходит математика. Давай переходить к изучению SQL-математики. Как я уже сказал, она не сложнее школьной программы для второклассника, поэтому особых мозгов сегодня не понадобиться. Выкладывай свои извилины в холодильник, пускай они охладятся после тяжёлого дня. Для полного счастья нам доступны несколько функций: • • • • •
COUNT - подсчёт количества строк. SUM - подсчёт суммы. AVG - подсчёт среднего значения. MAX - поиск максимального значения. MIN - поиск минимального значения.
Всё очень просто. Теперь рассмотрим пару примерчиков: SELECT COUNT(LecNumber) FROM User Этот запрос просто подсчитывает количество строк в базе. Не надо обращать внимания на параметр в скобках (LecNumber) у COUNT. Вроде всё просто, и не обязательно было для этого создавать запрос. Ладно, усложняем. SELECT COUNT(LecNumber) FROM User WHERE LecNumber=1 Вот это уже интересней. Этот запрос опять подсчитывает количество строк, но теперь результатом будет количество народу, у которых поле LecNumber = 1. Тоже просто? Ещё усложняем: SELECT COUNT(DISTINCT Country)
http://www.vr-online.ru
26
VR-online Journal (Horrific and VR-Team)
Для программистов №5
FROM User В результате будет количество разных стран присутствующих в базе. Например, если в твоей базе четыре строки и поле Country для строк равны: Россия, Американия, Украиния, Россия, то результатом будет 4 (четыре вида стран).Очень интересных эффектов можно добиться, если использовать математику вместе с GROP_BY. Если ты всё же используешь то ярр Теперь рассмотрим что-нибудь более возбуждающее. SELECT SUM(LecNumber) FROM User В результате этого запроса мы получим общее количество лицензий. Это количество не то, что было в случае с COUNT, это сумма данного поля для всех строк. Если немного напрячь то, что ты ещё не успел выложить в холодильник, то становиться видно одно правило: поле должно быть числовым (нельзя производить суммирование строковых полей). Остальные функции работают также, поэтому я не вижу смысла приводить для них примеры. Уж лучше я продвинусь дальше и расскажу кое-что ещё про математику в SQL. Давай взглянём на следующий пример: SELECT LecNumber+'шт.' FROM User Этот запрос выводить количество лицензий и единицу измерения в одном столбце. Здесь к числу прибавляется некий текст. Прибавлять можно и числовые значения: SELECT LecNumber+1 FROM User И самое интересное - прибавлять можно и другое поле: SELECT LecNumber+Cost FROM User Cost - это поле в базе, но не надо его искать. В той базе, что я использую, нет такого имени, я его придумал только для удобства. И Наглядности. Ещё можно вычитать умножать и делить значение полей. Потренируйся на эту тему сам. Должно тебе помочь. Напоследок я отвечу на один из вопросов присланных мне Как лучше проверять запросы? Не буду же я изменят каждый свою прогу. Достаточно простого выполнять через проги Data Bsse Desktop через SQL Explorer. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
27
VR-online Journal (Horrific and VR-Team)
Для программистов №5
SQL: Объединения Потихоньку мы созреваем до создания сложных запросов. Сегодня мы научимся формировать выходные данные в группы. Это очень удобная вещь, и если ты её поймёшь, то будешь использовать очень часто, потому что она очень сильно облегчает жизнь. Представим, что у нас есть две таблицы User1 и User2: User1.db Key1 OC LastName ------------------------1 Win Иванов 2 Unix Петров 3 Win Яковлев 4 Win Сидоров 5 Win Ковалёв 6 Unix Амаров User2.db Key1 OC LastName ------------------------2 Unix Богров 3 Win Сидоров 4 OS2 Ковалёв Мы хотим получить список всех пользователей Unix из двух таблиц сразу. Для этого нужно выполнить запрос выбора к первой таблице, а потом ко второй. Результатом будет две выходные таблицы. А если мы хотим получить одну? Для этого можно воспользоваться объединением - оператор UNION. Вот как это будет выглядеть: SELECT * FROM User1.db WHERE OC LIKE 'Unix' UNION SELECT * FROM User2.db WHERE OC LIKE 'Unix'; результатом будет одна таблица: User1.db Key1 OC LastName ------------------------2 Unix Петров 6 Unix Амаров 2 Unix Богров Не плохо?
http://www.vr-online.ru
28
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Чтобы запрос не завершился ошибкой, он должен удовлетворять следующим условиям: Количество и типы полей должны быть одинаковыми. Символьные поля должны иметь одинаковое число символов. Если одного поля в одном из запросов нет, то его можно заменить. Например: SELECT Key1, OC, LastName FROM User1.db WHERE OC LIKE 'Unix' UNION SELECT Key1, OC, 'NO FOUND' FROM User2.db WHERE OC LIKE 'Unix'; результатом будет одна таблица: User1.db Key1 OC LastName ------------------------2 Unix Петров 6 Unix Амаров 2 Unix NO NAME Здесь мы вместо поля LastName подсовываем текст 'NO FOUND', чтобы количество и тип полей совпадали. Таким образом можно украсить запрос вот до такого вида: SELECT Key1, OC, LastName, 'Table 1' FROM User1.db WHERE OC LIKE 'Unix' UNION SELECT Key1, OC, 'NO FOUND', 'Table 2' FROM User2.db WHERE OC LIKE 'Unix'; Результатом будет одна таблица: User1.db Key1 OC LastName --------------------------------2 Unix Петров Table 1 6 Unix Амаров Table 1 2 Unix NO NAME Table 2 И на последок упорядочим наш вывод: SELECT Key1, OC, LastName, 'Table 1' FROM User1.db WHERE OC LIKE 'Unix' UNION SELECT Key1, OC, 'NO FOUND', 'Table 2' FROM User2.db WHERE OC LIKE 'Unix'; ORDER BY 3
http://www.vr-online.ru
29
VR-online Journal (Horrific and VR-Team)
Для программистов №5
ORDER BY 3 - говорит, что надо упорядочить вывод по третьему столбцу. Результатом будет одна таблица: User1.db Key1 OC LastName --------------------------------2 Unix NO NAME Table 2 6 Unix Амаров Table 1 2 Unix Петров Table 1 На сегодня всё. В следующий раз я познакомлю тебя с изменением таблицы. До сих пор мы только получали данные, пора научиться и модифицировать их. Удачи!!! Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
30
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Java: Компоненты Медленно, но верно мы продвигаемся по ступенькам Java программирования. Сегодня мы познакомимся с его компонентами, без которых невозможно продвигаться дальше. За одну статью я не успею описать всё, поэтому эта тема будет продолжена в следующем месяце (а возможно, что и через месяц тоже). Сазу же взглянём на полный исходный код ButtonDemo.java . import java.awt.*; import java.applet.*; public class ButtonDemo extends Applet { public void init() { setLayout(null); int width = Integer.parseInt(getParameter("width")); int height = Integer.parseInt(getParameter("height")); Button yes = new Button("Давай"); Button no = new Button("Не стоит"); Button cancel = new Button("Ну его нафиг"); add(yes); add(no); add(cancel); yes.reshape(2, 0, width-4, height / 3); no.reshape(2, height / 3, width-4, height / 3); cancel.reshape(2, 2 * height / 3, width-4, height / 3); } } Текста не так уж и много, и он не очень сложен. Но я всё же растолкую, о чём тут идёт речь. Для человека, только вступающего на трудный путь программирования, и это может показаться сложным. Но я тебе докажу, что самое сложное здесь - это подумать, а когда это сделаешь, всё встаёт на свои места. Первые две строчки уже должны быть понятны. Это мы подключаем модули Java, а именно все модули awt и applet. После этого объявляется новый класс ButtonDemo и основная его функция init, без которой не куды не денешься. Внутри функции я начинаю с того, что устанавливаю выравниватель setLayout(null). В качестве параметра я передаю ноль, чтобы не было никакого выравнивания, пока что нам этого достаточно. Далее я получаю ширину и высоту апплета (int width = Integer.parseInt(getParameter("width"))) с помощью функции getParameter с параметром width и height . Далее я создаю три кнопки. Рассмотрим первую из них: Button yes = new Button("Давай");
http://www.vr-online.ru
31
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Функция new создаёт новый объект типа Button (кнопка). В скобках у Button, в кавычках указывается заголовок кнопки. Итак, "new Button("Давай")" создаёт новую кнопку с заголовком "Давай". Результат записывается в переменную yes типа Button. Через эту переменную мы будем обращаться к созданной кнопке. Теперь, созданные кнопки нужно добавить к апплету. Это делается с помощью функции add, а в скобках указывается кнопка, которую нужно добавить. Кнопки созданы, установлены на апплете, осталось только установить их размеры. Для этого используется метод reshape объекта Button . В качестве параметров, передаются левая верхняя позиция, ширина и высота (Кнопка.reshape( Левая, Правая, Ширина, Высота). Как видишь всё очень просто. Мы не так уж сильно напрягли мозги, поэтому изучим ещё и CheckBox. Результат смотри справа, а исходник смотри ниже: import java.awt.*; import java.applet.*; public class CheckBoxDemo extends Applet { public void init() { setLayout(null); int width = Integer.parseInt(getParameter("width")); int height = Integer.parseInt(getParameter("height")); Checkbox Delph = new Checkbox("Delphi", null, true); Checkbox Jav = new Checkbox("Java"); add(Delph); add(Jav); Delph.reshape(30, 0, width, height / 2); Jav.reshape(30, height / 2, width, height / 2); } } Практически всё уже знакомо, единственное отличие в том, как создаётся CheckBox. Рассмотрим первый CheckBox: Checkbox Delph = new Checkbox("Delphi", null, true); Новый CheckBox создаётся так же как и кнопка с помощью функции new. Далее идёт тип, который создаётся (Checkbox) и в скобках его параметры. Первый параметр - это заголовок, второй должен быть null , а третий указывает значение по умолчанию. Первый CheckBox я создал в выделенном состоянии. Результат создания записывается в переменную Delph типа CheckBox. Когда я создавал второй CheckBox, я указал только заголовок, а остальные параметры упустил. Это не ошибка, как может показаться. Если не указывать второй и третий параметр, то Java создаст не выделенный CheckBox. И напоследок, я познакомлю тебя с компонентом Label. Результат смотри справа, а исходник снизу: import java.awt.*; import java.applet.*; public class LabelDemo extends Applet { public void init() { setLayout(null); int width = Integer.parseInt(getParameter("width")); int height = Integer.parseInt(getParameter("height")); Label left = new Label("Left", Label.LEFT); Label right = new Label("Right", Label.RIGHT);
http://www.vr-online.ru
32
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Label center = new Label("Center", Label.CENTER); add(left); add(right); add(center); left.reshape(0, 0, width, height / 3); right.reshape(0, height / 3, width, height / 3); center.reshape(0, 2 * height / 3, width, height / 3); } } Рассмотрим только создание label, а остальное всё должно быть понятно. Label left = new Label("Left", Label.LEFT); Первый параметр - заголовок, который ты можешь видеть. Второй параметр выравнивание по горизонтали. Вот и всё. Сегодня мы познакомились с первыми тремя компонентами Java. Через месяц я продолжу знакомство с компонентами, а на сегодня всё. Исходники находятся в файле java.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
33
VR-online Journal (Horrific and VR-Team)
Для программистов №5
OpenGL: Примитивы библиотеки GNU В стандартном OpenGL нет таких очень важных примитивов как сфера или конус. Конечно, ты в любой момент можешь нарисовать сферу из множества точек, но это будет громоздко и тяжело. Есть выход проще -для OpenGL есть надстройка, которая называется glu, и находиться она в файле glu32.dll. Вот именно с ней мы сегодня и познакомимся.
Рис 1. Результат На рисунке 1 ты можешь увидеть результат сегодняшней работы. А самое интересное, что код программы будет даже меньше, чем в предыдущей статье. Это не шутка, я на полном серьезен. Я даже приведу полный листинг проги прямо здесь, а потом мы разберём её по кусочкам. unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, GLU, GL; type TForm1 = class(TForm) procedure FormPaint(Sender: TObject); procedure FormResize(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } DC:HDC; hrc:HGLRC; quadObj:PGLUquadricObj; procedure PixelFormat;
http://www.vr-online.ru
34
VR-online Journal (Horrific and VR-Team)
Для программистов №5
public { Public declarations } end; var Form1: TForm1; Implementation {$R *.DFM} procedure TForm1.FormPaint(Sender: TObject); var ps:TPaintStruct; begin BeginPaint(Handle,ps); glClearColor(1,0.5,0.5,1); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glPushMatrix; glRotated(45,1,0,0); gluQuadricDrawStyle (quadObj, GLU_FILL); glColor3f(1,0,0); gluSphere (quadObj,1, 100, 10); glTranslatef(1.5,1,1); gluCylinder (quadObj,0, 0.2, 1, 30, 10); gluDisk (quadObj, 0.2, 0.3, 20, 2); glTranslatef(-3,0,0); gluCylinder (quadObj,0, 0.2, 1, 30, 10); gluDisk (quadObj, 0.2, 0.3, 20, 2); glPopMatrix; glFlush(); swapBuffers(dc); EndPaint(Handle,ps); end; procedure TForm1.FormCreate(Sender: TObject); begin DC:=GetDC(Handle); PixelFormat; hrc:=wglCreateContext(DC); wglMakeCurrent(DC,hrc); quadObj:=gluNewQuadric(); end; Когда я сказал, что приведу полный листинг, я тебя немного обманул. Надеюсь, ты простишь меня засранца. Я не стал приводить текст трёх функций FormResize, FormDestroy и PixelFormat, потому что они абсолютно такие же, как и во всех предыдущих уроках. В самом начале, я объявляю переменную quadObj типа PGLUquadricObj. Если ты не используешь заголовочные файлы GL и GLU (которые можно скачать в разделе полезности), а меняешь их на один файл opengl (из стандартной поставки Delphi), то у тебя эта переменная будет типа GLUquadricObj (без буквы "Р" на конце). По событию OnCreate я инициализирую эту переменную (одна из последних строцек моего кода) quadObj:=gluNewQuadric();.
http://www.vr-online.ru
35
VR-online Journal (Horrific and VR-Team)
Для программистов №5
В процедуре FormPaint я задаю параметры рисования объекта gluQuadricDrawStyle. В качестве первого параметра выступает созданная мною переменная, с которой будет ассоциироваться "тип рисования". Второй параметр - непосредственно тип, он может иметь параметры • • • •
GLU_FILL - объект полностью закрашивается (рис 1). GLU_LINE - объект рисуется линиями (рис 2). GLU_SILHOUETTE - рисуется только силуэт объекта (рис 3.) GLU_POINT - объект рисуется точками (рис 4).
Рис 2.
Рис 3.
Рис 4.
После всего этого можно смело рисовать объекты библиотеки glu. В этом примере я использовал три из них, поэтому мы только их и рассмотрим: gluSphere - первый параметр это GLU объект (в нашем случае quadObj), второе радиус, третий - количество частей вокруг оси Z, четвёртый - количество частей вдоль оси Z. gluCylinder - первый параметр такой же, второе - маленький радиус, третий - большой радиус, четвёртый - количество частей вокруг оси Z, пятый - количество частей вдоль оси Z. gluDisk - первый параметр всё тот же, второе - внутренний радиус, третий - внешний радиус, четвёртый - количество частей вокруг оси Z, пятый - число колец (в принципе это всё те же части). Что такое "количество частей вокруг/вдоль оси Z"? Чем больше этих частей, тем более гладким будет объект. Я не буду особо приводить примеры, а покажу только один, в котором очень сильно уменьшено количество этих частей (рис5). Вот и всё. На сегодня новостей больше нет. Но это только на сегодня. Нас ещё ждут текстуры, Рис 5. материалы, освещение и что-нибудь ещё придумаю. Удачи!!! Исходники находятся в файле opengl1.zip Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
36
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Sockets: Компоненты ТServerSocket и ТClientSocket До сегодняшнего дня, я описывал низкоуровневое программирование в сети. Но это не говорит о том, что в Delphi нет компонентов, облегчающих работу с сокетами. Такие компоненты есть, и сейчас я расскажу о двух из них: ТServerSocket и ТClientSocket. В качестве примера я не буду изобретать колесо, а воспользуюсь примером, который поставляется с Delphi. На рисунке 1 ты можешь познакомиться с главной формой, а в самом конце, можно скачать русифицированные и немного поправленные мною исходники примера. Для начала о компонентах. ТServerSocket является сервером сокета. К нему могут подключаться клиенты ТClientSocket. Для того, чтобы ТclientSocket мог присоединиться к ТserverSocket у них должны быть одинаковые свойства ServerType (для обучения достаточно stNonBlocking) и Port (номер порта). Рис 1. Главная форма Номер порта желательно использовать больше чем 1000, это позволит избежать конфликтов с зарезервированными портами для других целей. В примере я использую 1024 для сервера и клиента. Я тут заметил, что я ещё не рассказывал, как создаётся меню. Сейчас исправлю. Чтобы создать меню, надо поставить на форму компонент TMainMenu. Дважды щёлкаем по нему и получите распишитесь. Перед нами возникает редактор. Просто пишем названия меню в поле Caption и всё. А в это время в Object Inspector можно изменять такие интересные значения, как Bitmap (картинка для пункта меню), ShotCut (быстрый вызов), RadioItem (пункт меню с галочкой, как у TRadioBox) и многое другое. Со всем очень легко разобраться, если есть хоть начальные знания английского. Если таким мама не наградила, то очень даже помогает словарь. Вставлять и удалять пункты меню можно клавишами Ins и Del . Теперь попробуй создать меню сам, и всё станет понятно. Теперь рассмотрим все пункты меню. "Включить прослушивание": FileListenItem.Checked := not FileListenItem.Checked; if FileListenItem.Checked then begin //Надо включить прослушивание ClientSocket.Active := False; ServerSocket.Active := True;
http://www.vr-online.ru
37
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Statusbar1.Panels[0].Text := 'Прослушиваем...'; end else//Иначе надо отключить прослушивание begin if ServerSocket.Active then ServerSocket.Active := False; Statusbar1.Panels[0].Text := ''; end; Первая строка изменяет состояние FileListenItem.Checked на противоположное. Это означает: Если уже включено прослушивание FileListenItem. Checked = true, то надо отключить, иначе надо включить. Для начала, рассмотрим ситуацию, когда надо включить сервер на прослушивание. Здесь я сначала отключаю клиента. ClientSocket.Active := False - делаю клиента не активным. Если у тебя на форме стоит одновременно и клиент и сервер, то они не могут быть оба одновременно включёнными. Одно приложение не может быть одновременно и сервером и клиентом для одного и того же порта. Это возможно только если порты разные, поэтому я на всякий случай отключаю клиента. ServerSocket.Active := True - включить сервер на прослушивание. После этого ТserverSocket начинает слушать порт на предмет подключения к нему. Statusbar1.Panels[0].Text := 'Прослушиваем...' - вывожу соответствующее сообщение в строку состояния. Теперь ситуация, когда надо отключить сервер: if ServerSocket.Active then ServerSocket.Active := False - эта строка означает: если сервер включён, то отключить его. Statusbar1.Panels[0].Text := '' - отчищаю строку состояния. Если ты один раз выберешь это меню, то запуститься сервер на прослушивание. При следующем нажатии, сервер отключится. Теперь о меню подключиться: procedure TChatForm.FileConnectItemClick(Sender: TObject); begin if ClientSocket.Active then ClientSocket.Active := False; if InputQuery('Присоединиться к...', 'Имя сервера:', Server) then if Length(Server) > 0 then with ClientSocket do begin Host := Server; Active := True; FileListenItem.Checked := False; end; end; В первой строке я проверяю, если клиент уже подключён, то я отключаю его. Затем я запрашиваю ввод имени сервера (имя компьютера или его IP адрес где запущена такая же прога и включено прослушивание). InputQuery - показывает
http://www.vr-online.ru
38
VR-online Journal (Horrific and VR-Team)
Для программистов №5
диалоговое окно, в котором можно производить ввод. В качестве первых двух параметров выступают заголовки. Это строки, которые подсказывают пользователю, что надо ввести. Третий параметр - это переменная типа строки, в которую будет записан результат ввода. Дальше идёт проверка if Length(Server)>0 then если длинна введённого пользователем текста больше 0 (то есть, если строка не пустая), то выполнить действия. В качестве действий выполняется три оператора. with ClientSocket do begin Host := Server; Active := True; FileListenItem.Checked := False; end; Обрати внимание на присутствие здесь конструкции with ClientSocket do. Я уже рассказывал о ней, но всё же повторюсь. Она говорит о том, что все действия между последующими BEGIN и END, надо по умолчанию выполнять с ClientSocket. Если какоето действие нельзя выполнить, то выполнять его с главной формой. Сейчас всё будет видно на примере. Host := Server. Если бы эта строка не стояла внутри конструкции with, то Host не был бы найден. А так, Delphi находит свойство Host у ClientSocket и присваивает в него введённое имя сервера. Можно было не использовать конструкцию with, но тогда наша фунция бы выглядела так: procedure TChatForm.FileConnectItemClick(Sender: TObject); begin if ClientSocket.Active then ClientSocket.Active := False; if InputQuery('Присоединиться к...', 'Имя сервера:', Server) then if Length(Server) > 0 then begin ClientSocket.Host := Server; ClientSocket.Active := True; FileListenItem.Checked := False; end; end; Итак, имя сервера ввели, можно его активировать (Active := True;). После этого я на всякий случай отключаю прослушивание (FileListenItem.Checked := False). Я уже говорил, что одно приложение не может быть одновременно и сервером и клиентом для одного порта. Если имя сервера было введено правильно, то приложение начнёт присоединяться к нему. Присоединились? Хорошо. Теперь надо разорвать связь. Вот процедура, вызываемая при щелчке по пункту меню "Отключиться": procedure TChatForm.Disconnect1Click(Sender: TObject); begin ClientSocket.Active := False; ServerSocket.Active := True; Statusbar1.Panels[0].Text := 'Прослушиваю...'; end;
http://www.vr-online.ru
39
VR-online Journal (Horrific and VR-Team)
Для программистов №5
Всё очень просто. Первая строка делает неактивным клиент (разрывает связь). Вторая строка включает сервер на прослушивание. Третья выводит сообщение в строку состояния. Всё это мы уже пережевали немного раньше. Теперь перейдём к отправке текста. Для этого в примере перехватывается событие OnKeyDown у TMemo: procedure TChatForm.Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_Return then if ServerSocket.Active then ServerSocket.Socket.Connections[0].SendText( Memo1.Lines[Memo1.Lines.Count - 1]) else ClientSocket.Socket.SendText(Memo1.Lines[Memo1.Lines.Count - 1]); end; Вначале идёт проверка: если нажата клавиша ENTER, то мы продолжаем работать. Дальше проверяется, если это сервер (к нам подключились), то для отправки используется компонент TServerSocket иначе TClientSocket. Я немного мухлюю. Я проверяю, активен ли сервер, но не проверяю, есть ли к нему присоединившиеся клиенты. Если их нет, то произойдёт ошибка. Если не хочешь, чтоб выскакивала эта ошибка то напиши так: if (ServerSocket.Active=true) and (ServerSocket.Socket.ActiveConnections>0) then ServerSocket.Socket.Connections[0].SendText( Memo1.Lines[Memo1.Lines.Count - 1]) Теперь будет проверяться активность сервера и количество конектов (ServerSocket.Socket.ActiveConnections), которое должно быть обязательно больше 0. Если хотябы одно из этихз условий не выполнено, то отправки текста не будет. Сначала разберёмся с клиентом, он проще. У него есть свойство Socket типа TClientWinSocket, которое отвечает за всю сетевую работу. У этого Socket есть интересующие нас методы SendBuf (отправить буфер), SendStream (отправить поток) и непосредственно SendText (отправить текст). Вот последняя нас и интересует. С помощью неё мы отправляем последнюю введённую пользователем строчку (Memo1.Lines[Memo1.Lines.Count - 1]). У сервера чуточку сложнее. Там тоже есть свойство Socket типа TServerWinSocket. Но тут нельзя сразу отправить текст. Для этого нужно выбрать, какому именно клиенту предназначается текст. Я отправляю всегда первому клиенту ServerSocket. Socket. Connections[0]. SendText, но если ты захочешь отправлять сообщения сразу всем, то это можно сделать так: for I:=0 to ServerSocket.Socket.ActiveConnections-1 do begin ServerSocket.Socket.Connections[i].SendText( Memo1.Lines[Memo1.Lines.Count - 1]) end; Весь остальной текст проги - это ответы на сообщения, генерируемые компонентами ТServerSocket и ТClientSocket, поэтому я не будут на них останавливаться. Все эти процедуры чисто информационные. Единственное, что нам надо - это сообщение
http://www.vr-online.ru
40
VR-online Journal (Horrific and VR-Team)
Для программистов №5
OnRead у ТClientSocket и OnClientRead у ТServerSocket . Эти сообщения происходят, когда на порт пришли какие-то данные (в нашем случае это текст). В примере, обе функции реализованы одинаковы, поэтому я приведу только одну: procedure TChatForm.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); begin Memo2.Lines.Add(Socket.ReceiveText); end; Это сообщение возвращает нам Socket типа TCustomWinSocket. Это очень удобно, потому что, вызвав Socket.ReceiveText, мы получаем переданный нам текст. Я его просто добавляю к Memo2. Программа готова. Запусти одну её копию на одном компьютере, а другую на другом. Теперь в одной из них включи прослушивание, а из другой попробуй подсоединиться, введя имя или IP- адрес первого компьютера. Попробуй напечатать какой-нибудь текст в поле Memo. После нажатия пимпы Enter, текст пересылается на другой компьютер. Развлекайся, а я пошёл работать дальше. Напоминаю: если с чем-то не разобрался, то не волнуйся. Просто поиграй с примером и всё само придёт. Я даю только основы, а всё самое главное придёт собственным потом. Тренируйся, и всё получится. Copyright © Фленов Михаил aka Horrific
http://www.vr-online.ru
41