DDEMLCL.C
 
 
/******************************************************************************\ 
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright 1993 - 1998 Microsoft Corporation. 
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation. 
*       See these sources for detailed information regarding the 
*       Microsoft samples programs. 
\******************************************************************************/ 
 
/*************************************************************************** 
 *                                                                         * 
 *  PROGRAM     : client.c                                                 * 
 *                                                                         * 
 *  PURPOSE     : To demonstrate how to use the DDEML library from the     * 
 *                client side and for basic testing of the DDEML API.      * 
 *                                                                         * 
 ***************************************************************************/ 
 
#include "client.h" 
#include <string.h> 
#include <memory.h> 
#include "infoctrl.h" 
 
/* global variables used in this module or among more than one module */ 
CONVCONTEXT CCFilter = { sizeof(CONVCONTEXT), 0, 0, 0, 0L, 0L, 
    { 
        sizeof(SECURITY_QUALITY_OF_SERVICE), 
        SecurityImpersonation, 
        SECURITY_STATIC_TRACKING, 
        TRUE 
    } 
}; 
DWORD idInst = 0; 
HANDLE hInst;                       /* Program instance handle               */ 
HANDLE hAccel;                      /* Main accelerator resource             */ 
HWND hwndFrame           = NULL;    /* Handle to main window                 */ 
HWND hwndMDIClient       = NULL;    /* Handle to MDI client                  */ 
HWND hwndActive          = NULL;    /* Handle to currently activated child   */ 
LONG DefTimeout      = DEFTIMEOUT;  /* default synchronous transaction timeout */ 
DWORD wDelay = 0; 
BOOL fBlockNextCB = FALSE;     /* set if next callback causes a CBR_BLOCK    */ 
BOOL fTermNextCB = FALSE;      /* set to call DdeDisconnect() on next callback */ 
BOOL fAutoReconnect = FALSE;   /* set if DdeReconnect() is to be called on XTYP_DISCONNECT callbacks */ 
HDDEDATA hDataOwned = 0;       /* Current owned huge data handle             */ 
DWORD fmtLink = 0;                  /* link clipboard format number          */ 
DWORD DefOptions = 0;               /* default transaction optons            */ 
OWNED aOwned[MAX_OWNED];            /* list of all owned handles.            */ 
DWORD cOwned = 0;                   /* number of existing owned handles.     */ 
FARPROC lpMsgFilterProc;            /* instance proc from MSGF_DDEMGR filter */ 
HSZ hszHuge;                        /* used for checking huge item data */ 
HHOOK ghhk = 0; 
 
/* 
 * This is the array of formats we support 
 */ 
FORMATINFO aFormats[] = { 
#ifdef UNICODE 
    { CF_UNICODETEXT, TEXT("CF_UNICODETEXT") },       // exception!  predefined format 
#else 
    { CF_TEXT, TEXT("CF_TEXT") },       // exception!  predefined format 
#endif 
    { 0, TEXT("Dummy1")  }, 
    { 0, TEXT("Dummy2")  }, 
}; 
 
/* Forward declarations of helper functions in this module */ 
VOID NEAR PASCAL CloseAllChildren(VOID); 
VOID NEAR PASCAL InitializeMenu (HANDLE); 
VOID NEAR PASCAL CommandHandler (HWND,DWORD); 
VOID NEAR PASCAL SetWrap (HWND,BOOL); 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : WinMain(HANDLE, HANDLE, LPTSTR, int)                        * 
 *                                                                          * 
 *  PURPOSE    : Creates the "frame" window, does some initialization and   * 
 *               enters the message loop.                                   * 
 *                                                                          * 
 ****************************************************************************/ 
int WINAPI WinMain( 
HINSTANCE hInstance, 
HINSTANCE hPrevInstance, 
LPSTR  lpszCmdLine, 
INT    nCmdShow) 
{ 
    MSG msg; 
 
    hInst = hInstance; 
 
    /* If this is the first instance of the app. register window classes */ 
    if (!hPrevInstance){ 
        if (!InitializeApplication ()) 
            return 0; 
    } 
 
    /* Create the frame and do other initialization */ 
    if (!InitializeInstance(nCmdShow)) 
        return 0; 
 
    /* Enter main message loop */ 
    while (GetMessage (&msg, NULL, 0, 0)){ 
        ((HOOKPROC)*lpMsgFilterProc)(MSGF_DDEMGR, 0, (LONG)(LPMSG)&msg); 
    } 
 
    // free up any appowned handles 
    while (cOwned) { 
        DdeFreeDataHandle(aOwned[--cOwned].hData); 
    } 
    DdeFreeStringHandle(idInst, hszHuge); 
    DdeUninitialize(idInst); 
 
    UnhookWindowsHook(WH_MSGFILTER, (HOOKPROC)lpMsgFilterProc); 
    FreeProcInstance(lpMsgFilterProc); 
 
    return 0; 
} 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : FrameWndProc (hwnd, msg, wParam, lParam )                  * 
 *                                                                          * 
 *  PURPOSE    : The window function for the "frame" window, which controls * 
 *               the menu and encompasses all the MDI child windows. Does   * 
 *               the major part of the message processing. Specifically, in * 
 *               response to:                                               * 
 *                                                                          * 
 ****************************************************************************/ 
LONG  APIENTRY FrameWndProc ( hwnd, msg, wParam, lParam ) 
HWND   hwnd; 
UINT   msg; 
WPARAM wParam; 
LPARAM lParam; 
 
