Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Ликбез по Делфи
Форум программистов > Системное программирование > Delphi и Pascal > Delphi - FAQ
Vadik(R)
Есть 6 вопросов по Делфи, которые не дают мне спокойно спать:
1. Как понять вариантную запись вида
x=record;
case integer of
0: (q:integer);
1: (q:string);
Не понятно в ней то, что в переключателе не написано имя переменной, тип которой Integer. Это вообще как?
И еще насчет вариантных записей. Если сменить переключатель, а потом снова вернуть прежний, то старые значения полей возобновяться, остануться пустыми и зануляться или будут хаотичными? - У переключателя нет привязки к переменным, зависящим от переключателя. При изменении переключателя значения полей не меняются.
Чем отличаеться обычная запись от "пакед"?
2. Как узнать когда соединение между сокетами точно установлено и можно посылать данные, не боясь, что они не дойдут?
3. Есть обработчик события onTimer1. Интервал таймера - 1 миллисекунда. Пусть там что-то вида:
x:=x+110;
y:=y+x;
z:=x*y;
Бывает так, что одна процедура уже не доделаеться, как начинает выполняться вторая, то есть произойдет:
x:=x+110;
y:=y+x;
x:=x+110;
z:=x*y;
y:=y+x;
z:=x*y;
Как мне сделать, чтобы код выполнялся последовательно:
x:=x+110;
y:=y+x;
z:=x*y;
x:=x+110;
y:=y+x;
z:=x*y;
4. Переменные, описанные в разделе var в том месте, где написано Form1: TForm1; всегда зануляються автоматически или могут принимать случаные значения?
5. Процедура FillChar может заполнять только массив символов? Или массив челых чисел тоже может? И коректно ли она работает во втором случае? - Да, корректно.

6. Как писать на элементах формы иероглифы? Когда я их пишу - они просто потом стираються, даже квадратики не вставляються.[/s]
European
2. Про реализацию Дельфийских компонентов говорить не буду, но в общем случае 100% вероятности получить нельзя. Можно "пингануть" сервер и если он ответил, то посылать данные. Хотя никто не дает гарантии, что в это время соединение не будет разорвано.
3. Нужно использовать средства синхронизации. Например, критические секции.
6. Нужно использовать компоненты, поддерживающие UNICODE. Эта тема обсуждалась на форуме, попробуйте поискать.
vital
2. У дельфийских компонентов есть события к-е срабатывают при соединении и есть (помоему) свойство connected boolean. F1 пробовал нажать?)))

3. Сделай что-то в духе
Var
sdelano:boolean;

sdelano:=false;

onTimer1:
if sdelano=true then
begin
sdelano:=false;
x:=x+110;
y:=y+x;
z:=x*y;
sdelano:=true;
end else exit;

Я пишу сходу, не проверял. Но идею ты понял) Примерно так)

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

Ключевое слово Packed говорит Delphi минимизировать память, взятую определенным объектом.
Обычно, сложные типы данных, такие как записи, имеют свои элементы по 2, 4 или 8 байта, соответствующие типам данных. Например, поле Word было бы 4-байтовое.
Записи также дополняются, для гарантии, что они закончены, 4-х байтовой границей.
Упаковка отменяет это, сжимая данные в наименьшую память, хотя с последующим уменьшенным доступом выполнения.
sax_ol
Цитата(Vadik(R) @ 27:08:2008 - 20:00) *
1. Как понять вариантную запись вида
x=record;
case integer of
0: (q:integer);
1: (q:string);
А никак не понимать, ибо бестолковая она.
Vadik(R)
vital, насчет пункта 3 согдасен с тобой, я только немного по-другому делал, но больше в голову ничего не приходило. Надо еще узнать, что такое критические секции. Насчет второго вопроса - надо поэксперементировать. Так же спасибо за информацию насчёт 1б.
sax_ol, эта запись не бестолковая, в оригинале она выглядит так:
type SunB=packed record
           s_b1,s_b2,s_b3,s_b4:u_char;
          end;

     SunW=packed record
           s_w1,s_w2:u_short;
          end;


     in_addr=record
              case Integer of
               0:(S_un_b:SunB);
               1:(S_un_w:SunW);
               2:(S_addr:u_long);
             end;
     TInAddr=in_addr;

     sockaddr_in=record
                  case Integer of
                   0:(sin_family:u_short;
                      sin_port:u_short;
                      sin_addr:TInAddr;
                      sin_zero:array[0..7] of Char);
                   1:(sa_family:u_short;
                      sa_data: array[0..13] of Char)
                 end;

     TSockAddrIn=sockaddr_in;
     TSockAddr=sockaddr_in;
