/**************/ /* ddecli.c */ /**************************************************************************** PROGRAM: ddecli PURPOSE: dde-asiakkaan yksinkertainen käyttö AUTHOR: Vesa Lappalainen 24.8.1993 USAGE: Projektiin laitettava ddecli.c ja clipboard.c. Kaikkein yksinkertaisinta asiakkaana oleminen on esimerkiksi seuraavassa tapauksessa: 1) Palvelinohjelma on käynnissä (vaikkapa Excel): 2) Omassa ohjelmassa alustetaan dialogi (hDlg) ja siinä tunnuksella ID_LINK on ikkuna, johon halutaan näyttää Excelin taulukon solu R1C1 Kutsutaan: AutoLinkFromText(0,hDlg,ID_LINK,"Excel|Sheet1!R1C1"); Nyt ko. ikkuna päivittyy aina automaattisesti 3) Ohjelman lopuksi suljetaan asiakkaat: CloseAllDDEClients(); Yleisempi käyttö: ----------------- DDE-palvelua voidaan pyytää esim. EXCELiltä muodossa: =Excel|Sheet1!R1C1:R3C3 ^ ^ ^ | | | item | | topic | service Jotta oma ohjelma osaisi käyttää tätä palvelua (Excel on tällöin palvelija, service ja oma ohjelma asiakas, client), pitää omaan ohjelmaan tehdä seuraavat muutokset: 1) (pää)ohjelmaan lisättävä alustus ja lopetus: Kun palvelu halutaan käyntiin kutsuttava jotakin seuraavista error = LinkFromSTI(n,hWnd,MyUpdate,hclWnd,wFmt,ser,top,item); error = LinkFromClipboard(n,hWnd,MyUpdate,hclWnd,wFmt); error = LinkFromText(n,hWnd,MyUpdate,hclWnd,wFmt,link); Parametrit: int n - samassa ohjelmassa on mahdollista käyttää MAX_CLIENT-1 palvelua. Tällä valitaan käytettävän palvelun numero. Jos numero liian iso tai pieni, kutsu hylätään HWND hWnd - sen ikkunan kahva, joka pyytää palvelua tClientFunc MyUpdate - funktio, joka suorittaa datan ottamisen (voi olla NULL, jolloin hclWnd toimii) HWND hclWnd - lapsi-ikkuna, johon tekstimuotoinen data voidaan siirtää muodossa SetWindowText(hclWnd,data); UINT wFmt - haluttu palvelun muoto, esim. CF_TEXT (oletus jos wFmt == 0 ) char* ser,top,item - service, topic, item, jos jokin on NULL, kutsu hylätään char* link - haluttu linkki merkkijonona muodossa ser|top!item Jos jokin kenttä puuttuu, kutsu hylätään. 2) Tietyn palvelun lopettamiseksi kutsuttava: CloseDDEClient(n); tai ohjelman lopuksi kaikkien lopettamiseksi: CloseAllDDEClients(); Jos suljetun palvelun sulkeminen ei aiheuta virhettä! 3) Jos datan käsittely halutaan suorittaa itse ja alustus on esimerkiksi: error = LinkFromClipboard(n,hWnd,MyUpdate,NULL,0); on kirjoitettava esimerkiksi seuraavanlainen funktio: int MyUpdate(HWND hWnd,int n,HDDEDATA hData,LONG size) { char ddedata[100]; HWND hswnd = GetDlgItem(hWnd,ID_LINK+n); DdeGetData(hData,ddedata,sizeof(ddedata),0); // ks. DDEML SetWindowText(hswnd,ddedata); return 0; } Yksinkertaisessa tapauksessa kuten edellä, riittäisi käyttää myös muotoa DdeData(hData,ddedata); 4) Jos datan käsittelyksi riittää siirtää se johonkin ikkunaan kutsulla SetWindowText, voidaan tämä jättää aliohjelmiston huoleksi välittämällä käsittelyfunktion osoitteena NULL ja kertomalla alustuksessa ikkunan kahva, johon data tulee error = LinkFromText(n,hWnd,NULL,hclWnd,CF_TEXT,"Excel|Sheet1!R1C1"); 5) Jos ei automaattinen ikkunaan päivitys, niin kun tieto tarvitaan uudelleen, kutsutaan error = UpdateClientData(n); Kaikkia kolmea eri alustuskutsua voidaan käyttää ristiin tarpeen mukaan. Jos asiakkaan palvelin (server) halutaan vaihtaa, voidaan kutsua uudestaan alustusta, jolloin automaatisesti ensin suljetaan vastaavan numeroinen asiakas. Sama funktio MyUpdate voi toimia samalla kertaa usealle asiakkaalle. Kovin järkevää ei ole käyttää samaa ikkunaa hclWnd usealle asiakkaalle, mutta sekin on mahdollista. Leikekirjakäyttö: ----------------- Useat ohjelmat laittavat leikekirjaan kopioitaessa tiedon myös linkitysohjeesta (esim. Excel, Quattro Pro for Windows). Kutsu LinkFromClipboard(...) muodostaa linkin tämän ohjeen perusteella. Omasta ohjelmasta saadaan helposti sellainen, jossa linkki on käyttäjän helposti vaihdettavissa tekemällä seuraavasti: 1) Määritellään ohjelmaan toiminta Edit/Paste Link, jonka tehtävänä on laittaa joko linkki johonkin ennalta valittuun paikkaan tai siihen, jossa kursori on valinnan yhteydessä. Kun toiminta valitaan tehdään kutsu: error = LinkFromClipboard(n,hWnd,NULL,hclWnd,CF_TEXT); Tai voi tietysti välittää käsittelyfunktionkin. 2) Ohjelman lopuksi suljetaan CloseAllDDEClients(); ============ Aliohjelmat: (ddeml, ddecli.c, ddecli.h) ============ Seuraavissa aliohjelmissa esiintyvä kokonaislukuparamteri n on asiakasnumero, jonka ohjelmoija joutuu keksimään erikseen kullekin asiakastoiminnolle väliltä 0-MAX_CLIENT DWORD DdeGetData(HDDEDATA hData,void *data,DWORD cbMax,DWORD offSrc); ks. DDEML Palauttaa siirrettyjen tavujen määrän (data-alueen koon jos data = NULL) hData - data-alueen kahva *data - puskurin osoite, johon kopioidaan cbMax - kopioitavien tavujen maks.määrä offSrc - kopioinnin alkukohta data-alueen alusta DdeGetData(hData,data) Makro edellisen yksinkertaiseen käyttöön, jossa offSrc on aina 0 ja datan max-koko saadaan oikein operaattorilla sizeof(data) ÄLÄ KÄYTÄ jos data on osoitin!!! int UpdateClientData(int n); Jos näyttö pitää piirtää uudelleen, voidaan kertoa että asiakkaan n alue on pilaantunut ja sille tarvitaan uusi tieto. Tarvittaessa tämä kutsuu alustuksessa mainittua omaa datankäsittelyfunktiota. int CloseDDEClient(int n); Sulkee asiakkaan n. Uudelleen sulkeminen ei haittaa. Aina alustuksen LinkFrom... aluksi kutsutaan vastaavan asiakaan sulkemista, joten linkki voidaan helposti vaihtaa vain alustamalla sama asiakasnumero uudelleen. int CloseAllDDEClients(void); Kutsuu sulkemista kaikille asiakkaille. int LinkFromSTI(int n,HWND hWnd,tClientFunc ClientFunc, HWND hclWnd,UINT wFmt, char *ser, char *top, char *item); int LinkFromClipboard(int n,HWND hWnd,tClientFunc ClientFunc, HWND hclWnd,UINT wFmt); int LinkFromText(int n,HWND hWnd,tClientFunc ClientFunc, HWND hclWnd,UINT wFmt,char *olink); int AutoLinkFromClipboard(int n,HWND hWnd,int ID) int AutoLinkFromText(int n,HWND hWnd,int ID,char *link) Makrot jotka kutsuvat vastaavia ilman auto-lisuketta olevia kutsuja Alustukset halutun tavan mukaan. Jos on annettu sekä ClientFunc että hclWnd, niin tällöin ensin kutsutaan funktiota ja sen jälkeen päivitetään ikkuna. Jollei ole annettu kumpaakaan, on asiakkaana toimiminen turhaa. Kaikki aliohjelmat palauttavat toiminnon onnistumisen (0) tai virhenumeron. ==================== Itsekirjoitettavat: (oma.c) ==================== int MyUpdate(HWND hWnd,int n,HDDEDATA hData,LONG size) palautus - 0 = onnistui, muutu on virhe hWnd - ikkunan kahva joka vietiin parametrina alustuksessa n - asiakkaan numero 0 - MAX_CLIENT-1 hData - DDE-kahva dataan Ehdotuksia: - bittikartan käsittely ****************************************************************************/ #include #include #include /* DDE Management Library */ /* d */ #include #include "ddecli.h" #include "clipboar.h" #define DDE_TIMEOUT 3000 /***************************************************************************/ /* DDE: */ /***************************************************************************/ typedef struct { HWND hWnd; /* Ikkuna joka toimii asiakkaana */ HWND hclWnd; /* Ikkuna, johon asiakkaan tulos tulee */ tClientFunc ClientFunc; /* Funktio, joka käsittelee tiedon */ HCONV hConvApp; /* Keskustelukahva */ HDDEDATA hData; /* Onko asiakasyhteys päällä vai ei */ HSZ hszService; /* Merkkijonokahva palvelun nimeen */ HSZ hszTopic; HSZ hszItem; UINT wFmt; /* Halutun tiedon muoto */ } tClientDDE; typedef struct { DWORD idInst; /* Ohjelman DDECallback funktion esiintymä */ FARPROC lpDdeProc; /* DDECallback funktion osoitin */ int OpenDDE; /* Montako yhteyttä avoinna */ tClientDDE client[MAX_CLIENT]; } tDDEClient; static tDDEClient DDE = {0,0,0}; /***************************************************************************/ static tClientDDE *GetClient(int n) /* Palautetaan n:ää vastaavan asiakkaan tiedot (osoite) tai NULL jollei ** tietoja ole */ { if ( n < 0 || MAX_CLIENT <= n ) return NULL; return DDE.client+n; } /***************************************************************************/ static int FindClient(HCONV hConv, WORD wFmt,HSZ hszTopic, HSZ hszItem) /* Etsitään löytyykö asiakasta, jolla on parametreissa tulevat tiedot ** Jollei löydy, palautetaan -1, muuten asiakkaan nro. */ { int i; for (i=0; iClientFunc ) ret = cDDE->ClientFunc(cDDE->hWnd,n,hData,size); if ( cDDE->hclWnd ) { /* Tehtdään ikkunan automaatinen päivitys */ char ddedata[100]; DdeGetData(hData,(void *)ddedata,sizeof(ddedata),0); SetWindowText(cDDE->hclWnd,ddedata); } return ret; } /***************************************************************************/ static int DDEAdvData(HCONV hConv, WORD wFmt, HSZ hszTopic, HSZ hszItem,HDDEDATA hData) /* Kerrotaan DDE-asiakkaalle tiedon muuttumisesta */ { return DoAdvData(FindClient(hConv,wFmt,hszTopic,hszItem),hData); } /***************************************************************************/ int UpdateClientData(int n) /* Pyydetään päivittämään asiakkaan n tiedot */ { HDDEDATA hData; int ret; tClientDDE *cDDE = GetClient(n); if ( !cDDE ) return 1; hData = DdeClientTransaction(NULL,0,cDDE->hConvApp,cDDE->hszItem, cDDE->wFmt,XTYP_REQUEST, DDE_TIMEOUT,NULL); ret = DoAdvData(n,hData); DdeFreeDataHandle(hData); return ret; } /***************************************************************************/ static DWORD MyError(char *szVirheMessu) { MessageBox(NULL,szVirheMessu,NULL, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION); return 0; } /***************************************************************************/ static DWORD MyInfo(char *szInfoMessu) { MessageBox(NULL, szInfoMessu, "DDE", MB_APPLMODAL | MB_OK); return 0; } /***************************************************************************/ int CloseDDEClient(int n) /* Suljetaan asiakas n ** Jos jo ennestään suljettuja osia, niin tämä ei haittaa. ** Viimeisellä asiakkaalla suljetaan koko DDE */ { tClientDDE *cDDE = GetClient(n); if ( !cDDE ) return 1; if ( cDDE->hData ) { DdeClientTransaction(NULL,0,cDDE->hConvApp,cDDE->hszItem,cDDE->wFmt, XTYP_ADVSTOP,DDE_TIMEOUT,NULL); cDDE->hData = 0; DDE.OpenDDE--; } cDDE->ClientFunc = NULL; cDDE->hWnd = 0; cDDE->hclWnd = 0; cDDE->wFmt = 0; if ( cDDE->hConvApp != NULL ) { DdeDisconnect( cDDE->hConvApp ); cDDE->hConvApp = NULL; } if ( DDE.idInst ) { DdeFreeStringHandle(DDE.idInst, cDDE->hszService); DdeFreeStringHandle(DDE.idInst, cDDE->hszTopic); DdeFreeStringHandle(DDE.idInst, cDDE->hszItem); if ( DDE.OpenDDE <= 0 ) { DdeUninitialize(DDE.idInst); DDE.idInst = 0; } } if ( DDE.lpDdeProc && DDE.OpenDDE <= 0 ) { (void)FreeProcInstance(DDE.lpDdeProc); DDE.lpDdeProc = NULL; DDE.OpenDDE = 0; } #ifdef MYINFO MyInfo ( "DDE suljettu." ); #endif return 0; } /***************************************************************************/ int CloseAllDDEClients(void) /* Suljetaan kaikki asiakkaat */ { int i; for (i=0; ihConvApp ) CloseDDEClient(i); return 0; } /***************************************************************************/ int ErrorCloseDDEClient(int n, int ret) { CloseDDEClient(n); return ret; } /***************************************************************************/ HDDEDATA EXPENTRY _export DDECallbackClient ( WORD wType, /* transaktion tyyppi */ WORD wFmt, /* datan tyyppi (clipboard type) */ HCONV hConv, /* keskustelukahva */ HSZ hsz1, /* merkkijonokahva, kts. ko. transaktiosta */ HSZ hsz2, /* " */ HDDEDATA hData, /* kahva globaaliin muistilohkoon */ DWORD dwData1, /* transaktiokohtaista dataa */ DWORD dwData2 ) /* " */ { #pragma argsused switch ( wType ) { case XTYP_ADVDATA: /* Saadaan dataa */ /* hsz1 = topic */ /* hsz2 = item */ /* hData = data */ if ( DDEAdvData(hConv,wFmt,hsz1,hsz2,hData) ) return DDE_FNOTPROCESSED; return (HDDEDATA)DDE_FACK; case XTYP_DISCONNECT: /* Asiakas halusi lopettaa keskustelun */ // MyInfo( "DDE asiakas sulki yhteyden." ); CloseAllhConv(hConv); break; case XTYP_ERROR: /* Jokin virhe */ MyInfo( "DDE virhe." ); break; } return NULL; } /***************************************************************************/ int InitDDEClient(int n, HINSTANCE hInst, const char *ser,const char *top, const char *item) /* Alustetaan asiakas n palveluun ser/top/item. */ { tClientDDE *cDDE = GetClient(n); if ( !cDDE ) return 1; (void)hInst; DDE.lpDdeProc = MakeProcInstance( (FARPROC) DDECallbackClient, hInst ); if ( DdeInitialize(&DDE.idInst,(PFNCALLBACK)DDE.lpDdeProc, APPCLASS_STANDARD | CBF_FAIL_EXECUTES | CBF_FAIL_POKES,0L ) ) { MyError( "DDE:n alustus ei onnistu!" ); return -1; } cDDE->hszService = DdeCreateStringHandle(DDE.idInst, ser , 0); cDDE->hszTopic = DdeCreateStringHandle(DDE.idInst, top , 0); cDDE->hszItem = DdeCreateStringHandle(DDE.idInst, item , 0); /* Rekisteröidään palvelu */ cDDE->hConvApp = DdeConnect(DDE.idInst, cDDE->hszService,cDDE->hszTopic,NULL); if ( !cDDE->hConvApp ) return ErrorCloseDDEClient(n,-2); cDDE->hData = DdeClientTransaction(NULL,0,cDDE->hConvApp, cDDE->hszItem,cDDE->wFmt, XTYP_ADVSTART | XTYPF_ACKREQ, DDE_TIMEOUT,NULL); if ( !cDDE->hData ) return ErrorCloseDDEClient(n,-3); DDE.OpenDDE++; UpdateClientData(n); #ifdef MYINFO MyInfo( "DDE alustettu." ); #endif return 0; } /***************************************************************************/ int LinkFromSTI(int n,HWND hWnd,tClientFunc ClientFunc, HWND hclWnd,UINT wFmt, const char *ser, const char *top, const char *item) /* Asiakassuhde n muodostetaan merkkijonojen ser,top,item perusteella */ { tClientDDE *cDDE = GetClient(n); if ( !cDDE ) return 1; CloseDDEClient(n); cDDE->hWnd = hWnd; cDDE->hclWnd = hclWnd; cDDE->ClientFunc = ClientFunc; if ( (cDDE->wFmt = wFmt) == 0 ) cDDE->wFmt = CF_TEXT; return InitDDEClient(n,GetWindowInstance(hWnd),ser,top,item ); } /***************************************************************************/ int LinkFromClipboard(int n,HWND hWnd,tClientFunc ClientFunc, HWND hclWnd,UINT wFmt) /* Asiakassuhde n muodostetaan leikekirjassa olevan linkin avulla. */ { const char *ser,*top,*item; const char *p = TakeLinkFromClipboard(hWnd,"Link",&ser,&top,&item); if ( !p ) return 1; return LinkFromSTI(n,hWnd,ClientFunc,hclWnd,wFmt,ser,top,item); } /***************************************************************************/ int LinkFromText(int n,HWND hWnd,tClientFunc ClientFunc, HWND hclWnd,UINT wFmt,const char *olink) /* Tekstin Excel|Sheet1!R1C1 perusteella muodostetaan linkki ** (asiakassuhde n) */ { char link[100]; char *ser,*top,*item; strncpy(link,olink,sizeof(link)); link[sizeof(link)-1]=0; ser = strtok(link,"|"); if ( !ser ) return -2; top = strtok(NULL,"!"); if ( !top ) return -2; item = strtok(NULL,"!"); if ( !item ) return -2; return LinkFromSTI(n,hWnd,ClientFunc,hclWnd,wFmt,ser,top,item); }