Umíme to s Delphi, 62. díl – nebojte se DDE, vytváříme klienty

Před týdnem jsme si vysvětlili, jak v Delphi naprogramovat aplikaci fungující jako server DDE. Dnes ani nemůžeme pokračovat jinak než popisem vytváření DDE klientů. V závěru dílu společně začneme vytvářet složitější ukázkovou aplikaci, na níž si předvedeme celou řadu možností, vlastností – a také úskalí technologie DDE.

Komponenta DdeClientItem – položka tématu klienta

Rozhodneme-li se vytvořit klienta DDE, budeme potřebovat dvě komponenty. První z nich, komponenta DdeClientItem (z palety System), definuje položku konverzace DDE. Společně s komponentou DdeClientConv (viz níže) umožňují, aby aplikace vystupovala jako klient DDE. Komponenta DdeClientItem specifikuje jednu datovou položku, kterou podporuje (poskytuje) server.

Pojďme si popsat vlastnosti této komponenty (viz tabulka):

Vlastnost Popis
DdeConv Specifikuje komponentu DdeClientConv, která je s příslušnou položkou asociována. Hodnota této vlastnosti je jméno komponenty DdeClientConv, která definuje aktuální konverzaci DDE.
DdeItem Velmi důležitá vlastnost – obsahuje název položky poskytované serverem DDE. Je typu string. Nastavením hodnoty této vlastnosti tedy říkáme, jakou položku ze serveru (obvykle je tato položka na serveru reprezentována komponentou DdeServerItem) hodláme do aktuální položky klienta (aktuální komponenty DdeClientItem) „načítat“. Nastavení je možné provést v návrhové fázi nejsnáze zapsáním jména do příslušného místa v Object Inspectoru; je možný ještě jeden způsob, který spočívá v modifikaci dialogu skrytého u „podvlastnosti“ DdeService nebo DdeTopis vlastnosti DdeConv.
Lines Obsahuje (textová) data pro DDE komunikaci (je typu TStrings). Hodnota této vlastnosti je automaticky aktualizována serverem DDE (za současného vyvolán události OnChange). Je možné i nastavení této vlastnosti (které způsobí poslání dat serveru, tzv. poke), nicméně touto možností se nebudeme zabývat. Vlastnost Lines koresponduje s vlastností Text.
Text Obsahuje (textová) data pro DDE komunikaci (je typu String). Používá se jen pro údaje kratší než 255 znaků, pro delší je nutné použít Lines.

Komponenta DdeClientItem má jen dvě vlastní metody – Create a Destroy. Obě slouží k vytvoření, resp. zrušení komponenty za běhu programu. Namísto Destroy je doporučené volat spíše Free.

Jediná událost této komponenty je OnChange, která vzniká ihned poté, co server aktualizoval data (vlastnosti Lines a Text).

Tolik ke komponentě DdeClientItem. Vidíme, že toto zapouzdření položky DDE komunikace je velmi jednoduché. Další (poslední) složkou DDE rozhovoru je komponenta DdeClientConv, která reprezentuje DDE konverzaci z hlediska klienta.

Komponenta DdeClientConv – základ DDE klienta

Komponenta DdeClientConv (z palety System) reprezentuje konverzaci DDE se serverem (je to tedy klient DDE). Využívá se ve spolupráci s komponentou DdeClientItem (viz výše). Společně umožňují aplikaci fungovat jako DDE klient. Komponenty DdeClientItem (položky komunikace) jsou s DdeClientConv asociovány prostřednictvím jejich vlastnosti DdeConv.

Nejprve si opět popíšeme vlastnosti komponenty DdeClientConv:

