使用動態數據交換

本節有下列工作的程序代碼範例:

起始交談

若要起始動態數據交換 (DDE) 交談,用戶端會傳送 WM_DDE_INITIATE 訊息。 用戶端通常會藉由呼叫 SendMessage 來廣播此訊息,並將 –1 當做第一個參數。 如果應用程式已經有伺服器應用程式的視窗句柄,它可以將訊息直接傳送至該視窗。 用戶端會呼叫 GlobalAddAtom 來準備應用程式名稱和主題名稱的 Atom。 用戶端可以藉由為應用程式和主題提供 NULL (通配符)Atom,以要求與任何潛在伺服器應用程式和任何潛在主題的交談。

下列範例說明用戶端如何起始交談,其中會同時指定應用程式和主題。

    static BOOL fInInitiate = FALSE; 
    char *szApplication; 
    char *szTopic; 
    atomApplication = *szApplication == 0 ? 
    NULL     : GlobalAddAtom((LPSTR) szApplication); 
    atomTopic = *szTopic == 0 ? 
    NULL     : GlobalAddAtom((LPSTR) szTopic); 
 
    fInInitiate = TRUE; 
    SendMessage((HWND) HWND_BROADCAST, // broadcasts message 
        WM_DDE_INITIATE,               // initiates conversation 
        (WPARAM) hwndClientDDE,        // handle to client DDE window 
        MAKELONG(atomApplication,      // application-name atom 
            atomTopic));               // topic-name atom 
    fInInitiate = FALSE; 
    if (atomApplication != NULL) 
        GlobalDeleteAtom(atomApplication); 
    if (atomTopic != NULL) 
        GlobalDeleteAtom(atomTopic);

注意

如果您的應用程式使用 NULL Atom,則不需要使用 GlobalAddAtom GlobalDeleteAtom 函式。 在此範例中,用戶端應用程式會分別建立包含伺服器名稱和主題名稱的兩個全域 Atom。

 

用戶端應用程式會在訊息的 lParam 參數中傳送具有這兩個 atom 的WM_DDE_INITIATE訊息。 在對 SendMessage 函式的呼叫中,特殊視窗句柄 –1 會指示系統將此訊息傳送給所有其他使用中應用程式。 SendMessage 不會傳回用戶端應用程式,直到所有接收訊息的應用程式都已將控制權傳回給系統為止。 這表示伺服器應用程式在傳回 SendMessage 呼叫時,用戶端一定會處理在回復中傳送的所有WM_DDE_ACK訊息。

SendMessage 傳回之後,用戶端應用程式會刪除全域 Atom。

伺服器應用程式會根據下圖所示的邏輯回應。

server application response logic

若要認可一或多個主題,伺服器必須為每個交談建立 Atom(如果有多個主題,需要重複的應用程式名稱 atom),併為每個交談傳送 WM_DDE_ACK 訊息,如下列範例所示。

if ((atomApplication = GlobalAddAtom("Server")) != 0) 
{ 
    if ((atomTopic = GlobalAddAtom(szTopic)) != 0) 
    { 
        SendMessage(hwndClientDDE, 
            WM_DDE_ACK, 
            (WPARAM) hwndServerDDE, 
            MAKELONG(atomApplication, atomTopic)); 
        GlobalDeleteAtom(atomTopic); 
    } 
 
    GlobalDeleteAtom(atomApplication); 
} 
 
if ((atomApplication == 0) || (atomTopic == 0)) 
{ 
    // Handle errors. 
}

當伺服器以 WM_DDE_ACK 訊息回應時,用戶端應用程式應該將句柄儲存至伺服器視窗。 接收句柄做為WM_DDE_ACK訊息 wParam 參數的用戶端,然後將所有後續的 DDE 訊息傳送至此句柄所識別的伺服器視窗。

如果您的用戶端應用程式針對應用程式名稱或主題名稱使用 NULL Atom,則預期應用程式會收到來自多個伺服器應用程式的通知。 即使用戶端應用程式不使用 NULL ,多個認可也可以來自 DDE 伺服器的多個實例。 伺服器應該一律針對每個交談使用唯一的視窗。 用戶端應用程式中的視窗程式可以使用伺服器視窗的句柄(以 WM_DDE_INITIATE 的 lParam 參數提供)來追蹤多個交談。 這可讓單一用戶端窗口處理數個交談,而不需要終止並重新連接每個交談的新客戶端視窗。

