Отправляет email-рассылки с помощью сервиса Sendsay

Web-Мастеринг - с нуля до профи

  Все выпуски  

нет.


Информационный Канал Subscribe.Ru

Познавательное программирование

(comp.soft.prog.urisprog)

Выпуск 22


Здравствуйте, Уважаемые подписчики!

Самый большой скачок в производительности и самые большие перемены в стиле работы готовит нам переход на широкомасштабное использование сетей.
- Билл Гейтс.


На заметку

Помещаю решения моей задачи из института, о которой речь шла ранее:

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

- Хотя господин Alf и "не разделил восторг автора по поводу редкой оригинальности задачи" :), тем не менее он убрал из моей версии лишние детали:
#include <stdio.h>

int Check(long i)
{
  int C = 0;
  short d;
  long n = i;
  while (!C && (n > 0))
  {
    d = n % 10;
    if (d)
    {
      C = i % d;
      n /= 10;
    }
    else
      C = 1;
  }
  return !C;
}

int main(int argc, char* argv[])
{
  long n = 100;
  for (long i=1; i<=n; i++)
    if (Check(i))
      printf("%d\n", i);
    return 0;
}


- А вот Дмитрию Максимову задача понравилась, он решал её 10 минут:
#include "stdafx.h"

bool IsNeed(int n)
{
        int k = n;
        while( k )
        {
                // Получаем последнюю цифру
                int digit = k % 10;
                if ( digit && ( n % digit ) )
                {
                        return false;
                }
                k /= 10;
        }
        return true;
}

int main()
{
        const int n = 1000;

        for(int i = 0; i <= n; ++i)
        {
                if ( IsNeed(i) )
                {
                        printf("%d ",i);
                }
        }

        return 0;
}


- Вариант от Дмитрия Плотникова:
#include <iostream>
#include <CONIO.H>
 
#define N 100000
 
inline bool TestDigit(int Digit){
 int CurDigit,End=Digit;
 while ( End!=0 ) {
  CurDigit=End%10;
  End/=10;
  if ( CurDigit!=0 && (Digit%CurDigit) )
   return false;
 }
 return true;
}
 
int main()
{
  for ( int i=1;i<=N;i++ ) {
  if ( TestDigit(i) ) std::cout<<i<<' '<<std::flush;
  }
 getch();
 return 0;
}


- Вариант не представившегося владельца e-mail twilight_sun@...:
#include "stdio.h"

int test(int n)
{ int r,res;
  for(r=n,res=1;r&&(res=!(n%(r%10)));r/=10);
  return res;
}

void main()
{
  int i=1,n;
  printf("nn :-> ");scanf("%i",&n);
  for(;i<=n;++i)if (test(i)) printf("%i ",i);
}


- А вот "приз" за САМОЕ ИЗВРАЩЁННОЕ РЕШЕНИЕ :) (при этом, судя по всему, достаточно быстрое, тем не менее) достаётся программисту по имени Raptor, встречайте:
BOOL CTestDivDlg::TestNumber1(DWORD dwNumber)
{
     DWORD dwTemp;
     BOOL IsDiv[8]; // 2 3 4 5 6 7 8 9
     memset(IsDiv,0,sizeof(BOOL)*8);

     // 2
     if((dwNumber&0x01)==0)
     IsDiv[0]=TRUE;

     // 3
     dwTemp=dwNumber*0xaaaaaaab;
     if(dwTemp<=0x55555555)
     IsDiv[1]=TRUE;

     // 4
     dwTemp=dwNumber>>1;
     if(((dwTemp&0x01)==0)&&IsDiv[0])
     IsDiv[2]=TRUE;

     // 5
     dwTemp=dwNumber*0xcccccccd;
     if(dwTemp<=0x33333333)
     IsDiv[3]=TRUE;

     // 6
     dwTemp=dwNumber;
     if(IsDiv[0]&&IsDiv[1])
     IsDiv[4]=TRUE;

     // 7
     dwTemp=dwNumber*0xb6db6db7;
     if(dwTemp<=0x24924924)
     IsDiv[5]=TRUE;

     // 8
     dwTemp=dwNumber>>2;
     if(((dwTemp&0x01)==0)&&IsDiv[2])
     IsDiv[6]=TRUE;

     // 9
     dwTemp=dwNumber*0x38e38e39;
     if(dwTemp<=0x1c71c71c)
     IsDiv[7]=TRUE;

/*
  // проверяем число
     while (dwNumber>0)
  {
  if(IsDiv[(dwNumber%10)+2]==FALSE)
  return FALSE;
  dwNumber/=10;
  }
  return TRUE;
*/

  dwTemp=0;
     while(dwNumber>999999999)
     {
          dwTemp+=1;
          dwNumber-=1000000000;
     }
     if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

     dwTemp=0;
     while(dwNumber>99999999)
     {
          dwTemp+=1;
          dwNumber-=100000000;
     }
     if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

     dwTemp=0;
     while(dwNumber>9999999)
     {
          dwTemp+=1;
          dwNumber-=10000000;
     }
     if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

    dwTemp=0;
     while(dwNumber>999999)
     {
          dwTemp+=1;
          dwNumber-=1000000;
     }
     if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
     return FALSE;

    dwTemp=0;
    while(dwNumber>99999)
    {
          dwTemp+=1;
          dwNumber-=100000;
    }
    if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

    dwTemp=0;
    while(dwNumber>9999)
    {
         dwTemp+=1;
        dwNumber-=10000;
    }
    if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

    dwTemp=0;
    while(dwNumber>999)
    {
         dwTemp+=1;
         dwNumber-=1000;
    }
    if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

    dwTemp=0;
    while(dwNumber>99)
    {
         dwTemp+=1;
         dwNumber-=100;
    }
    if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

    dwTemp=0;
    while(dwNumber>9)
    {
         dwTemp+=1;
         dwNumber-=10;
    }
    if((dwTemp>1)&&(IsDiv[dwTemp-2]==FALSE))
    return FALSE;

    if((dwNumber>1)&&(IsDiv[dwNumber-2]==FALSE))
    return FALSE;

    return TRUE;

}