, взял ]]>http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1021]]>. Только вот не понял как управлять переключателем.
Vadik(R)
Насчет вопроса про сокеты. Нету там свойства connected, поэтому единственный вариант, какой я придумал это сделать так:
ClientSocket1 подсоединяеться к ServerSocket1 и шлет данные, типа "привет" много раз пока они не дойдут до сервера. Как только сервер получает "привет", то ClientSocket2(на форме, где ServerSocket1) соединяеться с ServerSocket2(на форме, где ClientSocket1) и тоже тупо шлет "привет дошел". Если первое приложение увидит эту фразу, то 1 раз(ведь ClientSocket1-ServerSocket1 уже соединились точно) шлет "все ОК". Как только второе приложение увидит эту фразу, оно прекращает слать "привет дошел", и значит, соединенение было установлено. Попробую сейчас это реализовать, не будет ли каких ошибок.
П. с. А если ты имел в виду обработчики событий - onConnecting и onConnect, то первый из выполняеться при начале попытки установить соединение, и если в него впихнуть Socket.SendText('bla-bla-bla'), то ничего не дойдет. Если это впихнуть во второй обработчик, то данные доходят, но не всегда. Точнее на 127.0.0.1 у меня всегда доходят, а вот в Интеренете читал, что бывают случаи, когда данные могут не дойти.
European
Цитата(vital @ 28:08:2008 - 19:14) *
1б Обычная запись от записи пакед отличается размером и расположением в памяти. Пакед используется для больших структур.

Ключевое слово Packed говорит Delphi минимизировать память, взятую определенным объектом.
Обычно, сложные типы данных, такие как записи, имеют свои элементы по 2, 4 или 8 байта, соответствующие типам данных. Например, поле Word было бы 4-байтовое.
Записи также дополняются, для гарантии, что они закончены, 4-х байтовой границей.
Упаковка отменяет это, сжимая данные в наименьшую память, хотя с последующим уменьшенным доступом выполнения.

Что-то сложно все расписано... Для неупакованных структур (записей) каждый элемент выравнивается по определенной границе, которая устанавливается в настройках. Если запись упакована, то выравнивание не используется
Vadik(R)
Решен третий вопрос. Я пока с потоками не работал, но мне удобнее использовать идею, предложенную vital, в принципе то, что он и предложил и есть критическая секция, только писать её надо вручную и немного подкорректировать.
sax_ol
Цитата(Vadik(R) @ 28:08:2008 - 20:42) *
эта запись не бестолковая, в оригинале она выглядит так:
О майн год. smile.gif
European
Цитата(Vadik(R) @ 29:08:2008 - 11:59) *
Решен третий вопрос. Я пока с потоками не работал, но мне удобнее использовать идею, предложенную vital, в принципе то, что он и предложил и есть критическая секция, только писать её надо вручную и немного подкорректировать.

Это вариант худшее что можно придумать. Его использование обеспечит Вам десятки часов отладки по логам накануне сдачи проекта. Никто не запретит процессору приостановить ваш поток посередине вычислений, а после возобновления попытаться начать обработку новой итерации таймера. Хотя использование такого варианта - ваше право.
Vadik(R)
Цитата(sax_ol @ 29:08:2008, 16:20 ) *
О майн год. smile.gif

Вы бы лучше рассказали, как в ней управлять переключателем, чем издеваться, да еще и на НЕМЕЦКОМ.


Цитата(European @ 29:08:2008, 16:26 ) *
Это вариант худшее что можно придумать. Его использование обеспечит Вам десятки часов отладки по логам накануне сдачи проекта. Никто не запретит процессору приостановить ваш поток посередине вычислений, а после возобновления попытаться начать обработку новой итерации таймера. Хотя использование такого варианта - ваше право.

Ну проект я пока еще никому не собираюсь сдавать, я это делаю для себя, из интереса. Хотя свое время тоже надо беречь. Могу написать код, что у меня получилось по методу vital, а с секциями интересно конечно сделать, но у меня не получается. Не могу я почему-то sad.gif

Вот пример, может быть не самый удачный:
CODE
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, XPMan;

type
TForm1 = class(TForm)
Timer1: TTimer;
Timer2: TTimer;
Button1: TButton;
Button2: TButton;
XPManifest1: TXPManifest;
procedure Button1Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
fl: Boolean=False;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled:=True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
ShowMessage('Simple message.');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Timer2.Enabled:=True;
end;