傳送單一專案

建立 DDE 交談之後,用戶端可以藉由發出WM_DDE_REQUEST訊息,從伺服器擷取數據項的值,或發出WM_DDE_POKE,將數據項值提交至伺服器。

從伺服器擷取專案

若要從伺服器擷取專案,用戶端會傳送 WM_DDE_REQUEST 訊息,指定要擷取的專案和格式,如下列範例所示。

if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!PostMessage(hwndServerDDE, 
            WM_DDE_REQUEST, 
            (WPARAM) hwndClientDDE, 
            PackDDElParam(WM_DDE_REQUEST, CF_TEXT, atomItem))) 
    {
        GlobalDeleteAtom(atomItem); 
    }
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
}

在此範例中,用戶端會將剪貼簿格式指定為所要求數據項的慣用格式 CF_TEXT

WM_DDE_REQUEST訊息的接收者(伺服器)通常必須刪除專案 Atom,但如果 PostMessage 呼叫失敗,客戶端必須刪除 Atom。

如果伺服器可以存取要求的專案,而且可以使用要求的格式轉譯它,伺服器會將專案值複製為共用記憶體物件,並傳送WM_DDE_DATA訊息給用戶端,如下列範例所示。

// Allocate the size of the DDE data header, plus the data: a 
// string,<CR><LF><NULL>. The byte for the string's terminating 
// null character is counted by DDEDATA.Value[1].

size_t* pcch;
HRESULT hResult;
 