Вопрос - ответ

Вопрос Максима:
Здравствуйте. Подскажите пожалуйста как сделать в Delphi так, чтобы при работе программы в трее отображался её значок?

Ваши ответы присылайте на urisff@inbox.ru
Ваши вопросы по программированию вы можете прислать по адресу urisff@inbox.ru и остальные подписчики помогут вам найти решение.

Далее следует сборник вопросов-ответов от соавтора рассылки №1 Сергея Гусева.
Вопрос-Ответ:

Выпуск #1

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

procedure TForm1.mnuFileClick(Sender: TObject);
begin
 If mnuFile.Checked = False Then
   Begin
     mnuFile.Checked:=True;
     ShowMessage('Пункт меню отмечен !!!');
   End
 Else
   If mnuFile.Checked = True Then
     Begin
       mnuFile.Checked:=False;
       ShowMessage('Пункт меню не отмечен !!!');
     End;
end;


2. Как вывести текст поверх картинки загруженной в Image ?

procedure TForm1.FormCreate(Sender: TObject);
Var BMP : TBitmap;
begin
 BMP:=TBitmap.Create;
 BMP.LoadFromFile('MyPicture.bmp');
 Image1.Picture.Assign(BMP);
 Image1.Canvas.Brush.Color:=clBlue;
 Image1.Canvas.Font.Name:='Arial';
 Image1.Canvas.Font.Size:=10;
 Image1.Canvas.TextOut(10, 10, 'Некоторый текст !!!');
end;


3. При выполнении большого цикла мое приложение как бы замирает. Можно ли это как-то исправить ?

Для этого нужно в тело цикла вставить конструкцию Application.ProcessMessages. При этом можно будет нажимать на различные кнопки, пользоваться меню, но нельзя будет закрыть приложение :(.

procedure TForm1.Button1Click(Sender: TObject);
Var I: Integer;
begin
 For I:=1 To 100000 Do
   Begin
     Label1.Caption:=IntToStr(I);
     Application.ProcessMessages;      
   End;
end;


4. При удалении записи в БД выдается запрос на английском языке, хотя вся моя программа построена на русском языке. Можно ли как-нибудь сделать этот запрос русским ?

Я тоже столкнулся с такой неприятной мелочью. Лично мне пришла в голову одна очень простая мысль: я сделал отдельный пункт меню "Удалить запись", в котором я написал свой обработчик удаления записи из набора данных с русским диалоговым запросом. Далее я задал свойству пункта меню ShortCut (быстрый доступ "горячей клавишей") значение Ctrl+Del. Еще можно просто запретить вывод запроса на удаление путем установки значения False подсвойству dgComfirmDelete свойства сетки DBGrig, но это не очень хорошо, потому что можно и не заметить как запись будет удалена. Вот небольшой и понятный пример:

procedure TForm1.mnuDBDelClick(Sender: TObject);
begin
 If Application.MessageBox('Удалить запись ?', 'Удаление:', MB_YESNO) = idYES
   Then Table1.Delete;
end;


5. Как написать свой ScreenSaver ?

Честно говоря, я даже никогда об этом не задумывался :). По сути ScreenSaver - это обычная программа переименованная в расширение .scr. Даже если Вы любую программу переименуете из .exe в .scr, и поместите в каталог Windows или Windows\System, то через установленный промежуток времени переименованная программа сработает как ScreenSaver. Значит нужно написать свою собственную программу и в обработчики нажатия клавиш onKeyPress, onKeyDown и onKeyUp записать код закрытия программы. Аналогичным образом нужно "оформить" обработчики движения мыши. А еще надо проверять, чтобы программа запускалась в одном экземпляре, иначе таких хранителей экрана будет одновременно запущено сразу несколько :).


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

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

