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

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

unix.superglobalmegacorp.com

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