home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / ipc / ddeml / ddemo / ddemo.c next >
C/C++ Source or Header  |  1997-10-05  |  18KB  |  555 lines

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