|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.