procedure TForm1.Timer2Timer(Sender: TObject);
begin
while fl do Application.ProcessMessages;
fl:=true;
ShowMessage('Simple message.');
fl:=false;
end;

end.

А насчет отладки Вы оказались правы. Так если загрузиться около 1000 обработчиков и каждый будет выполнять while fl do Application.ProcessMessages;, то компьютер будет тормозить и придеться заводить еще другую переменную, считающую, сколько обработчиков событий ждут очереди своего выполнения.

П. С. А разработчики MicroSoft так старались сделать Windows многозадачным и многопоточным...
Проблема теперь избавиться от этого smile.gif
European
Цитата(Vadik(R) @ 29:08:2008 - 16:20) *
П. С. А разработчики MicroSoft так старались сделать Windows многозадачным и многопоточным...

Вот только не надо...
Цитата(Vadik(R) @ 29:08:2008 - 16:20) *
А насчет отладки Вы оказались правы. Так если загрузиться около 1000 обработчиков и каждый будет выполнять while fl do Application.ProcessMessages;, то компьютер будет тормозить и придеться заводить еще другую переменную, считающую, сколько обработчиков событий ждут очереди своего выполнения.

Критические секции все сделают за вас
zubr
1. Данная конструкция - заменитель сишного типа Union.
Структура с данным переключателем принимает размер по наибольшему типу. Значение устанавливается по одному из последних выбранных, то есть если в твоем примере
var
  adr: in_addr;

   adr.S_addr := 123456;

то и в структуре будет храниться только это значение. Если ты после этого назначишь значение другого типа, то и храниться уже будет то значение.
3. Здесь решение до стыда простое:
procedure TForm1.Timer1Timer(Sender: TObject);
var
  i:Integer;
begin
Timer1.Enabled:=False;
x:=x+110;
y:=y+x;
z:=x*y;
Timer1.Enabled:=True;
end;
Vadik(R)
Насчет третьего вопроса жжешь, че-то я видимо заумным стал... или вообще отупел smile.gif
А, вспомнил, что меня переклинило. Я применял эту конструкцию для последовательно чтения данных, пришедших в сокет. Там была похожая ситуация, как только вызовешь Socket.RecieveText, сразу может начаться следующий обработчик onClientRead.
А насчет второго вопроса я так понял это делаеться для экономии памяти. Например можно было описать три переменных, а то можно одну, но занимать она будет размер самой длинной. И переключателем там никак не управлять. Просто это стандартный способ описания такой переменной.

Только теперь я перестал видеть смысла в записях, с переключателем, где указан сам переключатель. Например,
var
  x:record
  case i:Integer of
  0: (q:integer);
  1: (s:shortstring);
  end;
begin
  x.q:=1;
  x.s:='g';
  x.i:=0;
  ShowMessage(IntToStr(x.q));
  ShowMessage(x.s);
end;

Хоть я и поставил переключатель в "0", все равно переменная s сохранилась. А если явно не присваивать x.i:=0, а вообще это убрать, то при вызове ShowMessage(IntToStr(x.i)); будет показано случайное число. Чето я не вижу в этой записи привязку переключателя. Помоему, одно и тоже, что записать:
x:record
i:Integer;
case integer of
0: (q:integer);
1: (s:shortstring);
end;
zubr
Да смысл в том, что при выделении памяти на данную структуру все члены структуры будут размещаться с одного адреса. В Delphi применяется для описания типа variant (посмотри исходники на variant), а также для замещения типа union в C++ прототипах фугкций.
Vadik(R)
Нашел ответ на второй вопрос. Конечно, может не самый лучший, но для моих целей пойдет. Вот код клиента:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, XPMan, ScktComp, ExtCtrls;

type
  TForm1 = class(TForm)
    XPManifest1: TXPManifest;
    ClientSocket1: TClientSocket;
    ServerSocket1: TServerSocket;
    Memo1: TMemo;
    Edit1: TEdit;
    Button1: TButton;
    Timer1: TTimer;
    Timer2: TTimer;
    procedure ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure FormCreate(Sender: TObject);
    procedure ClientSocket1Connect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Timer2Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Get, First, Second, Fl: Boolean;

implementation