procedure TForm1.mnuDBGotoClick(Sender: TObject);
Var Go: String;
begin
 Go:='1';
 InputQuery('Переход к записи', 'Перейти к записи с номером:', Go);
 Try
   If NOT Table1.Locate('Code', Go, []) Then
     MessageDlg('Запись с номером' + Go + 'не найдена', mtWarning, [mbOK], 0);
 Except
   MessageDlg('Значение' + Go + 'не допустимо', mtWarning, [mbOK], 0); 
 End;
end;


7. При вводе текста в Memo и достижении правого края контрола текст автоматически переносится на новую строку. Как с этим бороться ?

Для этого можно поступить двумя способами:

1) Установить свойству WordWrap значение False.
2) Установить свойству ScrollBars значение ssHorizontal - для появления горизонтальной полосы прокрутки, или ssBoth - для появления обоих полос прокрутки.


8. Как можно программно управлять открытием и закрытием компонента ComboBox и узнать его состояние в текущий момент ?

procedure TForm1.Button1Click(Sender: TObject);
begin
 //Открываем список
 ComboBox1.DroppedDown:=True;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
 //Закрываем список
 ComboBox1.DroppedDown:=False;
end;

procedure TForm1.Bla-Bla-Bla(Sender: TObject);
begin
 If ComboBox1.DroppedDown = True Then ShowMessage('Список открыт')
   Else ShowMessage('Список закрыт')
end;


9. В программе 1С я заметил, что при редактировании ячейки БД в правой ее части появляется кнопка с тремя точками. Если нажать эту кнопку, то появляется отдельная форма для выбора товара. А можно ли такое сделать в Делфи ?

Конечно можно, для этого нужно воспользоваться т.н. статическими полями (о них я рассказывал в одной из статей о БД). Для определенного статического поля нужно установить свойство ButtonStyle в значение cbsEllipsis. А затем обрабатывать событие onEditButtonClick сетки DBDrid.

procedure TForm1.DBGrid1EditButtonClick(Sender: TObject);
begin
 FormAdd2.ShowModal;
end;


10. Как программно создать компонент и навесить на него обработчик события?

uses ..., StdCtrls;

...

procedure TForm1.FormCreate(Sender: TObject);
Var Memo1 : TMemo;
begin
 Memo1:=TMemo.Create(Self);
 Memo1.Parent:=Form1;
 Memo1.Top:=10;
 Memo1.Left:=10;
 Memo1.Height:=90;
 Memo1.Width:=150;
 Memo1.OnKeyPress:=MyMemoKeyPress;
end;

procedure TForm1.MyMemoKeyPress(Sender: TObject; var Key: Char);
begin
 If NOT(Key In ['0'..'9', Chr(8), Chr(32)]) Then Key:=Chr(0);
end;


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

Разделитель в своей программе можно задать свой. А так для этого служит DecimalSeparator.

procedure TForm1Main.FormCreate(Sender: TObject);
begin
 DecimalSeparator:='.';
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 ShowMessage('Текущий разделитель: ' + DecimalSeparator);
 Edit1.Text:='10' + DecimalSeparator + '5';
end;


12. Как установить курсор мыши в определенную позицию экрана ?

procedure TForm1.Button1Click(Sender: TObject);
Var P: TPoint;
begin
 GetCursorPos(P); // Сохраняем позицию курсора
 ...
 // Что-то делаем
 ...
 SetCursorPos(P.X, P.Y); // Восстанавливаем позицию курсора
end;


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

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

procedure TForm1.FormCreate(Sender: TObject);
begin
 Try
   If Button1.CanFocus Then Button1.SetFocus;
 Except
 End;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
 If Button1.CanFocus Then Button1.SetFocus;
end;


14. Как из моей программы некоторый файл сделать скрытым ?

Для этого ему нужно задать атрибут как "скрытый".

procedure TForm1.FormCreate(Sender: TObject);
begin
 SetFileAttributes('File.txt', FILE_ATTRIBUTE_HIDDEN);
end;


15. Где можно взять архивы и учебники по Delphi?

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

