DDEMO.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:18k
源码类别:

Windows编程

开发平台:

Visual C++

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