{$R *.dfm}

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  s: String;
begin
  while Get do Application.ProcessMessages;
  Get:=True;
  if First then
  begin
    First:=False;
    Fl:=False;
    Timer2.Enabled:=True;
    while Timer1.Enabled do Application.ProcessMessages;
    ClientSocket1.Socket.SendText('2');
    Socket.ReceiveText
  end
  else
  if Second then
  begin
    s:=Socket.ReceiveText;
    if pos('2', s)>0 then
    begin
      Second:=False;
      Form1.Caption:='Клиент - подключен';
      Edit1.ReadOnly:=False;
      Button1.Enabled:=True
    end
  end
  else
  Memo1.Lines.Add(Socket.ReceiveText);
  Get:=False
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ServerSocket1.Active:=True;
  ClientSocket1.Active:=True
end;

procedure TForm1.ClientSocket1Connect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  while Fl do
  begin
    ClientSocket1.Socket.SendText('1');
    Timer1.Enabled:=True;
    while Timer1.Enabled do Application.ProcessMessages
  end
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:=False
end;

procedure TForm1.Timer2Timer(Sender: TObject);
begin
  Timer2.Enabled:=False
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ClientSocket1.Socket.SendText(Edit1.Text);
  Edit1.Text:=''
end;

begin
  Get:=False;
  First:=True;
  Second:=True;
  Fl:=True
end.

И код сервера:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ScktComp, XPMan, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    XPManifest1: TXPManifest;
    ClientSocket1: TClientSocket;
    ServerSocket1: TServerSocket;
    Memo1: TMemo;
    Edit1: TEdit;
    Button1: TButton;
    Timer1: TTimer;
    Timer2: TTimer;
    procedure ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure FormCreate(Sender: TObject);
    procedure ClientSocket1Connect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Timer2Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Get, First, Second, Fl: Boolean;

implementation

{$R *.dfm}

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  s: String;
begin
  while Get do Application.ProcessMessages;
  Get:=True;
  if First then
  begin
    First:=False;
    ClientSocket1.Active:=True;
    Socket.ReceiveText
  end
  else
  if Second then
  begin
    s:=Socket.ReceiveText;
    if pos('2', s)>0 then
    begin
      Fl:=False;
      Timer2.Enabled:=True;
      while Timer1.Enabled do Application.ProcessMessages;
      ClientSocket1.Socket.SendText('2');
      Second:=False;
      Form1.Caption:='Сервер - подключен';
      Edit1.ReadOnly:=False;
      Button1.Enabled:=True
    end
  end
  else
  Memo1.Lines.Add(Socket.ReceiveText);
  Get:=False
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ServerSocket1.Active:=True
end;

procedure TForm1.ClientSocket1Connect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  while Fl do
  begin
    ClientSocket1.Socket.SendText('1');
    Timer1.Enabled:=True;
    while Timer1.Enabled do Application.ProcessMessages
  end
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:=False
end;

procedure TForm1.Timer2Timer(Sender: TObject);
begin
  Timer2.Enabled:=False
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ClientSocket1.Socket.SendText(Edit1.Text);
  Edit1.Text:=''
end;

begin
  Get:=False;
  First:=True;
  Second:=True;
  Fl:=True
end.

Единственное примечание, Таймер1 я установил на 1 с, Таймер2 - на 2 секунды. Самое главное, чтоб таймер2 был больше таймера1. Естественно, в самом начале оба таймера надо отключить.
P.S. Тут я уже прогу до мини-чата доработал, просто интересно было smile.gif

Насчет 6 вопроса. Скачал я себе TNT Unicode Contols, установил (вроде бы). Но почему-то при вставке TNT'вского эдита не компилируется программа - ошибка "Не найден TntStdCtrls.dcu". Куда его вставить надо, чтобы его Делфи мог найти?
П. с. Может на форуме эта тема уже обсуждалась, но я её не нашёл sad.gif
zubr
Цитата
Насчет 6 вопроса. Скачал я себе TNT Unicode Contols, установил (вроде бы). Но почему-то при вставке TNT'вского эдита не компилируется программа - ошибка "Не найден TntStdCtrls.dcu". Куда его вставить надо, чтобы его Делфи мог найти?

Можно прописать путь в настройках окружения Delphi, меню Tools->Enviroment Options, вкладка Library, параметр Library Patch, ну или скопируй данный .dcu в папку Delphi\Lib
Vadik(R)
С 6 вопросом разобрался, все получилось, теперь даже в Caption формы можно писать иероглифы smile.gif
zubr, ありがとう ございます smile.gif
С 4 тоже все понятно, зануляются всегда.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2008 IPS, Inc.