Annotation of mstools/samples/ddeml/ddemo/ddemo.c, revision 1.1.1.2

1.1.1.2 ! root        1: 
        !             2: /******************************************************************************\
        !             3: *       This is a part of the Microsoft Source Code Samples. 
        !             4: *       Copyright (C) 1993 Microsoft Corporation.
        !             5: *       All rights reserved. 
        !             6: *       This source code is only intended as a supplement to 
        !             7: *       Microsoft Development Tools and/or WinHelp documentation.
        !             8: *       See these sources for detailed information regarding the 
        !             9: *       Microsoft samples programs.
        !            10: \******************************************************************************/
        !            11: 
1.1       root       12: /*****************************************************************************\
                     13: *
                     14: * DDEMO.C
                     15: *
                     16: * This file implements a simple DDEML sample application that demonstrates
                     17: * some of the ways the DDEML APIs can be used.
                     18: *
                     19: * Each instance of this application becomes both a DDE client and a DDE
                     20: * server with any other instances of this application that are found.
                     21: *
                     22: * Since it assumes it is talking to itself, this program takes some liberties
                     23: * to simplify things.  For instance, this application does not support the
                     24: * standard SysTopic topic and does not use any standard formats.
                     25: *
                     26: * The basic concepts this application will show you are:
                     27: *
                     28: *   How to use lists of conversations properly
                     29: *   How to handle links
                     30: *   How to handle simple asynchronous transactions
                     31: *   How to use your own custom formats
                     32: *
                     33: \*****************************************************************************/
                     34: #include <windows.h>
                     35: #include <ddeml.h>
                     36: #include <stdlib.h>
                     37: #include <string.h>
                     38: 
                     39: HDDEDATA CALLBACK DdeCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hszTopic,
                     40:         HSZ hszItem, HDDEDATA hData, DWORD lData1, DWORD lData2);
                     41: VOID PaintDemo(HWND hwnd);
                     42: LONG  APIENTRY MainWndProc(HWND hwnd, UINT message, WPARAM wParam,
                     43:         LONG lParam);
                     44: 
                     45: /*
                     46:  * Define this value to limit how fast data changes.  If we just let data
                     47:  * change as fast a possible, we might bog down the system with DDE
                     48:  * messages.
                     49:  */
                     50: #define BASE_TIMEOUT 100
                     51: 
                     52: BOOL        fActive;                    // indicates data is changing
                     53: DWORD       idInst = 0;                 // our DDEML instance object
                     54: HANDLE      hInst;                      // our instance/module handle
                     55: HCONVLIST   hConvList = 0;              // the list of all convs we have open
                     56: HSZ         hszAppName = 0;             // the generic hsz for everything
                     57: HWND        hwndMain;                   // our main window handle
                     58: TCHAR       szT[20];                    // static buffer for painting
                     59: TCHAR       szTitle[] = "DDEmo";        // the generic string for everything
                     60: UINT        OurFormat;                  // our custom registered format
                     61: int         InCount = 0;                // static buffer to hold incomming data
                     62: int         cConvs = 0;                 // number of active conversations
                     63: int         count = 0;                  // our data
                     64: int         cyText, cxText, cyTitle;    // sizes for painting
                     65: 
                     66: int WINAPI WinMain(
1.1.1.2 ! root       67: HINSTANCE hInstance,
        !            68: HINSTANCE hPrevInstance,
1.1       root       69: LPSTR lpCmdLine,
                     70: INT nCmdShow)
                     71: {
                     72:     MSG msg;
                     73:     WNDCLASS  wc;
                     74:     TEXTMETRIC metrics;
                     75:     HDC hdc;
                     76: 
                     77:     wc.style = 0;
                     78:     wc.lpfnWndProc = MainWndProc;
                     79:     wc.cbClsExtra = 0;
                     80:     wc.cbWndExtra = 0;
                     81:     wc.hInstance = hInstance;
                     82:     wc.hIcon = NULL;
                     83:     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
                     84:     wc.hbrBackground = NULL;
                     85:     wc.lpszMenuName =  NULL;
                     86:     wc.lpszClassName = szTitle;
                     87: 
                     88:     if (!RegisterClass(&wc))
                     89:         return(FALSE);
                     90: 
                     91:     /*
                     92:      * Here we tell DDEML what we will be doing.
                     93:      *
                     94:      * 1) We let it know our callback proc address - MakeProcInstance
                     95:      *      is called just to be more portable.
                     96:      * 2) Filter-inits - don't accept any WM_DDE_INITIATE messages for
                     97:      *      anything but our registered service name.
                     98:      * 3) Don't bother to notify us of confirmed connections
                     99:      * 4) Don't allow connections with ourselves.
                    100:      * 5) Don't bother us with XTYP_POKE transactions.
                    101:      */
                    102:     if (DdeInitialize(&idInst,
                    103:             (PFNCALLBACK)MakeProcInstance((FARPROC)DdeCallback, hInstance),
                    104:             APPCMD_FILTERINITS |
                    105:             CBF_SKIP_CONNECT_CONFIRMS |
                    106:             CBF_FAIL_SELFCONNECTIONS |
                    107:             CBF_FAIL_POKES,
                    108:             0))
                    109:         return(FALSE);
                    110: 
                    111:     hInst = hInstance;
                    112:     hwndMain = CreateWindow(
                    113:         szTitle,
                    114:         szTitle,
                    115:         WS_CAPTION | WS_BORDER | WS_SYSMENU,
                    116:         CW_USEDEFAULT,
                    117:         CW_USEDEFAULT,
                    118:         0,
                    119:         0,
                    120:         NULL,
                    121:         NULL,
                    122:         hInstance,
                    123:         NULL
                    124:     );
                    125: 
                    126:     if (!hwndMain) {
                    127:         DdeUninitialize(idInst);
                    128:         return(FALSE);
                    129:     }
                    130: 
                    131:     hdc = GetDC(hwndMain);
                    132:     GetTextMetrics(hdc, &metrics);
                    133:     cyText = metrics.tmHeight + metrics.tmExternalLeading;
                    134:     cxText = metrics.tmMaxCharWidth * 8;
                    135:     cyTitle = GetSystemMetrics(SM_CYCAPTION);
                    136:     ReleaseDC(hwndMain, hdc);
                    137: 
                    138:     SetWindowPos(hwndMain, 0, 0, 0, cxText, cyText + cyTitle,
                    139:             SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
                    140:     ShowWindow(hwndMain, nCmdShow);
                    141:     UpdateWindow(hwndMain);
                    142: 
                    143:     /*
                    144:      * Initialize all our string handles for lookups later
                    145:      */
                    146:     hszAppName = DdeCreateStringHandle(idInst, szTitle, 0);
                    147:     /*
                    148:      * Register our formats
                    149:      */
                    150:     OurFormat = RegisterClipboardFormat(szTitle);
                    151:     /*
                    152:      * Connect to any other instances of ourselves that may already be
                    153:      * running.
                    154:      */
                    155:     hConvList = DdeConnectList(idInst, hszAppName, hszAppName, hConvList, NULL);
                    156:     /*
                    157:      * Register our service -
                    158:      *  This will cause DDEML to notify DDEML clients about the existance
                    159:      *  of a new DDE service.
                    160:      */
                    161:     DdeNameService(idInst, hszAppName, 0, DNS_REGISTER);
                    162: 
                    163:     while (GetMessage(&msg, 0, 0, 0)) {
                    164:         TranslateMessage(&msg);
                    165:         DispatchMessage(&msg);
                    166:     }
                    167: 
                    168:     DestroyWindow(hwndMain);
                    169:     UnregisterClass(szTitle, hInstance);
                    170:     return(FALSE);
                    171: }
                    172: 
                    173: 
                    174: /*
                    175:  * BroadcastTransaction
                    176:  *
                    177:  * Does the specified transaction on all conversations in hConvList
                    178:  */
                    179: VOID BroadcastTransaction(
                    180: PBYTE pSrc,
                    181: DWORD cbData,
                    182: UINT fmt,
                    183: UINT xtyp)
                    184: {
                    185:     HCONV hConv;
                    186:     DWORD dwResult;
                    187:     int cConvsOrg;
                    188: 
                    189:     cConvsOrg = cConvs;
                    190:     cConvs = 0;
                    191:     if (hConvList) {
                    192:         /*
                    193:          * Enumerate all the conversations within this list - note that
                    194:          * DDEML will only return active conversations.  Inactive conversations
                    195:          * are automatically removed.
                    196:          */
1.1.1.2 ! root      197:         hConv = DdeQueryNextServer(hConvList, 0);
1.1       root      198:         while (hConv) {
                    199:             /*
                    200:              * Count the active conversations while we're at it.
                    201:              */
                    202:             cConvs++;
                    203:             /*
                    204:              * Spawn an asynchronous transaction - this was chosen because
                    205:              * we have not particular action if an error ocurrs so we just
                    206:              * don't care too much about the results - this technique will
                    207:              * NOT do for XTYP_REQUEST transactions though.
                    208:              */
                    209:             if (DdeClientTransaction(pSrc, cbData, hConv, hszAppName, fmt,
                    210:                     xtyp, TIMEOUT_ASYNC, &dwResult)) {
                    211:                 /*
                    212:                  * We immediately abandon the transaction so we don't get
                    213:                  * a bothersome XTYP_XACT_COMPLETE callback which we don't
                    214:                  * care about.
                    215:                  */
                    216:                 DdeAbandonTransaction(idInst, hConv, dwResult);
                    217:             }
                    218: 
                    219:             hConv = DdeQueryNextServer(hConvList, hConv);
                    220:         }
                    221:     }
                    222:     if (cConvs != cConvsOrg) {
                    223:         /*
                    224:          * Oh, the number of active conversations has changed.  Time to
                    225:          * repaint!
                    226:          */
                    227:         InvalidateRect(hwndMain, NULL, TRUE);
                    228:     }
                    229: }
                    230: 
                    231: 
                    232: /*
                    233:  * MyProcessKey
                    234:  *
                    235:  * We demonstrate the robustness of NT here by forcing a GP anytime the
                    236:  * 'B' key is pressed while this window has the focus.  NT should properly
                    237:  * fake termination to all other apps connected to us.
                    238:  */
                    239: VOID MyProcessKey(
                    240: TCHAR tchCode,
                    241: LONG lKeyData)
                    242: {
                    243:     switch (tchCode) {
                    244:     case TEXT('B'):
                    245:     case TEXT('b'):
                    246:         *((PBYTE)(-1)) = 0;    // Cause GP fault!
                    247:         break;
                    248:     }
                    249: }
                    250: 
                    251: 
                    252: 
                    253: LONG  APIENTRY MainWndProc(
                    254: HWND hwnd,
                    255: UINT message,
                    256: WPARAM wParam,
                    257: LONG lParam)
                    258: {
                    259:     RECT rc;
                    260: 
                    261:     switch (message) {
                    262:     case WM_CREATE:
                    263:         /*
                    264:          * initially we are inactive - this reduces some of the message
                    265:          * traffic while we are initializing - but we could start active fine.
                    266:          */
                    267:         fActive = FALSE;
                    268:         break;
                    269: 
                    270:     case WM_RBUTTONDOWN:
                    271:         if (GetKeyState(VK_CONTROL) & 0x8000) {
                    272:             /*
                    273:              * A CTRL R_BUTTON click will cause ALL instances of this app
                    274:              * to become inactive.
                    275:              */
                    276:             BroadcastTransaction("PAUSE", 6, 0, XTYP_EXECUTE);
                    277:             MessageBeep(0);
                    278:         }
                    279:         /*
                    280:          * A R_BUTTON click makes us inactive.  Repaint to show state change.
                    281:          * We do a synchronous update in case there is too much DDE message
                    282:          * activity to allow the WM_PAINT messages through.  Remember DDE
                    283:          * messages have priority over others!
                    284:          */
                    285:         KillTimer(hwndMain, 1);
                    286:         fActive = FALSE;
                    287:         InvalidateRect(hwnd, NULL, TRUE);
                    288:         UpdateWindow(hwnd);
                    289:         break;
                    290: 
                    291:     case WM_LBUTTONDOWN:
                    292:         if (GetKeyState(VK_CONTROL) & 0x8000) {
                    293:             /*
                    294:              * A CTRL L_BUTTON click will cause ALL instances of this app
                    295:              * to become active.
                    296:              */
                    297:             BroadcastTransaction("RESUME", 7, 0, XTYP_EXECUTE);
                    298:             MessageBeep(0);
                    299:         }
                    300:         /*
                    301:          * An L_BUTTON click makes us active.  Repaint to show state change.
                    302:          */
                    303:         SetTimer(hwndMain, 1, BASE_TIMEOUT + (rand() & 0xff), NULL);
                    304:         fActive = TRUE;
                    305:         InvalidateRect(hwnd, NULL, TRUE);
                    306:         UpdateWindow(hwnd);
                    307:         break;
                    308: 
                    309:     case WM_CHAR:
                    310:         MyProcessKey((TCHAR)wParam, lParam);
                    311:         break;
                    312: 
                    313:     case WM_TIMER:
                    314:         /*
                    315:          * We use timers for simplicity.  On Win3.1 we could run out of
                    316:          * timers easily but we don't have this worry on NT.
                    317:          *
                    318:          * Each tick, we increment our data and call DdePostAdvise() to
                    319:          * update any links there may be on this data.  DDEML makes link
                    320:          * updates on specific items quite easy.
                    321:          */
                    322:         count++;
                    323:         DdePostAdvise(idInst, hszAppName, hszAppName);
                    324:         /*
                    325:          * Invalidate the part of ourselves that shows our data and
                    326:          * synchronously update it in case DDE message activity is blocking
                    327:          * paints.
                    328:          */
                    329:         SetRect(&rc, 0, 0, cxText, cyText);
                    330:         InvalidateRect(hwndMain, &rc, TRUE);
                    331:         UpdateWindow(hwndMain);
                    332:         break;
                    333: 
                    334:     case WM_PAINT:
                    335:         PaintDemo(hwnd);
                    336:         break;
                    337: 
                    338:     case WM_CLOSE:
                    339:         KillTimer(hwnd, 1);
                    340:         /*
                    341:          * We do DDE cleanup here.  It is best to do DDE cleanup while
                    342:          * still in the message loop to allow DDEML to recieve messages
                    343:          * while shutting down.
                    344:          */
                    345:         DdeDisconnectList(hConvList);
                    346:         DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
                    347:         DdeFreeStringHandle(idInst, hszAppName);
                    348:         DdeUninitialize(idInst);
                    349:         PostQuitMessage(0);
                    350:         break;
                    351: 
                    352:     default:
                    353:         return (DefWindowProc(hwnd, message, wParam, lParam));
                    354:     }
                    355:     return(0);
                    356: }
                    357: 
                    358: 
                    359: VOID PaintDemo(
                    360: HWND hwnd)
                    361: {
                    362:     PAINTSTRUCT ps;
                    363:     RECT rc;
                    364:     HCONV hConv;
                    365:     CONVINFO ci;
                    366:     int cConvsOrg = cConvs;
                    367: 
                    368:     BeginPaint(hwnd, &ps);
                    369:     /*
                    370:      * Draw our data on top - Black for active, Grey for inactive.
                    371:      */
                    372:     SetRect(&rc, 0, 0, cxText, cyText);
                    373:     SetBkMode(ps.hdc, TRANSPARENT);
                    374:     SetTextColor(ps.hdc, 0x00FFFFFF);   // white text
                    375:     FillRect(ps.hdc, &rc, GetStockObject(fActive ? BLACK_BRUSH : GRAY_BRUSH));
                    376:     DrawText(ps.hdc, itoa(count, szT, 10), -1, &rc, DT_CENTER | DT_VCENTER);
                    377: 
                    378:     /*
                    379:      * Now draw the most recently recieved data from each server we are
                    380:      * connected to.
                    381:      */
                    382:     if (hConvList) {
                    383:         OffsetRect(&rc, 0, cyText);
                    384:         SetTextColor(ps.hdc, 0);    // draw black text
                    385:         cConvs = 0;
1.1.1.2 ! root      386:         hConv = DdeQueryNextServer(hConvList, 0);
1.1       root      387:         while (hConv) {
                    388:             cConvs++;
                    389:             /*
                    390:              * count how many conversations are active while we're at it.
                    391:              */
                    392:             ci.cb = sizeof(CONVINFO);
                    393:             DdeQueryConvInfo(hConv, QID_SYNC, &ci);
                    394:             FillRect(ps.hdc, &rc, GetStockObject(WHITE_BRUSH));  // white bkgnd
                    395:             DrawText(ps.hdc, itoa(ci.hUser, szT, 10), -1, &rc,
                    396:                     DT_CENTER | DT_VCENTER);
                    397:             OffsetRect(&rc, 0, cyText);
                    398:             hConv = DdeQueryNextServer(hConvList, hConv);
                    399:         }
                    400:     }
                    401:     EndPaint(hwnd, &ps);
                    402:     if (cConvsOrg != cConvs) {
                    403:         /*
                    404:          * The number of active conversations changed!  Resize to fit.
                    405:          */
                    406:         SetWindowPos(hwndMain, 0, 0, 0, cxText,
                    407:                 (cyText * (cConvs + 1)) + cyTitle,
                    408:                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
                    409:     }
                    410: }
                    411: 
                    412: 
                    413: 
                    414: /*
                    415:  * This is the main DDEML callback proc.  It handles all interaction with
                    416:  * DDEML that is DDEML originated.
                    417:  */
                    418: HDDEDATA CALLBACK DdeCallback(
                    419: WORD wType,
                    420: WORD wFmt,
                    421: HCONV hConv,
                    422: HSZ hszTopic,
                    423: HSZ hszItem,
                    424: HDDEDATA hData,
                    425: DWORD lData1,
                    426: DWORD lData2)
                    427: {
                    428:     LPTSTR pszExec;
                    429: 
                    430:     switch (wType) {
                    431:     case XTYP_CONNECT:
                    432:         /*
                    433:          * Only allow connections to us.  We can always return TRUE because
                    434:          * the CBF_FILTERINITS bit given to DdeInitialize() told DDEML to
                    435:          * never bother us with connections to any service names other than
                    436:          * what we have registered.
                    437:          *
                    438:          * Note that we do not handle the XTYP_WILD_CONNECT transaction.
                    439:          * This means that no wild-card initiates to us will work.
                    440:          */
                    441:         return(TRUE);
                    442: 
                    443:     case XTYP_ADVREQ:
                    444:     case XTYP_REQUEST:
                    445:         /*
                    446:          * These two transactions are the only ones that require us to
                    447:          * render our data.  By using a custom format, we don't have to
                    448:          * convert our count to text form to support CF_TEXT.
                    449:          */
                    450:         return(DdeCreateDataHandle(idInst, (PBYTE)&count, sizeof(count), 0,
                    451:                 hszAppName, OurFormat, 0));
                    452: 
                    453:     case XTYP_ADVSTART:
                    454:         /*
                    455:          * Only allow links to our Item in our format.
                    456:          */
1.1.1.2 ! root      457:         return((UINT)wFmt == OurFormat && hszItem == hszAppName);
1.1       root      458: 
                    459:     case XTYP_ADVDATA:
                    460:         /*
                    461:          * Data is comming in.  We don't bother with XTYP_POKE transactions,
                    462:          * but if we did, they would go here.  Since we only allow links
                    463:          * on our item and our format, we need not check these here.
                    464:          */
                    465:         if (DdeGetData(hData, (PBYTE)&InCount, sizeof(InCount), 0)) {
                    466:             DdeSetUserHandle(hConv, QID_SYNC, InCount);
                    467:         }
                    468:         /*
                    469:          * update ourselves to reflect the new incomming data.
                    470:          */
                    471:         InvalidateRect(hwndMain, NULL, TRUE);
                    472:         /*
                    473:          * This transaction requires a flag return value.  We could also
                    474:          * stick other status bits here if needed but its not recommended.
                    475:          */
                    476:         return(DDE_FACK);
                    477: 
                    478:     case XTYP_EXECUTE:
                    479:         /*
                    480:          * Another instance wants us to do something.  DdeAccessData()
                    481:          * makes parsing of execute strings easy.  Also note, that DDEML
                    482:          * will automatically give us the string in the right form
                    483:          * (UNICODE vs ASCII) depending on which form of DdeInitialize()
                    484:          * we called.
                    485:          */
                    486:         pszExec = DdeAccessData(hData, NULL);
                    487:         if (pszExec) {
                    488:             if (fActive && !stricmp("PAUSE", pszExec)) {
                    489:                 KillTimer(hwndMain, 1);
                    490:                 fActive = FALSE;
                    491:                 InvalidateRect(hwndMain, NULL, TRUE);
                    492:                 UpdateWindow(hwndMain);
                    493:             } else if (!fActive && !stricmp("RESUME", pszExec)) {
                    494:                 SetTimer(hwndMain, 1, BASE_TIMEOUT + (rand() & 0xff), NULL);
                    495:                 fActive = TRUE;
                    496:                 InvalidateRect(hwndMain, NULL, TRUE);
                    497:                 UpdateWindow(hwndMain);
                    498:             }
                    499:             /*
                    500:              * The beep gives good feedback on how fast the execute was.
                    501:              */
                    502:             MessageBeep(0);
                    503:         }
                    504:         break;
                    505: 
                    506:     case XTYP_DISCONNECT:
                    507:         /*
                    508:          * Somebody went away, repaint so we update our cConvs count.
                    509:          */
                    510:         InvalidateRect(hwndMain, NULL, TRUE);
                    511:         break;
                    512: 
                    513:     case XTYP_REGISTER:
                    514:         /*
                    515:          * Since a new server just arrived, lets make sure our links are
                    516:          * up to date.  Note that only one link on a
                    517:          * conversation/topic/item/format set will work anyway so we don't
                    518:          * worry about duplicate links.
                    519:          *
                    520:          * Note also that we are using hszItem - which is the InstanceSpecific
                    521:          * name of the server that is registering.  This greatly reduces the
                    522:          * number of messages that go flying around.
                    523:          */
                    524:         hConvList = DdeConnectList(idInst, hszItem, hszAppName, hConvList, NULL);
                    525:         BroadcastTransaction(NULL, 0, OurFormat, XTYP_ADVSTART);
                    526:         SetWindowPos(hwndMain, 0, 0, 0, cxText,
                    527:                 (cyText * (cConvs + 1)) + cyTitle, SWP_NOMOVE | SWP_NOZORDER);
                    528:         UpdateWindow(hwndMain);
                    529:         return(TRUE);
                    530:     }
                    531:     return(0);
                    532: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.