Ликбез по Делфи

  • Автор темы Vadik(R)
  • Дата начала
V

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]
 
E

European

2. Про реализацию Дельфийских компонентов говорить не буду, но в общем случае 100% вероятности получить нельзя. Можно "пингануть" сервер и если он ответил, то посылать данные. Хотя никто не дает гарантии, что в это время соединение не будет разорвано.
3. Нужно использовать средства синхронизации. Например, критические секции.
6. Нужно использовать компоненты, поддерживающие UNICODE. Эта тема обсуждалась на форуме, попробуйте поискать.
 
V

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-х байтовой границей.
Упаковка отменяет это, сжимая данные в наименьшую память, хотя с последующим уменьшенным доступом выполнения.
 
V

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;
, взял . Только вот не понял как управлять переключателем.
 
V

Vadik(R)

Насчет вопроса про сокеты. Нету там свойства connected, поэтому единственный вариант, какой я придумал это сделать так:
ClientSocket1 подсоединяеться к ServerSocket1 и шлет данные, типа "привет" много раз пока они не дойдут до сервера. Как только сервер получает "привет", то ClientSocket2(на форме, где ServerSocket1) соединяеться с ServerSocket2(на форме, где ClientSocket1) и тоже тупо шлет "привет дошел". Если первое приложение увидит эту фразу, то 1 раз(ведь ClientSocket1-ServerSocket1 уже соединились точно) шлет "все ОК". Как только второе приложение увидит эту фразу, оно прекращает слать "привет дошел", и значит, соединенение было установлено. Попробую сейчас это реализовать, не будет ли каких ошибок.
П. с. А если ты имел в виду обработчики событий - onConnecting и onConnect, то первый из выполняеться при начале попытки установить соединение, и если в него впихнуть Socket.SendText('bla-bla-bla'), то ничего не дойдет. Если это впихнуть во второй обработчик, то данные доходят, но не всегда. Точнее на 127.0.0.1 у меня всегда доходят, а вот в Интеренете читал, что бывают случаи, когда данные могут не дойти.
 
E

European

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

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

Vadik(R)

Решен третий вопрос. Я пока с потоками не работал, но мне удобнее использовать идею, предложенную vital, в принципе то, что он и предложил и есть критическая секция, только писать её надо вручную и немного подкорректировать.
 
E

European

Решен третий вопрос. Я пока с потоками не работал, но мне удобнее использовать идею, предложенную vital, в принципе то, что он и предложил и есть критическая секция, только писать её надо вручную и немного подкорректировать.
Это вариант худшее что можно придумать. Его использование обеспечит Вам десятки часов отладки по логам накануне сдачи проекта. Никто не запретит процессору приостановить ваш поток посередине вычислений, а после возобновления попытаться начать обработку новой итерации таймера. Хотя использование такого варианта - ваше право.
 
V

Vadik(R)

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


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

Вот пример, может быть не самый удачный:[codebox]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.[/codebox]
А насчет отладки Вы оказались правы. Так если загрузиться около 1000 обработчиков и каждый будет выполнять while fl do Application.ProcessMessages;, то компьютер будет тормозить и придеться заводить еще другую переменную, считающую, сколько обработчиков событий ждут очереди своего выполнения.

П. С. А разработчики MicroSoft так старались сделать Windows многозадачным и многопоточным...
Проблема теперь избавиться от этого :wacko:
 
E

European

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

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;
 
V

Vadik(R)

Насчет третьего вопроса жжешь, че-то я видимо заумным стал... или вообще отупел :blink:
А, вспомнил, что меня переклинило. Я применял эту конструкцию для последовательно чтения данных, пришедших в сокет. Там была похожая ситуация, как только вызовешь 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;
 
Z

zubr

Да смысл в том, что при выделении памяти на данную структуру все члены структуры будут размещаться с одного адреса. В Delphi применяется для описания типа variant (посмотри исходники на variant), а также для замещения типа union в C++ прототипах фугкций.
 
V

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. Тут я уже прогу до мини-чата доработал, просто интересно было :)

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

zubr

Насчет 6 вопроса. Скачал я себе TNT Unicode Contols, установил (вроде бы). Но почему-то при вставке TNT'вского эдита не компилируется программа - ошибка "Не найден TntStdCtrls.dcu". Куда его вставить надо, чтобы его Делфи мог найти?
Можно прописать путь в настройках окружения Delphi, меню Tools->Enviroment Options, вкладка Library, параметр Library Patch, ну или скопируй данный .dcu в папку Delphi\Lib
 
V

Vadik(R)

С 6 вопросом разобрался, все получилось, теперь даже в Caption формы можно писать иероглифы :)
zubr, ありがとう ございます :(
С 4 тоже все понятно, зануляются всегда.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!