Annotation of mstools/samples/ddeml/ddemo/ddemo.c, revision 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.