Windowsissa ohjelmat voivat vaihtaa tietoa seuraavilla eri tavoilla
* tiedostojen välityksellä
* leikekirjan välityksellä
* DDE-linkin avulla
* OLE-linkin avulla
* ikkunoille lähetettävien viestien välityksellä
* Drag and drop -menetelmällä (edellisen sovellus)
* jaetun globaalin muistin kautta (lopulta DDE yms. johtavat tähän)
* suoraan lukemalla tietoa toisen ikkunasta (harvinaista)
Tallettamalla oman ohjelman tiedot aina tekstimuodossa tai ainakin lisäämällä ohjelmaan valinta, jolla voidaan lukea ja kirjoittaa tekstimuotoista tietoa, saadaan jonkinlainen tiedostojen välityksellä tapahtuva yhteys lähes mihin tahansa ohjelmaan.
Muista siirtotavoista ainakin yksinkertainen leikekirjamuoto ja miksei yksinkertainen DDE-linkkikin pitäisi sisällyttää jokaiseen hyötykäyttöön tulevaan ohjelmaan. Koska DDE- ja OLE- ovat hyvin monipuolisia välitysmekanismeja, on niiden ohjelmointi myös varsin työlästä. Tämän luvun lopussa esitellään kaksi aliohjelmakirjastoa joilla yksinkertaiset DDE-tapaukset voidaan ohjelmoida muutamalla rivillä.
Perusmuoto ohjelmien väliseen tiedon siirtoon on leikekirja (clipboard). Siinä tieto kopioidaan leikekirjaan ja siitä edelleen johonkin ohjelmaan. Ohjelmaan kopioitu tieto ei ole enää mitenkään sidoksissa alkuperäisen tiedon kanssa. DDE:tä voi myös käyttää tällaiseen kertaalleen tapahtuvaan siirtoon, mutta useimmiten DDE:tä käytetään "on-line" yhteyksissä, joissa tiedon muutos halutaan välittää automaattisesti johonkin toiseen ohjelmaan.
DDE on kuten muukin Windows-ohjelmointi viestien lähettämistä ja vastaanottamista. DDEML (Dynamic Data Exchange Management Library) tarjoaa ohjelmointiliittymän DDE-viestien välittämiseen. Vanhemmat Windows-ohjelmat käyttävät suoraan DDE API-kutsuja, mutta ovat kuitenkin yhteensopivia DDEML:n kanssa. DDEML on käytettävissä Windows 3.0:sta alkaen, ei kuitenkaan tue Windows 3.0:n real-tilaa.
Asiakasohjelma voi olla samanaikaisesti yhteydessä useaan eri palvelimeen (jopa useasti samaan) ja palvelimella voi olla yhtäaikaa useita asiakkaita. Sama ohjelma voi olla sekä asiakas että palvelin.
Esimerkiksi asiakasohjelma alustaa keskustelun (conversation) kutsumalla DdeConnect-funktiota. Kutsusta seuraa, että DDEML lähettää XTYP_CONNECT -transaktion palvelinohjelman DDE-funktiolle. DDE-funktio voi sallia keskustelun palauttamalla TRUE:n DDEML:lle tai kieltäytymällä keskustelusta palauttamalla FALSE:n. Edelleen DDEML lähettää asiakkaalle transaktion XTYP_CONNECT_CONFIRM, josta asiakas tietää yhteyden muodostuneen.
palvelu service |
merkkijono, joka asiakkaan täytyy tuntea alustaessaan keskustelun. Tämän tunnuksen DDE-palvelin yleensä rekisteröi käynnistyessään ja DDE-asiakkaisiin on mahdollista tehdä ominaisuus, jolla käynnissä olevia DDE-palvelimia voidaan tiedustella. Tämä on yleensä sama kuin ohjelman nimi. |
aihe topic |
jakaa palvelun loogisiin kokonaisuuksiin. Esimerkkinä olevassa autolaskuriohjelmassa (palvelin) aihe on "Lkm" eli tästä palvelun osasta saadaan selville lukumääriä. Toinen aihe voisi olla "Asetukset", jossa voitaisiin vaikkapa määrätä lisää laskettavia asioita tai määrätä palvelin nollaamaan laskurit. |
asia item |
määrää lopulta kohteen, jota transaktio koskee. Asia voi määrätä esimerkiksi mitä kokonaislukua, merkkijonoa, tekstiä tai bittikarttaa transaktio koskee. |
Esimerkiksi jos Excelissä pyydetään DDE-palvelua autolaskurin henkilöautojen lukumäärän selvittämiseksi, voisi Excelissä olla kaava:
DWORD idInst = 0L; /* DDE-esiintymän tunnus */ HANDLE hInst; /* koko ohjelman esiintymän tunnus */ FARPROC lpDdeProc; /* DDE-funktion esiintymän osoite */ ... lpDdeProc = MakeProcInstance ( (FARPROC) DDECallback, hInst ); if ( DdeInitialize(&idInst, (PFNCALLBACK)lpDdeProc, APPCLASS_STANDARD | CBF_FAIL_EXECUTES | CBF_FAIL_POKES, 0L ) ) { MyError( "DDE:n alustus ei onnistu!" ); return FALSE; }
/***************************************************************************/ #pragma argsused HDDEDATA EXPENTRY DDECallback ( /* d */ 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 ) /* " */ { int i; char szBuffer[10]; switch ( wType ) { case XTYP_CONNECT: // Asiakas haluaa avata keskustella // hsz1 = topic // hsz2 = service if ( hsz2 == hszService ) return TRUE; // Meillä on sopiva keskusteluaihe, ok else return FALSE; // Ei sopivaa aihetta case XTYP_ADVSTART: // Asiakas haluaa tietää jokaisesta muutoksesta case XTYP_ADVSTOP: // Asiakas ei enää halua tietää ... // hsz1 = topic // hsz2 = item // Tarkistetaan, että pyydetty laskuri löytyy if ( MikaLaskuri( wFmt, hsz2 ) < 0 ) return FALSE; // Ei löydy // Jos laskuri löytyi, palautetaan asiakkaalle myönteinen vastaus return TRUE; case XTYP_REQUEST: // Asiakas pyytänyt dataa case XTYP_ADVREQ: // Data muuttunut palvelimella // hsz1 = topic // hsz2 = item if ( (i= MikaLaskuri(wFmt,hsz2) ) < 0 ) return NULL; // Ei löydy laskuria /* muutetaan laskurin arvo merkkijonoksi */ wsprintf(szBuffer,"%5d.",laskurit[i]); return DdeCreateDataHandle(idInst,&szBuffer,sizeof(szBuffer)+1, 0L,hsz2,wFmt,0); case XTYP_CONNECT_CONFIRM: // Asiakas hyväksynyt keskusteluyhteyden // hsz1 = topic // hsz2 = service hConvApp = hConv; break; case XTYP_DISCONNECT: // Asiakas halusi lopettaa keskustelun // hsz1 = topic // hsz2 = service MyInfo( "DDE asiakas sulki yhteyden." ); hConvApp = NULL; break; case XTYP_ERROR: // Jokin virhe break; } return NULL; }
DDE-funktion wType-parametri kertoo transaktion tyypin ja sitä seuraavat parametrit ovat transaktiotyyppikohtaisia. Esimerkkiin on merkitty parametrien hsz1 ja hsz2 merkitykset.
Merkkijonokahva luodaan kutsumalla DdeCreateStringHandle-funktiota. Funktio rekisteröi merkkijonon Windows-systeemiin. Seuraavassa esimerkissä luodaan kahva palvelun nimeä varten:
HSZ hszServName; ... hszService = DdeCreateStringHandle ( idInst, // DDE esiintymän tunnus "AUTOLDDE", // rekisteröitävä merkkijono CP_WINANSI ); // koodisivu merkkijonon käännöstä varten
idInst-parametri on jälleen peräisin DdeInitialize-funktion kutsusta. Viimeinen parametri kertoo mitä Windowsin koodisivua käytetään merkkijonon kääntämiseen. Windowsissa paraikaa käytössä oleva koodisivun tunnus saadaan GetKBCodePage-funktiolla.
Tällä tavoin luotu merkkijono sijoittuu systeemin globaaliin muistiin. Kun merkkijono välitetään kahvana DDEML:n kautta, merkkijono on kopioitava globaalista muistista lokaaliin muistiin ennenkuin se on normaalisti käsiteltävissä C:n merkkijonona. Esimerkkiohjelman MikaLaskuri-funktiossa merkkijono haetaan kahvasta lokaaliin merkkitaulukkoon ja käytetään tätä jonoa tutkittaessa löytyykö haluttua tietoa.
int MikaLaskuri( HSZ hszItem ) { int i; char szItem[10]; // Tarkistetaan, että on pyydetty tekstimuotoista dataa if ( wFmt != CF_TEXT ) return -1; DdeQueryString( idInst, // DDE:n esiintymän tunnus hszItem, // kahva mistä kopioidaan szItem, // osoitin mihin kopioidaan sizeof(szItem),// maksimissaan kopioitava määrä 0); // koodisivu
Esimerkissä kuitenkin se vika, että tunnus ei voi olla vakiopituutta suurempi ja merkkejä voi jäädä lukematta. Parempi tapa olisi vaikkapa seuraavan näköinen:
DWORD idInst; DWORD nLen; HSZ hszServ; PSTR pszServName; ... nLen = DdeQueryString( idInst, hszServm (LPSTR) NULL, 0L, CP_WINANSI ) + 1; pszServName = (PSTR) LocalAlloc( LPTR, (WORD) nLen ); ... LocalFree( (HLOCAL) pszServName );
Jos merkkijonokahva halutaan säilyttää myöhempiä DDE-funktion kutsukertoja varten, täytyy kutsua
DdeKeepStringHandle(hInst,hsz)
-funktiota. Ilman kutsua merkkijonoon ei päästä myöhemmin käsiksi.
Kun ohjelma kutsuu DdeCreateStringHandle -funktiota, systeemi luo merkkijonon koko systeemin kattavaan merkkijonotauluun ja kahvan, jolla merkkijonoon päästää käsiksi. Kun luodaan kahva merkkijonoon, joka on jo olemassa, ei uutta merkkijonoa luoda, vaan kasvatetaan olemassaolevan merkkijonokahvan luontikertoja ( DdeKeepStringHandle myös kasvattaa luontikertoja). Lukuarvoa vastaavasti pienennetään DdeFreeStringHandle-kutsulla. Kun luontikertojen määrä menee nollaan, merkkijono hävitetään. Tästä syystä ohjelmissa on oltava tarkkana ettei luontikertoja vähennetä enempää kuin niitä lisätty, koska jollakin muulla DDE-ohjelmalla voi olla sama merkkijonokahva käytössä.
DDEML:n merkkijonojen hallinta perustuu Windowsin atomi systeemiin, joten DDE-merkkijonoilla on vastaavat kokorajoitukset.
Jos halutaan sallia kaikkien XTYP_CONNECT-transaktioiden tulo DDE-funktiolle, lisätään lippu DNS_FILTEROFF DdeNameService-kutsuun (pätkä MyInitDDE-funktiosta):
// Rekisteröidään palvelu DdeNameService ( idInst, hszService, // merkkijonokahva palvelun nimeen (HSZ) NULL, // varattu DNS_REGISTER | DNS_FILTEROFF ); // liput
DDE-palvelimen tulee rekisteröidä palvelunsa mahdollisimman pian DdeInitialize-kutsun jälkeen ja poistaa palvelun nimi rekisteristä juuri ennen DdeUninitialize-kutsua.
// Poistetaan palvelu rekisteristä DdeNameService ( idInst, hszService, (HSZ) NULL, DNS_UNREGISTER ); DdeUnInitialize( idInst );
Esimerkeissä tieto autolaskurin arvoista on välitetty taulukkolaskentaohjelmille. Asiakas voisi lukea laskurin arvon seuraavasti:
HDDEDATA hData; // DDE-datakahva LPBYTE lpszGlobalData; // muistikahva DWORD cbDataLen; // saadun datan määrä HSZ hszLkmHA; // merkkijonokahva HA-tunnukselle HCONV hConv; // keskustelukahva char szLocalData[80]; // lokaali puskuri datalle ... hData = DdeClientTransaction( (LPBYTE) NULL, // ei dataa palvelimelle 0, // datan pituus (palvelimen suuntaan) hConv, hszLkmHa, CF_TEXT, // tiedon tyyppi XTYP_REQUEST, // pyydetään data kertaalleen 10000, // odotetaan enintään 10 sekuntia NULL ); // ei olla kiinnostuneita mahdollisen // virheen syystä if ( hData != NULL ) { lpszGlobalData = DdeAccessdata( hData, &cbdataLen ); strncpy( szLocalData, lpszGlobaldata, sizeof(szLocalData) ); DdeGetData( hData, // kahva globaaliin muistilohkoon (mistä kopioidaan) szLocalData, // osoite johon kopioidaan cbDataLen, // mksimimäärä mitä kopioidaan 0 ); // offset mihin kopioidaan DdeUnaccessData( hData ); } /* tehdään jotakin saadulle lukuarvolle */ ...
Komentoja lähetetään palvelimelle XTYP_EXECUTE-transaktiolla.
Asynkronisessa siirrossa DdeClientTransaction-funktion palaa heti kun transaktio on siirretty DDEML:lle. Kun palvelin on suorittanut transaktion, DDEML lähettää asiakkaalle XTYP_XACT_COMPLETE-transaktion, jossa on mukana sama tunnus, jonka DdeClientTransaction palautti lähetettyään tehtävän DDEML:lle. Näitä tunnuksia tutkimalla asiakas voi päätellä, minkä tehtävän palvelin sai suoritettua.
Käyttö vaatii tietysti DDEMORFO.EXE:n olemassa olon, mutta toiminnan ajatuksen voi selvittää hyvin asiakasaliohjelmasta MORFO.C.
Tosin Morfon tätä monistetta kirjoitettaessa käytössä ollut versio käytti DDE-palveluita varsin ahnaasti ja pudotti koneen suorituskyvyn puoleen silloin kun Morfo oli ladattuna.
Siis tutki ohjelmasi kuluttamat resurssit!
Useat ohjelmat ymmärtävät automaattisen linkin merkkijonon avulla muodossa:
service|topic!item
Näiden yksinkertaisten linkkien käsittelemiseksi on kirjoitettu kaksi kirjastoa DDESER ja DDECLI. Seuraavassa kummastakin kirjastosta on lyhyt kuvaus yksinkertaisimmasta käytöstä. Lisätietoa monipuolisemmasta käytöstä löytyy kummankin aliohjelmakirjaston .C -tiedoston kommenteista.
=AUTOLDDE|LKM!HA
Quattro Pro for Windowsissa vastaava kaava olisi:
@DDELINK([AUTOLDDE|LKM]"HA")
Tavoitteena on saada ainakin tämä yksinkertainen tilanne helpoksi. Useinhan jonkin tietyn dialogin (tai ikkunan) Edit- tai Static-kentän tieto halutaan välittää toiselle ohjelmalle.
Ohjelmoijan kannalta tavoite voisi olla vaikkapa seuraava:
1) (pää)ohjelmaan lisättävä alustukset ja lopetus: InitDDEServer(hInstance,"AutolDDE",NULL); AddAutoTextService("Lkm","ha",HAL,hWnd); AddAutoTextService("Lkm","ka",KAL,hWnd); ... CloseDDEServer(); 2) Kun tieto muuttuu (vaikkapa aliohjelmassa ShowStatic) kutsutaan: InformDDEChangeSN(nr); jossa nr on muutoksen mukaan joko HAL tai KAL
Paljon tämän vähemmällä ohjelmoinnilla ei DDE-linkkiä voida saavuttaa. Kirjasto DDESER.C sisältää mm. edellä mainitut aliohjelmat. Jos palveltava data on monimutkaisempaa kuin pelkkä tietyn Edit- tai Static- kentän sisältö, voidaan linkin alustuksessa (AddService, tai InitDDEService) ilmoittaa funktio, joka käsittelee datan ja palauttaa sen.
Käyttäjän kannalta olisi mukavaa, jos linkkikenttiä vastaavaa kaavaa ei tarvitsisi asiakasohjelmassa (Excel tai Quattro tms.) kirjoittaa. Usein ohjelmien Edit-valikosta löytyy kohta Paste link, jolla leikekirjassa oleva linkkitietous muutetaan ko. ohjelman ymmärtämäksi linkiksi (taulukkolaskennan tapauksessa oikeaksi kaavaksi).
Leikekirjassa ei ole valmista linkkityyppiä, mutta nykyisin useat ohjelmat tunnistavat lisätyn tyypin "Link" (ks. ALI\CLIPBOARD.C). Voimme tehdä omaan ohjelmaan muutoksen, jolla leikekirjaan lisätään linkkityyppi kutsumalla jotakin DDESER-kirjaston kutsuista:
LinkToClipboardSN(hWnd,HAL) LinkToClipboard(hWnd,"Lkm","ha");
Asiakasohjelmassa sitten otetaan tämä linkki leikekirjasta. Em. kutsut laittavat leikekirjaan linkin lisäksi myös itse kentän sisällön, jolloin kutsuja voidaan käyttää myös staattiseen tiedonsiirtoon.
Olkoon dialogissa hDlg Edit-ikkuna numerolla ID_TULOS varattuna Excelin soluun R5C7 laskemalle tulokselle. Excel käyttää esimerkiksi taulukkoa LASKU.XLS. Ohjelmoija lisää ohjelmaansa kutsun:
AutoLinkFromText(0,hDlg,ID_TULOS,"Excel|LASKU.XLS!R5C7");
Ensimmäinen kokonaisluku on ohjelmoijan keksimä asiakasnumero, joka on luku väliltä 0-MAX_CLIENT.
Tietysti linkit pitää purkaa ohjelman lopuksi vaikkapa kutsulla:
CloseAllDDEClients();
Siis vain kaksi kutsua ja automaattinen asiakaslinkki on valmis! Miksei tätä ole tehty Windowsiin valmiiksi?
Jos linkkiä halutaan muuttaa ohjelman suorituksen aikana, voidaan merkkijono selvittää jonkin Edit-kentän avulla ja sitten pyytää linkitystä samalle asiakasnumerolle. Tai voidaan jopa kutsua leikekirjasta linkin ottavaa muotoa:
AutoLinkFromClipboard(n,hWnd,ID_TULOS);
Monimutkaisemmissa tapauksissa voidaan itse kirjoittaa funktio, joka lukee datan, käsittelee sen ja toimii sitten vaaditulla tavalla.
Asiakasikkunaan voidaan laittaa asiakkaita joko leikekirjasta tai kirjoittamalla palvelimen tiedot Edit-ikkunaan (nämä menevät vain isoon asiakaskenttään). Asiakaskentät ovat kaikki monirivisiä Edit-ikkunoita, jotka Windows 3.1:en mahdollistamana on merkitty Read Only -muotoisiksi. Tällä on Static-ikkunaan verrattuna se etu, että kentät (ikkunat) voidaan tehdä aktiivisiksi ja niistä voidaan kopioida leikekirjaan staattista tietoa, kuitenkaan pystymättä muuttamaan kenttien sisältöä.
Asiakaskentät ovat monirivisiä, koska yksirivinen Edit-ikkuna ei osaa käsitellä CR/LF-merkkejä, vaan näyttää ne mustina laatikoina. Usein yksirivisenkin tiedon lopussa nimittäin on CR/LF (esim. Excelin solusta otettu linkki).
Oikeassa ohjelmassa asiakasikkunan ja palvelinikkunan ei välttämättä tarvitse olla erillisiä kuten tässä mallissa.
OLE:ssa dokumentin palanen voi olla linkki (linking) alkuperäiseen tietoon, jolloin kaikki muutokset välittyvät käsiteltävään dokumenttiin. Tällöin palanen ei talletu dokumentin mukana vaan pelkkä tieto, mistä se on peräisin.
Toinen vaihtoehto on, että dokumentin alkio on upotettu (embedding), jolloin varsinainen tieto alkiosta on dokumentin mukana sekä tieto miten alkio on muodostettu. Kun tällainen alkio valitaan, kopioidaan alkio sen luoneelle ohjelmalle käsittelyyn ja käyttäjä voi muokata alkion sisältöä. OLE 2:ssa muokkaus tapahtuu vieläpä itse dokumentin sisällä, eli käyttäjä ei edes huomaa luoneen ohjelman käynnistymistä.
Upotukset ovat mukavia, mutta jos esimerkiksi MS Wordissä on kirjoitettu matemaattisia kaavoja upotusta (Equation) käyttäen, ei dokumentti enää siirrykään muihin käyttöjärjestelmiin (esim Maciin)! Samoin käy tietysti upotettujen kuvien kanssa.
Windows 3.1:ssä valmiina on FileManagerin DaD, jonka avulla voidaan välittää tiedostojen nimiä ohjelmalta toiselle (lyhennetään DaDF). DaD-palvelimessa valitaan joukko tiedostoja, palvelin muuttaa kursorin ulkonäön sallituksi ( tai ) jokaisen sellaisen ikkunan kohdalla, joka pystyy toimimaan DaD-asiakkaana ja kielletyksi ( ) sellaisten ikkunoiden kohdalla, jotka eivät hyväksy "pudotusta". Jos hiiren nappula vapautetaan asiakasikkunan kohdalla, saa asiakasikkuna tästä viestin ja se voi sitten lukea tiedostojen nimet palvelimelta ja tehdä näiden nimien perusteella mitä tahansa. Esimerkiksi MacIntoshin roskakoria vastaava toiminto voitaisiin helposti toteuttaa tällä periaatteella.
int DropFiles(HWND hWnd,LPCSRT files) - laittaa merkkijonossa files olevat tiedostot "pudotukseen" merkkijonon muoto: "hak1 t1 t2 t3;hak2 t4 t5 t6;hak3\t7" esimerkiksi: "C:\DOS SUBST.COM FC.EXE;C:\BIN\TGREP.COM int DropSelectFiles(HWND hWnd) - näyttää tiedoston avausdialogin, antaa valita sieltä joukon tiedostoja ja laittaa ne "pudotukseen" int DropListBoxFiles(HWND hWnd,UINT id,LPCSTR path) - laittaa list-boxissa hWnd,id olevat tiedostojen nimet "pudotukseen" kunkin nimen eteen lisätään polku path
Aliohjelmat hoitavat kursorin liikuttamisen, kursorin muodon vaihtamisen ja lopuksi tiedottamisen asiakasikkunalle. Jälleen yksi aliohjelma hoitamaan hommat ja yksi "miksei Windowsissa ole valmiina" kysymys lisää!
Toteutus perustuu DropAcceptFiles -aliohjelman käyttämän tietorakenteen "nuuskimiseen". FileManager hoitaa kuitenkin toiminnon viestien välityksellä ja tästä seuraa se, että FileManagerille ei saada pudotettua tiedostoja. Muille ohjelmille kylläkin. Joka tapauksessa jos joskus tulee dokumenttia siitä miten Drop tehdään, ei omassa ohjelmassa tarvitse tehdä muutoksia, vaan riittää päivittää DROPFILE.C.
Tiedostoon on laitettu lisäksi muutamia asiakastoimintoja helpottavia kutsuja:
int DropFilesToListBox(HDROP hDrop,HWND hWnd,int id,int clear) - ottaa Drop listassa olevat nimet ja laittaa ne list-boxiin hWnd,id. clear == 1 => poistaa nimistä polkuosan == 2 => tyhjentää ensin list-boxin == 3 => molemmat edelliset ( == 1 | 2 ) LPSTR DropFilesToString(HDROP hDrop,LPSTR s,int mb) - ottaa Drop-listassa olevat nimet ja palauttaa ne muodossa path1\n1;path2\n2;path3\n3
DragAcceptFiles Aloitetaan ja lopetaan tiedostojen hyväksyminen DragFinish Vapautetaan siirtomuisti kun tiedot on otettu DragQueryFile Otetaan tiedostojen nimiä DragQueryPoint Hiiren paikka, jossa vasen näppäin päästettiin ylös WM_DROPFILES Viesti joka saadaan kun tiedostot pudotetaan
Seuraavassa esimerkki DaD-asiakkaana ja palvelimena toimimisesta:
/***************************************************************************** PROGRAM: DaDTest.c PURPOSE: Malliohjelma Drag- and Drop käytöstä Asiakas toiminnot (client, vastaanottaa pudotuksia) merkitty dadc Palvelin (server) on merkitty dads Editor: Vesa Lappalainen typistänyt malliohjelmista. PROJECT: dadtest.c, dadtest.def ALI\tabhand.c, ALI\optdlg.c, ALI\mdialog.c ALI\dropfile.c, ALI\clipboard.c, ALI\mjonot.c, ALI\dropfile.rc *****************************************************************************/ #include <windows.h> #include <windowsx.h> #include <string.h> #include <shellapi.h> /* Drag and Drop, ks. BC esx. DragDrop */ /* dadc */ #include "dropfile.h" /* dads */ #include "tabhand.h" #define ID_LISTBOX 100 #define MAX_FILES 20 static char FileStr[MAX_FILES+1][40] = { "Vedä tiedostoja FileManagerista tai", "WinCD:stä ja pudota tähän.", "Maalaa sitten haluamasi ja vedä", "ne vaikka WinCDhen!", "Oikea näppäin antaa valita dialogista", "ja keskinäppäin tyhjentää näytön", ""}; /****************************************************************************/ TblClassSWindowMAIN("DADWClass",NULL,"Drag and Drop",MsgTbl,0); /****************************************************************************/ /****************************************************************************/ int TakeFiles(HDROP hDrop) { int i,n = min(DragQueryFile(hDrop,-1,NULL,0),MAX_FILES); /* Nimien määrä */ for (i=0; i<n; i++) { DragQueryFile(hDrop,i,FileStr[i],sizeof(FileStr[i])); /* i:s nimi */ } FileStr[n][0]=0; DragFinish(hDrop); /* Muistilohkon vapautus! */ return n; } /****************************************************************************/ /* Viestien käsittely: */ /****************************************************************************/ static EVENT WM_create(tMSGParam *msg) { HWND lhWnd = CreateWindow("Listbox","Kissa", WS_BORDER | LBS_MULTIPLESEL | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |LBS_EXTENDEDSEL , 10,10,280,250,msg->hWnd,(HMENU)ID_LISTBOX, GetWindowInstance(msg->hWnd),NULL); MoveWindow(msg->hWnd,10,10,320,400,FALSE); SET_NOTIFYDRAG(lhWnd); /* dads */ DragAcceptFiles(msg->hWnd,TRUE); /* dadc */ return 0; } static EVENT WM_paint(tMSGParam *msg) /* # MAKE_DC # */ { int i,y = 250; int dy = HIWORD(GetTextExtent(msg->hDC,"X",1)); for (i=0; FileStr[i][0]; i++) TextOut(msg->hDC, 10, y+=dy, FileStr[i],strlen(FileStr[i])); return 0; } static EVENT WM_dropfiles(tMSGParam *msg) /* dadc */ { #if 0 /* Laita tähän 1,jos haluat tiedostojen nimet itse ikkunaan */ TakeFiles((HDROP)msg->wParam); /* Oma funktio nimien ottamiseksi */ InvalidateRect(msg->hWnd,NULL,TRUE); /* Huono tapa,mutta sopii lyhyeen mal*/ #else DropFilesToListBox((HDROP)msg->wParam,msg->hWnd,100,0); #endif return 0; } static EVENT WM_mbuttondown(tMSGParam *msg) { (void)ListBox_ResetContent(GetDlgItem(msg->hWnd,ID_LISTBOX)); return 0; } static EVENT WM_lbuttondown(tMSGParam *msg) /* dads */ { DropListBoxFiles(msg->hWnd,ID_LISTBOX,""); return 0; } static EVENT WM_rbuttondown(tMSGParam *msg) /* dads */ { DropSelectFiles(msg->hWnd); return 0; } static EVENT WM_destroy(tMSGParam *msg) { DragAcceptFiles(msg->hWnd,FALSE); /* dadc */ PostQuitMessage(0); return 0; } /****************************************************************************/ /* Viestien käsittelytaulukko */ /****************************************************************************/ tMSGEntry MsgTbl[] = { EV_HANDLE_WM_DESTROY, { WM_BEGINDRAG , DoC , DoC , WM_lbuttondown }, /* dads */ { WM_CREATE , DoC , DoC , WM_create }, /*a*/ { WM_DROPFILES , DoC , DoC , WM_dropfiles }, /*a*/ { WM_PAINT , DoC , DoC , WM_paint, MAKE_DC }, /*a*/ { WM_MBUTTONDOWN , DoC , DoC , WM_mbuttondown }, /*a*/ { WM_LBUTTONDOWN , DoC , DoC , WM_lbuttondown }, /*a*/ { WM_RBUTTONDOWN , DoC , DoC , WM_rbuttondown }, /*a*/ { WM_DESTROY , DoC , DoC , WM_destroy }, /*a*/ { 0 } }; /****************************************************************************/
Huomattakoon, että tiedostojen nimet saadaan täydellisenä polkuna, joten niille on helppo tehdä haluttu operaatio, esimerkiksi tulostaa, tuhota, kopioida tai siirtää. Eli edellisestä mallista olisi saatu roskakori, jos se olisi pistetty näkymään aina ikonina ja kun nimet on noudettu, olisi kaikki tiedostot tuhottu.