Udostępnij przez


Podstawowe pojęcia (DDE)

Te pojęcia są kluczem do zrozumienia dynamicznej wymiany danych (DDE) i dynamicznej biblioteki zarządzania wymianą danych (DDEML).

Interakcja klienta i serwera

Funkcja DDE zawsze występuje między aplikacją kliencką a aplikacją serwera. Aplikacja kliencka DDE inicjuje wymianę przez nawiązanie rozmowy z serwerem w celu wysyłania transakcji na serwer. Transakcja jest żądaniem danych lub usług. Aplikacja serwera DDE odpowiada na transakcje, dostarczając dane lub usługi klientowi. Na przykład aplikacja graficzna może zawierać wykres słupkowy reprezentujący kwartalne zyski korporacji, ale dane wykresu słupkowego mogą być zawarte w aplikacji arkusza kalkulacyjnego. Aby uzyskać najnowsze dane zysku, aplikacja graficzna (klient) może nawiązać rozmowę z aplikacją arkusza kalkulacyjnego (serwer). Aplikacja graficzna może następnie wysłać transakcję do aplikacji arkusza kalkulacyjnego, żądając najnowszych danych zysku.

Serwer może mieć wielu klientów jednocześnie, a klient może żądać danych z wielu serwerów. Aplikacja może być również klientem i serwerem. Klient lub serwer mogą w dowolnym momencie zakończyć konwersację.

Transakcje i funkcja wywołania zwrotnego DDE

DDEML powiadamia aplikację o działaniu DDE, które wpływa na aplikację, wysyłając transakcje do funkcji wywołania zwrotnego DDE aplikacji. Transakcja DDE jest podobna do komunikatu, ponieważ jest to stała nazwana z innymi parametrami, które zawierają dodatkowe informacje o transakcji.

DDEML przekazuje transakcję do funkcji wywołania zwrotnego DDE zdefiniowanej przez aplikację, która wykonuje akcję odpowiednią dla typu transakcji. Na przykład gdy aplikacja kliencka próbuje nawiązać konwersację z aplikacją serwera, klient wywołuje funkcję DdeConnect. Ta funkcja powoduje, że DDEML wysyła transakcję XTYP_CONNECT do funkcji wywołania zwrotnego DDE serwera. Funkcja wywołania zwrotnego może zezwalać na konwersację, zwracając TRUE do DDEML, lub może odrzucać konwersację, zwracając FALSE. Aby zapoznać się ze szczegółowym omówieniem transakcji, zobacz Transaction Management.

Nazwy usług, nazwy tematów i nazwy elementów

Serwer DDE używa trzy-poziomowej nazwy usługi hierarchii (nazywanej "nazwą aplikacji" w poprzedniej dokumentacji DDE), nazwy tematu i nazwy elementu w celu unikatowego zidentyfikowania jednostki danych, które serwer może wymieniać podczas konwersacji.

Nazwa usługi to ciąg, na który aplikacja serwera reaguje, gdy klient próbuje nawiązać rozmowę z serwerem. Klient musi określić tę nazwę usługi, aby nawiązać konwersację z serwerem. Chociaż serwer może odpowiadać na wiele nazw usług, większość serwerów odpowiada tylko na jedną nazwę.

Nazwa tematu to ciąg znaków, który identyfikuje logiczny kontekst danych. W przypadku serwerów, które działają na dokumentach opartych na plikach, nazwy tematów są zazwyczaj nazwami plików; w przypadku innych serwerów są to inne ciągi specyficzne dla aplikacji. Klient musi podać nazwę tematu wraz z nazwą usługi serwera podczas próby nawiązania rozmowy z serwerem.

Nazwa elementu to ciąg identyfikujący jednostkę danych, którą serwer może przekazać do klienta podczas transakcji. Na przykład nazwa elementu może identyfikować liczbę całkowitą, ciąg, kilka akapitów tekstu lub mapę bitową.

Nazwy usług, tematów i elementów umożliwiają klientowi nawiązanie rozmowy z serwerem i odbieranie danych z serwera.

Temat systemowy

Temat System zawiera kontekst informacji o interesie ogólnym dla dowolnego klienta DDE. Zaleca się, aby aplikacje serwerowe obsługiwały cały czas temat System. Temat System jest zdefiniowany w pliku nagłówkowym DDEML.H jako SZDDESYS_TOPIC.

