Umíme to s Delphi, 60. díl – nebojte se DDE

V dnešním dílu seriálu otvíráme další téma – dynamickou datovou výměnu (Dynamic Data Exchange, DDE). Tato technologie umožňuje aplikacím, aby si za běhu vzájemně vyměňovaly data. Přestože implementace DDE nepatří k nejtriviálnějším úkolům, v Delphi jde všechno snáze, a tak již dnes společně vytvoříme jednoduchý DDE klient a server.

Dynamic Data Exchange – základní informace

DDE neboli Dynamic Data Exchange (dynamická výměna dat) je protokol vyvinutý firmou Microsoft, který slouží ve dvaatřicetibitovém prostředí Windows ke komunikaci mezi programy. Win32 API poskytuje řadu metod pro výměnu dat mezi (spuštěnými) aplikacemi a jedna z nich spočívá právě ve využívání protokolu DDE. Základní princip mechanismu dynamické datové výměny spočívá v zasílání speciálních zpráv mezi aplikacemi, která sdílí (cítí potřebu vyměňovat si) data; data jsou následně vyměňována přes sdílenou paměť. Komunikující aplikace mohou použít DDE jen k jednorázové výměně dat, stejně jako k trvalému datovému toku, v jehož rámci si posílají aktualizace dat vždy, když se vyskytnou nová data.

Hned na začátku je nutné podotknout, že mechanismus DDE je dnes již poněkud zastaralý. V nově vyvíjených aplikacích se obvykle používá spíše technologie OLE Automation, případně jiná metoda založená na technologii COM. V našem seriálu se budeme podrobně věnovat jak OLE Automation (což je vlastně „rozšíření“ – avšak značné – mechanismu OLE, kterým jsme se podrobně zabývali v dílech 38 – 40), tak i technologiím COM (a DCOM). Proto (z důvodu úplnosti), ale také pro jednoduchost používání DDE, se nevyhneme ani tomuto (poněkud staršímu) konceptu. Konec konců – programátoři by neměli znát jen nejnovější technologické výstřelky, ale i metody, jež se v hojné míře používaly před časem.

Používání DDE se trochu podobá používání schránky systému Windows, neboť ta se také používá k výměně dat mezi aplikacemi. Jeden rozdíl však spočívá v tom, že schránka je (skoro) vždy použita jako jednorázový důsledek specifické akce uživatele (např. zvolení položky Vložit z hlavní nabídky). Přestože dynamická datová výměna pomocí DDE může být také iniciována uživatelem, typicky pokračuje bez jeho dalších zásahů.

Nejčastější využití DDE spočívá v tom, že si dvě aplikace mezi sebou „zřídí spojení“, které následně udržují a využívají k přenosu dat.

Jak používat DDE

Rozhodneme-li se začít využívat DDE, máme k dispozici několik možností, přičemž už teď samozřejmě víme, že zvolíme ten nejjednodušší, který nabízí Delphi a jeho komponenty zapouzdřující DDE komunikaci. Přesto však pohovoříme i ke zbývajícím způsobům.

První verze DDE (která byla používána především jako dodatek k tabulkám Microsoft Excelu) skutečně spočívala v existenci množiny zpráv a pravidel, která bylo nutné používat (a dodržovat), aby bylo možné komunikovat s aplikacemi napsanými jinými programátory. Později se však situace trochu změnila, když byla do Windows 3.1 přidána knihovna DDEML (Dynamic Data Exchange Management Library, knihovna pro správu DDE). Tato knihovna (je to typická dynamicky linkovaná knihovna DDL) poskytuje jakési rozhraní, které značně zjednodušuje proces „přidání DDE do vaší Win32 aplikace“. Namísto přímého posílání a zpracovávání DDE zpráv může aplikace využívat funkce, které ke správě DDE spojení poskytuje DDEML.