hResult = StringCchLength(szItemValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
if (!(hData = GlobalAlloc(GMEM_MOVEABLE,
  (LONG) sizeof(DDEDATA) + *pcch + 2)))  
{
    return; 
}
 
if (!(lpData = (DDEDATA FAR*) GlobalLock(hData)))  
{
    GlobalFree(hData); 
    return; 
} 
 
lpData->cfFormat = CF_TEXT;
hResult = StringCchCopy((LPSTR) lpData->Value, *pcch +1, (LPCSTR) szItemValue); // copies value to be sent
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
 
// Each line of CF_TEXT data is terminated by CR/LF. 
hResult = StringCchCat((LPSTR) lpData->Value, *pcch + 3, (LPCSTR) "\r\n");
if (FAILED(hResult)
{
// TODO: Write error handler.
 return;
} 
GlobalUnlock(hData); 
if ((atomItem = GlobalAddAtom((LPSTR) szItemName)) != 0) 
{ 
    lParam = PackDDElParam(WM_DDE_ACK, (UINT) hData, atomItem); 
    if (!PostMessage(hwndClientDDE, 
            WM_DDE_DATA, 
            (WPARAM) hwndServerDDE, 
            lParam)) 
    { 
        GlobalFree(hData); 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_ACK, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors.  
}

在此範例中,伺服器應用程式會配置記憶體物件來包含數據項。 數據物件會初始化為 DDEDATA 結構。

然後,伺服器應用程式會將 結構的 cfFormat 成員設定為 CF_TEXT,以通知用戶端應用程式數據為文字格式。 用戶端會藉由將要求之數據的值複製到 DDEDATA 結構的 Value 成員來回應。 在伺服器填入數據對象之後,伺服器會解除鎖定數據,並建立包含數據項名稱的全域 Atom。

最後,伺服器會呼叫 PostMessage發出WM_DDE_DATA訊息。 數據物件的句柄和包含項目名稱的 Atom 會由 PackDDElParam 函式封裝到訊息的 lParam 參數中。

如果 PostMessage 失敗,伺服器必須使用 FreeDDElParam 函式來釋放已封裝的 lParam 參數。 伺服器也必須為其收到的WM_DDE_REQUEST訊息釋放已封裝的 lParam 參數。

如果伺服器無法滿足要求,它會將負 WM_DDE_ACK 訊息傳送給用戶端,如下列範例所示。

// Negative acknowledgment. 
 
PostMessage(hwndClientDDE, 
    WM_DDE_ACK, 
    (WPARAM) hwndServerDDE, 
    PackDDElParam(WM_DDE_ACK, 0, atomItem));

收到 WM_DDE_DATA 訊息時,用戶端會視需要處理數據項值。 然後,如果WM_DDE_DATA訊息中指向的 fAckReq 成員為 1,客戶端必須將正WM_DDE_ACK訊息傳送給伺服器,如下列範例所示。

UnpackDDElParam(WM_DDE_DATA, lParam, (PUINT) &hData, 
    (PUINT) &atomItem); 
if (!(lpDDEData = (DDEDATA FAR*) GlobalLock(hData)) 
        || (lpDDEData->cfFormat != CF_TEXT)) 
{ 
    PostMessage(hwndServerDDE, 
        WM_DDE_ACK, 
        (WPARAM) hwndClientDDE, 
        PackDDElParam(WM_DDE_ACK, 0, atomItem)); // Negative ACK. 
} 
 
// Copy data from lpDDEData here. 
 
if (lpDDEData->fAckReq) 
{ 
    PostMessage(hwndServerDDE, 
        WM_DDE_ACK, 
        (WPARAM) hwndClientDDE, 
        PackDDElParam(WM_DDE_ACK, 0x8000, 
            atomItem)); // Positive ACK 
} 
 
bRelease = lpDDEData->fRelease; 
GlobalUnlock(hData); 
if (bRelease) 
    GlobalFree(hData);

在此範例中,用戶端會檢查數據的格式。 如果格式未CF_TEXT(或用戶端無法鎖定數據的記憶體),用戶端會傳送負WM_DDE_ACK訊息,表示它無法處理數據。 如果客戶端因為句柄包含 fAckReq 成員而無法鎖定數據句柄,則客戶端不應該傳送負 WM_DDE_ACK 訊息。 相反地,客戶端應該終止交談。

如果客戶端傳送負通知以回應WM_DDE_DATA訊息,伺服器會負責釋放與負通知相關聯的WM_DDE_DATA訊息所參考的記憶體(但不是 lParam 參數)。

如果它可以處理數據,用戶端會檢查 DDEDATA 結構的 fAckReq 成員,以判斷伺服器是否要求該伺服器收到並成功處理數據。 如果伺服器確實要求這項資訊,用戶端會將正 WM_DDE_ACK 訊息傳送給伺服器。

因為解除鎖定數據會使數據的指標失效,用戶端會在解除鎖定數據物件之前儲存 fRelease 成員的值。 儲存值之後,用戶端會檢查它,以判斷伺服器應用程式是否要求客戶端釋放包含數據的記憶體;用戶端會據以採取動作。

收到負 WM_DDE_ACK 訊息時,用戶端可以再次要求相同的專案值,並指定不同的剪貼簿格式。 一般而言,用戶端會先要求其可支援的最複雜格式,然後在必要時透過較簡單的格式逐步關閉,直到找到伺服器可以提供的格式為止。

如果伺服器支援系統主題的 Formats 專案,用戶端可以判斷伺服器所支援的剪貼簿格式,而不是每次用戶端要求專案時決定它們。

將專案提交至伺服器

用戶端可以使用WM_DDE_POKE訊息,將專案值傳送至伺服器。 用戶端會轉譯要傳送並傳送WM_DDE_POKE訊息的專案,如下列範例所示。

size_t* pcch;
HRESULT hResult;
 
hResult = StringCchLength(szValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
if (!(hPokeData = GlobalAlloc(GMEM_MOVEABLE,
  (LONG) sizeof(DDEPOKE) + *pcch + 2))) 
{
    return; 
}
 
if (!(lpPokeData = (DDEPOKE *) GlobalLock(hPokeData))) 
{ 
    GlobalFree(hPokeData); 
    return; 
} 
 
lpPokeData->fRelease = TRUE; 
lpPokeData->cfFormat = CF_TEXT;
hResult = StringCchCopy((LPSTR) lpPokeData->Value, *pcch +1, (LPCSTR) szValue); // copies value to be sent
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}  
 
// Each line of CF_TEXT data is terminated by CR/LF. 
hResult = StringCchCat((LPSTR) lpPokeData->Value, *pcch + 3, (LPCSTR) "\r\n");
if (FAILED(hResult)
{
// TODO: Write error handler.
 return;
}
GlobalUnlock(hPokeData); 
if ((atomItem = GlobalAddAtom((LPSTR) szItem)) != 0) 
{ 
 
        if (!PostMessage(hwndServerDDE, 
                WM_DDE_POKE, 
                (WPARAM) hwndClientDDE, 
                PackDDElParam(WM_DDE_POKE, (UINT) hPokeData, 
                    atomItem))) 
        { 
            GlobalDeleteAtom(atomItem); 
            GlobalFree(hPokeData); 
        } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
} 

注意

使用WM_DDE_POKE訊息傳送數據基本上與使用 WM_DDE_DATA 傳送數據相同,不同之處在於WM_DDE_POKE會從用戶端傳送至伺服器。

 

如果伺服器能夠接受用戶端轉譯格式的數據項值,伺服器會適當地處理專案值,並將正 WM_DDE_ACK 訊息傳送給用戶端。 如果因為專案格式或其他原因而無法處理專案值,則伺服器會傳送負 WM_DDE_ACK 訊息給用戶端。

UnpackDDElParam(WM_DDE_POKE, lParam, (PUINT) &hPokeData, 
    (PUINT) &atomItem); 
GlobalGetAtomName(atomItem, szItemName, ITEM_NAME_MAX_SIZE); 
if (!(lpPokeData = (DDEPOKE *) GlobalLock(hPokeData)) 
        || lpPokeData->cfFormat != CF_TEXT 
        || !IsItemSupportedByServer(szItemName)) 
{ 
    PostMessage(hwndClientDDE, 
        WM_DDE_ACK, 
        (WPARAM) hwndServerDDE, 
        PackDDElParam(WM_DDE_ACK, 0, atomItem)); // negative ACK  
}
hResult = StringCchLength(szItemValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
} 
hResult = StringCchCopy(szItemValue, *pcch +1, lpPokeData->Value); // copies value 
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}  
bRelease = lpPokeData->fRelease; 
GlobalUnlock(hPokeData); 
if (bRelease) 
{ 
    GlobalFree(hPokeData); 
} 
 
PostMessage(hwndClientDDE, 
    WM_DDE_ACK, 
    (WPARAM) hwndServerDDE, 
    PackDDElParam(WM_DDE_ACK, 
         0x8000, atomItem));    // positive ACK.

在此範例中,伺服器會呼叫 GlobalGetAtomName 來擷取用戶端所傳送項目的名稱。 然後,伺服器會判斷它是否支持專案,以及專案是否以正確的格式轉譯(也就是CF_TEXT)。 如果專案不受支援且未以正確的格式轉譯,或伺服器無法鎖定數據的記憶體,伺服器會將負通知傳回用戶端應用程式。 請注意,在此情況下,傳送負通知是正確的,因為 WM_DDE_POKE 訊息一律假設已 設定 fAckReq 成員。 伺服器應該忽略成員。

如果伺服器傳送負通知以回應WM_DDE_POKE訊息,用戶端會負責釋放與負通知相關聯的WM_DDE_POKE訊息所參考的記憶體(但不是 lParam 參數)。

用戶端應用程式可以使用 DDE 來建立伺服器應用程式中項目的連結。 建立這類連結之後,伺服器通常會在專案值變更時,將連結專案的定期更新傳送給用戶端。 因此,會在兩個應用程式之間建立永久數據流;此數據流會維持原狀,直到明確中斷連線為止。

用戶端會藉由張貼 WM_DDE_ADVISE 訊息來起始數據連結,如下列範例所示。

if (!(hOptions = GlobalAlloc(GMEM_MOVEABLE, 
        sizeof(DDEADVISE)))) 
    return; 
if (!(lpOptions = (DDEADVISE FAR*) GlobalLock(hOptions))) 
{ 
    GlobalFree(hOptions); 
    return; 
} 
 
lpOptions->cfFormat = CF_TEXT; 
lpOptions->fAckReq = TRUE; 
lpOptions->fDeferUpd = FALSE; 
GlobalUnlock(hOptions); 
if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!(PostMessage(hwndServerDDE, 
            WM_DDE_ADVISE, 
            (WPARAM) hwndClientDDE, 
            PackDDElParam(WM_DDE_ADVISE, (UINT) hOptions, 
                atomItem)))) 
    { 
        GlobalDeleteAtom(atomItem); 
        GlobalFree(hOptions); 
        FreeDDElParam(WM_DDE_ADVISE, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors 
 
}

在此範例中,用戶端應用程式會將WM_DDE_ADVISE訊息的 fDeferUpd 旗標設定FALSE。 這會指示伺服器應用程式在資料變更時將數據傳送至用戶端。

如果伺服器無法服務WM_DDE_ADVISE要求,則會將負WM_DDE_ACK訊息傳送給用戶端。 但是,如果伺服器具有專案的存取權,而且可以使用要求的格式轉譯它,伺服器會記下新的連結(回想 hOptions 參數中指定的旗標),並將正WM_DDE_ACK訊息傳送給用戶端。 從此為止,在用戶端發出相符 WM_DDE_UNADVISE 訊息之前,伺服器會在每次伺服器中的專案值變更時,將新數據傳送至用戶端。

WM_DDE_ADVISE訊息會建立連結期間要交換的數據格式。 如果客戶端嘗試使用相同的專案建立另一個連結,但使用不同的數據格式,伺服器可以選擇拒絕第二個數據格式或嘗試支援它。 如果已針對任何數據項建立暖鏈接,伺服器一次只能支援一個數據格式。 這是因為暖連結的WM_DDE_DATA訊息具有 NULL 數據句柄,否則會包含格式資訊。 因此,伺服器必須拒絕已連結之專案的所有暖連結,而且必須拒絕具有暖連結之專案的所有連結。 另一個解譯可能是當要求相同數據項的第二個連結時,伺服器會變更連結的格式和經常性或暖狀態。

一般而言,用戶端應用程式一次不應該嘗試建立一個以上的數據項連結。

支援經常性或暖數據連結的應用程式通常支援名為Link的已註冊剪貼簿格式。 當與應用程式的 [複製和貼上連結] 命令相關聯時,此剪貼簿格式可讓使用者建立應用程式之間的 DDE 交談,只要將伺服器應用程式中的數據項複製並貼入用戶端應用程式即可。

伺服器應用程式支援連結剪貼簿格式,方法是在使用者從 [編輯] 功能表選擇 [複製] 命令時,將包含應用程式、主題和專案名稱的字串放在剪貼簿中。 以下是標準連結格式:

application**\0topic\0item\0\0**

單一 Null 字元會分隔名稱,而兩個 Null 字元會終止整個字串。

用戶端和伺服器應用程式都必須註冊連結剪貼簿格式,如下所示:

cfLink = RegisterClipboardFormat("Link");

用戶端應用程式透過 [編輯] 功能表上的 [貼上連結] 命令,支援連結剪貼簿格式。 當使用者選擇此命令時,用戶端應用程式會剖析連結格式剪貼簿數據中的應用程式、主題和專案名稱。 使用這些名稱,如果這類交談不存在,用戶端應用程式會起始應用程式和主題的交談。 用戶端應用程式接著會將WM_DDE_ADVISE訊息傳送至伺服器應用程式,並指定連結格式剪貼簿數據中包含的項目名稱。

以下是當使用者選擇 [貼上連結] 命令時用戶端應用程式回應的範例。

void DoPasteLink(hwndClientDDE) 
HWND hwndClientDDE; 
{ 
    HANDLE hData; 
    LPSTR lpData; 
    HWND hwndServerDDE; 
    CHAR szApplication[APP_MAX_SIZE + 1]; 
    CHAR szTopic[TOPIC_MAX_SIZE + 1]; 
    CHAR szItem[ITEM_MAX_SIZE + 1]; 
    size_t * nBufLen; 
 HRESULT hResult;
 
    if (OpenClipboard(hwndClientDDE)) 
    { 
        if (!(hData = GetClipboardData(cfLink)) || 
                !(lpData = GlobalLock(hData))) 
        { 
            CloseClipboard(); 
            return; 
        } 
 
        // Parse the clipboard data.
  hResult = StringCchLength(lpData, STRSAFE_MAX_CCH, nBufLen);
 if (FAILED(hResult) || nBufLen == NULL)
 {
// TODO: Write error handler.
  return;
 }
 if (*nBufLen >= APP_MAX_SIZE)
        { 
            CloseClipboard(); 
            GlobalUnlock(hData); 
            return; 
        }
 hResult = StringCchCopy(szApplication, APP_MAX_SIZE +1, lpData);
 if (FAILED(hResult)
 {
// TODO: Write error handler.
  return;
 }
        lpData += (*nBufLen + 1); // skips over null
 hResult = StringCchLength(lpData, STRSAFE_MAX_CCH, nBufLen);
 if (FAILED(hResult) || nBufLen == NULL)
 {
 // TODO: Write error handler.
  return;
 }
 if (*nBufLen >= TOPIC_MAX_SIZE)
        { 
            CloseClipboard(); 
            GlobalUnlock(hData); 
            return; 
        }
 hResult = StringCchCopy(szTopic, TOPIC_MAX_SIZE +1, lpData);
 if (FAILED(hResult)
 {
 // TODO: Write error handler.
  return;
 }
        lpData += (nBufLen + 1); // skips over null
 hResult = StringCchLength(lpData, STRSAFE_MAX_CCH, nBufLen);
 if (FAILED(hResult) || nBufLen == NULL)
 {
 // TODO: Write error handler.
  return;
 }
 if (*nBufLen >= ITEM_MAX_SIZE)
        { 
            CloseClipboard(); 
            GlobalUnlock(hData); 
            return; 
        }

 hResult = StringCchCopy(szItem, ITEM_MAX_SIZE +1, lpData);
 if (FAILED(hResult)
 {
 // TODO: Write error handler.
  return;
 } 
        GlobalUnlock(hData); 
        CloseClipboard(); 
 
        if (hwndServerDDE = 
                FindServerGivenAppTopic(szApplication, szTopic)) 
        { 
            // App/topic conversation is already started. 
 
            if (DoesAdviseAlreadyExist(hwndServerDDE, szItem)) 
            {
                MessageBox(hwndMain, 
                    "Advisory already established", 
                    "Client", MB_ICONEXCLAMATION | MB_OK); 
            }
            else SendAdvise(hwndClientDDE, hwndServerDDE, szItem); 
        } 
        else 
        { 
            // Client must initiate a new conversation first. 
            SendInitiate(szApplication, szTopic); 
            if (hwndServerDDE = 
                    FindServerGivenAppTopic(szApplication, 
                        szTopic)) 
            {
                SendAdvise(hwndClientDDE, hwndServerDDE, szItem); 
            }
        } 
    } 
    return; 
}

在此範例中,用戶端應用程式會開啟剪貼簿,並判斷它是否包含先前註冊的連結格式 (亦即 cfLink) 中的數據。 如果沒有,或如果它無法鎖定剪貼簿中的數據,客戶端會傳回 。

用戶端應用程式擷取剪貼簿數據的指標之後,它會剖析數據以擷取應用程式、主題和項目名稱。

用戶端應用程式會判斷主題上的交談是否存在於該主題與伺服器應用程式之間。 如果交談確實存在,用戶端會檢查數據項的連結是否存在。 如果存在這類連結,用戶端就會向用戶顯示消息框;否則,它會呼叫它自己的 SendAdvise 函式,將WM_DDE_ADVISE訊息傳送至該專案的伺服器。

如果主題上的交談不存在於用戶端與伺服器之間,用戶端會先呼叫自己的 SendInitiate 函式來廣播WM_DDE_INITIATE訊息以要求交談,其次,呼叫自己的 FindServerGivenAppTopic 函式,以建立與代表伺服器應用程式回應之視窗的交談。 交談開始之後,用戶端應用程式會呼叫 SendAdvise 來要求連結。

通知客戶端數據已變更

當用戶端使用 WM_DDE_ADVISE 訊息建立連結時,未在 DDEDATA 結構中設定 fDeferUpd 成員(也就是等於零),用戶端會要求伺服器在每次專案的值變更時傳送數據項。 在這種情況下,伺服器會以先前指定的格式轉譯數據項的新值,並傳送WM_DDE_DATA訊息給用戶端,如下列範例所示。

// Allocate the size of a DDE data header, plus data (a string), 
// plus a <CR><LF><NULL> 

size_t* pcch;
HRESULT hResult;
 
hResult = StringCchLength(szItemValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
if (!(hData = GlobalAlloc(GMEM_MOVEABLE,
  sizeof(DDEDATA) + *pcch + 3))) 
{
    return; 
}
if (!(lpData = (DDEDATA FAR*) GlobalLock(hData))) 
{ 
    GlobalFree(hData); 
    return; 
} 
lpData->fAckReq = bAckRequest;       // as in original WM_DDE_ADVISE 
lpData->cfFormat = CF_TEXT;
hResult = StringCchCopy(lpData->Value, *pcch +1, szItemValue); // copies value to be sent
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
// add CR/LF for CF_TEXT format
hResult = StringCchCat(lpData->Value, *pcch + 3, "\r\n");
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
GlobalUnlock(hData); 
if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!PostMessage(hwndClientDDE, 
            WM_DDE_DATA, 
            (WPARAM) hwndServerDDE, 
            PackDDElParam(WM_DDE_DATA, (UINT) hData, atomItem))) 
    { 
        GlobalFree(hData); 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_DATA, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
 
}

在此範例中,用戶端會適當地處理專案值。 如果設定專案的 fAckReq 旗標,用戶端就會傳送正WM_DDE_ACK訊息給伺服器。

當用戶端建立連結時,使用 fDeferUpd 成員集 (即等於 1),用戶端已要求每次數據變更時,只會傳送通知,而不是數據本身。 在這種情況下,當專案值變更時,伺服器不會轉譯值,而只會傳送具有 Null 數據句柄的WM_DDE_DATA訊息給用戶端,如下列範例所示。

if (bDeferUpd)      // check whether flag was set in WM_DDE_ADVISE
{
    if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
    { 
        if (!PostMessage(hwndClientDDE, 
                WM_DDE_DATA, 
                (WPARAM) hwndServerDDE, 
                PackDDElParam(WM_DDE_DATA, 0, 
                    atomItem)))                  // NULL data
        {
            GlobalDeleteAtom(atomItem); 
            FreeDDElParam(WM_DDE_DATA, lParam); 
        } 
    } 
} 
 
if (atomItem == 0) 
{ 
     // Handle errors. 
} 

視需要,用戶端可以發出一般 WM_DDE_REQUEST 訊息來要求數據項的最新值,也可以直接忽略來自伺服器已變更數據的通知。 在任一情況下,如果 fAckReq 等於 1,客戶端應該會將正 WM_DDE_ACK 訊息傳送至伺服器。

如果用戶端要求終止特定數據連結,用戶端就會傳送WM_DDE_UNADVISE訊息給伺服器,如下列範例所示。

if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!PostMessage(hwndServerDDE, 
            WM_DDE_UNADVISE, 
            (WPARAM) hwndClientDDE, 
            PackDDElParam(WM_DDE_UNADVISE, 0, atomItem))) 
    { 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_UNADVISE, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
}

伺服器會檢查用戶端目前是否具有此交談中特定項目的連結。 如果連結存在,伺服器會傳送正 WM_DDE_ACK 訊息給用戶端;然後不再需要伺服器傳送有關專案的更新。 如果沒有連結,伺服器就會傳送負 WM_DDE_ACK 訊息給用戶端。

WM_DDE_UNADVISE訊息會指定數據格式。 零的格式會通知伺服器停止指定專案的所有連結,即使已建立數個經常性連結,而且每個連結都使用不同的格式。

若要終止交談的所有連結,用戶端應用程式會傳送具有 Null 專案 Atom 的WM_DDE_UNADVISE訊息給伺服器。 伺服器會判斷交談目前是否至少有一個連結已建立。 如果連結存在,伺服器會將正 WM_DDE_ACK 訊息傳送給用戶端;然後伺服器就不再需要在交談中傳送任何更新。 如果沒有連結,伺服器就會傳送負 WM_DDE_ACK 訊息給用戶端。

在伺服器應用程式中執行命令

應用程式可以使用 WM_DDE_EXECUTE 訊息,在另一個應用程式中執行特定命令或一系列命令。 若要這樣做,用戶端會將包含句柄的WM_DDE_EXECUTE訊息傳送給伺服器,如下列範例所示。

HRESULT hResult;
  
if (!(hCommand = GlobalAlloc(GMEM_MOVEABLE, 
        sizeof(szCommandString) + 1))) 
{
    return; 
}
if (!(lpCommand = GlobalLock(hCommand))) 
{ 
    GlobalFree(hCommand); 
    return; 
} 

hResult = StringCbCopy(lpCommand, sizeof(szCommandString), szCommandString);
if (hResult != S_OK)
{
// TODO: Write error handler.
 return;
}
 
GlobalUnlock(hCommand); 
if (!PostMessage(hwndServerDDE, 
        WM_DDE_EXECUTE, 
        (WPARAM) hwndClientDDE, 
        PackDDElParam(WM_DDE_EXECUTE, 0, (UINT) hCommand))) 
{ 
    GlobalFree(hCommand); 
    FreeDDElParam(WM_DDE_EXECUTE, lParam); 
}

在此範例中,伺服器會嘗試執行指定的命令字串。 如果成功,伺服器會將正 WM_DDE_ACK 訊息傳送給客戶端,否則會傳送負 WM_DDE_ACK 訊息。 此WM_DDE_ACK訊息會重複使用在原始WM_DDE_EXECUTE訊息中傳遞的 hCommand 句柄。

如果用戶端的命令執行字串要求伺服器終止,則伺服器應該藉由傳送正 WM_DDE_ACK 訊息來回應,然後在終止之前張貼 WM_DDE_TERMINATE 訊息。 以WM_DDE_EXECUTE訊息傳送的所有其他命令都應該同步執行;也就是說,伺服器應該只在成功完成命令之後才會傳送WM_DDE_ACK訊息。

終止交談

用戶端或伺服器都可以發出 WM_DDE_TERMINATE 訊息,隨時終止交談。 同樣地,客戶端和伺服器應用程式都應該準備好隨時接收此訊息。 應用程式必須在關閉之前終止其所有交談。

在下列範例中,終止交談的應用程式會張貼 WM_DDE_TERMINATE 訊息。

PostMessage(hwndServerDDE, WM_DDE_TERMINATE, 
    (WPARAM) hwndClientDDE, 0);

這會通知另一個應用程式,傳送應用程式不會再傳送任何訊息,而收件者可以關閉其視窗。 在所有情況下,收件者都會透過傳送 WM_DDE_TERMINATE 訊息來立即回應。 收件者不得傳送負面、忙碌或正 WM_DDE_ACK 訊息。

在應用程式在 DDE 交談中將WM_DDE_TERMINATE訊息傳送給合作夥伴之後,它不得響應該夥伴的訊息,因為夥伴可能終結了傳送回應的視窗。

如果應用程式在張貼WM_DDE_TERMINATE之後收到WM_DDE_TERMINATE以外的 DDE 訊息,它應該釋放與已接收訊息相關聯的所有物件,但未設定 fRelease 成員之WM_DDE_DATAWM_DDE_POKE訊息的數據句柄除外。

當應用程式即將終止時,它應該會在完成處理WM_DESTROY訊息之前結束所有作用中的 DDE 交談。 不過,如果應用程式未結束其作用中的 DDE 交談,當窗口終結時,系統會終止與視窗相關聯的任何 DDE 交談。 下列範例示範伺服器應用程式如何終止所有 DDE 交談。

void TerminateConversations(hwndServerDDE) 
HWND hwndServerDDE; 
{ 
    HWND hwndClientDDE; 
 
    // Terminate each active conversation. 
 
    while (hwndClientDDE = GetNextLink(hwndClientDDE)) 
    { 
        SendTerminate(hwndServerDDE, hwndClientDDE); 
    } 
    return; 
} 
 
BOOL AtLeastOneLinkActive(VOID) 
{ 
    return TRUE; 
} 
 
HWND GetNextLink(hwndDummy) 
    HWND hwndDummy; 
{ 
    return (HWND) 1; 
} 
 
VOID SendTerminate(HWND hwndServerDDE, HWND hwndClientDDE) 
{ 
    return; 
}