Aby określić, które serwery są obecne i jakie informacje mogą dostarczyć, aplikacja kliencka może zażądać konwersacji w temacie System po uruchomieniu, ustawiając nazwę urządzenia na wartość null. Takie konwersacje z symbolami wieloznacznymi są kosztowne pod względem wydajności systemu, więc powinny być utrzymywane do minimum. Aby uzyskać więcej informacji na temat inicjowania konwersacji DDE, zobacz Conversation Management.

Serwer musi obsługiwać następujące nazwy elementów w temacie System i wszelkie inne nazwy elementów, które są przydatne dla klienta.

Przedmiot Opis
SZDDE_ITEM_ITEMLIST Lista elementów obsługiwanych w temacie niesystemowym. (Ta lista może się różnić od chwili do chwili i od tematu do tematu).
SZDDESYS_ITEM_FORMATS Rozdzielana tabulatorami lista ciągów reprezentujących wszystkie formaty schowka potencjalnie obsługiwane przez aplikację usługi. Ciągi reprezentujące wstępnie zdefiniowane formaty schowka są równoważne wartościom CF_ z usuniętym prefiksem "CF_". Na przykład format CF_TEXT jest reprezentowany przez ciąg "TEXT". Te ciągi muszą być wielkimi literami, aby dodatkowo zidentyfikować je jako wstępnie zdefiniowane formaty. Lista formatów musi być wyświetlana w kolejności od najbogatszych w zawartość do najmniej bogatych w zawartość. Aby uzyskać więcej informacji na temat formatów schowka i renderowania danych, zobacz Schowek.
SZDDESYS_ITEM_HELP Informacje czytelne dla użytkownika ogólnego zainteresowania. Ten element musi zawierać co najmniej informacje dotyczące korzystania z funkcji DDE aplikacji serwera. Te informacje mogą obejmować, ale nie ograniczają się do, sposobów określania elementów w tematach, jakie ciągi serwer może wykonywać, jakie transakcje poke są dozwolone i jak znaleźć pomoc dotyczącą innych elementów tematu systemowego.
SZDDESYS_ITEM_RTNMSG Informacje pomocnicze dotyczące ostatnio używanego komunikatu WM_DDE_ACK. Ten element jest przydatny, gdy wymagane jest więcej niż 8 bitów danych zwracanych przez aplikację.
SZDDESYS_ITEM_STATUS Wskazanie bieżącego stanu serwera. Zazwyczaj ten element obsługuje tylko format CF_TEXT i zawiera ciąg Gotowy lub Zajęty.
SZDDESYS_ITEM_SYSITEMS Lista elementów obsługiwanych w temacie System na tym serwerze.
SZDDESYS_ITEM_TOPICS Lista tematów obsługiwanych przez serwer w bieżącej chwili. (Ta lista może się różnić od chwili do chwili).

Te nazwy elementów są wartościami zdefiniowanymi w pliku nagłówkowym DDEML.H. Aby uzyskać dojścia ciągów do tych ciągów, aplikacja musi używać funkcji zarządzania ciągami DDEML, podobnie jak w przypadku dowolnego innego ciągu w aplikacji DDEML. Aby uzyskać więcej informacji na temat zarządzania ciągami, zobacz String Management.

Inicjalizacja

Przed wywołaniem dowolnej innej funkcji DDEML aplikacja musi wywołać funkcję DdeInitialize. DdeInitialize uzyskuje identyfikator wystąpienia aplikacji, rejestruje funkcję wywołania zwrotnego aplikacji z DDE, i określa flagi filtru transakcji dla tej funkcji wywołania zwrotnego.

Każde wystąpienie aplikacji lub biblioteki DLL musi przekazać identyfikator wystąpienia jako parametr idInst do dowolnej innej funkcji DDEML, która jej wymaga. Celem wielu wystąpień DDEML jest obsługa bibliotek DLL, które muszą używać DDEML jednocześnie z aplikacją. Aplikacja nie może używać więcej niż jednego wystąpienia DDEML.

Filtry transakcji optymalizują wydajność systemu, zapobiegając przekazywaniu przez DDEML niechcianych transakcji do funkcji wywołania zwrotnego DDE aplikacji. Aplikacja ustawia filtry transakcji w parametrze DdeInitializeufCmd. Aplikacja musi określić flagę filtru transakcji dla każdego typu transakcji, która nie jest przetwarzana w funkcji wywołania zwrotnego. Aplikacja może zmienić filtry transakcji przy użyciu kolejnego wywołania metody DdeInitialize. Aby uzyskać więcej informacji na temat transakcji, zobacz Transaction Management.

