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

Vadik(R)

Well-known member
12.12.2007
469
0
#1
Есть 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]
 
04.09.2006
2 566
3
#2
2. Про реализацию Дельфийских компонентов говорить не буду, но в общем случае 100% вероятности получить нельзя. Можно "пингануть" сервер и если он ответил, то посылать данные. Хотя никто не дает гарантии, что в это время соединение не будет разорвано.
3. Нужно использовать средства синхронизации. Например, критические секции.
6. Нужно использовать компоненты, поддерживающие UNICODE. Эта тема обсуждалась на форуме, попробуйте поискать.
 

vital

Больной Компом Детектед
29.01.2006
2 432
33
#3
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-х байтовой границей.
Упаковка отменяет это, сжимая данные в наименьшую память, хотя с последующим уменьшенным доступом выполнения.
 

Vadik(R)

Well-known member
12.12.2007
469
0
#4
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)

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

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

Vadik(R)

Well-known member
12.12.2007
469
0
#7
Решен третий вопрос. Я пока с потоками не работал, но мне удобнее использовать идею, предложенную vital, в принципе то, что он и предложил и есть критическая секция, только писать её надо вручную и немного подкорректировать.
 
04.09.2006
2 566
3
#8
Решен третий вопрос. Я пока с потоками не работал, но мне удобнее использовать идею, предложенную vital, в принципе то, что он и предложил и есть критическая секция, только писать её надо вручную и немного подкорректировать.
Это вариант худшее что можно придумать. Его использование обеспечит Вам десятки часов отладки по логам накануне сдачи проекта. Никто не запретит процессору приостановить ваш поток посередине вычислений, а после возобновления попытаться начать обработку новой итерации таймера. Хотя использование такого варианта - ваше право.
 

Vadik(R)

Well-known member
12.12.2007
469
0
#9
Вы бы лучше рассказали, как в ней управлять переключателем, чем издеваться, да еще и на НЕМЕЦКОМ.


Это вариант худшее что можно придумать. Его использование обеспечит Вам десятки часов отладки по логам накануне сдачи проекта. Никто не запретит процессору приостановить ваш поток посередине вычислений, а после возобновления попытаться начать обработку новой итерации таймера. Хотя использование такого варианта - ваше право.
Ну проект я пока еще никому не собираюсь сдавать, я это делаю для себя, из интереса. Хотя свое время тоже надо беречь. Могу написать код, что у меня получилось по методу 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:
 
04.09.2006
2 566
3
#10
П. С. А разработчики MicroSoft так старались сделать Windows многозадачным и многопоточным...
Вот только не надо...
А насчет отладки Вы оказались правы. Так если загрузиться около 1000 обработчиков и каждый будет выполнять while fl do Application.ProcessMessages;, то компьютер будет тормозить и придеться заводить еще другую переменную, считающую, сколько обработчиков событий ждут очереди своего выполнения.
Критические секции все сделают за вас
 
Z
#11
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)

Well-known member
12.12.2007
469
0
#12
Насчет третьего вопроса жжешь, че-то я видимо заумным стал... или вообще отупел :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
#13
Да смысл в том, что при выделении памяти на данную структуру все члены структуры будут размещаться с одного адреса. В Delphi применяется для описания типа variant (посмотри исходники на variant), а также для замещения типа union в C++ прототипах фугкций.
 

Vadik(R)

Well-known member
12.12.2007
469
0
#14
Нашел ответ на второй вопрос. Конечно, может не самый лучший, но для моих целей пойдет. Вот код клиента:
Код:
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
#15
Насчет 6 вопроса. Скачал я себе TNT Unicode Contols, установил (вроде бы). Но почему-то при вставке TNT'вского эдита не компилируется программа - ошибка "Не найден TntStdCtrls.dcu". Куда его вставить надо, чтобы его Делфи мог найти?
Можно прописать путь в настройках окружения Delphi, меню Tools->Enviroment Options, вкладка Library, параметр Library Patch, ну или скопируй данный .dcu в папку Delphi\Lib
 

Vadik(R)

Well-known member
12.12.2007
469
0
#16
С 6 вопросом разобрался, все получилось, теперь даже в Caption формы можно писать иероглифы :)
zubr, ありがとう ございます :(
С 4 тоже все понятно, зануляются всегда.