Vlastnost Popis
ConnectMode Určuje, zda bude spojení se serverem DDE navázáno automaticky v okamžiku otevření formuláře (okna) obsahujícího komponentu. Možné hodnoty jsou ddeAutomatic (automatické navázání ihned po zavolání metody SetLink) a ddeManual (ruční navázání, je nutné zavolat metodu OpenLink).
Conv Handle aktuální konverzace (read-only vlastnost). Používá se pouze v případě, že potřebujete zavolat nějakou funkci Windows API.
DdeFmt Reprezentuje (schránkový) formát dat aktuální konverzace (pokud nebyl specifikován serverem).
DdeService Udává název služby DDE (tedy aplikace fungující jako server DDE) pro aktuálního klienta. Je typu String. Obvykle je hodnotou této vlastnosti název programu (příp. včetně adresářové cesty, bez přípony *.exe) vystupujícího jako server DDE. V době návrhu je možné nastavit hodnotu této vlastnosti např. zapsáním příslušného názvu v Object Inspectoru. Za běhu programu dosáhneme téhož zavoláním metody SetLink s odpovídajícími parametry.
DdeTopic Specifikuje téma DDE konverzace, je typu String. Tématem může být buď název nějakého datového souboru (který používá server), případně titulek formuláře (poskytujícího položky daného tématu). Další možností je název (vlastnost Name) komponenty DdeServerConv umístěné na serveru (pokud ji ovšem server obsahuje). V době návrhu se hodnota vlastnosti nastavuje zapsáním v Object Inspectoru, za běhu programu poslouží metoda SetLink (s vhodnými parametry).
FormatCharts Logická vlastnost určující, mají-li se „odfiltrovat“ určité znaky z dat poslaných serverem. Typicky jde o znaky backspace, linefeed (LF), carriage return (CR), tabulátory, konce řádků apod. Tyto znaky mohou způsobit nesprávné formátování dat na klientu (hodláme-li si je formátovat jinak), proto je možné potlačit je nastavením hodnoty této vlastnosti na False.
ServiceApplication Udává jméno souboru (s případnou adresářovou cestou, avšak bez přípony *.exe) programu fungujícího jako server DDE. Je typu String. Typicky je tato hodnota stejná jako hodnota vlastnosti DdeService. Občas však mohou být tyto hodnoty odlišné. Pokud se komponenta DdeClientConv pokouší navázat DDE spojení se serverem a tento server neběží, metoda OpenLink použije hodnotu vlastnosti ServiceApplication ke spuštění serveru.
WaitStat Logická vlastnost indikující, zda je server schopen zahájit další DDE transakci (hodnota False (!)) nebo nikoliv (hodnota Ture).

Tolik k vlastnostem komponenty DdeClientConv. Další důležitou složkou každé komponenty jsou její metody, proto si je popíšeme:

Metoda Popis
CloseLink Ukončí aktuální konverzaci.
Create Vytvoří komponentu DdeClientConv za běhu programu.
Destroy Zruší komponentu DdeClientConv za běhu programu. Místo ní však volejte Free.
ExecuteMacro Pošle řetězec obsahující příkaz makra serveru DDE. Metoda vrátí True, pokud makro bylo úspěšně předáno serveru (neříká tedy nic o tom, bylo-li makro serverem úspěšně provedeno!). Prvním parametrem metody je (nulou ukončený) řetězec obsahující text makra. Druhý parametr určuje, zda klient DDE má čekat na dokončení provádění makra (a do té doby neumožnit další transakci DDE).
ExecuteMacroLines Pošle seznam několika maker serveru DDE. Používá se podobně jako ExecuteMacro (to však umožňuje poslat jen jedno makro).
OpenLink Inicializuje novou DDE konverzaci. Zavoláním této metody se pokoušíme navázat spojení (konverzaci) se službou specifikovanou ve vlastnosti DdeService. Pokud server zadaný touto vlastností neběží, OpenLink se jej pokusí spustit (a použije k tomu hodnotu vlastnosti ServerApplication). Je-li spojení úspěšně navázáno, dojde k vyvolání události OnOpen a metoda vrátí True. V opačném případě je návratovou hodnotu False. Pozor: je-li hodnota vlastnosti OpenMode nastavena na ddeAutomatic, metoda OpenLink je vyvolána automaticky ihned po zavolání metody SetLink.
PasteLink Otevře DDE konverzaci s aktuálním objektem ve schránce. Volání této metody nastaví vlastnosti DdeTopic a DdeService podle údaje ve schránce. V případě úspěchu vrací metoda True.
PokeData Pošle data serveru. Přestože obvyklý „tok dat“ je ze serveru klientovi, některé servery umožňují i příjem dat (tzv. „poke-data“). Tímto způsobem komunikace se však nebudeme v našem seriálu zabývat.
PokeDataLines Stejný význam jako metoda PokeData, jen je možné poslat více řetězců (údaj typu TStrings).
RequestData Pošle serveru požadavek na zadanou položku dat. Tuto položku můžeme získat např. z vlastnosti DdeClientItem.DdeItem, která specifikuje položku na serveru, kterou chceme přenést do položky klienta reprezentované komponentou DdeClientItem. Návratovou hodnotu jsou právě tato data. RequestData automaticky alokuje paměť pro takto přijatá data; nezajistí však uvolnění této paměti. O to se musíme postarat ručně (metodou StrDispose).
SetLink Pomocí této metody zadáváme jméno služby a tématu, s nímž chceme vést DDE konverzaci. Pokud je hodnota vlastnosti ConnectMode nastavena na ddeAutomatic, vyvolá tato metoda také metodu OpenLink a pokusí se ihned navázat spojení. Je-li OpenMode rovno ddeManual, zavolání SetLink je ekvivalentní pouhému nastavení vlastností DdeTopic a DdeService (a vyčištění všech dat z asociovaných komponent DdeClientItem).

