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

Тема в разделе "Delphi - FAQ", создана пользователем Vadik(R), 27 авг 2008.

  1. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Есть 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]
     
  2. European

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

    vital Больной Компом Детектед

    Регистрация:
    29 янв 2006
    Сообщения:
    2.468
    Симпатии:
    27
    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-х байтовой границей.
    Упаковка отменяет это, сжимая данные в наименьшую память, хотя с последующим уменьшенным доступом выполнения.
     
  4. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    vital, насчет пункта 3 согдасен с тобой, я только немного по-другому делал, но больше в голову ничего не приходило. Надо еще узнать, что такое критические секции. Насчет второго вопроса - надо поэксперементировать. Так же спасибо за информацию насчёт 1б.
    sax_ol, эта запись не бестолковая, в оригинале она выглядит так:
    Код (Text):
    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. Только вот не понял как управлять переключателем.
     
  5. Vadik(R)

    Vadik(R) Well-Known Member

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

    Регистрация:
    4 сен 2006
    Сообщения:
    2.580
    Симпатии:
    0
    Что-то сложно все расписано... Для неупакованных структур (записей) каждый элемент выравнивается по определенной границе, которая устанавливается в настройках. Если запись упакована, то выравнивание не используется
     
  7. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Решен третий вопрос. Я пока с потоками не работал, но мне удобнее использовать идею, предложенную vital, в принципе то, что он и предложил и есть критическая секция, только писать её надо вручную и немного подкорректировать.
     
  8. European

    Регистрация:
    4 сен 2006
    Сообщения:
    2.580
    Симпатии:
    0
    Это вариант худшее что можно придумать. Его использование обеспечит Вам десятки часов отладки по логам накануне сдачи проекта. Никто не запретит процессору приостановить ваш поток посередине вычислений, а после возобновления попытаться начать обработку новой итерации таймера. Хотя использование такого варианта - ваше право.
     
  9. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Вы бы лучше рассказали, как в ней управлять переключателем, чем издеваться, да еще и на НЕМЕЦКОМ.


    Ну проект я пока еще никому не собираюсь сдавать, я это делаю для себя, из интереса. Хотя свое время тоже надо беречь. Могу написать код, что у меня получилось по методу 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:
     
  10. European

    Регистрация:
    4 сен 2006
    Сообщения:
    2.580
    Симпатии:
    0
    Вот только не надо...
    Критические секции все сделают за вас
     
  11. zubr

    zubr Гость

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

    adr.S_addr := 123456;
    то и в структуре будет храниться только это значение. Если ты после этого назначишь значение другого типа, то и храниться уже будет то значение.
    3. Здесь решение до стыда простое:
    Код (Text):
    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;
     
  12. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Насчет третьего вопроса жжешь, че-то я видимо заумным стал... или вообще отупел :blink:
    А, вспомнил, что меня переклинило. Я применял эту конструкцию для последовательно чтения данных, пришедших в сокет. Там была похожая ситуация, как только вызовешь Socket.RecieveText, сразу может начаться следующий обработчик onClientRead.
    А насчет второго вопроса я так понял это делаеться для экономии памяти. Например можно было описать три переменных, а то можно одну, но занимать она будет размер самой длинной. И переключателем там никак не управлять. Просто это стандартный способ описания такой переменной.

    Только теперь я перестал видеть смысла в записях, с переключателем, где указан сам переключатель. Например,
    Код (Text):
    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;
     
  13. zubr

    zubr Гость

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

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Нашел ответ на второй вопрос. Конечно, может не самый лучший, но для моих целей пойдет. Вот код клиента:
    Код (Text):
    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.
    И код сервера:
    Код (Text):
    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". Куда его вставить надо, чтобы его Делфи мог найти?
    П. с. Может на форуме эта тема уже обсуждалась, но я её не нашёл :(
     
  15. zubr

    zubr Гость

    Можно прописать путь в настройках окружения Delphi, меню Tools->Enviroment Options, вкладка Library, параметр Library Patch, ну или скопируй данный .dcu в папку Delphi\Lib
     
  16. Vadik(R)

    Vadik(R) Well-Known Member

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

Поделиться этой страницей