Existující aplikace využívající (přímé) DDL jsou samozřejmě zcela kompatibilní s těmi, které pracují pomocí DDEML, jinak řečeno – aplikace, která při správě DDE spojení přímo zasílá zprávy, může navázat spojení s aplikací používající funkce DDEML. Je asi zbytečné dodávat, že rozhodnete-li se použít ve své aplikaci DDE, silně je doporučováno využívání knihovny DDEML.

Nutno ovšem podotknout, že problematika DDE versus DDEML se týká především programátorů ve Windows API. My, uživatelé Delphi:-), máme (jako obvykle) nesmírně zjednodušenou úlohu, neboť Delphi poskytuje několik komponent, s nimiž je používání DDE procházkou růžovým sadem.

Jak pracuje DDE

Při komunikaci pomocí DDE se často používání pojem „DDE konverzace“, případně „DDE rozhovor“. V těchto pojmech se skrývá velká část principu technologie DDE: jedná se o interakci mezi aplikací vystupující jako „server“ a aplikací typu „klient“. Server poskytuje informace, klient si je v případě potřeby vyžádá (v podstatě tedy řídí komunikaci). Každá aplikace může působit jako server pro více klientů, stejně tak klient může mít navázané spojení s více servery a využívat data více serverů. V zásadě je také možné, aby jedna aplikace vystupovala zároveň v roli serveru i klienta (a to dokonce v tentýž časový okamžik).

Úlohou serveru, jak už bylo zmíněno, je posílat data klientům. Úloha klienta je (trochu paradoxně) složitější: je zodpovědný za navázání spojení, zasílá požadavky na data, případně požádat server o provedení příkazu (v takových případech se však již skoro výhradně používá mechanismus OLE Automation). Klient může také požádat server, aby mu poslal data vždy v případě jejich změny (nebo změny jejich části).

Abychom si mohli ukázat navázání spojení, musíme si vysvětlit tři důležité pojmy:

  • služba (service) – v podstatě název aplikace, která vystupuje jako DDE server. Není to sice podmínkou, ale ve většině případů to platí. Budeme-li mít aplikaci DATUM.EXE, která bude DDE Serverem, budeme jako název služby uvádět vždy řetězec `DATUM`.
  • téma (topic) – „jemnější“ určení komunikace. Služba (tedy server) může být schopna poskytovat informace týkající se více témat. Tématem může být např. jméno souboru s daty, ale také jedno z oken serveru, případě vlastně cokoliv jiného. Vždy, když klient navazuje rozhovor se serverem, udává název tématu, o němž se hodlá se serverem „bavit“. Z tohoto popisu pravděpodobně nemůže být zřejmé, co to vlastně téma je, nicméně z praktických ukázek to okamžitě pochopíte.
  • položka (item) – nejkonkrétnější identifikace přenášených dat, neboť ta se člení právě na položky. Položkou může být např. údaj z databáze, prvek z tabulky, nebo v zásadě (opět) cokoliv jiného :-) V rámci jednoho navázaného rozhovoru si může klient se serverem vyměňovat informace skládající se z mnoha položek.

DDE v Delphi

Tolik tedy k obecnému a teoretickému (avšak nezbytnému) úvodu. Nyní se můžeme směle vrhnout do Delphi a ukázat si, jakým způsobem tohoto způsobu komunikace využívat. Jako tradičně, začneme ukázkovou aplikací, abyste viděli, nač se můžete později těšit:-)

Z toho, co jsme si dosud řekli, je zřejmé, že vytvoříme dvě aplikace. První z nich bude sloužit jako jednoduchý DDE server, druhá jako klient. Úlohou serveru bude vypisovat do nápisu text, který uživatel serveru zadává do editačního pole. Celé manévry rozdělíme do dvou kroků – nejprve vytvoříme server, posléze klienta.

Vytvoření jednoduchého DDE serveru

  • Vytvořte novou aplikaci a na formulář (frmHlavni) umístěte nejprve editační pole (edtText).
  • Dále je nutné vložit na formulář komponentu zapouzdřující položku DDE komunikace. Komponenta se nazývá DdeServerItem a naleznete ji v paletě System. Název (vlastnost Name) ponecháme na přednastaveném DdeServerItem1.
  • Ošetříme událost OnChange komponenty edtText. Obsluha bude nesmírně jednoduchá: v případě změny textu v editačním poli se tento text přiřadí do vlastnosti Text komponenty DdeServerItem, jinak řečeno – změněný text se stane položkou komunikace:

procedure TfrmHlavni.edtTextChange(Sender: TObject);
begin
  DdeServerItem1.Text := edtText.Text;
end;

Toť vše – server pro DDE komunikaci máme vytvořen. V rámci zdrojového kódu si jen můžete všimnout, že při vytvoření formuláře změníme jeho pozici, velikost a titulek:

unit Hlavni;

interface

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

type
  TfrmHlavni = class(TForm)
    edtText: TEdit;
    DdeServerItem1: TDdeServerItem;
    procedure edtTextChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var                                   
  frmHlavni: TfrmHlavni;

implementation

{$R *.dfm}

procedure TfrmHlavni.edtTextChange(Sender: TObject);
begin
  DdeServerItem1.Text := edtText.Text;
end;

procedure TfrmHlavni.FormCreate(Sender: TObject);
begin
  frmHlavni.Left := 100;
  frmHlavni.Top := 100;

  frmHlavni.Height := 150;
  frmHlavni.Width := 300;

  frmHlavni.Caption := `DDE Server`;
  edtText.Text := ``;
end;

end.

Vytvoření jednoduchého DDE klienta

Nyní vytvoříme druhou aplikaci – klienta. Vzhledem k tomu, že klient je zodpovědný za mnohem více činností než server, bude jeho vytvoření nepatrně složitější.

  • Vytvořte novou aplikaci, na formulář (frmHlavni) vložte titulek (Label, Name = lblText) a tlačítko (Buton, Name = btnSpojeni).
  • Nyní je nutné vložit komponenty pro DDE komunikaci. Tentokráte si nevystačíme s jednou: na formulář umístěte komponenty DdeClientConv a DdeClientItem (obě z palety System). Názvy ponechte předdefinované.
  • Komunikace bude navázána a udržována pomocí komponenty DdeClientConv, zatímco DdeClientItem obsahuje položku komunikace (přenášený text). Do vlastnosti DdeConv komponenty DdeClientItem tedy nastavte název komunikační komponenty (DdeClientConv1, stačí vybrat z rozbalovacího seznamu v Object Inspectoru).
  • Nyní ošetříme událost OnClick tlačítka btnSpojeni. V jeho rámci se pokusíme navázat spojení se serverem. K tomu použijeme metodu SetLink, jejímž prvním parametrem je název služby (service, tedy název aplikace serveru bez přípony *.exe), druhým pak jméno tématu (topic, v našem případě bude tématem hlavní okno služby, je nutné zadat jeho titulek a ten je v serveru nastaven na text `DDE Server`). Pokud se spojení úspěšně naváže, zbývá spojit položku (item) DdeClientItem klienta s některou položkou serveru; v našem případě server obsahuje jedinou položku, ta je zapouzdřená komponentou DdeServerItem1, takže tento název přiřadíme do vlastnosti DdeItem komponenty DdeClientConv:

    procedure TfrmHlavni.btnSpojeniClick(Sender: TObject);
    begin
      if DdeClientConv1.SetLink(`prjDDEServer`,`DDE Server`) then begin
        ShowMessage(`Spojeni bylo uspesne navazano!`);
        DdeClientItem1.DdeItem := `DdeServerItem1`;
      end
      else
        ShowMessage(`Chyba pri navazovani spojeni!`);
    end;

  • Nyní již máme zajištěno, že po změně příslušné položky na serveru (tedy po změně textu v editačním poli serveru) budou data poslána klientovi, v němž budou obsažena v komponentě DdeClientItem1 (tedy v položce). Poslední akce, o niž se musíme postarat, spočívá v aktualizace komponenty Label tak, aby vždy obsahovala text této položky. Tato aktualizace se (logicky vzato) musí provádět vždy, když dojde ke změně položky, proto ošetříme událost OnChange komponenty DdeClientItem:

procedure TfrmHlavni.DdeClientItem1Change(Sender: TObject);
begin
  lblText.Caption := DdeClientItem1.Text;
end;

Tím je vytváření klienta dokončeno. V události OnCreate formuláře jen opět změníme pozici, velikost a titulek formuláře a můžeme klienta přeložit a spustit:

unit Hlavni;

interface

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

type
  TfrmHlavni = class(TForm)
    lblText: TLabel;
    btnSpojeni: TButton;
    DdeClientConv1: TDdeClientConv;
    DdeClientItem1: TDdeClientItem;
    procedure btnSpojeniClick(Sender: TObject);
    procedure DdeClientItem1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmHlavni: TfrmHlavni;

implementation

{$R *.dfm}

procedure TfrmHlavni.btnSpojeniClick(Sender: TObject);
begin
  if DdeClientConv1.SetLink(`prjDDEServer`,`DDE Server`) then begin
    ShowMessage(`Spojeni bylo uspesne navazano!`);
    DdeClientItem1.DdeItem := `DdeServerItem1`;
  end
  else
    ShowMessage(`Chyba pri navazovani spojeni!`);
end;

procedure TfrmHlavni.DdeClientItem1Change(Sender: TObject);
begin
  lblText.Caption := DdeClientItem1.Text;
end;

procedure TfrmHlavni.FormCreate(Sender: TObject);
begin
  frmHlavni.Left := 300;
  frmHlavni.Top := 500;

  frmHlavni.Width := 300;
  frmHlavni.Height := 200;

  frmHlavni.Caption := `DDE Client`;
  lblText.Caption := ``;
  btnSpojeni.Caption := `Spojeni`;
end;

end.

Vyzkoušejte DDE komunikaci

Server i klient je hotov, nezbývá, než začít experimentovat. Nejprve vyzkoušíme spuštění klienta a navázání spojení (aniž běží server). Po klepnutí na tlačítko Spojeni bude samozřejmě hlášena chyba při navazování spojení.

Necháme klienta spuštěného a spustíme také server. Než se pokusíme znovu navázat spojení, napíšeme nějaký údaj do editačního pole na serveru. Klient samozřejmě nezobrazuje nic.

Pokud však nyní klepneme na tlačítko Spojeni, bude již spojení úspěšně navázáno. Důsledkem je hlášení o úspěšném navázání spojení:

Vyzkoušejte si, že nyní se změny v editačním poli serveru ihned promítají do nápisu na klientu:

Ve své podstatě jsme tedy vytvořili DDE spojení mezi editačním polem jedné aplikace a nápisem druhé aplikace.

Pokud se více zahloubáte do zdrojových kódů, můžete si všimnout, že průběh spojení (tedy vlastní výměnu dat) zajišťují obě „položkové“ komponenty – DdeServerItem a DdeClientItem. „Konverzační“ komponenty (v tomto jednoduchém případě jsme ji pro server ani nepoužili, využíváme jen klientskou DdeClientConv) jsou zodpovědné prakticky jen za navázání spojení; vlastní výměna dat probíhá pak jen prostřednictvím položek (proto položky je to, co je vyměňuje).

Poznámka pro detailůchtivé uživatele: použitím těchto Delphi komponent jsme vytvořili DDE spojení pomocí knihovny DDEML, jejíž funkce jsou v těchto komponentách zapouzdřeny. I kdybychom chtěli pracovat přímo s DDEML (bez použití komponent Delphi, takže pomocí funkcí Windows API), bylo by to podstatně složitější. Používání přímého DDE ani nemá smysl zmiňovat.

Na závěr

Tolik pro dnešek k technologii DDE: Vysvětlili jsme si její princip a bez velkých teoretických znalostí vytvořili dvě aplikace, které spolu dokáží vcelku úspěšně komunikovat pomocí DDE. Za týden budeme pokračovat, přičemž začneme systematickým popisem jednotlivých Delphi komponent zapouzdřujících DDE komunikaci.

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

Články odjinud