{ 
    switch (msg){ 
        case WM_CREATE:{ 
            CLIENTCREATESTRUCT ccs; 
 
            /* Find window menu where children will be listed */ 
            ccs.hWindowMenu = GetSubMenu (GetMenu(hwnd),WINDOWMENU); 
            ccs.idFirstChild = IDM_WINDOWCHILD; 
 
            /* Create the MDI client filling the client area */ 
            hwndMDIClient = CreateWindow (TEXT("mdiclient"), 
                                          NULL, 
                                          WS_CHILD | WS_CLIPCHILDREN | 
                                          WS_VSCROLL | WS_HSCROLL, 
                                          0, 
                                          0, 
                                          0, 
                                          0, 
                                          hwnd, 
                                          (HMENU)0xCAC, 
                                          hInst, 
                                          (LPTSTR)&ccs); 
 
 
            ShowWindow (hwndMDIClient,SW_SHOW); 
            break; 
        } 
 
        case WM_INITMENU: 
            InitializeMenu ((HMENU)wParam); 
            break; 
 
        case WM_COMMAND: 
            CommandHandler (hwnd, LOWORD(wParam)); 
            break; 
 
        case WM_CLOSE: 
            CloseAllChildren(); 
            DestroyWindow(hwnd); 
            break; 
 
        case WM_DESTROY: 
            PostQuitMessage(0); 
            break; 
 
        default: 
            /*  use DefFrameProc() instead of DefWindowProc() since there 
             *  are things that have to be handled differently because of MDI 
             */ 
            return DefFrameProc (hwnd,hwndMDIClient,msg,wParam,lParam); 
    } 
    return 0; 
} 
 
 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : MDIChildWndProc                                            * 
 *                                                                          * 
 *  PURPOSE    : The window function for the "child" conversation and list  * 
 *               windows.                                                   * 
 *                                                                          * 
 ****************************************************************************/ 
LONG  APIENTRY MDIChildWndProc( hwnd, msg, wParam, lParam ) 
HWND   hwnd; 
UINT   msg; 
WPARAM wParam; 
LPARAM lParam; 
{ 
    MYCONVINFO *pmci; 
    RECT rc; 
 
    switch (msg){ 
    case WM_CREATE: 
        /* 
         * Create a coresponding conversation info structure to link this 
         * window to the conversation or conversation list it represents. 
         * 
         * lParam: points to the conversation info to initialize our copy to. 
         */ 
        pmci = (MYCONVINFO *)MyAlloc(sizeof(MYCONVINFO)); 
        if (pmci != NULL) { 
            memcpy(pmci, 
                    (LPTSTR)((LPMDICREATESTRUCT)((LPCREATESTRUCT)lParam)->lpCreateParams)->lParam, 
                    sizeof(MYCONVINFO)); 
            pmci->hwndXaction = 0;              /* no current transaction yet */ 
            pmci->x = pmci->y = 0;              /* new transaction windows start here */ 
            DdeKeepStringHandle(idInst, pmci->hszTopic);/* keep copies of the hszs for us */ 
            DdeKeepStringHandle(idInst, pmci->hszApp); 
 
             // link hConv and hwnd together 
            SetWindowLong(hwnd, 0, (DWORD)pmci); 
 
            /* 
             * non-list windows link the conversations to the windows via the 
             * conversation user handle. 
             */ 
            if (!pmci->fList) 
                DdeSetUserHandle(pmci->hConv, QID_SYNC, (DWORD)hwnd); 
        } 
        goto CallDCP; 
        break; 
 
    case UM_GETNEXTCHILDX: 
    case UM_GETNEXTCHILDY: 
        /* 
         * Calculate the next place to put the next transaction window. 
         */ 
        { 
            pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0); 
            GetClientRect(hwnd, &rc); 
            if (msg == UM_GETNEXTCHILDX) { 
                pmci->x += 14; 
                if (pmci->x > (rc.right - 200 - rc.left)) 
                    pmci->x = 0; 
                return(pmci->x); 
            } else { 
                pmci->y += 12; 
                if (pmci->y > (rc.bottom - 100 - rc.top)) 
                    pmci->y = 0; 
                return(pmci->y); 
            } 
        } 
        break; 
 
    case UM_DISCONNECTED: 
        /* 
         * Disconnected conversations can't have any transactions so we 
         * remove all the transaction windows here to show whats up. 
         */ 
        { 
            HWND hwndT; 
            while (hwndT = GetWindow(hwnd, GW_CHILD)) 
                DestroyWindow(hwndT); 
            InvalidateRect(hwnd, NULL, TRUE); 
        } 
        break; 
 
    case WM_DESTROY: 
        /* 
         * Cleanup our conversation info structure, and disconnect all 
         * conversations associated with this window. 
         */ 
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0); 
        pmci->hwndXaction = 0;      /* clear this to avoid focus problems */ 
        if (pmci->hConv) { 
            if (pmci->fList) { 
                DdeDisconnectList((HCONVLIST)pmci->hConv); 
            } else { 
                MyDisconnect(pmci->hConv); 
            } 
        } 
        DdeFreeStringHandle(idInst, pmci->hszTopic); 
        DdeFreeStringHandle(idInst, pmci->hszApp); 
        MyFree(pmci); 
        goto CallDCP; 
        break; 
 
    case WM_SETFOCUS: 
        /* 
         * This catches focus changes caused by dialogs. 
         */ 
        lParam = (LPARAM)hwnd; 
        // fall through 
 
    case WM_MDIACTIVATE: 
        hwndActive = (HWND)(lParam); 
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0); 
        /* 
         * pass the focus onto the current transaction window. 
         */ 
        if ((lParam == (LONG)hwnd) && 
                IsWindow(pmci->hwndXaction)) 
            SetFocus(pmci->hwndXaction); 
        break; 
 
    case ICN_HASFOCUS: 
        /* 
         * update which transaction window is the main one. 
         */ 
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0); 
        pmci->hwndXaction = wParam ? (HWND)lParam : NULL; 
        break; 
 
    case ICN_BYEBYE: 
        /* 
         * Transaction window is closing... 
         * 
         * wParam = hwndXact 
         * lParam = lpxact 
         */ 
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0); 
        if (pmci != NULL) { 
            XACT *pxact; 
 
            pxact = (XACT *)lParam; 
            if (pxact != NULL) { 
                /* 
                 * If this transaction is active, abandon it first. 
                 */ 
                if (pxact->fsOptions & XOPT_ASYNC && 
                        !(pxact->fsOptions & XOPT_COMPLETED)) { 
                    DdeAbandonTransaction(idInst, pmci->hConv, pxact->Result); 
                } 
                /* 
                 * release resources associated with transaction. 
                 */ 
                DdeFreeStringHandle(idInst, pxact->hszItem); 
                MyFree((PTSTR)pxact); 
                /* 
                 * Locate next apropriate transaction window to get focus. 
                 */ 
                if (!pmci->hwndXaction || pmci->hwndXaction == (HWND)wParam) 
                    pmci->hwndXaction = GetWindow(hwnd, GW_CHILD); 
                if (pmci->hwndXaction == (HWND)wParam) 
                    pmci->hwndXaction = GetWindow((HWND)wParam, GW_HWNDNEXT); 
                if (pmci->hwndXaction == (HWND)wParam || 
                        !IsWindow(pmci->hwndXaction) || 
                        !IsChild(hwnd, pmci->hwndXaction)) 
                    pmci->hwndXaction = NULL; 
                else 
                    SetFocus(pmci->hwndXaction); 
            } 
        } 
        break; 
 
    case WM_PAINT: 
        /* 
         * Paint this conversation's related information. 
         */ 
        pmci = (MYCONVINFO *)GetWindowLong(hwnd, 0); 
        { 
            PAINTSTRUCT ps; 
            PTSTR psz; 
 
            BeginPaint(hwnd, &ps); 
            SetBkMode(ps.hdc, TRANSPARENT); 
            psz = pmci->fList ? GetConvListText((HCONVLIST)pmci->hConv) : 
                    GetConvInfoText(pmci->hConv, &pmci->ci); 
            if (psz) { 
                GetClientRect(hwnd, &rc); 
                DrawText(ps.hdc, psz, -1, &rc, 
                        DT_WORDBREAK | DT_LEFT | DT_NOPREFIX | DT_TABSTOP); 
                MyFree(psz); 
            } 
            EndPaint(hwnd, &ps); 
        } 
        break; 
 
    case WM_QUERYENDSESSION: 
        return TRUE; 
 
    default: 