http://soobcha.ru/rushelp/download.php?id=3  - Технология COM (1,7 Мб)
http://soobcha.ru/rushelp/download.php?id=4  - Примеры (510 Кб)
http://soobcha.ru/rushelp/download.php?id=2  - Основы Delphi (790 Кб)
http://soobcha.ru/rushelp/download.php?id=6  - Программирование интерфейса юзера (13 Мб)
http://soobcha.ru/rushelp/download.php?id=7  - Пакет справочников (800 Кб)
http://soobcha.ru/rushelp/download.php?id=15 - Мультимедиа для Windows (510 Кб)
http://soobcha.ru/rushelp/download.php?id=11 - Азбука программирования API (19,2 Мб)
http://soobcha.ru/rushelp/download.php?id=12 - Работа с файлами в API (12,8 Мб)
http://soobcha.ru/rushelp/download.php?id=10 - Программирование для Вин`95 (7,7 Мб)
http://soobcha.ru/rushelp/download.php?id=21 - Руководство разработчика Часть 1 (5,1 Мб)
http://soobcha.ru/rushelp/download.php?id=22 - Руководство разработчика Часть 2 (5,4 Мб)
http://soobcha.ru/rushelp/download.php?id=19 - Memory SDK (190 Кб)
http://soobcha.ru/rushelp/download.php?id=18 - Библиотека программиста Delphi (8 Мб)
http://soobcha.ru/rushelp/download.php?id=13 - Советы по Delphi (160 Кб)
http://soobcha.ru/rushelp/download.php?id=2  - Основы COM (2,2 Мб)
http://soobcha.ru/rushelp/download.php?id=5  - Сборник FAQ (280 Кб)
http://soobcha.ru/rushelp/download.php?id=16 - OpenGL в Delphi (1,3 Мб)
http://soobcha.ru/rushelp/download.php?id=17 - Примеры (600 Кб)
http://soobcha.ru/rushelp/download.php?id=20 - ActivX в Delphi (1,2 Мб).
http://soobcha.ru/rushelp/download.php?id=14 - Русская справка для Паскаля (310 Кб)

http://delphimaster.ru/articles/book/Chap08.pdf - GDI, графика, шрифты
http://delphimaster.ru/articles/book/Chap09.pdf
http://delphimaster.ru/articles/book/Chap11.pdf - Многопоточные приложения
http://delphimaster.ru/articles/book/Chap17.pdf - Буфер обмена
http://delphimaster.ru/articles/book/Chap18.pdf - Мультимедиа

http://cydsoft.com/vr-online/delphi/index.htm - Библия программиста
http://hot.ee/del5vol1/Delphi5vol1.pdf
http://hot.ee/del5vol2/Delphi5vol2.pdf

http://bcbdev.ru/winapi/win32api.exe - Немного WinAPI
http://bcbdev.ru/winapi/win32api.zip

http://filesearch.ru/cgi-bin/s?t=n&q=ftp.aflp.ru/pub/bn.fe/BN.FE.DELPHI
http://jenyay.wallst.ru/index.php?id=articles
http://forum.vingrad.ru/index.php?act=SF&f=32

P.S.: Для уменьшения объема рассылки все вопросы были отредактированы !!!


Любые комментарии, жалобы, пожелания и сообщения об ошибках настоятельная просьба присылать на e-mail.

Гусев Сергей.
e-mail: satanzone@yandex.ru
site: http://icops.narod.ru


Статьи

DDE-Чат

В очередной своей работе я хочу рассказать о DDE - Dynamic Data Exchange, то есть о динамическом обмене данными между приложениями. Начав изучать тему DDE, я понял, что это как-то похоже на чат. Принцип работы с DDE - это технология Клиент-Сервер. Delphi позволяет создавать как сервер, так и клиент. Все компоненты для организации технологии DDE находятся на странице System Палитры компонентов. Сразу хочу отметить, что при использовании DDE возникает множество проблем, которые будут оговорены далее.

Приложение Сервера:

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

  • 1. TDdeServerConv;
  • 2. TDdeServerItem;
  • 3. Два компонента TMemo для отправляемых и принимаемых сообщений;
  • 4. Кнопку TButton.

Компонент TDdeServerConv представляет собой сеанс передачи данных, а компонент TDdeServerItem - сами передаваемые данные. Обычно приложение Сервера содержит один компонент TDdeServerConv и несколько компонентов TDdeServerItem. Эти компоненты используются совместно. Через свойство ServerConv компонент TDdeServerItem связывается с TDdeServerConv.

 ...
 DdeServerItem1.ServerConv:=DdeServerConv1;
 ...

Так как TDdeServerItem содержит сами передаваемые данные, то эти данные должны где-то содержаться. Все данные заносятся в два основных свойства: Lines - для передачи сразу нескольких строк и Text - для передачи одной строки. Как только одно из этих свойств изменяться, они сразу же передаются приложению Клиента.

Для того чтобы организовать соединение Клиента с Сервером, Клиенту нужно знать все параметры Сервера. Для этого нужно использовать метод CopyToClipboard;

 ...
 DdeServerItem1.CopyToClipboard;
 ...

При получении данных от клиента нужно обрабатывать событие onPokeData. В нашем случае нужно просто отобразить переданные данные на одном из компонентов TMemo.

procedure TForm1.DdeServerItem1PokeData(Sender: TObject);
begin
 ServerMemoIn.Lines:=DdeServerItem1.Lines;
end;