Komponenta DdeClientConv má pouze dvě události OnOpen a OnClose. První je (samozřejmě) vyvolána ihned poté, co došlo k úspěšnému navázání konverzace se serverem, druhá po ukončení (uzavření) této konverzace.

Práce s DDE – příklad

Abychom technologii Dynamic Data Exchange lépe zažili, vytvoříme společně ukázkovou aplikaci. Tentokráte bude složitější a bude využívat i posílání maker.

Zadání příkladu: vytvořte DDE server, který poskytuje následující služby:

  • obsahuje editační pole; načítá do něj text od uživatele a každou změnu ihned posílá klientům;
  • obsahuje tlačítko Potvrď; po klepnutí pošle klientům informaci o tom, že uživatel potvrdil zadaná data;
  • obsahuje tlačítko Zruš; po klepnutí pošle klientům informaci o tom, že uživatel zrušil všechna dosud potvrzená data;
  • je schopen vymazat aktuální obsah svého editačního pole, poté, co jej o to klient požádá;
Dále vytvoříme ukázkového klienta, který využívá služby serveru např. následujícím způsobem:
  • klient vypisuje do nápisu aktuální stav editačního pole serveru;
  • zjistí-li klient, že uživatel na serveru potvrdil aktuální obsah editačního pole, přidá tento obsah do své komponenty ListBox;
  • zjistí-li naopak klient, že uživatel na serveru zrušil všechna potvrzená data, vymaže obsah svého seznamu ListBox;
  • klient obsahuje tlačítko Vymaž; po klepnutí pošle serveru požadavek na vymazání jeho editačního pole (tedy editačního pole serveru);
  • klient může kdykoliv ukončit (a znovu navázat) spojení se serverem.
K tomuto zadání dodejme asi tolik:
  • Je zřejmé, že toho zadání je poněkud příliš komplikované, a že nemá ani nejmenší reálné využití, využijeme jej především z pedagogických důvodů, abychom demonstrovali co nejvíce vlastností DDE.
  • Zatímco klient vždy komunikuje s konkrétním serverem (např. posílá požadavek na výmaz editačního pole konkrétnímu serveru) a tedy „zná svůj server“, server poskytuje klientům pouze jakési anonymní informace (upozornění), že „by měli něco udělat“ (např. poskytne informaci o tom, že klient potvrdil data). Server „nezná své klienty“, je mu lhostejné, kdo s ním komunikuje a jak nakládá s poskytovanými údaji. To je typický princip klient/server aplikací; ohromná jednoduchost jejich implementace prostřednictvím DDE byla jedním z důvodů, proč se tento (zastaralý) koncept v našem seriálu vůbec objevil. Každý zájemce zde může velmi snadno pochopit princip vytváření aplikací klient/server.
Pozor: protože řada ze čtenářů kopíruje zdrojové kódy přímo do svých aplikací do Delphi, rozhodl jsem se od dnešního dílu nepřejmenovávat komponenty a ponechávat jim jejich standardní jména. Zdůrazňuji, že je to velmi zavrženíhodný postup, který používám výhradně kvůli tomu, abyste si mohli příklady snáze zkoušet přímo v Delphi (a nebyli nuceni provádět různá přejmenování). Znovu zdůrazňuji, že základní programátorská slušnost velí – pojmenování komponent by mělo být co možná nejvíce vypovídající (a rozhodně ne standardní).

První část příkladu – vytvoření DDE serveru

