Az eddigiek során kiderült az, hogy a Windows alatt több program is futhat egyszerre. Ezek a programok kommunikálhatnak egymással. A kommunikációra számos lehetőség kínálkozik. Az előzőek folyamán már megismerkedtünk az egyik ilyen fajta kommunikációval a DLL-el. Amint azt a korábbiakban láttuk, a DLL használata biztosítja annak a lehetőségét, hogy több program hívja a függvényeit vagy betöltse az erőforrásait. Ez természetesen bizonyos esetekben nem bizonyul elegendőnek az adatcserére. Számos esetben adódhat olyan feladat, amelynél az egyik applikációnak folyamatosan kell küldenie az adatokat a másiknak. Például egy ipari megjelenítő rendszer esetén gyakran előfordulhat, hogy számos applikáció fut egyszerre. Az egyik begyűjti az adatokat, a másik feldolgozza illetve értékeli és a harmadik megjeleníti. Az adatoknak egyik applikációról a másikra való juttatására a Windows több lehetőséget kinál, ezek közül az egyik a DDE protokoll. Ez egy kliens-szerver alapú kommunikáció, ahol a szerver kiszolgálja a kliens kéréseit és a megfelelő adatokat is eljuttatja hozzá. Egy másik ilyen adatcsere módszer a Clipboard (vágólap)használata. Biztos, hogy majdnem mindenki használta már a vágólapot. Pl. amikor egy szövegszerkesztő egyik dokumentumából adatot másolunk(Copy), és egy másik dokumentumba beillesztjük (Paste), akkor az adatok a vágólapon keresztül kerülnek az egyik dokumentumból a másikba. A DDE esetén lehetőség van arra, hogy amennyiben Windows for Workgroups-ot használunk az egymással kommunikáló programok külön gépen fussanak. Ez az NDDE. Ilyenkor a kommunikációhoz szükségünk van a gépnevére is. Egy másik kommunikációs módszert jelent az OLE (Object Linking and Embedding). Ebben a fejezetben nem térünk ki az OLE-re, de a VC++ tárgyalásakor elő fog kerülni az OLE illetve az OCX (OLE Custom Control) létrehozása és felhasználása.
A vágólap a globális memóriának olyan területe, amelyet megosztva használ minden futó applikáció. A program írhat adatokat a vágólapra és olvashat adatokat a vágólapról. Általában az egyik program ír a vágólapra, a másik pedig olvas róla.
Amikor a program lefoglal egy memóriablokkot a GHND attribútummal (GMEM_MOVEABLE és GMEM_ZEROINIT kombinációja), akkor azt jelzi, hogy ez a blokk ehhez a programhoz tartozik. Amikor a program ír a vágólapra (átad egy globális leírót a vágólapnak), akkor a Windows átveszi magának a programtól a leíróhoz tartozó memóriablokk tulajdonát. Ilyenkor a Windows megváltoztatja a blokk attribútumát (GMEM_MODIFY és GMEM_DDESHARE)-re. A USER modul veszi át a memóriablokk kezelését. Ezek után a program már nem tudja használni ezt a blokkot, amíg a vágólap nem adja neki a blokkhoz való hozzáférési jogot, más szavakkal, amíg nem nyitja meg a vágólapot. A USER modul gondoskodik a memória blokk felszabadításáról.
Egyidejűleg több olyan program is működhet a Windows felügyelete alatt, amelyek az átmeneti tárolót olvassák. Ezeknek mind üzenetet kell kapniuk az átmeneti tárolón történt változásokról. A Windowsnak csak egyetlen átmeneti tárolót olvasó programja létezik, az aktuális olvasó program. A Windows csak ezt értesíti a változásokról. A vágólap olvasó applikációkat láncba kell kötni. Ha a lánc egyik szeméhez megérkezett valamilyen információ a vágólapról, akkor értesítenie kell a következő láncbeli szomszédját arról.
Az összes vágólapot olvasó alkalmazás fő ablakkezelője kell, hogy reagáljon a WM_CHANGECBCCHAN, a WM_DESTROY és a WM_DRAWCLIPBOARD üzenetekre.
A WM_CHANGECBCCHAN üzenetet a lánc változásakor küldi a Windows. A wParam a kivett láncszem ablakleírója, az lParam alsó szava a következő láncszem leírója. Az alkalmazásnak az üzenet hatására ugyanezt az üzenetet el kell küldenie a követő ablaknak.
A WM_DRAWCLIPBOARD üzenetet a Windows az első láncszemnek küldi, ha a vágólap tartalma megváltozott. Ezt minden láncbeli ablakkezelő függvénynek tovább kell küldenie.
A WM_DESTROY üzenetnél az ablakkezelőnek ki kell iktatnia magát a láncból, ha szerepelt ebben a láncban.
A következő táblázat összefoglalja a vágólapot kezelő függvényeket:
Függvény |
Funkció |
BOOL ChangeClipboardChain(HWND hwnd, HWND hwndNext) |
Egy láncszemet távolít el. |
BOOL CloseClipboard(void) |
Bezárja a vágólapot. Megszünteti az elérését |
BOOL EmptyClipboard(void) |
Törli a vágólapot |
UINT EnumClipboardFormats(UINT uFormat) |
Felsorolja a rendelkezésre álló vágólap formátumokat |
HANDLE GetClipboardData(UINT uFormat) |
A vágólap adatának leíróját adja meg |
int GetClipboardFormatName(UINT uFormat, LPCSTR lpszFormatname, int cMax) |
Megadja az aktuális format nevét |
HWND GetClipboardOwner(HWND hwnd) |
Megadja annak az ablaknak a leíróját, amelyhez a vágólap éppen tartozik |
HWND GetClipboardViewer(void) |
Megadja a lánc első szemének leíróját |
HWND GetOpenClipboardWindow(void) |
Megadja annak az ablaknak a leíróját, amely éppen megnyitotta a vágólapot |
int GetPriorityClipboardFormat(UINT FAR* PriorityList, int cEntries) |
Megadja az adatokhoz tartozó legjobb formátumot |
BOOL IsClipboardFormatAvailable(UINT uFormat) |
Az adatformátum rendelkezésre áll-e |
BOOL OpenClipboard(HWND hwnd) |
Megnyitja a vágólapot |
UINT RegisterClipboardFormat(LPCSTR lpszFormatName) |
Bejegyzi az aktuális adatformátumot |
HANDLE SetClipboardData(UINT uFormat, HANDLE hData) |
Adatot helyez a vágólapra |
HWND SetClipboardViewer(HWND hwnd) |
A lánchoz tesz egy új szemet |
A gyakrabban használt formátumok a következő táblázatban szerepelnek
Formátum |
Formátum típusa |
CF_BITMAP |
Bitmap |
CF_DIB |
Bitmap és BITMAPINFO fejléc |
CF_DIF |
Data Interchamge formátum |
CF_DSPBITMAP |
Egyéni bitmap |
CF_DSPENHMETAFILE |
Kiterjesztett egyéni metafile |
CF_DSPMETAFILEPICT |
Egyéni metafile |
CF_DSPTEXT |
Egyéni szöveg |
CF_ENHMETAFILE |
kiterjesztett metafile |
CF_METAFILEPICT |
METAFILESTRUCT stílusú metafile |
CF_OEMTEXT |
OEM szöveg |
CF_OWNERDISPLAY |
Egyénileg definiált formátum |
CF_PALETTE |
Színpaletta |
CF_PENDATA |
Toll-alapú rendszerek kiterjesztett formátuma |
CF_RIFF |
Resource interchange file formátum |
CF_SYLK |
Symbolic link |
CF_TEXT |
Szöveg |
CF_TIFF |
Címke kép formátum |
CF_WAVE |
Hang forráskód WAVE file-ok |
CF_UNICODETEXT |
Szöveg UNICODE stílusban |
Az adatok vágólapra helyezésének általános módszere a következő:
Megfelelő méretű memória foglalása.
Az adat bemásolása a globális memóriába.
A vágólap megnyitása.
A vágólap minden korábbi adatának törlése.
A megadott memóriarésznek, mint a vágólap adatának a megadása.
A vágólap bezárása.
A következő néhány sor példát mutat az adatok vágólapra való helyezéséről
hGout=GlobalAlloc(GHND | GMEM_DDESHARE, (DWORD)100);
p=GlobalLock(hGout);
// adatok másolása a globális memóriába
GlobalUnlock(hGout);
if (OpenClipboard(hwnd))
Az adatok beolvasásának általános módszere a következő:
A vágólap megnyitása.
A vágólapon lévő adatokhoz tartozó mutató lekérése.
Az adatok másolása a vágólapról.
A vágólap bezárása.
A következő néhány sor példát mutat az adatok vágólapról való olvasásáról
if (OpenClipboard(hwnd)
Érdemes megemlíteni, hogy egy időben egy program nyithatja meg a vágólapot. Ezért fontos, hogy minden vágólapot kezelő program rendben nyissa és zárja azt. Ez az oka annak, hogy a vágólap megnyitása és lezárása közti időben nem szabad használni a SendMessage függvényt, nehogy másik programhoz kerüljön a vezérlés.
Példa a Vágólap használatáról \PELDAK\PELDA4
#include <windows.h>
#include "proba1.h"
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
long FAR PASCAL _export GraphProc(HWND hDlg, unsigned message,WORD wParam,LONG lParam);
HWND hwnd ;
HWND hTextWnd;
HANDLE hInst;
HBITMAP hBitmap ;
HANDLE hGMem ;
HDC hdc ;
LPSTR lpGMem ;
#pragma argsused
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
hInst=hInstance;
hwnd = CreateWindow (szAppName, "Clipboard Viewer",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd,SW_SHOWMAXIMIZED ) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
return msg.wParam ;
}
#pragma argsused
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
else
}
CloseClipboard () ;
EndPaint (hwnd, &ps) ;
return 0;
case WM_DESTROY:
ChangeClipboardChain (hwnd, hwndNextViewer) ;
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
Természetesen a vágólapon levő adatokat fájlba is lehet menteni. Ennek a fájlnak a kiterjesztése általában .clp. Ennek a fájlnak a formátuma ismert és a fejléce tartalmaz információt az adatformátumról, az adatok hosszáról, stb.
A vágólap egy egyszerű kommunikációs formát jelent a Windows-ban. Korlátozott lehetőségei vannak. A másik kommunikációs módszer a DDE. Windows üzeneteket és megosztott memóriát használ ahhoz, hogy az applikációk adatokat cseréljenek egymással. A fő előnye a DDE -nek, hogy a felhasználó beavatkozása nélkül folyamatosan tudja biztosítani a kommunikációt a programok között. A DDE a Windows applikációk szabványos kommunikáció protokollja lett. Szinte alig találunk olyan Windows programot, ami nem támogatja a DDE használatát. A Windows for Workgroups, Win95 és Windows NT esetén az egymással kommunikáló programok külön külön gépen lehetnek.
A DDE egy kliens-szerver kommunikációs forma. Egy applikáció lehet egyszerre kliens is és szerver is. Sőt egy szervere lehet több kliensnek vagy egy kliens több szervertől kérhet adatokat. A DDE esetén mind a kliensnek mind a szervernek ismernie kell az adatformátumot. Általában a DDE sztringekkel dolgozik. A kapcsolat felvételét mindig a kliens kezdeményezi. Minden DDE kommunikáció a következő lépésekből áll.
Kapcsolat felvétel kliens szerver
Adatcsere kliens szerver
kapcsolat lebontás
3 fajta DDE protokoll létezik a hideg, meleg és a forró
Csak kis különbség van a 3 protokoll között. A különbség az adott váltózó frissítési módjában jelentkezik.
Hideg: A hideg (cold) DDE protokoll esetében az adatcsere úgy működik, hogy a szerver azokat az információkat küldi a kliensnek, amelyeket kért a kliens. Amennyiben a lekért információ a későbbiekben megváltozik, akkor a kliens nem vesz tudomást róla és a szerver sem küldi neki.
Meleg: (warm) Annyiban más mint a hideg, hogy amennyiben változik a kért információ, a szerver értesíti a klienst a változásról. A szerver akkor küldi az új információt, amikor a kliens kéri.(Advise)
Forró: (hot) Ebben az esetben a szerver nemcsak értesítést küld a kliensnek a megváltozásról, hanem az új információt is küldi. Természetesen mindegyik típusnak megvan a maga felhasználási területe és az, hogy, melyiket használjuk ez a feladat jellegétől függ. (Advise).
A DDE üzenetek WM_DDE_XXXX felépítésűek. Az XXXX az üzenettől függ (INITIATE, REQUEST, POKE, stb.). Mint említettük a DDE kommunikáció Windows üzeneteken alapszik. Ennek az implementálása eléggé körülményes és sok kódot igényel, nem is beszélve arról, hogy mindenki másképpen implementálja a programjában. A kommunikáció implementálásának az egyszerűsítésére és egyesítésére a Microsoft bevezette a DDE Management Library-t a DDEML-t. Ez a Library API függvényeket tartalmaz, amelyek kezelik a Windows üzeneteket, a programozónak csak ezeket a függvényeket kell meghívnia és nem kell foglalkoznia az üzenetekkel. Természetesen ez nem azt jelenti, hogy aki sima DDE üzenetekkel írja a programját, az nem fog működni, de nehezebb implementálni.
A Microsoft bevezette a DDEML API-t, amely takarja a Windows üzeneteken alapuló DDE protokoll nehézségeit. Ahelyett, hogy a programozó SendMessage és PostMessage függvényekkel küldene DDE üzeneteket, ehelyett a DDEML API függvényeit hívja. Ezzel egyszerűbb dolga lett a programozónak.
A DDEML alapgondolata, hogy mind a szerver mind a kliens rendelkezik egy úgynevezett DDE Callback függvénnyel, amely tartja a kapcsolatot a DDEML-lel. A DDEML és a Callback függvény műveletcsomagokat küldenek egymásnak. Ilyen módon jön létre a kapcsolat a DDEML-en keresztül a szerver és kliens között. A műveletcsomagok hasonlítanak az üzenetekhez. Ezek egy konstansból és tőle függő paraméterekből állnak. A szerver és a kliens DDEML függvényeket hív meg a különböző adatcsere megvalósításához. Minden adatcsere a DDEML-en keresztül bonyolódik le. A következő ábra szemlélteti az alapgondolatot.
Mielőtt
belevágnánk a DDEML programozásba néhány fogalmat kell tisztáznunk:
1. Kliens és szerver
A kliens az az applikáció, amely inicializálja a kapcsolatot (felveszi) és parancsokat illetve kéréseket küld a szervernek. A szerver az az applikáció, amely válaszol a kliensnek vagy a kért adatokkal vagy egy kliens által küldött parancs végrehatásával. Egy applikáció lehet kliens is és szerver is egyszerre.
2. Műveletcsomagok (Transactions and Conversations)
Amikor a kliens felveszi a kapcsolatot a szerverrel, akkor a két applikáció elkezd beszélgetni egymással. A kliens vagy adatokat kér és a szerver ezeket küldi, vagy parancsokat küld és a szerver ezeket végrehajtja. A kliens és a szerver között az adatok csomagok formájában mennek át. Természetesen ezek a csomagok minden oldalon a Callback függvény és a DDEML között mozognak.
3. Service, Topic, Item
A DDE szerver egy vagy több service-t (szolgáltatás név) támogathat. Minden szolgáltatás egy névvel azonosítandó. Általában azt a megoldást használják, hogy a szerverhez egy service tartozik, amelynek a neve megegyezik a szerver applikáció nevével. Például a Program Manager egy Progman, a Word egy WinWord és az Excel pedig egy Excel nevű szolgáltatást támogat. A fő előnye annak, hogy a service név megegyezik a szerverével, hogy megtudhatjuk, hogy melyik programot kell futtatni, ha nem sikerült felvenni a kapcsolatot a szerverrel. Például ha valaki kliensként inicializálni akar egy kapcsolatot az Excellel és az nem válaszol, akkor a WinExec API függvény segítségével indíthatjuk az Excelt és újból próbálkozhatunk. Az NDDE esetén (természetesen csak Workgroups, Win95, Win NT esetén) a service neve a következőképpen kell hogy kinézzen \\COMPUTER_NAME\NDDE$.
Minden service számos topic-ot (témakör) támogat. Például az Excel esetében a topic név egy megnyitott fájl neve. Természetesen a mi programunk tetszés szerinti topic nevet vehet fel. Az NDDE esetén a topic neve egy úgynevezett Shared névvel kell, hogy megegyezzen. Ez nem más mint egy bejegyzés, ami a system.ini-ben van és vagy kézzel vagy egy úgynevezett share manager segéd program segítségével jegyezhetjük be. Erre jó példa a chat program, amely a Workgroupsban van. Ha egy Workgroups system.ini-jébe belenézünk a [DDEShares] szekcióban a következő sort látjuk. CHAT$=winchat,chat,,31,,0,,0,0,0. Ilyenkor a topic név a CHAT$ kell, hogy legyen. Természetesen érdemes használni az említett segéd programot , hogy ne kelljen nekünk beállítani a paramétereket.
Minden topic-hoz több Item (tétel) tartozhat. Egy item egy adatnak felel meg, amit a kliens kaphat a szervertől. Például az Excelnél maradva egy item egy cella intervallumot jelent pl. R1C1:R5C6.
System topic
A 3.1 verziótól a kliens applikációk segítségére egy speciális topic (system topic) került bevezetésre. A DDE szerverek többsége támogatja a system topic-ot. A system topic segítségével a DDE kliensek könnyen jutnak fontos információhoz. Ez az információ lehet: milyen topic-ok érvényesek az adott szervernél, milyen adatformátumokat támogat a szerver, a szerver státusza és hasonló információk. A következő táblázat tartalmazza azokat a konstansokat és értelmezésüket, melyeket támogat a DDE szerver. Ezek a konstansok a DDEML.h fájlban találhatók:
Definiált konstans |
Név |
Magyarázat |
SZDDESYS_TOPIC |
"System" |
A System topic kiválasztására használható szimbólum. |
SZDDESYS_ITEM_SYSITEMS |
"SysItems" |
A system topic Item-einek listája. Minden szervernek minimum tudnia kell a topic-okat, Item-eket, adatformátumokat. |
SYDDESYS_ITEM_TOPICS |
"Topics" |
A szerveren pillanatnyilag érvényes topic-ok |
SZDDESYS_ITEM_FORMATS |
"Formats" |
Az érvényes adatformátumok. Minden DDE szervernek támogatnia kell a CF_TEXT adatformátumot. Az NT esetén a CF_UNICODETEXT támogatása is kötelező. |
SZDDE_ITEM_ITEMLIST |
"TopicItemList" |
A system topic-on kívül minden topichoz milyen itemek tartoznak. |
SZDDESYS_ITEM_HELP |
"Help" |
Egy szöveg, amely megmondja, hogy hogyan kell használni a DDE szervert |
SZDDESYS_ITEM_STATUS |
"Status" |
Busy vagy Ready. |
SZDDESYS_ITEM_RTNMSG |
"ReturnMessage" |
Speciális adatforgalom is van. |
5. DDE Commands (parancsok)
Az adatokon kívül parancsok is mehetnek a kliensről a szerverre. (XTYP_EXECUTE transaction)
6. DDE Handles (leírók)
A DDEML -ben a sztringek és adatok leírókkal azonosíthatók. Az adat, amely a szervertől a kliens felé megy egy HDDEDATA típusúval azonosítható. A sztringek, amelyek azonosítják a service-t, topic-ot és item-et HSZ típusúak. Itt kell megemlíteni, hogy a DDE esetén a kommunikáció üzeneteken keresztül valósul meg. Ilyenkor a service, topic és item sztringek azonosítására egy úgynevezett ATOM táblát hoznak létre és ennek a táblának az egyik indexét adják át egymásnak a kommunikáló applikációk. A DDEML esetén is hasonló a helyzet a HSZ (Handle Sztring) bevezetésével.
7. DDE kommunikáció megvalósításának lépései
A lépések szekvenciáját a következő táblázatban foglaljuk össze:
A kliens |
A szerver |
1. Inicializálja a DDEML-t a DdeInitialize. Kijelőli a Callback függvényt. |
Inicializálja a DDEML-t a DdeInitialize. Kijelőli a Callback függvényt. |
2. Kapcsolódik a szerverhez. Erre vagy a DdeConnect vagy a DdeCoonectList függvényt hívja meg. |
A szerver Callback függvénye kap egy XTYP_CONNECT tranzakciót. A Callback függvénnyel True-val tér vissza ha a topic és az Item érvényesek. |
3. Inicializálja a tranzakciót a DdeClientTransaction függvény meghívásával (ez a szinkron eset. Lásd később). |
A szerver Callback függvénye egy XTYP_REQUEST tranzakciót kap. Erre ha tud a szerver válaszolni, létrehoz egy memória blokkot (DdeCreateDataHandle függvénnyel) és ebbe másolja az adatokat. Visszatér a HDDEDATA leíróval. |
4. A DdeClientTransaction visszatér a HDDEDATA leíróval. A kliens a DdeAccessData függvénnyel olvashatja az adatokat, ha akarja. (Amennyiben a kommunikáció aszinkron, akkor a kliens Callback függvénye egy XTYP_XACT_COMPLETE tranzakciót kap, hogy az adat kész, elvihető.) |
A szerver vár a többi tranzakciókra. |
5. A 3. és 4. lépés ismétlése a további adatok eléréséhez. |
A szerver kiadja az adatokat, ha tudja. |
6. A beszélgetés befejezése a DdeDisconnect vagy a DdeDisconnectList függvénnyel. |
A szerver Callback függvénye kap egy XTYP_DISCONNECT tranzakciót. A kapcsolatnak vége van. |
Meghívja a DdeUninitialize függvényt, jelezve, hogy nincs többé szüksége a DDEML.DLL-re. |
A szervernek is meg kell hívnia a DdeUnitialize függvényt ha már nincsen szüksége a DDEML-re. |
Lássuk részletesebben a táblázatban felsorolt lépéseket:
1. Inicializálás
Amikor az applikáció használja a DDEML-t meg kell hívnia a DdeInitialize függvényt mielőtt bármilyen DDEML függvényt meghívna.
UINT DdeInitialize (
LPWORD pidInst, // a DDE példány leíró
PFNCALLBACK pfnCallback, // DDE Callback függvény
DWORD afCmd, // Az applikáció típusa
DWORD ulRes); // 0-nak kell lennie
A függvény visszatérési értéke DMLERR_NO_ERROR ha nem történt hiba. Különben a visszatérési érték lehet: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER vagy DMLERR_SYSERR.
Az első argumentum (pidInst) NULL-nak kell lennie. Amikor a függvény sikeresen tér vissza, ebben a változóban lesz a DDE példány leíró. (Az ACCESS, EXCEL esetén ezt csatorna számnak hívják). Ezt használja minden DDEML függvény a kapcsolat azonosítójaként.
Az (afCmd) argumentum egy vagy kapcsolatban levő flagek egy kombinációja, amely specifikálja, hogy milyen típusú az applikáció illetve szűrőként használható, hogy milyen tranzakciók érdeklik őt. Például a Kliens esetén ez lehet APPCMD_CLIENTONLY. A flagek prefixuma APPCLASS_, APPCMD_, CBF_, MF_. Ha egyik tranzakcióra sem vagyunk kíváncsiak, akkor a CBF_SKIP_ALLNOTIFICATIONS.
A pfnCallback a Callback függvényünk címe. Ha nincsen Callback függvényünk, akkor ez NULL.
DDE Callback függvény
A DDEML meghívja a DDE Callback függvényt, hogy értesítse az applikációt a különböző fajta eseményekről. A DDE Callback függvény prototípusa a következő:
HDDEDATA CALLBACK DdeCallback(
UINT uType, // Tranzakció típusa (lásd táblázat)
UINT uFmt, // Adatformátum
HCONV hConv, // CONVTEXT info. Struktúrára pointer
HSZ hsz1, // String Handle a tranzakciótól függ
HSZ hsz2, // String Handle a tranzakciótól függ
HDDEDATA hData, // DDE adatok leírója
DWORD dwdata1, // Tranzakció specifikus adat
DWORD dwdata2); // Tranzakció specifikus adat
Mivel a DDE Callback függvény sok fajta tranzakciót továbbít, ezért sok argumentuma függ az első argumentumtól, ami a tranzakció típusa. A következő táblázat összefoglalja a tranzakció típusokat és egy kis magyarázattal látja el őket:
Tranzakció |
Magyarázat |
XTYP_ADVDATA |
Értesíti a klienst, hogy az adat megváltozott. A hsz1, hsz2 tartalmazzák a topicot és az itemet. Az adat a hData-ban van. A Callback függvény visszatérési értéke DDE_FACK ha végrehajtotta a tranzakciót, DDE_FBUSY ha foglalt és DDE_FNOTPROCESSED ha nem akarja, vagy nem tudja teljesíteni a kérést. Érdemes itt említeni, hogy az XTYP_ADVXXX tranzakciók advise-t jelentenek azaz meleg vagy forró kapcsolat esetén van értelmük. ( Az advise azt jelenti, hogy vagy értesítést kér az adat megváltozása esetén, vagy magát a megváltozott adatot). |
XTYP_ADVREQ |
Értesíti a szervert, hogy egy advise tranzakció érkezett és még nem intézte el (ez akkor keletkezik, ha a szerver meghívja a DdePostAdvise függvényt). A hsz1 és hsz2 a topic-ot és az itemet tartalmazzák. A Callback függvénynek meg kell hívnia a DdeCreateDataHandle-t, hogy hozzon létre egy adatleírót és ezzel térjen vissza. Ha a szerver nem fejezte be a tranzakciót, akkor NULL-lal tér vissza. |
XTYP_ADVSTART |
Értesíti a szervert, hogy egy advise ciklus indul. Ez forró kapcsolatot jelent. Amennyiben meleg kapcsolatot kér a kliens, akkor a fenti tranzakciót egy vagy kapcsolattal kell kombinálnia az XTYPF_NODATA konstanssal. A hsz1 és hsz2 ugyanaz mint az előző. A szerver Callback függvénye TRUE-val tér vissza ha indulhat a ciklus és FALSE-sal ha nem. |
XTYP_ADVSTOP |
Értesíti a szervert, hogy vége van a ciklusnak, és ne értesítse a klienst az adat megváltozása esetén többé. A hsz1 és hsz2 ugyanaz mint az előzőnél. |
XTYP_CONNECT |
Értesíti a szervert, hogy a kliens csatlakozást kér a hsz2 service és hsz1 topic-ú azonosítással. A dwData1 argumentum egy CONVTEXT-re mutató pointer kell hogy legyen, amely információkat tartalmaz a továbbiakban történő beszélgetésekről. A dwData2 argumentum 1 ha a szerver és a kliens ugyanahhoz az applikációhoz tartozik, különben 0. A Callback függvénynek TRUE-val kell visszatérnie, ha sikerült a kapcsolat felvétele és FALSE-sal egyébként. |
XTYP_CONNECT_CONFIRM |
Válasz a szervernek a klienstől, hogy rendben van minden a kapcsolat létrehozásával kapcsolatban. A hsz1, hsz2, dwData2 ugyanaz mint az előzőekben. Nincs visszatérési értéke. |
XTYP_DISCONNECT |
Értesíti a szervert vagy a klienst, hogy a partner befejezte a kapcsolatot. A dwData2 ugyanaz mint az előzőekben. Nem kell visszatérési érték. |
XTYP_ERROR |
Értesíti a szervert vagy a klienst, hogy valamilyen kritikus hiba történt. A dwData1 alsó szava tartalmazza a hibakódot. |
XTYP_EXECUTE |
Értesíti a szervert, hogy a kliens egy végrehajtási parancsot küldött. Hsz1 a topic. A Callback függvény visszatérési értéke DDE_FACK ha végrehajtotta a tranzakciót, DDE_FBUSY ha foglalt és DDE_FNOTPROCESSED ha nem akarja, vagy nem tudja teljesíteni a kérést. |
XTYP_POKE |
Értesíti a szervert, hogy a kliens által küldött adat kéretlen(önkéntes). Hsz1 a topic, Hsz2 az item. A callback függvény visszatérési értéke DDE_FACK ha végrehajtotta a tranzakciót, DDE_FBUSY ha foglalt és DDE_FNOTPROCESSED ha nem akarja, vagy nem tudja teljesíteni a kérést. |
XTYP_REGISTER |
Értesít egy DDE applikációt, hogy egy DDE szerver meghívta a DdeNameService, hogy regisztráljon egy service nevet, vagy egy nem DDEML applikáció indult, amely támogatja a system topic-ot. A hsz1 tartalmazza az alap service nevet és a hsz2 tartalmazza a példány specifikus service nevet. |
XTYP_REQUEST |
Értesíti a szervert, hogy a kliens egy adatot kért. hsz1 a topic, hsz2 az item. A szerver Callback függvénye meghívja a DdeCreateHandleData függvényt, hogy létrehozzon egy adat objektumot. Bemásolja az adatot ebbe az objektumba és visszatér az adatleíróval. Ha nem sikerült teljesíteni a tranzakciót, akkor NULL-la tér vissza. A DDEML küld egy DDE_FNOTPROCESSED flaget a kliensnek. |
XTYP_UNREGISTER |
Értesíti a DDEML applikációt, hogy DDEML szerver applikáció használta a DdeNameService, hogy unregisztrálja a service nevet. A hsz1, hsz2 ugyanaz mint a regiszter esetén. |
XTYP_WILDCONNECT |
Értesít egy szervert, hogy egy kliens kapcsolatot akar felvenni bármilyen szerverrel, ami rendelkezik az adott service és topic szolgáltatással. A hsz1 topic, hsz2 service. Ha hsz1 és hsz2 NULL, akkor minden topic érdekli a klienst, amit a szerver támogat. A dwData1 pointer a CONVTEXT stuktúra, amely információkat tartalmaz a kapcsolatról. A dwData2 azt jelzi, hogy azonos applikációhoz tartozik-e a két partner vagy sem. A Callback függvény egy HSZPAIR struktúráju tömbbel tér vissza. A tömb tartalmazza a topic-service párokat. |
XTYP_XACT_COMPLETE |
Értesíti a klienst, hogy aszinkron tranzakció ért véget. A hsz1 és hsz2 tartalmazzák a topicot és az itemet. A hData tartalmazza az adatokat ha van. A Callback függvény meghívja a DdeGetData-t, hogy másolja az adatokat. |
Egy tipikus DDE Callback függvény egy hosszú switch-et tartalmaz, amelyben a különböző tranzakciókra történik a reagálás. Például egy kliens Callback függvény a következőképpen néz ki:
// DDE Callback függvény egy DDEML kliens esetén
HDDEDATA FAR PASCAL _export DdeCallback (UINT iType, UINT iFmt, HCONV hConv,HSZ hsz1, HSZ hsz2, HDDEDATA hData,DWORD dwData1, DWORD dwData2)
return NULL ;
}
3. DDE Beszélgetés indítása
Az inicializálás után a kliens felveheti a kapcsolatot a DdeConnect függvény meghívásával. Például ahhoz, hogy elkezdjünk egy DDE beszélgetést egy Excel service név és MINTA.XLS topic nevű szerverrel a következő programrész szükséges:
HSZ hszService, hszTopic;
HCONV hConv;
//tételezzük fel, hogy a DdeInitialize után a példány visszatérési értéke dwDDEInst (a //DdeInitialize első argumentuam)
// A stringből létrehozunk egy leírót, amelyet felhasználunk a kommunikáció során
hszService =DdeCreateStringHandle(dwDDEInst, "Excel", CP_WINANSI);
hszTopic =DdeCreateStringHandle(dwDDEInst, "MINTA.XLS", CP_WINANSI);
hConv=DdeConnect(dwDDEInst, hszService, hszTopic, NULL);
if(!hConv)
// A stringből létrehozott leírók felszabadítása
DdeFreeStringHandle(dwDDEInst, hszService);
DdeFreeStringHandle(dwDDEInst, hszTopic);
A DDE szerverek ilyenkor egy XTYP_CONNECT tranzakciót kapnak. Az a szerver, amely támogatja az adott service és Topic nevet TRUE-val válaszol.
A kliens több szerverre is tud csatlakozni egyszerre. Ehhez a DdeConnect helyett a DdeConnectList függvényt kell meghívni, amely egy HCONVLIST típusú listával tér vissza. Lehet "sétálni" a lista elemein és mindegyiknek a részleteit megvizsgálni. A léptetés a lista elemei között a DdeQueryNextServer segítségével lehetséges.
Általában a fő oka annak, hogy a DdeConnect NULL-lal tér vissza az, hogy az adott applikáció nincsen elindítva. Ilyen esetben el lehet indítani a programot a WinExec API függvény segítségével. Ilyenkor a fenti program a következőre módosul.
HSZ hszService, hszTopic;
HCONV hConv;
//tételezzük fel, hogy a DdeInitialize után a példány visszatérési értéke dwDDEInst (a //DdeInitialize első argumentuam)
// A stringből létrehozunk egy leírót, amelyet felhasználunk a kommunikáció során
hszService =DdeCreateStringHandle(dwDDEInst, "Excel", CP_WINANSI);
hszTopic =DdeCreateStringHandle(dwDDEInst, "MINTA.XLS", CP_WINANSI);
hConv=DdeConnect(dwDDEInst, hszService, hszTopic, NULL);
if(!hConv) // itt van a különbség
// A stringből létrehozott leírók felszabadítása
DdeFreeStringHandle(dwDDEInst, hszService);
DdeFreeStringHandle(dwDDEInst, hszTopic);
4. Adat lekérdezés
Miután létrejött a kapcsolat a kliens és a szerver között kezdődhet az adat lekérdezés-küldés. A következő arra ad példát:
BYTE dbuff;
HSZ hszItem;
HDDEDATA hData;
DWORD dwResult;
// A DdeConnect után
if (hConv)
A szerver Callback függvénye ilyenkor egy ilyen tranzakciót kap. Erre reagálva létrehoz egy leírót a DdeCreateDataHandle függvény segítségével, ebbe bemásolja az adatot és visszatér ezzel a leíróval.
5. Az adatok folyamatos frissítése, küldése
A fent említett eset egy hideg kapcsolatot (cold link) képez. Gyakran felmerül az igény arra, hogy a kliens tudomást szerezzen az adat változásáról. Ilyenkor a hideg kapcsolat nem tudja ezt garantálni. Ennek megvalósításához a meleg vagy a forró kapcsolat ad megoldást. Advise alatt azt értjük, hogy a kliens mindig kap értesítést az adat megváltozásáról vagy magát az adatot. Az első eset a meleg kapcsolat, az utóbbi pedig a forró. A meleg és a forró kapcsolat esetén egy ciklusban történik az adatfrissítés. A ciklus addig tart, amíg nem szakítjuk meg. A ciklust az XTYP_ADVSTART tranzakcióval lehet indítani és az XTYP_ADVSTOP-pal megszakítani. Amennyiben egy meleg kapcsolatot szeretnénk megvalósítani, akkor az XTYP_ADVSTART-ot kombinálni kell az XTYP_ADVNODATA konstanssal vagy kapcsolattal. Mind a két kapcsolat esetén a kliens Callback függvénye egy XTYP_ADVDATA tranzakciót kap. A forró kapcsolat esetén a hData tartalmazza az adatot, a meleg esetén pedig NULL és csak értesítésről van szó.
6. A DDE beszélgetés befejezése, a kapcsolat lebontása
Miután már nem kell nekünk a DDE kapcsolat, akkor ezt lebonthatjuk. A lebontás a DdeDisconnect, illetve a DdeDisconnectList-tel történik, attól függően, hogy a DdeConnect, illetve DdeConnectList-et használtunk a kapcsolat létrehozásakor. A lebontás után meg kell hívni a DdeUninitialize függvényt, jelezve azt, hogy már egyáltalan nem kell nekünk a DDE szolgáltatás. Példa:
DdeDisconnect(hConv);
DdeUninitialize(dwDDEInst);
7. Szinkron és aszinkron tranzakciók
A tranzakciók kezelhetők szinkron és aszinkron módon. Szinkron kapcsolat esetén definiálható egy timeout, ameddig a kliens vár a szerverre. Ebben az esetben a DdeClientTransaction függvény addig nem tér vissza, amíg a szerver nem fejezi be a tranzakció feldolgozását, vagy megszakítja azt, vagy lejár a timeout. Amíg a DdeClientTransaction függvény nem tér vissza addig a kliens nem küldhet újabb tranzakciót.
Az aszinkron kapcsolat esetén a DdeClientTransaction azonnal visszatér. Az aszinkron mód megvalósításához a DdeClientTransaction uType paraméterét a TIMEOUT_ASYNC konstanssal kell kombinálni. Amikor a szerver befejezi a feldolgozást, akkor egy XTYP_XACT_COMPLETE értesítést küld a kliensnek.
A DdeAbandomTransaction függvénnyel megszakítható az aszinkron üzenetcsomag továbbítása.
8. DDEML függvények
A következő táblázatban összefoglaljuk a DDEML függvényeket.
Függvény neve |
funkció |
DdeAbandomTransaction |
megszakít egy aszinkron tranzakciót |
DdeAccessData |
Lokkolja a DDE adat objektumot. Egy pointerrel tér vissza, ami az adatokra mutat. NULL-lal tér vissza, ha nem sikerült a lokkolás. |
DdeAddData |
Újabb adatot ad a specifikált HDDEDATA objektumra. |
DdeClientTransaction |
Elkezd egy DDE tranzakciót egy adott szerver felé. |
DdeCmpStringHandles |
Összehasonlít két string leírót. (Case Insensitive) |
DdeConnect |
Csatlakozás a DDE szerverre. |
DdeConnectList |
Csatlakozás több mint egy szerverre. |
DdeCreateDataHandle |
Adat leíró létrehozása. |
DdeCreateStringHandle |
Egy stringből létrehoz egy leírót. |
DdeDisconnect |
Kapcsolat lebontás egy szervertől. |
DdeDisconnectList |
Kapcsolatlebontás több szervertől |
DdeEnableCallback |
Szűrő, milyen tranzakciók jöhetnek. |
DdeFreeDataHandle |
Adat leíró felszabadítása |
DdeFreeStringHandle |
String leíró felszabadítása |
DdeGetData |
Adatok másolása a HDDEDATA típusú leíróból |
DdeGetLastError |
Az utolsó DDE hibakód lekérdezése. |
DdeInitialize |
Kapcsolat felvétele a DDEML-lel. |
DdeKeepStringHandle |
A string leíró megtartása. |
DdeNameService |
Service név regisztrálása a DDEML-lel. |
DdePostAdvise |
A szerver hívja meg, hogy értesítse a klienst az adat megváltozásáról vagy ezzel küldi az adatokat. |
DdeQueryConvInfo |
DDE tranzakcióról szerez információt. |
DdeQueryNextSzerver |
A szerverlistán való gyaloglás. |
DdeQueryString |
Egy stringleíróból visszaadja a stringet. |
DdeReconnect |
Újból csatlakozás. |
DdeUnaccessData |
A lokkolás feloldása. |
DdeUninitialize |
A DDE befejezése. Ez felszabadítja a DDEML erőforrásokat. |
|