А теперь рассмотрим наглядный пример приложения Сервера. Начнем с процедуры создания формы:

procedure TForm1.FormCreate(Sender: TObject);
begin
 ServerMemoOut.Clear; // Компонент для передаваемых данных
 ServerMemoIn.Clear; // Компонент для получаемых данных

 DdeServerItem1.CopyToClipboard; // Сохраняем параметры Сервера
 DdeServerItem1.ServerConv:=DdeServerConv1;
end;

При получении данных от Клиента отображаемых их в одном из компонентов TMemo:

procedure TForm1.DdeServerItem1PokeData(Sender: TObject);
begin
 ServerMemoIn.Lines:=DdeServerItem1.Lines;
end;

Кнопка для отправки сообщения Клиенту. При нажатии на кнопку происходи присвоение текста, введенного в другой компонент TMemo и его немедленная отправка Клиенту:

procedure TForm1.btnSendClick(Sender: TObject);
begin
 DdeServerItem1.Lines:=ServerMemoOut.Lines;
end;

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

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
Var H: HWND;
begin
 H:=FindWindow(Nil, 'ChatClient');
 If H <> 0 Then SendMessage(H, WM_CLOSE, 0, 0);
end;

Trouble № 1:   Мною была замечена одна очень не приятная вещь. Приложение Сервера без проблем принимает многострочные данные от Клиента, а в свою очередь Клиент не "хочет" принимать многострочные данных от Сервера. Именно поэтому я решил запретить нажатие клавиши Enter, чтобы хоть как-то скрыть этот огромный недостаток.

procedure TForm1.ServerMemoOutKeyPress(Sender: TObject; var Key: Char);
begin
 If Key = #13 Then Key:=#0; 
end;

Приложение Клиента:

Теперь попробуем создать приложение Клиента. Клиент предназначен для получения данных от приложения Сервера. Теоретически Клиентов может быть несколько. Создадим новый проект. Затем положить на форму стандартные и специальные компоненты присущие Клиенту:

  • 1. TDdeClientConv;
  • 2. TDdeClientItem;
  • 3. Два компонента TMemo для отправляемых и принимаемых сообщений;
  • 4. Кнопку TButton.

Компонент TDdeClientConv также представляет собой сеанс передачи данных, а компонент TDdeClientItem - сами передаваемые данные, которые также хранятся в свойствах Lines - для отправки нескольких строк и Text - для отправки одной строки. Обычно приложение клиента также содержит один компонент TDdeClientConv и несколько компонентов TDdeClientItem. Эти компоненты используются совместно. Через свойство DdeConv компонент TDdeClientItem связывается с компонентом TDdeClientConv. А через свойство DdeItem задается источник данных.

 ...
 DdeClientItem1.DdeConv:=DdeClientConv1;
 DdeClientItem1.DdeItem:='DdeServerItem1';
 ...

Для организации параметров соединения используются следующие свойства:

  • DdeService - Имя Сервера, если Сервер располагается в текущей директории;
  • DdeTopic - Тема обмена, обычно это компонент TDdeServerConv
  • ServiceApplication - Имя Сервера, если Сервер располагается не в текущей директории.

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

Trouble № 2:    Установить перечисленные свойства динамически, то есть во время выполнения программы, скорее всего не получиться. Все эти команды будут просто игнорированы. Это нужно делать на этапе конструирования формы через Инспектор объектов.

 ...
 DdeClientConv1.DdeService:='ChatServer';
 DdeClientConv1.DdeTopic:='DdeServerConv1';
 ...
 // ИЛИ
 ...
 DdeClientConv1.ServiceApplication:='С:\ChatServer';
 DdeClientConv1.DdeTopic:='DdeServerConv1';
 ...

При установки свойств через Инспектор объектов нужно воспользоваться одним из свойств компонента TDdeClientConv: DdeService или DdeTopic. При этом заполнение полей одного свойства приведет к заполнению полей другого свойства. Также можно нажать кнопку "Paste Link", что приведет к автоматическому заполнению полей. Но, эта кнопка будет активна только в том случае, если приложение Сервера было хоть раз запущено ранее, то есть "сработал" метод CopyToClipboard.

При помощи свойства ConnectMode можно управлять способ подключения к Серверу. Если это свойство установлено в ddeAutomatic, то подключение осуществляется автоматически, а если свойство установлено в ddeManual, то подключение осуществляется вручную и для нормальной работы приложения программисту придется самому описывать все операторы. Для этого нужно воспользоваться группой операторов SetLink, OpenLink, CloseLink и RequestData.

Для того чтобы отправить текстовое сообщение на Сервер существуют две основные функции PokeData - для отправки на Сервер однострочных данных и функция PokeDataLines для отправки многострочных данных.

А теперь рассмотрим наглядный пример приложения Клиента. Начнем с процедуры создания формы:

procedure TForm1.FormCreate(Sender: TObject);
begin
 ClientMemoOut.Clear;
 ClientMemoIn.Clear;

 DdeClientItem1.DdeConv:=DdeClientConv1;
 DdeClientItem1.DdeItem:='DdeServerItem1';

 DdeClientConv1.DdeService:='ChatServer';
 DdeClientConv1.DdeTopic:='DdeServerConv1';
end;

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

procedure TForm1.btnSendClick(Sender: TObject);
begin
 DdeClientConv1.PokeData(DdeClientItem1.DdeItem, PChar(Trim(ClientMemoOut.Text)));
 DdeClientConv2.PokeDataLines(DdeClientItem2.DdeItem, ClientMemoOut.Lines);
end;

Процедура для отображения полученных данных от Сервера:

procedure TForm1.DdeClientItem1Change(Sender: TObject);
begin
 ClientMemoIn.Lines:=DdeClientItem1.Lines;
end;

При закрытии приложения Клиента также закрываем и приложение Сервера:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
Var H: HWND;
begin
 H:=FindWindow(Nil, 'ChatServer');
 If H <> 0 Then SendMessage(H, WM_CLOSE, 0, 0);
end;

Здесь также запрещаем использовании клавиши Enter:

procedure TForm1.ClientMemoOutKeyPress(Sender: TObject; var Key: Char);
begin
 If Key = #13 Then Key:=#0;
end;

В процессе написания и отладки программы иногда необходимо узнать некоторые данные о соединении. Так, например, можно узнать имя Сервера:

 ...
 ShowMessage('Имя сервера 1 способом ' + DdeClientItem1.DdeConv.DdeService);
 ShowMessage('Имя сервера 2 способом ' + DdeClientConv1.DdeService);
 ...

А так можно узнать тему обмена данными и DdeServerItem:

 ...
 ShowMessage('Тема ' + DdeClientConv1.DdeTopic);
 ShowMessage('DdeServerItem ' + DdeClientItem1.DdeItem);
 ...

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


Любые комментарии, жалобы, пожелания и сообщения об ошибках настоятельная просьба присылать на e-mail.

Гусев Сергей.
e-mail: satanzone@yandex.ru
site: http://icops.narod.ru

Сменщик обоев Рабочего стола

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

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

  • 1. Компонент CheckListBox - для хранения списка файлов;
  • 2. Компонент Image - для отображения текущей картинки;
  • 4. Диалог OpenPictureDialog - для открытия графических файлов.
  • 3. И некоторое количество кнопок SpeedButton;

Информация о картинке Рабочего стола находиться в системном реестре по адресу: HKEY_CURRENT_USER\Control Panel\Desktop. Имя картинки содержится в параметре WallPaper, а информация о положении картинки располагается в параметрах TileWallpaper и WallPaperStyle. Вот список соответствующих значений:

Параметр "По центру" "Растянуть" "Рядом"
TileWallpaper 0 0 1
WallPaperStyle 0 2 0

И начнем мы с процедуры для автозапуска программы при старте Windows. Для этого по соответствующему адресу системного реестра занесем имя программы и ее путь: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run

...
// Описание глобальных переменных 
Var
  Form1  : TForm1;
  SS     : TSearchRec; // Переменная для поиска файлов
  Path   : String;     // Переменная для указания пути к файлам
  PicPos : String;     // Переменная для задания положения картинки на Рабочем столе
...

procedure TForm1.AutoRun(ProgTitle, Command: String; RunOnce: Boolean);
Var Key : String;
    Reg : TRegIniFile;