Začneme vytvořením serveru. Postupujte podle následujících bodů:
  • Vytvořte novou aplikaci, na formulář umístěte komponenty Edit1, Button1 (Caption = Potvrď) a Button2 (Caption = Zruš) – titulky komponent nastavujeme jako obvykle při vytváření hlavního formuláře.
  • Na formulář dále vložte jednu komponentu DdeServerConv a tři komponenty DdeServerItem (DdeServerItem1, DdeServerItem2, DdeServerItem3).
  • Do vlastnosti ServerConv každé z komponent DdeServerItem zadejte DdeServerConv1 (tedy jméno komponenty DdeServerConv). Tím jste zajistili, že všechny tři položky DdeServerItem se budou týkat tématu (budou poskytovány tématem) DdeServerConv.
  • Ošetřete událost OnChange komponenty Edit1. Při každé změně obsahu editačního pole se tento obsah zkopíruje do vlastnosti Text položky DdeServerItem1:

    procedure TForm1.Edit1Change(Sender: TObject);
    begin
      DdeServerItem1.Text := Edit1.Text;
    end;

  • Ošetřete událost OnClick tlačítka Button1. Při klepnutí na toto tlačítko se vyvolá změna položky DdeServerItem2 (sice se do ní zapíše vždy tatáž hodnota, nicméně vždy bude generována událost OnChange, která způsobí poslání této položky klientům):

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      DdeServerItem2.Text := `1`;
    end;

  • Podobně ošetřete událost OnClick tlačítka Button2. Při klepnutí na toto tlačítko se aktualizuje položka DdeServerItem3:

    procedure TForm1.Button2Click(Sender: TObject);
    begin
      DdeServerItem3.Text := `1`;
    end;

  • Ošetřete událost OnExecuteMacro komponenty DdeServerConv. Touto obsluhou bude zajištěno, že pokud klient pošle makro, otestuje se, je-li tímto makrem test „vymaz“ a v kladném případě dojde k vymazání aktuálního textu v editačním poli. Zároveň se vypíše informační hláška:

procedure TForm1.DdeServerConv1ExecuteMacro(Sender: TObject;
  Msg: TStrings);
begin
  if Msg[0] = `vymaz` then begin
    ShowMessage(`Klient zada o vymazani.`);
    Edit1.Text := ``;
  end;
end;

Tím je server hotov. Uvedeme si pro úplnost kompletní zdrojové kódy serveru:

unit Hlavni;

interface

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

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    DdeServerItem1: TDdeServerItem;
    DdeServerItem2: TDdeServerItem;
    DdeServerItem3: TDdeServerItem;
    Button2: TButton;
    DdeServerConv1: TDdeServerConv;
    procedure Edit1Change(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure DdeServerConv1ExecuteMacro(Sender: TObject; Msg: TStrings);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Edit1Change(Sender: TObject);
begin
  DdeServerItem1.Text := Edit1.Text;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  DdeServerItem2.Text := `1`;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.Caption := `Potvrd`;
  Button2.Caption := `Zrus`;

  Form1.Caption := `DDE Server`;
  Form1.Top := 100;
  Form1.Left := 100;
  Form1.Width := 200;
  Form1.Height := 150;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DdeServerItem3.Text := `1`;
end;

procedure TForm1.DdeServerConv1ExecuteMacro(Sender: TObject;
  Msg: TStrings);
begin
  if Msg[0] = `vymaz` then begin
    ShowMessage(`Klient zada o vymazani.`);
    Edit1.Text := ``;
  end;
end;

end.

Poznámky ke zdrojovému kódu:

  • Ještě jednou bych rád požádal, abyste příliš nepřemýšleli o praktickém využití tohoto příkladu:-)
  • Klíčová informace spočívá v tom, že chceme-li klientům poslat nějakou informaci, musíme změnit obsah vlastnosti Text příslušné položky (příslušné komponenty DdeServerItem). Není však dobrým nápadem pokoušet se v rámci této změny modifikovat také obsah dalších položek! Raději si to předvedeme na příkladu. Mohlo by se třeba zdát užitečné nastavit v obsluze události OnChange editačního pole Edit1 hodnoty zbývajících dvou položek na nulu, aby klientům bylo jasné, že nedošlo ke klepnutí na žádné tlačítko. Obsluha by mohla vypadat např. takto:

procedure TForm1.Edit1Change(Sender: TObject);
begin
  DdeServerItem1.Text := Edit1.Text;
  DdeServerItem2.Text := `0`;
  DdeServerItem3.Text := `0`;
end;

Ale pozor, toto řešení není správné. Pojďme si vysvětlit důvod. V okamžiku, kdy se změní obsah editačního pole, vyvolá se tato obsluha. Na prvním řádku se nastaví (změní) text položky DdeServerItem1, což způsobí bezprostřední poslání této položky klientům. Klienti zareagují vyvoláním události OnChange své položky korespondující s DdeServerItem1 (dejme tomu např. DdeClientItem1). Pokud by se v rámci této obsluhy pokusili přečíst také obsahy dalších dvou položek („na serveru se přece zároveň se změnou jedné změnily i druhé dvě, ne?“), výsledky budou pravděpodobně špatné, protože v okamžiku provádění této klientské obsluhy ještě ani na serveru nemuselo proběhnout nastavení zbylých dvou položek na nulu! (Je to podobné jako při práci s vlákny.)

Abych tento poněkud komplikovaný odstavec nějak shrnul, pokusím vyprodukovat zásadu: v jednom okamžiku měníme na serveru vždy jen jednu položku, na klientu pak vždy jen jednu položku čteme.

Tím je klient hotov. Uložte jej (File – Save All), soubor projektu pojmenujte prjDDEServer.dpr (je důležité dodržet tento název, při programování klienta poznáme důvod:-)), přeložte a zkuste spustit (prozatím se nic zajímavého nebude dít):

Klepněte pro větší obrázek

Na závěr

Opět došel prostor dříve, než bylo možné vysvětlit vše potřebné:-) Proto budeme pokračovat za týden: jako jsme dnes vytvářeli DDE server, naprogramujeme příště klienta. A to nebude vše: zahrajeme si na špióny a ukážeme si, jak sledovat libovolný DDE rozhovor probíhající v systému. Kromě toho zapojíme do DDE komunikace také Microsoft Word.
Diskuze (3) Další článek: Minilab na stole

Témata článku: Software, Programování, Destroy, Důležitá vlastnost, Lines, Makro, Díl, Poskytovaný údaj, DEL, Pole, Anonymní informace, Poke, Položka, Server, Textové pole, Konverzace, Ukázkový příklad, Zavolání, Toto, Důležitá složka, Důležitý údaj, Klient, Komponenta, Tito, Zbylý prostor


Určitě si přečtěte

Google není jen vyhledávač: 15 užitečných funkcí, o kterých možná ani nevíte

Google není jen vyhledávač: 15 užitečných funkcí, o kterých možná ani nevíte

** Google umí kromě vyhledávání i spoustu dalších věcí ** Vybrali jsme více než 15 užitečných funkcí a schopností ** Stačí zadat do vyhledávače ta správná klíčová slova

Karel Kilián | 22

Karel Kilián
TipyVyhledávačeGoogle
Technici nestíhají. Cetin dočasně přerušil zavádění VDSL bondingu
Lukáš Václavík
CETINPřipojení k internetu
Finanční správa tento měsíc spustí Moje Daně. Přiznání má být hračka
Lukáš Václavík
eIdentitaČeskoeGovernment
Týden Živě: On fakt dnes ještě někdo stahuje filmy z Ulož.to?

Týden Živě: On fakt dnes ještě někdo stahuje filmy z Ulož.to?

** Kauza Ulož.to a proč my dva už (moc) newarezíme ** Windows 10X existují, ale nabízí se otázka proč ** Nissan ukázal vizi kanceláře v podobě karavanu

Jakub Čížek, Vladislav Kluska | 152

Jakub ČížekVladislav Kluska
Týden ŽivěVideo
Apple Macbook Air M1: testujeme výkon, výdrž, a hlavně kompatibilitu aplikací [průběžně aktualizováno]

Apple Macbook Air M1: testujeme výkon, výdrž, a hlavně kompatibilitu aplikací [průběžně aktualizováno]

** Testujeme Apple Macbook Air s procesorem M1 ** Zajímá nás nejen výkon, ale zejména kompatibilita aplikací ** Článek je průběžně doplňován na základě vašich dotazů

Jiří Kuruc | 209

Jiří Kuruc
Apple
Nešťastný vývojář ukazuje, proč není dobré být závislý na Googlu
Lukáš Václavík
InternetGoogle