CallDCP: 
        /* Again, since the MDI default behaviour is a little different, 
         * call DefMDIChildProc instead of DefWindowProc() 
         */ 
        return DefMDIChildProc (hwnd, msg, wParam, lParam); 
    } 
    return FALSE; 
} 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : Initializemenu ( hMenu )                                   * 
 *                                                                          * 
 *  PURPOSE    : Sets up greying, enabling and checking of main menu items  * 
 *               based on the app's state.                                  * 
 *                                                                          * 
 ****************************************************************************/ 
VOID NEAR PASCAL InitializeMenu ( hmenu ) 
HANDLE hmenu; 
{ 
    BOOL fLink      = FALSE; // set if Link format is on the clipboard; 
    BOOL fAny       = FALSE; // set if hwndActive exists 
    BOOL fList      = FALSE; // set if hwndActive is a list window 
    BOOL fConnected = FALSE; // set if hwndActive is a connection conversation. 
    BOOL fXaction   = FALSE; // set if hwndActive has a selected transaction window 
    BOOL fXactions  = FALSE; // set if hwndActive contains transaction windows 
    BOOL fBlocked   = FALSE; // set if hwndActive conversation is blocked. 
    BOOL fBlockNext = FALSE; // set if handActive conversation is blockNext. 
    MYCONVINFO *pmci = NULL; 
 
    if (OpenClipboard(hwndFrame)) { 
        fLink = (IsClipboardFormatAvailable(fmtLink)); 
        CloseClipboard(); 
    } 
 
    if (fAny = (IsWindow(hwndActive) && 
            (pmci = (MYCONVINFO *)GetWindowLong(hwndActive, 0)))) { 
        fXactions = (BOOL)GetWindow(hwndActive, GW_CHILD); 
        if (!(fList = pmci->fList)) { 
            CONVINFO ci; 
 
            ci.cb = sizeof(CONVINFO); 
            DdeQueryConvInfo(pmci->hConv, QID_SYNC, &ci); 
            fConnected = ci.wStatus & ST_CONNECTED; 
            fXaction = IsWindow(pmci->hwndXaction); 
            fBlocked = ci.wStatus & ST_BLOCKED; 
            fBlockNext = ci.wStatus & ST_BLOCKNEXT; 
        } 
    } 
 
    EnableMenuItem(hmenu,   IDM_EDITPASTE, 
            fLink           ? MF_ENABLED    : MF_GRAYED); 
 
    // IDM_CONNECTED - always enabled. 
 
    EnableMenuItem(hmenu,   IDM_RECONNECT, 
            fList           ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_DISCONNECT, 
            fConnected && !(fXaction || fXactions) ? MF_ENABLED : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_TRANSACT, 
            fConnected      ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem(hmenu,   IDM_ABANDON, 
            fXaction        ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem(hmenu,   IDM_ABANDONALL, 
            fXactions ? MF_ENABLED : MF_GRAYED); 
 
 
    EnableMenuItem (hmenu,  IDM_BLOCKCURRENT, 
            fConnected && !fBlocked ? MF_ENABLED    : MF_GRAYED); 
    CheckMenuItem(hmenu, IDM_BLOCKCURRENT, 
            fBlocked        ? MF_CHECKED    : MF_UNCHECKED); 
 
    EnableMenuItem (hmenu,  IDM_ENABLECURRENT, 
            fConnected && (fBlocked || fBlockNext) ? MF_ENABLED : MF_GRAYED); 
    CheckMenuItem(hmenu,    IDM_ENABLECURRENT, 
            !fBlocked       ? MF_CHECKED    : MF_UNCHECKED); 
 
    EnableMenuItem (hmenu,  IDM_ENABLEONECURRENT, 
            fConnected && (fBlocked) ? MF_ENABLED : MF_GRAYED); 
    CheckMenuItem(hmenu,    IDM_ENABLEONECURRENT, 
            fBlockNext      ? MF_CHECKED    : MF_UNCHECKED); 
 
    EnableMenuItem (hmenu,  IDM_BLOCKALLCBS, 
            fAny            ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_ENABLEALLCBS, 
            fAny            ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_ENABLEONECB, 
            fAny            ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem(hmenu,   IDM_BLOCKNEXTCB, 
            fAny || fBlockNextCB ? MF_ENABLED    : MF_GRAYED); 
    CheckMenuItem(hmenu,    IDM_BLOCKNEXTCB, 
            fBlockNextCB    ? MF_CHECKED    : MF_UNCHECKED); 
 
    EnableMenuItem(hmenu,   IDM_TERMNEXTCB, 
            fAny || fTermNextCB ? MF_ENABLED    : MF_GRAYED); 
    CheckMenuItem(hmenu,    IDM_TERMNEXTCB, 
            fTermNextCB     ? MF_CHECKED    : MF_UNCHECKED); 
 
    // IDM_DELAY - always enabled. 
 
    // IDM_TIMEOUT - alwasy enabled. 
 
    EnableMenuItem (hmenu,  IDM_WINDOWTILE, 
            fAny            ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_WINDOWCASCADE, 
            fAny            ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_WINDOWICONS, 
            fAny            ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_WINDOWCLOSEALL, 
            fAny            ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_XACTTILE, 
            fXactions       ? MF_ENABLED    : MF_GRAYED); 
 
    EnableMenuItem (hmenu,  IDM_XACTCASCADE, 
            fXactions       ? MF_ENABLED    : MF_GRAYED); 
 
    CheckMenuItem(hmenu,   IDM_AUTORECONNECT, 
            fAutoReconnect  ? MF_CHECKED    : MF_UNCHECKED); 
 
    // IDM_HELPABOUT - always enabled. 
} 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : CloseAllChildren ()                                        * 
 *                                                                          * 
 *  PURPOSE    : Destroys all MDI child windows.                            * 
 *                                                                          * 
 ****************************************************************************/ 
VOID NEAR PASCAL CloseAllChildren () 
{ 
    HWND hwndT; 
 
    /* hide the MDI client window to avoid multiple repaints */ 
    ShowWindow(hwndMDIClient,SW_HIDE); 
 
    /* As long as the MDI client has a child, destroy it */ 
    while ( hwndT = GetWindow (hwndMDIClient, GW_CHILD)){ 
 
        /* Skip the icon title windows */ 
        while (hwndT && GetWindow (hwndT, GW_OWNER)) 
            hwndT = GetWindow (hwndT, GW_HWNDNEXT); 
 
        if (!hwndT) 
            break; 
 
        SendMessage(hwndMDIClient, WM_MDIDESTROY, (DWORD)hwndT, 0L); 
    } 
 
    ShowWindow( hwndMDIClient, SW_SHOW); 
} 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : CommandHandler ()                                          * 
 *                                                                          * 
 *  PURPOSE    : Processes all "frame" WM_COMMAND messages.                 * 
 *                                                                          * 
 ****************************************************************************/ 
VOID NEAR PASCAL CommandHandler ( 
HWND hwnd, 
DWORD id) 
 
{ 
    MYCONVINFO *pmci = NULL; 
 
    if (hwndActive) 
        pmci = (MYCONVINFO *)GetWindowLong(hwndActive, 0); 
 
    switch (id){ 
        case IDM_EDITPASTE: 
            { 
                HANDLE hClipData; 
                LPTSTR psz; 
                XACT xact; 
 
                if (OpenClipboard(hwnd)) { 
                    if (hClipData = GetClipboardData(fmtLink)) { 
                        if (psz = GlobalLock(hClipData)) { 
                            /* 
                             * Create a conversation with the link app and 
                             * begin a request and advise start transaction. 
                             */ 
                            xact.hConv = CreateConv(DdeCreateStringHandle(idInst, psz, 0), 
                                    DdeCreateStringHandle(idInst, &psz[_tcslen(psz) + 1], 0), 
                                    FALSE); 
                            if (xact.hConv) { 
                                psz += _tcslen(psz) + 1; 
                                psz += _tcslen(psz) + 1; 
                                xact.ulTimeout = DefTimeout; 
                                xact.wType = XTYP_ADVSTART; 
                                xact.hDdeData = 0; 
#ifdef UNICODE 
                                xact.wFmt = CF_UNICODETEXT; 
#else 
                                xact.wFmt = CF_TEXT; 
#endif 
                                xact.hszItem = DdeCreateStringHandle(idInst, psz, 0); 
                                xact.fsOptions = 0; 
                                ProcessTransaction(&xact); 
                                xact.wType = XTYP_REQUEST; 
                                ProcessTransaction(&xact); 
                            } 
                            GlobalUnlock(hClipData); 
                        } 
                    } 
                    CloseClipboard(); 
                } 
            } 
            break; 
 
        case IDM_CONNECT: 
        case IDM_RECONNECT: 
            DoDialog(MAKEINTRESOURCE(IDD_CONNECT), (DLGPROC)ConnectDlgProc, 
                    id == IDM_RECONNECT, FALSE); 
            break; 
 
        case IDM_DISCONNECT: 
            if (hwndActive) { 
                SendMessage(hwndMDIClient, WM_MDIDESTROY, (DWORD)hwndActive, 0L); 
            } 
            break; 
 
        case IDM_TRANSACT: 
            if (DoDialog(MAKEINTRESOURCE(IDD_TRANSACT), (DLGPROC)TransactDlgProc, 
                    (DWORD)(LPTSTR)pmci->hConv, FALSE)) 
                SetFocus(GetWindow(hwndActive, GW_CHILD)); 
            break; 
 
        case IDM_ABANDON: 
            if (pmci != NULL && IsWindow(pmci->hwndXaction)) { 
                DestroyWindow(pmci->hwndXaction); 
            } 
            break; 
 
        case IDM_ABANDONALL: 
            DdeAbandonTransaction(idInst, pmci->hConv, 0L); 
            { 
                HWND hwndXaction; 
 
                hwndXaction = GetWindow(hwndActive, GW_CHILD); 
                while (hwndXaction) { 
                    DestroyWindow(hwndXaction); 
                    hwndXaction = GetWindow(hwndActive, GW_CHILD); 
                } 
            } 
            break; 
 
        case IDM_BLOCKCURRENT: 
            DdeEnableCallback(idInst, pmci->hConv, EC_DISABLE); 
            InvalidateRect(hwndActive, NULL, TRUE); 
            break; 
 
        case IDM_ENABLECURRENT: 
            DdeEnableCallback(idInst, pmci->hConv, EC_ENABLEALL); 
            InvalidateRect(hwndActive, NULL, TRUE); 
            break; 
 
        case IDM_ENABLEONECURRENT: 
            DdeEnableCallback(idInst, pmci->hConv, EC_ENABLEONE); 
            InvalidateRect(hwndActive, NULL, TRUE); 
            break; 
 
        case IDM_BLOCKALLCBS: 
            DdeEnableCallback(idInst, 0, EC_DISABLE); 
            InvalidateRect(hwndMDIClient, NULL, TRUE); 
            break; 
 
        case IDM_ENABLEALLCBS: 
            DdeEnableCallback(idInst, 0, EC_ENABLEALL); 
            InvalidateRect(hwndMDIClient, NULL, TRUE); 
            break; 
 
        case IDM_ENABLEONECB: 
            DdeEnableCallback(idInst, 0, EC_ENABLEONE); 
            InvalidateRect(hwndMDIClient, NULL, TRUE); 
            break; 
 
        case IDM_BLOCKNEXTCB: 
            fBlockNextCB = !fBlockNextCB; 
            break; 
 
        case IDM_TERMNEXTCB: 
            fTermNextCB = !fTermNextCB; 
            break; 
 
        case IDM_DELAY: 
            DoDialog(MAKEINTRESOURCE(IDD_VALUEENTRY), (DLGPROC)DelayDlgProc, 0, 
                    TRUE); 
            break; 
 
        case IDM_TIMEOUT: 
            DoDialog(MAKEINTRESOURCE(IDD_VALUEENTRY), (DLGPROC)TimeoutDlgProc, 0, 
                    TRUE); 
            break; 
 
        case IDM_CONTEXT: 
            DoDialog(MAKEINTRESOURCE(IDD_CONTEXT), (DLGPROC)ContextDlgProc, 0, TRUE); 
            break; 
 
        case IDM_AUTORECONNECT: 
            fAutoReconnect = !fAutoReconnect; 
            break; 
 
        /* The following are window commands - these are handled by the 
         * MDI Client. 
         */ 
        case IDM_WINDOWTILE: 
            /* Tile MDI windows */ 
            SendMessage (hwndMDIClient, WM_MDITILE, 0, 0L); 
            break; 
 
        case IDM_WINDOWCASCADE: 
            /* Cascade MDI windows */ 
            SendMessage (hwndMDIClient, WM_MDICASCADE, 0, 0L); 
            break; 
 
        case IDM_WINDOWICONS: 
            /* Auto - arrange MDI icons */ 
            SendMessage (hwndMDIClient, WM_MDIICONARRANGE, 0, 0L); 
            break; 
 
        case IDM_WINDOWCLOSEALL: 
            CloseAllChildren(); 
            break; 
 
        case IDM_XACTTILE: 
            TileChildWindows(hwndActive); 
            break; 
 
        case IDM_XACTCASCADE: 
            MyCascadeChildWindows(hwndActive); 
            break; 
 
        case IDM_HELPABOUT:{ 
            DoDialog(MAKEINTRESOURCE(IDD_ABOUT), (DLGPROC)AboutDlgProc, 0, TRUE); 
            break; 
        } 
 
        default: 
           /* 
            * This is essential, since there are frame WM_COMMANDS generated 
            * by the MDI system for activating child windows via the 
            * window menu. 
            */ 
            DefFrameProc(hwnd, hwndMDIClient, WM_COMMAND, 
                    (WPARAM)MAKELONG(id, 0), (LONG)(0)); 
    } 
} 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : MPError (flags, id, ...)                            * 
 *                                                                          * 
 *  PURPOSE    : Flashes a Message Box to the user. The format string is    * 
 *               taken from the STRINGTABLE.                                * 
 *                                                                          * 
 *  RETURNS    : Returns value returned by MessageBox() to the caller.      * 
 *                                                                          * 
 ****************************************************************************/ 
INT FAR cdecl MPError( 
DWORD bFlags, 
DWORD id, 
...) 
{ 
    TCHAR sz[160]; 
    TCHAR szFmt[128]; 
    va_list args; 
    va_start(args, id); 
 
    LoadString (hInst, id, szFmt, sizeof(szFmt)); 
    wvsprintf (sz, szFmt, args); 
    LoadString (hInst, IDS_APPNAME, szFmt, sizeof(szFmt)); 
    return MessageBox (hwndFrame, sz, szFmt, bFlags); 
} 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : CreateConv()                                               * 
 *                                                                          * 
*  PURPOSE    :                                                            * 
 *                                                                          * 
 *  RETURNS    :                                                            * 
 *                                                                          * 
 ****************************************************************************/ 
HCONV CreateConv( 
HSZ hszApp, 
HSZ hszTopic, 
BOOL fList) 
{ 
    HCONV hConv; 
    HWND hwndConv = 0; 
    CONVINFO ci; 
 
    if (fList) { 
        hConv = (HCONV)DdeConnectList(idInst, hszApp, hszTopic, 0, &CCFilter); 
    } else { 
        hConv = DdeConnect(idInst, hszApp, hszTopic, &CCFilter); 
    } 
    if (hConv) { 
        if (fList) { 
            ci.hszSvcPartner = hszApp; 
            ci.hszTopic = hszTopic; 
        } else { 
            ci.cb = sizeof(CONVINFO); 
            DdeQueryConvInfo(hConv, QID_SYNC, &ci); 
        } 
        hwndConv = AddConv(ci.hszSvcPartner, ci.hszTopic, hConv, fList); 
        // HSZs get freed when window dies. 
    } 
    if (!hwndConv) { 
        DdeFreeStringHandle(idInst, hszApp); 
        DdeFreeStringHandle(idInst, hszTopic); 
    } 
    return(hConv); 
} 
 
 
 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : AddConv()                                                  * 
 *                                                                          * 
 *  PURPOSE    : Creates an MDI window representing a conversation          * 
 *               (fList = FALSE) or a set of MID windows for the list of    * 
 *               conversations (fList = TRUE).                              * 
 *                                                                          * 
 *  EFFECTS    : Sets the hUser for the conversation to the created MDI     * 
 *               child hwnd.  Keeps the hszs if successful.                 * 
 *                                                                          * 
 *  RETURNS    : created MDI window handle.                                 * 
 *                                                                          * 
 ****************************************************************************/ 
HWND  APIENTRY AddConv( 
HSZ hszApp, 
HSZ hszTopic, 
HCONV hConv, 
BOOL fList) 
{ 
    HWND hwnd; 
    MDICREATESTRUCT mcs; 
    MYCONVINFO mci; 
 
    if (fList) { 
        /* 
         * Create all child windows FIRST so we have info for list window. 
         */ 
        CONVINFO ci; 
        HCONV hConvChild = 0; 
 
        ci.cb = sizeof(CONVINFO); 
        while (hConvChild = DdeQueryNextServer((HCONVLIST)hConv, hConvChild)) { 
            if (DdeQueryConvInfo(hConvChild, QID_SYNC, &ci)) { 
                AddConv(ci.hszSvcPartner, ci.hszTopic, hConvChild, FALSE); 
            } 
        } 
    } 
 
    mcs.szTitle = GetConvTitleText(hConv, hszApp, hszTopic, fList); 
 
    mcs.szClass = fList ? szList : szChild; 
    mcs.hOwner  = hInst; 
    mcs.x = mcs.cx = CW_USEDEFAULT; 
    mcs.y = mcs.cy = CW_USEDEFAULT; 
    mcs.style = GetWindow(hwndMDIClient, GW_CHILD) ? 
            WS_CLIPCHILDREN : (WS_MAXIMIZE | WS_CLIPCHILDREN); 
 
    // mci.hwndXaction = 
    mci.fList = fList; 
    mci.hConv = hConv; 
    mci.hszTopic = hszTopic; 
    mci.hszApp = hszApp; 
    // mci.x = 
    // mci.y = 
    // mci.ci = 
    mcs.lParam = (DWORD)(LPTSTR)&mci; 
 
    hwnd = (HWND)SendMessage (hwndMDIClient, WM_MDICREATE, 0, 
             (LONG)(LPMDICREATESTRUCT)&mcs); 
 
    MyFree((PTSTR)(DWORD)mcs.szTitle); 
 
    return hwnd; 
} 
 
 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : GetConvListText()                                          * 
 *                                                                          * 
 *  RETURN     : Returns a ponter to a string containing a list of          * 
 *               conversations contained in the given hConvList freeable    * 
 *               by MyFree();                                               * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR GetConvListText( 
HCONVLIST hConvList) 
{ 
    HCONV hConv = 0; 
    DWORD cConv = 0; 
    CONVINFO ci; 
    DWORD cb = 0; 
    DWORD d; 
    TCHAR *psz, *pszStart; 
 
    ci.cb = sizeof(CONVINFO); 
 
    // find out size needed. 
 
    while (hConv = DdeQueryNextServer(hConvList, hConv)) { 
        if (DdeQueryConvInfo(hConv, QID_SYNC, &ci)) { 
            if (!IsWindow((HWND)ci.hUser)) { 
                if (ci.wStatus & ST_CONNECTED) { 
                    /* 
                     * This conversation doesn't have a corresponding 
                     * MDI window.  This is probably due to a reconnection. 
                     */ 
                    ci.hUser = (DWORD)AddConv(ci.hszSvcPartner, ci.hszTopic, hConv, FALSE); 
                } else { 
                    continue;   // skip this guy - he was closed locally. 
                } 
            } 
            cb += GetWindowTextLength((HWND)ci.hUser); 
            if (cConv++) 
                cb += 2;        // room for CRLF 
        } 
    } 
    cb++;                       // for terminator. 
 
    // allocate and fill 
 
    if (pszStart = psz = MyAlloc(cb * sizeof(TCHAR))) { 
        *psz = TEXT('\0'); 
        while (hConv = DdeQueryNextServer(hConvList, hConv)) { 
            if (DdeQueryConvInfo(hConv, QID_SYNC, &ci) && 
                    IsWindow((HWND)ci.hUser)) { 
                d = GetWindowText((HWND)ci.hUser, psz, cb); 
                psz += d; 
                cb -= d; 
                if (--cConv) { 
                    *psz++ = TEXT('\r'); 
                    *psz++ = TEXT('\n'); 
                    cb -= 2; 
                } 
            } 
        } 
    } 
    return(pszStart); 
} 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : GetConvInfoText()                                          * 
 *                                                                          * 
 *  PURPOSE    : Returns a pointer to a string that reflects a              * 
 *               conversation's information.  Freeable by MyFree();         * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR GetConvInfoText( 
HCONV hConv, 
CONVINFO *pci) 
{ 
    PTSTR psz; 
    PTSTR szApp; 
 
    psz = MyAlloc(300 * sizeof(TCHAR)); 
    pci->cb = sizeof(CONVINFO); 
    if (hConv) { 
        if (!DdeQueryConvInfo(hConv, QID_SYNC, (PCONVINFO)pci)) { 
            _tcscpy(psz, TEXT("State=Disconnected")); 
            return(psz); 
        } 
        szApp = GetHSZName(pci->hszServiceReq); 
        wsprintf(psz, 
                TEXT("hUser=0x%lx\r\nhConvPartner=0x%lx\r\nhszServiceReq=%s\r\nStatus=%s\r\nState=%s\r\nLastError=%s"), 
                pci->hUser, pci->hConvPartner, (LPTSTR)szApp, 
                (LPTSTR)Status2String(pci->wStatus), 
                (LPTSTR)State2String(pci->wConvst), 
                (LPTSTR)Error2String(pci->wLastError)); 
        MyFree(szApp); 
    } else { 
        _tcscpy(psz, Error2String(DdeGetLastError(idInst))); 
    } 
    return(psz); 
} 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : GetConvTitleText()                                         * 
 *                                                                          * 
 *  PURPOSE    : Creates standard window title text based on parameters.    * 
 *                                                                          * 
 *  RETURNS    : psz freeable by MyFree()                                   * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR GetConvTitleText( 
HCONV hConv, 
HSZ hszApp, 
HSZ hszTopic, 
BOOL fList) 
{ 
    DWORD cb; 
    PTSTR psz; 
 
    cb = (DWORD)DdeQueryString(idInst, hszApp, NULL, 0, 0) + 
            (DWORD)DdeQueryString(idInst, hszTopic, (LPTSTR)NULL, 0, 0) + 
            (fList ? 30 : 20); 
 
    if (psz = MyAlloc(cb * sizeof(TCHAR))) { 
        DdeQueryString(idInst, hszApp, psz, cb, 0); 
        _tcscat(psz, TEXT("|")); 
        DdeQueryString(idInst, hszTopic, &psz[_tcslen(psz)], cb, 0); 
        if (fList) 
            _tcscat(psz, TEXT(" - LIST")); 
        wsprintf(&psz[_tcslen(psz)], TEXT(" - (%lx)"), hConv); 
    } 
    return(psz); 
} 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : Status2String()                                            * 
 *                                                                          * 
 *  PURPOSE    : Converts a conversation status word to a string and        * 
 *               returns a pointer to that string.  The string is valid     * 
 *               till the next call to this function.                       * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR Status2String( 
DWORD status) 
{ 
    DWORD c, i; 
    static TCHAR szStatus[6 * 18]; 
    static struct { 
        TCHAR *szStatus; 
        DWORD status; 
    } s2s[] = { 
        { TEXT("Connected")    ,   ST_CONNECTED }, 
        { TEXT("Advise")       ,   ST_ADVISE }, 
        { TEXT("IsLocal")      ,   ST_ISLOCAL }, 
        { TEXT("Blocked")      ,   ST_BLOCKED }, 
        { TEXT("Client")       ,   ST_CLIENT }, 
        { TEXT("Disconnected") ,   ST_TERMINATED }, 
        { TEXT("BlockNext")    ,   ST_BLOCKNEXT }, 
    }; 
#define CFLAGS 7 
    szStatus[0] = TEXT('\0'); 
    c = 0; 
    for (i = 0; i < CFLAGS; i++) { 
        if (status & s2s[i].status) { 
            if (c++) 
                _tcscat(szStatus, TEXT(" | ")); 
            _tcscat(szStatus, s2s[i].szStatus); 
        } 
    } 
    return szStatus; 
#undef CFLAGS 
} 
 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : State2String()                                             * 
 *                                                                          * 
 *  PURPOSE    : converts a conversation state word to a string and         * 
 *               returns a pointer to that string.  The string is valid     * 
 *               till the next call to this routine.                        * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR State2String( 
DWORD state) 
{ 
    static TCHAR *s2s[] = { 
        TEXT("NULL")             , 
        TEXT("Incomplete")       , 
        TEXT("Standby")          , 
        TEXT("Initiating")       , 
        TEXT("ReqSent")          , 
        TEXT("DataRcvd")         , 
        TEXT("PokeSent")         , 
        TEXT("PokeAckRcvd")      , 
        TEXT("ExecSent")         , 
        TEXT("ExecAckRcvd")      , 
        TEXT("AdvSent")          , 
        TEXT("UnadvSent")        , 
        TEXT("AdvAckRcvd")       , 
        TEXT("UnadvAckRcvd")     , 
        TEXT("AdvDataSent")      , 
        TEXT("AdvDataAckRcvd")   , 
        TEXT("?")                ,    // 16 
    }; 
 
    if (state >= 17) 
        return s2s[17]; 
    else 
        return s2s[state]; 
} 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : Error2String()                                             * 
 *                                                                          * 
 *  PURPOSE    : Converts an error code to a string and returns a pointer   * 
 *               to that string.  The string is valid until the next call   * 
 *               to this function.                                          * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR Error2String( 
DWORD error) 
{ 
    static TCHAR szErr[23]; 
    static TCHAR *e2s[] = { 
        TEXT("Advacktimeout")              , 
        TEXT("Busy")                       , 
        TEXT("Dataacktimeout")             , 
        TEXT("Dll_not_initialized")        , 
        TEXT("Dll_usage")                  , 
        TEXT("Execacktimeout")             , 
        TEXT("Invalidparameter")           , 
        TEXT("Low Memory warning")         , 
        TEXT("Memory_error")               , 
        TEXT("Notprocessed")               , 
        TEXT("No_conv_established")        , 
        TEXT("Pokeacktimeout")             , 
        TEXT("Postmsg_failed")             , 
        TEXT("Reentrancy")                 , 
        TEXT("Server_died")                , 
        TEXT("Sys_error")                  , 
        TEXT("Unadvacktimeout")            , 
        TEXT("Unfound_queue_id")           , 
    }; 
    if (!error) { 
        _tcscpy(szErr, TEXT("0")); 
    } else if (error > DMLERR_LAST || error < DMLERR_FIRST) { 
        _tcscpy(szErr, TEXT("???")); 
    } else { 
        _tcscpy(szErr, e2s[error - DMLERR_FIRST]); 
    } 
    return(szErr); 
} 
 
 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : Type2String()                                              * 
 *                                                                          * 
 *  PURPOSE    : Converts a wType word and fsOption flags to a string and   * 
 *               returns a pointer to that string.  the string is valid     * 
 *               until the next call to this function.                      * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR Type2String( 
DWORD wType, 
DWORD fsOptions) 
{ 
    static TCHAR sz[30]; 
    static TCHAR o2s[] = TEXT("^!#$X*<?"); 
    static TCHAR *t2s[] = { 
        TEXT("")                 , 
        TEXT("AdvData")          , 
        TEXT("AdvReq")           , 
        TEXT("AdvStart")         , 
        TEXT("AdvStop")          , 
        TEXT("Execute")          , 
        TEXT("Connect")          , 
        TEXT("ConnectConfirm")   , 
        TEXT("XactComplete")    , 
        TEXT("Poke")             , 
        TEXT("Register")         , 
        TEXT("Request")          , 
        TEXT("Term")             , 
        TEXT("Unregister")       , 
        TEXT("WildConnect")      , 
        TEXT("")                 , 
    }; 
    DWORD bit, c, i; 
 
    _tcscpy(sz, t2s[((wType & XTYP_MASK) >> XTYP_SHIFT)]); 
    c = _tcslen(sz); 
    sz[c++] = TEXT(' '); 
    for (i = 0, bit = 1; i < 7; bit = bit << 1, i++) { 
        if (fsOptions & bit) 
            sz[c++] = o2s[i]; 
    } 
    sz[c] = TEXT('\0'); 
    return(sz); 
} 
 
 
 
 
/**************************************************************************** 
 *                                                                          * 
 *  FUNCTION   : GetHSZName()                                               * 
 *                                                                          * 
 *  PURPOSE    : Allocates local memory for and retrieves the string form   * 
 *               of an HSZ.  Returns a pointer to the local memory or NULL  * 
 *               if failure.  The string must be freed via MyFree().        * 
 *                                                                          * 
 ****************************************************************************/ 
PTSTR GetHSZName( 
HSZ hsz) 
{ 
    PTSTR psz; 
    DWORD cb; 
 
    cb = (DWORD)DdeQueryString(idInst, hsz, NULL, 0, 0) + 1; 
    psz = MyAlloc(cb * sizeof(TCHAR)); 
    DdeQueryString(idInst, hsz, psz, cb, 0); 
    return(psz); 
} 
 
 
/**************************************************************************** 
 * 
 *  FUNCTION   : MyMsgFilterProc 
 * 
 *  PURPOSE    : This filter proc gets called for each message we handle. 
 *               This allows our application to properly dispatch messages 
 *               that we might not otherwise see because of DDEMLs modal 
 *               loop that is used while processing synchronous transactions. 
 * 
 *               Generally, applications that only do synchronous transactions 
 *               in response to user input (as this app does) does not need 
 *               to install such a filter proc because it would be very rare 
 *               that a user could command the app fast enough to cause 
 *               problems.  However, this is included as an example. 
 * 
 ****************************************************************************/ 
LRESULT CALLBACK MyMsgFilterProc( 
int nCode, 
WPARAM wParam, 
LPARAM lParam) 
{ 
    UNREFERENCED_PARAMETER(wParam); 
 
#define lpmsg ((LPMSG)lParam) 
    if (nCode == MSGF_DDEMGR) { 
 
        /* If a keyboard message is for the MDI , let the MDI client 
         * take care of it.  Otherwise, check to see if it's a normal 
         * accelerator key.  Otherwise, just handle the message as usual. 
         */ 
 
        if ( !TranslateMDISysAccel (hwndMDIClient, lpmsg) && 
             !TranslateAccelerator (hwndFrame, hAccel, lpmsg)){ 
            TranslateMessage (lpmsg); 
            DispatchMessage (lpmsg); 
        } 
        return(1); 
    } 
    if (nCode < 0) { 
        CallNextHookEx(ghhk, nCode, wParam, lParam); 
    } 
    return(0); 
#undef lpmsg 
}