W poniższym przykładzie pokazano, jak zainicjować aplikację do używania DDEML.

DWORD idInst = 0; 
HINSTANCE hinst; 
 
DdeInitialize(&idInst,         // receives instance identifier 
    (PFNCALLBACK) DdeCallback, // pointer to callback function 
    CBF_FAIL_EXECUTES |        // filter XTYPE_EXECUTE 
    CBF_SKIP_ALLNOTIFICATIONS, // filter notifications 
    0); 

Aplikacja musi wywołać funkcję DdeUninitialize, gdy nie będzie już używać DDEML. Ta funkcja kończy wszelkie konwersacje obecnie otwarte dla aplikacji i zwalnia zasoby DDEML przydzielone przez system dla aplikacji.

Funkcja wywołania zwrotnego

Aplikacja korzystająca z DDEML musi udostępnić funkcję wywołania zwrotnego, która przetwarza zdarzenia DDE wpływające na aplikację. DDEML powiadamia aplikację o takich zdarzeniach, wysyłając transakcje do funkcji wywołania zwrotnego DDE aplikacji. Transakcje odbierane przez funkcję wywołania zwrotnego zależą od tego, które flagi filtru wywołania zwrotnego zostały określone przez aplikację w DdeInitialize i czy aplikacja jest klientem, serwerem, czy zarówno jednym, jak i drugim. Aby uzyskać więcej informacji, zobacz DdeCallback.

W poniższym przykładzie przedstawiono ogólną strukturę funkcji wywołania zwrotnego dla typowej aplikacji klienckiej.

HDDEDATA CALLBACK DdeCallback(uType, uFmt, hconv, hsz1, 
    hsz2, hdata, dwData1, dwData2) 
UINT uType;       // transaction type 
UINT uFmt;        // clipboard data format 
HCONV hconv;      // handle to conversation 
HSZ hsz1;         // handle to string 
HSZ hsz2;         // handle to string 
HDDEDATA hdata;   // handle to global memory object 
DWORD dwData1;    // transaction-specific data 
DWORD dwData2;    // transaction-specific data 
{ 
    switch (uType) 
    { 
        case XTYP_REGISTER: 
        case XTYP_UNREGISTER: 
            . 
            . 
            . 
            return (HDDEDATA) NULL; 
 
        case XTYP_ADVDATA: 
            . 
            . 
            . 
            return (HDDEDATA) DDE_FACK; 
 
        case XTYP_XACT_COMPLETE: 
            
            // 
            
            return (HDDEDATA) NULL; 
 
        case XTYP_DISCONNECT: 
            
            // 
            
            return (HDDEDATA) NULL; 
 
        default: 
            return (HDDEDATA) NULL; 
    } 
} 

Parametr uType określa typ transakcji wysyłany do funkcji wywołania zwrotnego przez DDEML. Wartości pozostałych parametrów zależą od typu transakcji. Typy transakcji i zdarzenia, które je generują, zostały opisane w poniższych tematach. Aby uzyskać szczegółowe informacje o każdym typie transakcji, zobacz Transaction Management.

Zarządzanie ciągami

Aby wykonać zadanie DDE, wiele funkcji DDEML wymaga dostępu do ciągów. Na przykład klient musi określić nazwę usługi i nazwę tematu podczas wywoływania funkcji DdeConnect w celu żądania konwersacji z serwerem. Aplikacja określa ciąg, przekazując uchwyt ciągu (HSZ), a nie wskaźnik w funkcji DDEML. Identyfikator ciągu jest wartością DWORD przypisaną przez system, która identyfikuje ciąg.

Aplikacja może uzyskać dojście ciągu do określonego ciągu, wywołując funkcję DdeCreateStringHandle. Ta funkcja rejestruje ciąg w systemie i zwraca do aplikacji uchwyt ciągu. Aplikacja może przekazać dojście do funkcji DDEML, które muszą uzyskać dostęp do ciągu. Poniższy przykład uzyskuje dojścia ciągów do ciągu tematu systemowego i ciągu nazwy usługi.

HSZ hszServName; 
HSZ hszSysTopic; 
hszServName = DdeCreateStringHandle( 
    idInst,         // instance identifier 
    "MyServer",     // string to register 
    CP_WINANSI);    // Windows ANSI code page 
 