begin
  If RunOnce = True Then Key:='Once' Else Key:='';
  Reg:=TRegIniFile.Create;
  Reg.RootKey:=HKEY_LOCAL_MACHINE;
  Reg.WriteString('Software\Microsoft' + '\Windows\CurrentVersion\Run' +
                   Key + #0, ProgTitle, Command);
  Reg.Free;
end;

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

procedure TForm1.FormPaint(Sender: TObject);
Var BMP_Back: TBitmap;
begin
 Try
   BMP_Back:=TBitmap.Create;
   BMP_Back.LoadFromFile(ExtractFilePath(Application.ExeName) + 'Back.bmp');
   Canvas.Draw(-1, -20, BMP_Back);
   BMP_Back.Free;
 Except
   Form1.Visible:=False;
   Application.MessageBox('Файл Back.bmp для фона программы не найден !',
                          'CoolWall - Сменщик обоев Рабочего стола',
                           MB_OK + MB_ICONERROR);
   Form1.Close;
 End;  
end;

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

procedure TForm1.FormCreate(Sender: TObject);
Var Rgn1 : HRGN;
    I    : Integer;
    Ini  : TIniFile;
begin
 // Вызов процедуры автозапуска программы
 AutoRun('CoolWall', (ExtractFilePath(Application.ExeName) + 'CoolWall.exe'), False);

 // Делаем форму не стандартного вида, в виде овала без заголовка 
 Rgn1:=CreateRoundRectRgn(5, 22, 640, 380, 180, 180);
 SetWindowRgn(Handle, Rgn1, True);

 // Считываем последний указанный путь с графическими файлами
 Ini:=TIniFile.Create(ExtractFilePath(Application.ExeName) + 'CoolWall.ini');
 Path:=Ini.ReadString('Main', 'LastPath', Path);

 // Последовательно ищем файлы формата .jpg и .bmp 
 If FindFirst(Path + '*.bmp', faAnyFile, SS) = 0
   Then CheckListBox1.Items.Add(Path + SS.Name);

 While FindNext(SS) = 0 Do CheckListBox1.Items.Add(Path + SS.Name);
 FindClose(SS);

 If FindFirst(Path + '*.jpg', faAnyFile, SS) = 0
   Then CheckListBox1.Items.Add(Path + SS.Name);

 While FindNext(SS) = 0 Do CheckListBox1.Items.Add(Path + SS.Name);
 FindClose(SS);

 // Все строки CheckListBox устанавливаем во включенное состояние
 For I:=1 To CheckListBox1.Items.Count Do CheckListBox1.Checked[I-1]:=True;

 // Если список файла не пуст, то загружаем первую картинку и отображаем путь
 If CheckListBox1.Items.Count <> 0 Then
   Begin
     Image1.Picture.LoadFromFile(CheckListBox1.Items.Strings[0]);
     Label2.Caption:=CheckListBox1.Items.Strings[0];
   End;
end;

Так как форма не содержит заголовка, то делаем возможность перетаскивания формы за любое место, а не только за заголовок:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
 If Button = mbLeft Then
   Begin
     ReleaseCapture;
     Form1.Perform(WM_SYSCOMMAND, $F012, 0);
   End;
end;

Кнопка "Открыть". При нажатии на кнопку происходит заполнение списка файлов, и путь к этим файлам автоматически сохраняется в файл инициализации:

procedure TForm1.btnOpenClick(Sender: TObject);
Var I   : Integer;
    Ini : TIniFile;
begin
 If OpenPictureDialog1.Execute Then
   Begin
     CheckListBox1.Clear;
     For I:=0 To OpenPictureDialog1.Files.Count-1
       Do CheckListBox1.Items.Add(OpenPictureDialog1.Files.Strings[I]);
   End;

 For I:=0 To CheckListBox1.Items.Count-1 Do CheckListBox1.Checked[I]:=True;
 Image1.Visible:=True;

 Ini:=TIniFile.Create(ExtractFilePath(Application.ExeName) + 'CoolWall.ini');
 Ini.WriteString('Main', 'LastPath', Path);
 Ini.Free;
end;

Кнопка для указания параметра "По центру":

procedure TForm1.btnCenterClick(Sender: TObject);
begin
 PicPos:='По центру';
end;

Кнопка для указания параметра "Растянуть":

procedure TForm1.btnRastClick(Sender: TObject);
begin
 PicPos:='Растянуть';
end;

Кнопка для указания параметра "Рядом":

procedure TForm1.btnRadClick(Sender: TObject);
begin
 PicPos:='Рядом';
end;

Главная процедура для указания картинки Рабочего стола и положения картинки:

procedure TForm1.btnOKClick(Sender: TObject);
Var Reg : TRegistry;
    BMP : TBitmap;
    JPG : TJPEGImage;
begin
 If CheckListBox1.Items.Count = 0 Then
   Application.MessageBox('Файл не указан !', 'Ошибка:', MB_OK + MB_ICONERROR)
 Else
   Begin
     BMP:=TBitmap.Create;
     JPG:=TJPEGImage.Create;

     Reg:=TRegistry.Create;
     Reg.RootKey:=HKEY_CURRENT_USER;

     Try
       If Reg.OpenKey('Control Panel\Desktop', False) Then
         Begin
           Reg.WriteString('WallPaper', 'C:\WINDOWS\CoolWall.bmp');
           If PicPos = 'По центру' Then
             Begin
               Reg.WriteString('TileWallpaper', '0');
               Reg.WriteString('WallPaperStyle', '0');
             End
           Else
             If PicPos = 'Растянуть' Then
               Begin
                 Reg.WriteString('TileWallpaper', '0');
                 Reg.WriteString('WallPaperStyle', '2');
               End
             Else
               If PicPos = 'Рядом' Then
                 Begin
                   Reg.WriteString('TileWallpaper', '1');
                   Reg.WriteString('WallPaperStyle', '0');
                 End;
         End;
       Reg.CloseKey;
     Finally
       Reg.Free;
     End;

     // Если файл с списке имеет формат .jpg то переводим его в формат .bmp
     If CheckListBox1.Checked[CheckListBox1.ItemIndex] Then
       If ExtractFileExt(CheckListBox1.Items.Strings[CheckListBox1.ItemIndex]) = '.jpg'
         Then
           Begin
             JPG.LoadFromFile(CheckListBox1.Items.Strings[CheckListBox1.ItemIndex]);
             BMP.Assign(JPG);
             BMP.SaveToFile('C:\WINDOWS\CoolWall.bmp');
           End
       Else
         If ExtractFileExt(CheckListBox1.Items.Strings[CheckListBox1.ItemIndex]) = '.bmp'
           Then
             Begin
               BMP.LoadFromFile(CheckListBox1.Items.Strings[CheckListBox1.ItemIndex]);
               BMP.SaveToFile('C:\WINDOWS\CoolWall.bmp');
             End;

     JPG.Free;
     BMP.Free;

     // А вот самая главная функция всей программы
     SystemParametersInfo(SPI_SETDESKWALLPAPER,
                          0,
                          Nil,
                          SPIF_SENDCHANGE);
   End;
end;

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

procedure TForm1.CheckListBox1Click(Sender: TObject);
begin
 If CheckListBox1.Checked[CheckListBox1.ItemIndex] = False Then
   Begin
     Label2.Caption:='Не активен';
     Image1.Visible:=False;
   End
 Else
   Begin
     Label2.Caption:=CheckListBox1.Items.Strings[CheckListBox1.ItemIndex];
     Image1.Visible:=True;
     Image1.Picture.LoadFromFile(CheckListBox1.Items.Strings[CheckListBox1.ItemIndex]);
   End;
end;

Процедура для очистки списка файлов:

procedure TForm1.btnClearClick(Sender: TObject);
begin
 If CheckListBox1.Items.Count = 0
   Then Application.MessageBox('Список файлов пуст !', 'Внимание:', MB_OK + MB_ICONERROR)
 Else
   Begin
     CheckListBox1.Clear;
     Image1.Visible:=False;
   End;  
end;

Кнопка для навигации по списку файлов вперед:

procedure TForm1.btnNextClick(Sender: TObject);
begin
 If CheckListBox1.Items.Count = 0
   Then Application.MessageBox('Список файлов пуст !', 'Внимание:', MB_OK + MB_ICONERROR)
 Else
   Begin
     If CheckListBox1.ItemIndex = CheckListBox1.Items.Count-1
       Then CheckListBox1.ItemIndex:=-1;

     If CheckListBox1.ItemIndex = -1 Then CheckListBox1.ItemIndex:=0
       Else CheckListBox1.ItemIndex:=CheckListBox1.ItemIndex + 1;

     If CheckListBox1.CanFocus Then CheckListBox1.SetFocus;
     CheckListBox1Click(Sender);
   End;
end;

Кнопка для навигации по списку файлов назад:

procedure TForm1.bntPriorClick(Sender: TObject);
begin
 If CheckListBox1.Items.Count = 0
   Then Application.MessageBox('Список файлов пуст !', 'Внимание:', MB_OK + MB_ICONERROR)
 Else
   Begin
     If CheckListBox1.ItemIndex = -1 Then CheckListBox1.ItemIndex:=0;

     If CheckListBox1.ItemIndex = 0 Then CheckListBox1.ItemIndex:=CheckListBox1.Items.Count-1
       Else CheckListBox1.ItemIndex:=CheckListBox1.ItemIndex-1;

     If CheckListBox1.CanFocus Then CheckListBox1.SetFocus;
     CheckListBox1Click(Sender);
   End;  
end;

При выходе из программы сохраняем последний указанный путь к графическим файлам:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
Var Ini: TIniFile;
begin
 Ini:=TIniFile.Create(ExtractFilePath(Application.ExeName) + 'CoolWall.ini');

 If Path = '' Then Ini.WriteString('Main', 'LastPath', ExtractFilePath(Application.ExeName))
   Else Ini.WriteString('Main', 'LastPath', Path);

 Ini.Free;
end;

P.S.: Если кто-нибудь из Вас знает как в Delphi работать с графическими файлами любых других форматов, то напишите мне, пожалуйста, или поделитесь ссылочкой. Буду весьма признателен.


Любые комментарии, жалобы, пожелания и сообщения об ошибках настоятельная просьба присылать на e-mail.

Гусев Сергей.
e-mail: satanzone@yandex.ru
site: http://icops.narod.ru



Все вопросы, пожелания и предложения ссылкообмена ведущему рассылки, прошу слать на e-mail: urisff@inbox.ru
Все статьи публикуются здесь только с разрешения (или рекомендации) автора.
Если вы хотите опубликовать здесь свою статью, то сделайте это, переслав её мне. В случае, если вы будете использовать приведённые в этой рассылке материалы, не забудьте сделать ссылку на эту рассылку.

Ведущий рассылки:
Набатников Иван
urisff@inbox.ru
http://WWW.URiS.TK



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу


В избранное