hszSysTopic = DdeCreateStringHandle( 
    idInst,         // instance identifier 
    SZDDESYS_TOPIC, // System topic 
    CP_WINANSI);    // Windows ANSI code page 
    

Parametr idInst w poprzednim przykładzie określa identyfikator wystąpienia uzyskany przez funkcję DdeInitialize.

Funkcja wywołania zwrotnego DDE aplikacji odbiera co najmniej jeden uchwyt ciągu podczas większości transakcji DDE. Na przykład serwer odbiera dwa dojścia ciągów podczas transakcji XTYP_REQUEST: jeden identyfikuje ciąg określający nazwę tematu, a drugi identyfikuje ciąg określający nazwę elementu. Aplikacja może uzyskać długość ciągu odpowiadającego uchwytowi ciągu i skopiować ciąg do bufora zdefiniowanego przez aplikację, wywołując funkcję DdeQueryString, jak pokazano w poniższym przykładzie.

DWORD idInst; 
DWORD cb; 
HSZ hszServ; 
PSTR pszServName; 
cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0, 
    CP_WINANSI) + 1; 
pszServName = (PSTR) LocalAlloc(LPTR, (UINT) cb); 
DdeQueryString(idInst, hszServ, pszServName, cb, CP_WINANSI); 

Nie można zamapować uchwytu ciągu specyficznego dla instancji z uchwytu ciągu na ciąg i z powrotem na uchwyt ciągu. Na przykład, mimo że DdeQueryString tworzy ciąg z uchwytu ciągu, a następnie DdeCreateStringHandle tworzy uchwyt ciągu z tego ciągu, dwa uchwyty nie są takie same, jak pokazano w poniższym przykładzie.

DWORD idInst; 
DWORD cb; 
HSZ hszInst, hszNew; 
PSZ pszInst; 
DdeQueryString(idInst, hszInst, pszInst, cb, CP_WINANSI); 
hszNew = DdeCreateStringHandle(idInst, pszInst, CP_WINANSI); 
// hszNew != hszInst ! 

Aby porównać wartości dwóch uchwytów ciągów, użyj funkcji DdeCmpStringHandles.

Wskaźnik do ciągu przekazywany do funkcji wywołania zwrotnego DDE aplikacji staje się nieprawidłowy, gdy funkcja ta zwraca. Aplikacja może przy użyciu funkcji DdeKeepStringHandle zapisać uchwyt ciągu do wykorzystania po zakończeniu funkcji wywołania zwrotnego.

Gdy aplikacja wywołuje DdeCreateStringHandle, system wprowadza określony ciąg w tabeli ciągów i generuje uchwyt używany do uzyskiwania dostępu do ciągu. System utrzymuje również liczbę użycia dla każdego ciągu w tabeli ciągów.

Gdy aplikacja wywołuje DdeCreateStringHandle i określa ciąg, który już istnieje w tabeli, system zwiększa liczbę użycia zamiast dodawać kolejne wystąpienie ciągu. (Aplikacja może również zwiększać licznik użycia przy użyciu DdeKeepStringHandle.) Gdy aplikacja wywołuje funkcję DdeFreeStringHandle, system dekrementuje licznik użycia.

Ciąg jest usuwany z tabeli, gdy jego liczba użycia jest równa zero. Ponieważ więcej niż jedna aplikacja może uzyskać uchwyt do określonego ciągu, aplikacja nie może zwolnić uchwytu ciągu więcej razy, niż go utworzyła lub zachowała. W przeciwnym razie aplikacja może spowodować usunięcie ciągu z tabeli, odmawiając innym aplikacjom dostępu do ciągu.

Funkcje zarządzania ciągami DDEML są oparte na menedżerze atomów i podlegają tym samym ograniczeniom rozmiaru, co atomy.

DDEML i wątki

Funkcja DdeInitialize rejestruje aplikację w DDEML, tworząc wystąpienie DDEML. Wystąpienie DDEML jest oparte na wątku powiązanym z wątkiem, który wywołał DdeInitialize.

Wszystkie wywołania funkcji DDEML dla obiektów należących do wystąpienia DDEML muszą zostać wykonane z tego samego wątku o nazwie DdeInitialize, aby utworzyć wystąpienie. Jeśli wywołasz funkcję DDEML z innego wątku, funkcja zakończy się niepowodzeniem. Nie można uzyskać dostępu do konwersacji DDEML z wątku innego niż ten, który przydzielił konwersację.