|
|
1.1 ! root 1: /***************************************************************************\ ! 2: * wmchar.c -- Displays WM_CHAR messages ! 3: * ! 4: * Created by Microsoft, IBM Corporation 1990 ! 5: * ! 6: * DISCLAIMER OF WARRANTIES. The following [enclosed] code is ! 7: * sample code created by Microsoft Corporation and/or IBM ! 8: * Corporation. This sample code is not part of any standard ! 9: * Microsoft or IBM product and is provided to you solely for ! 10: * the purpose of assisting you in the development of your ! 11: * applications. The code is provided "AS IS", without ! 12: * warranty of any kind. Neither Microsoft nor IBM shall be ! 13: * liable for any damages arising out of your use of the sample ! 14: * code, even if they have been advised of the possibility of ! 15: * such damages. ! 16: * ! 17: \***************************************************************************/ ! 18: ! 19: ! 20: #define INCL_WIN ! 21: #define INCL_GPILCIDS ! 22: #include <os2.h> ! 23: #include <string.h> ! 24: #include <stdio.h> ! 25: #include "wmchar.h" ! 26: ! 27: ! 28: #define max2(a, b) ((a) > (b) ? (a) : (b)) ! 29: #define min2(a, b) ((a) < (b) ? (a) : (b)) ! 30: ! 31: ! 32: /* display dependent values */ ! 33: SHORT gcyChar; ! 34: SHORT gcxAveChar; ! 35: SHORT gcxMaxChar; ! 36: SHORT gcxMargin; ! 37: ! 38: /* program globals */ ! 39: USHORT gcMessages = 0; /* also the "number" of the most recent message */ ! 40: CHAR gszNull[] = " --"; /* denotes an empty field */ ! 41: BOOL gfKeyUps = FALSE; ! 42: ! 43: ! 44: /* char messages are stored in a circular buffer */ ! 45: typedef struct _CHMSG { /* cm */ ! 46: USHORT usKC; ! 47: UCHAR uchRep; ! 48: UCHAR uchScan; ! 49: USHORT ch; ! 50: USHORT usVK; ! 51: } CHMSG; ! 52: ! 53: #define CBUFFERELTS 20 ! 54: ! 55: CHMSG gacm[CBUFFERELTS]; ! 56: SHORT gicmStart = 0; ! 57: ! 58: ! 59: /* display field attributes are stored in an array */ ! 60: typedef struct _MSGFIELD { /* mf */ ! 61: BOOL fDisplay; /* TRUE if field is currently being displayed */ ! 62: SHORT cxWidth; /* field width, initially chars, then pixels */ ! 63: CHAR *szHeading; /* field heading */ ! 64: } MSGFIELD; ! 65: ! 66: #define INUMBER 0 ! 67: #define IVIRTUALKEY 1 ! 68: #define ICHAR 2 ! 69: #define ISCANCODE 3 ! 70: #define IREPEAT 4 ! 71: #define IFLAGS 5 ! 72: ! 73: MSGFIELD gamf[CDISPLAYFIELDS] = { ! 74: TRUE, 7, " #", ! 75: TRUE, 15, "VK_ Value", ! 76: TRUE, 14, "Char", ! 77: FALSE, 10, "Scan", ! 78: FALSE, 8, "Rep", ! 79: TRUE, 0, "KC_ Flags" /* variable width, so it's last */ ! 80: }; ! 81: ! 82: SHORT gcxMargin = 3; ! 83: ! 84: ! 85: /* char flag strings */ ! 86: CHAR *gachFlags[] = { ! 87: "char", ! 88: "virtualkey", ! 89: "scancode", ! 90: "shift", ! 91: "ctrl", ! 92: "alt", ! 93: "keyup", ! 94: "prevdown", ! 95: "lonekey", ! 96: "deadkey", ! 97: "composite", ! 98: "invalidcomp", ! 99: "toggle", ! 100: "invalidchar", ! 101: "dbcsrsrvd1", ! 102: "dbcsrsrvd2" ! 103: }; ! 104: ! 105: /* virtual key strings */ ! 106: CHAR *gachVK[] = { ! 107: gszNull, ! 108: "button1", ! 109: "button2", ! 110: "button3", ! 111: "break", ! 112: "backspace", ! 113: "tab", ! 114: "backtab", ! 115: "newline", ! 116: "shift", ! 117: "ctrl", ! 118: "alt", ! 119: "altgraf", ! 120: "pause", ! 121: "capslock", ! 122: "esc", ! 123: "space", ! 124: "pageup", ! 125: "pagedown", ! 126: "end", ! 127: "home", ! 128: "left", ! 129: "up", ! 130: "right", ! 131: "down", ! 132: "printscrn", ! 133: "insert", ! 134: "delete", ! 135: "scrllock", ! 136: "numlock", ! 137: "enter", ! 138: "sysrq", ! 139: "f1", ! 140: "f2", ! 141: "f3", ! 142: "f4", ! 143: "f5", ! 144: "f6", ! 145: "f7", ! 146: "f8", ! 147: "f9", ! 148: "f10", ! 149: "f11", ! 150: "f12", ! 151: "f13", ! 152: "f14", ! 153: "f15", ! 154: "f16", ! 155: "f17", ! 156: "f18", ! 157: "f19", ! 158: "f20", ! 159: "f21", ! 160: "f22", ! 161: "f23", ! 162: "f24" ! 163: }; ! 164: ! 165: ! 166: ! 167: /***************************************************************************\ ! 168: * DrawText ! 169: * ! 170: * This function displays a text string. ! 171: * ! 172: * Parameters: ! 173: * hps: presentation space ! 174: * cx, cy: point to begin drawing ! 175: * sz: string to draw ! 176: * iColor: text color ! 177: * fExtent: whether to calc extent or not ! 178: * ! 179: * Returns: ! 180: * x position of end of painted string if fExtent == TRUE ! 181: \***************************************************************************/ ! 182: ! 183: SHORT DrawText( ! 184: HPS hps, ! 185: SHORT cx, ! 186: SHORT cy, ! 187: CHAR *sz, ! 188: LONG iColor, ! 189: BOOL fExtent) ! 190: { ! 191: RECTL rcl; ! 192: SHORT cch; ! 193: ! 194: cch = strlen(sz); ! 195: ! 196: rcl.xLeft = cx; ! 197: rcl.xRight = cx + cch * gcxMaxChar; /* hack */ ! 198: rcl.yBottom = cy; ! 199: rcl.yTop = cy + gcyChar; ! 200: ! 201: WinDrawText(hps, cch, (PCH)sz, (PRECTL)&rcl, iColor, SYSCLR_WINDOW, 0); ! 202: ! 203: if (fExtent) { ! 204: WinDrawText(hps, cch, (PCH)sz, (PRECTL)&rcl, iColor, ! 205: SYSCLR_WINDOW, DT_QUERYEXTENT); ! 206: return (SHORT)rcl.xRight; ! 207: } ! 208: ! 209: return 0; ! 210: } ! 211: ! 212: ! 213: ! 214: /***************************************************************************\ ! 215: * FormatHexChar ! 216: * ! 217: * This function formats an unsigned hex number into a string. It is needed ! 218: * to get around shortcomings of sprintf(). ! 219: * ! 220: * Parameters: ! 221: * uch: number to format ! 222: * sz: string to get number ! 223: \***************************************************************************/ ! 224: ! 225: VOID FormatHexChar( ! 226: USHORT uch, ! 227: CHAR *sz) ! 228: { ! 229: sprintf(sz, "0x%2x", uch); ! 230: ! 231: /* patch up hex byte display */ ! 232: if (strlen(sz) > 4) { ! 233: /* remove sign extension of byte */ ! 234: sz[2] = sz[4]; ! 235: sz[3] = sz[5]; ! 236: sz[4] = 0; ! 237: } else if (sz[2] == ' ') { ! 238: /* fill in blank with 0 */ ! 239: sz[2] = '0'; ! 240: } ! 241: } ! 242: ! 243: ! 244: ! 245: /***************************************************************************\ ! 246: * DrawMessage ! 247: * ! 248: * This function draws a single message line. ! 249: * ! 250: * Parameters: ! 251: * hps: presentation space ! 252: * iMessage: message number to display ! 253: * pcm: pointer to message structure ! 254: * cy: y position of message ! 255: \***************************************************************************/ ! 256: ! 257: VOID DrawMessage( ! 258: HPS hps, ! 259: SHORT iMessage, ! 260: CHMSG *pcm, ! 261: SHORT cy) ! 262: { ! 263: CHAR sz[16]; ! 264: USHORT fsFlags; ! 265: SHORT i; ! 266: SHORT cx; ! 267: ! 268: cx = gcxMargin; ! 269: ! 270: if (gamf[INUMBER].fDisplay) { ! 271: /* draw message number */ ! 272: sprintf(sz, "%u.", iMessage); ! 273: DrawText(hps, cx, cy, sz, SYSCLR_WINDOWTEXT, FALSE); ! 274: cx += gamf[INUMBER].cxWidth; ! 275: } ! 276: ! 277: if (gamf[IVIRTUALKEY].fDisplay) { ! 278: /* draw virtual key code */ ! 279: DrawText(hps, cx, cy, gachVK[pcm->usVK], SYSCLR_WINDOWTEXT, ! 280: FALSE); ! 281: cx += gamf[IVIRTUALKEY].cxWidth; ! 282: } ! 283: ! 284: if (gamf[ICHAR].fDisplay) { ! 285: /* draw character */ ! 286: if ((CHAR)pcm->ch) { ! 287: FormatHexChar(pcm->ch, sz); ! 288: DrawText(hps, cx, cy, sz, SYSCLR_WINDOWTEXT, FALSE); ! 289: sprintf(sz, "'%c'", (CHAR)pcm->ch); ! 290: DrawText(hps, cx + 7 * gcxAveChar, cy, sz, SYSCLR_WINDOWTEXT, ! 291: FALSE); ! 292: } else { ! 293: DrawText(hps, cx, cy, gszNull, SYSCLR_WINDOWTEXT, FALSE); ! 294: } ! 295: cx += gamf[ICHAR].cxWidth; ! 296: } ! 297: ! 298: if (gamf[ISCANCODE].fDisplay) { ! 299: if (pcm->uchScan) { ! 300: FormatHexChar(pcm->uchScan, sz); ! 301: DrawText(hps, cx, cy, sz, SYSCLR_WINDOWTEXT, FALSE); ! 302: } else { ! 303: DrawText(hps, cx, cy, gszNull, SYSCLR_WINDOWTEXT, FALSE); ! 304: } ! 305: cx += gamf[ISCANCODE].cxWidth; ! 306: } ! 307: ! 308: if (gamf[IREPEAT].fDisplay) { ! 309: sprintf(sz, "%u", pcm->uchRep); ! 310: DrawText(hps, cx, cy, sz, SYSCLR_WINDOWTEXT, FALSE); ! 311: cx += gamf[IREPEAT].cxWidth; ! 312: } ! 313: ! 314: if (gamf[IFLAGS].fDisplay) { ! 315: /* draw KC_ flags */ ! 316: fsFlags = pcm->usKC; ! 317: for (i = 0; i < 16; i++) { ! 318: if (fsFlags & 1) { ! 319: switch (i) { ! 320: case 0: ! 321: if (gamf[ICHAR].fDisplay) ! 322: goto DF_PRINTIT; ! 323: break; ! 324: case 1: ! 325: if (gamf[IVIRTUALKEY].fDisplay) ! 326: goto DF_PRINTIT; ! 327: break; ! 328: case 2: ! 329: if (gamf[ISCANCODE].fDisplay) ! 330: goto DF_PRINTIT; ! 331: break; ! 332: default: ! 333: DF_PRINTIT: ! 334: cx = DrawText(hps, cx, cy, gachFlags[i], SYSCLR_WINDOWTEXT, TRUE); ! 335: cx += gcxAveChar; ! 336: } ! 337: } ! 338: fsFlags >>= 1; ! 339: } ! 340: cx += gamf[IFLAGS].cxWidth; ! 341: } ! 342: } ! 343: ! 344: ! 345: ! 346: /***************************************************************************\ ! 347: * PaintClient ! 348: * ! 349: * This function paints a client window. ! 350: * ! 351: * Parameters: ! 352: * hwnd: window handle ! 353: \***************************************************************************/ ! 354: ! 355: VOID PaintClient( ! 356: HWND hwnd) ! 357: { ! 358: HPS hps; ! 359: RECTL rcl; ! 360: SHORT iMessage; ! 361: SHORT yBottom; ! 362: SHORT cx; ! 363: SHORT i; ! 364: ! 365: hps = WinBeginPaint(hwnd, (HPS)NULL, (PRECTL)&rcl); ! 366: ! 367: /* clear invalidated region */ ! 368: WinFillRect(hps, (PRECTL)&rcl, SYSCLR_WINDOW); ! 369: ! 370: /* get y position to start drawing */ ! 371: WinQueryWindowRect(hwnd, (PRECTL)&rcl); ! 372: yBottom = (SHORT)(rcl.yTop - gcyChar - gcyChar / 2); ! 373: ! 374: /* draw headings */ ! 375: cx = gcxMargin; ! 376: for (i = 0; i < CDISPLAYFIELDS; i++) { ! 377: if (gamf[i].fDisplay) { ! 378: DrawText(hps, cx, yBottom, gamf[i].szHeading, ! 379: SYSCLR_WINDOWSTATICTEXT, FALSE); ! 380: cx += gamf[i].cxWidth; ! 381: } ! 382: } ! 383: ! 384: /* draw each entry */ ! 385: i = gicmStart; ! 386: iMessage = gcMessages; ! 387: yBottom -= (gcyChar + gcyChar / 2); ! 388: do { ! 389: if (!iMessage || yBottom < -gcyChar) ! 390: break; ! 391: ! 392: DrawMessage(hps, iMessage, &gacm[i], yBottom); ! 393: ! 394: i = (i + 1) % CBUFFERELTS; ! 395: iMessage--; ! 396: yBottom -= gcyChar; ! 397: ! 398: } while (i != gicmStart); ! 399: ! 400: WinEndPaint(hps); ! 401: } ! 402: ! 403: ! 404: ! 405: /***************************************************************************\ ! 406: * NewMessage ! 407: * ! 408: * This function adds a new WM_CHAR message to the list. ! 409: * ! 410: * Parameters: ! 411: * hwnd: window handle ! 412: * mp1, mp2: ! 413: * usKC: key flags ! 414: * usVK: virtual key code ! 415: * ch: char; ! 416: \***************************************************************************/ ! 417: ! 418: VOID NewMessage( ! 419: HWND hwnd, ! 420: MPARAM mp1, ! 421: MPARAM mp2) ! 422: { ! 423: RECTL rclScroll, rclUpdate; ! 424: HPS hps; ! 425: ! 426: /* overwrite oldest entry */ ! 427: if (gicmStart) ! 428: gicmStart--; ! 429: else ! 430: gicmStart = CBUFFERELTS - 1; ! 431: ! 432: gacm[gicmStart].usKC = SHORT1FROMMP(mp1); ! 433: gacm[gicmStart].uchRep = CHAR3FROMMP(mp1); ! 434: gacm[gicmStart].uchScan = CHAR4FROMMP(mp1); ! 435: gacm[gicmStart].ch = SHORT1FROMMP(mp2); ! 436: gacm[gicmStart].usVK = SHORT2FROMMP(mp2); ! 437: ! 438: gcMessages++; ! 439: ! 440: /* scroll and update intelligently */ ! 441: WinQueryWindowRect(hwnd, (PRECTL)&rclScroll); ! 442: rclScroll.yTop -= 2 * gcyChar; ! 443: rclScroll.yBottom = max2(0L, rclScroll.yTop - (LONG)(CBUFFERELTS * gcyChar)); ! 444: ! 445: WinScrollWindow(hwnd, 0, -gcyChar, (PRECTL)&rclScroll, (PRECTL)&rclScroll, ! 446: NULL, &rclUpdate, 0); ! 447: ! 448: hps = WinGetPS(hwnd); ! 449: WinFillRect(hps, (PRECTL)&rclUpdate, SYSCLR_WINDOW); ! 450: DrawMessage( hps ! 451: , gcMessages ! 452: , &gacm[gicmStart] ! 453: , (SHORT)rclScroll.yTop - gcyChar ); ! 454: WinReleasePS(hps); ! 455: } ! 456: ! 457: ! 458: ! 459: /***************************************************************************\ ! 460: * CheckMenuItem ! 461: * ! 462: * This function sets the check state of a menu item. ! 463: * ! 464: * Parameters: ! 465: * hwnd: frame window ! 466: * id: menu id ! 467: * fCheck: check state ! 468: \***************************************************************************/ ! 469: ! 470: VOID CheckMenuItem( ! 471: HWND hwndFrame, ! 472: SHORT id, ! 473: BOOL fCheck) ! 474: { ! 475: HWND hwndMenu; ! 476: ! 477: /* toggle menu checkmark */ ! 478: hwndMenu = WinWindowFromID(hwndFrame, FID_MENU); ! 479: WinSendMsg(hwndMenu, MM_SETITEMATTR, MPFROM2SHORT(id, TRUE), ! 480: MPFROM2SHORT(MIA_CHECKED, fCheck ? MIA_CHECKED : 0)); ! 481: } ! 482: ! 483: ! 484: ! 485: /***************************************************************************\ ! 486: * MenuCommand ! 487: * ! 488: * This function processes WM_COMMAND messages. ! 489: * ! 490: * Parameters: ! 491: * hwnd: client window handle ! 492: * id: command identifier ! 493: \***************************************************************************/ ! 494: ! 495: VOID MenuCommand( ! 496: HWND hwnd, ! 497: SHORT id) ! 498: { ! 499: BOOL *pfDisplay; ! 500: HWND hwndFrame; ! 501: ! 502: if (id == IDM_CLEAR) { ! 503: ! 504: /* clear current messages */ ! 505: gcMessages = 0; ! 506: WinInvalidateRect(hwnd, NULL, FALSE); ! 507: ! 508: } else if (id == IDM_KEYUPS) { ! 509: ! 510: /* toggle logging keyups */ ! 511: gfKeyUps = !gfKeyUps; ! 512: hwndFrame = WinQueryWindow(hwnd, QW_PARENT, FALSE); ! 513: CheckMenuItem(hwndFrame, IDM_KEYUPS, gfKeyUps); ! 514: ! 515: } else if (id >= IDM_DISPLAYFIRST && id <= IDM_DISPLAYLAST) { ! 516: ! 517: /* toggle display field */ ! 518: pfDisplay = &(gamf[id - IDM_DISPLAYFIRST].fDisplay); ! 519: *pfDisplay = !*pfDisplay; ! 520: hwndFrame = WinQueryWindow(hwnd, QW_PARENT, FALSE); ! 521: CheckMenuItem(hwndFrame, id, *pfDisplay); ! 522: ! 523: /* repaint in new format */ ! 524: WinInvalidateRect(hwnd, NULL, FALSE); ! 525: } ! 526: } ! 527: ! 528: ! 529: ! 530: /***************************************************************************\ ! 531: * MainWndProc ! 532: * ! 533: * This is the window procedure for the main client window. ! 534: \***************************************************************************/ ! 535: ! 536: MRESULT EXPENTRY MainWndProc( ! 537: HWND hwnd, ! 538: USHORT msg, ! 539: MPARAM mp1, ! 540: MPARAM mp2) ! 541: { ! 542: switch (msg) { ! 543: ! 544: case WM_PAINT: ! 545: PaintClient(hwnd); ! 546: break; ! 547: ! 548: case WM_COMMAND: ! 549: MenuCommand(hwnd, SHORT1FROMMP(mp1)); ! 550: break; ! 551: ! 552: case WM_CHAR: ! 553: if (gfKeyUps || !(KC_KEYUP & SHORT1FROMMP(mp1))) { ! 554: ! 555: /* add new message to list */ ! 556: NewMessage(hwnd, mp1, mp2); ! 557: } ! 558: break; ! 559: ! 560: default: ! 561: return WinDefWindowProc(hwnd, msg, mp1, mp2); ! 562: } ! 563: ! 564: return (MRESULT)0; ! 565: } ! 566: ! 567: ! 568: ! 569: /***************************************************************************\ ! 570: * CheckMenus ! 571: * ! 572: * This function sets menu check marks according to their current internal ! 573: * state. ! 574: * ! 575: * Parameters: ! 576: * hwndFrame: main app window handle ! 577: \***************************************************************************/ ! 578: ! 579: VOID CheckMenus( ! 580: HWND hwndFrame) ! 581: { ! 582: SHORT id; ! 583: ! 584: CheckMenuItem(hwndFrame, IDM_KEYUPS, gfKeyUps); ! 585: ! 586: for (id = IDM_DISPLAYFIRST; id <= IDM_DISPLAYLAST; id++) { ! 587: CheckMenuItem(hwndFrame, id, gamf[id - IDM_DISPLAYFIRST].fDisplay); ! 588: } ! 589: } ! 590: ! 591: ! 592: ! 593: /***************************************************************************\ ! 594: * RegisterSwitchEntry ! 595: * ! 596: * This function registers the app with the switch list. ! 597: * ! 598: * Parameters: ! 599: * hwndFrame: main app window handle ! 600: \***************************************************************************/ ! 601: ! 602: VOID RegisterSwitchEntry( ! 603: HWND hwndFrame) ! 604: { ! 605: PID pid; ! 606: TID tid; ! 607: SWCNTRL swc; ! 608: ! 609: WinQueryWindowProcess(hwndFrame, (PPID)&pid, (PTID)&tid); ! 610: WinQueryWindowText(hwndFrame, MAXNAMEL, (PSZ)swc.szSwtitle); ! 611: swc.hwnd = hwndFrame; ! 612: swc.hwndIcon = (ULONG)NULL; ! 613: swc.hprog = (HPROGRAM)NULL; ! 614: swc.idProcess = pid; ! 615: swc.idSession = NULL; ! 616: swc.uchVisibility = SWL_VISIBLE; ! 617: swc.fbJump = SWL_JUMPABLE; ! 618: WinAddSwitchEntry((PSWCNTRL)&swc); ! 619: } ! 620: ! 621: ! 622: ! 623: /***************************************************************************\ ! 624: * GetDisplayValues ! 625: * ! 626: * This function sets display dependent global variables. ! 627: \***************************************************************************/ ! 628: ! 629: VOID GetDisplayValues(VOID) ! 630: { ! 631: HPS hps; ! 632: FONTMETRICS fm; ! 633: SHORT i; ! 634: ! 635: hps = WinGetPS(HWND_DESKTOP); ! 636: GpiQueryFontMetrics(hps, (ULONG)sizeof(FONTMETRICS), (PFONTMETRICS)&fm); ! 637: WinReleasePS(hps); ! 638: ! 639: gcyChar = (SHORT)(fm.lMaxBaselineExt + fm.lExternalLeading); ! 640: gcxAveChar = (SHORT)fm.lAveCharWidth; ! 641: gcxMaxChar = (SHORT)fm.lMaxCharInc; ! 642: ! 643: /* adjust display widths for this font */ ! 644: gcxMargin *= gcxAveChar; ! 645: for (i = 0; i < CDISPLAYFIELDS; i++) { ! 646: gamf[i].cxWidth *= gcxAveChar; ! 647: } ! 648: } ! 649: ! 650: ! 651: ! 652: /***************************************************************************\ ! 653: * main ! 654: * ! 655: * This is the main procedure for the app. ! 656: \***************************************************************************/ ! 657: ! 658: VOID cdecl main(VOID) ! 659: { ! 660: HAB hab; ! 661: HMQ hmq; ! 662: QMSG qmsg; ! 663: ULONG ulCreate; ! 664: HWND hwndFrame; ! 665: HWND hwndClient; ! 666: static CHAR szApp[] = "Char Messages"; ! 667: ! 668: /* init app */ ! 669: hab = WinInitialize(NULL); ! 670: hmq = WinCreateMsgQueue(hab, 0); ! 671: GetDisplayValues(); ! 672: ! 673: /* create main window */ ! 674: WinRegisterClass(hab, szApp, MainWndProc, 0L, 0); ! 675: ulCreate = FCF_TITLEBAR | FCF_SYSMENU | FCF_MINMAX | FCF_SIZEBORDER | ! 676: FCF_MENU | FCF_SHELLPOSITION | FCF_ICON; ! 677: hwndFrame = WinCreateStdWindow(HWND_DESKTOP, WS_VISIBLE, &ulCreate, ! 678: szApp, szApp, 0L, NULL, ID_RESOURCES, ! 679: &hwndClient); ! 680: ! 681: /* register with switch list */ ! 682: RegisterSwitchEntry(hwndFrame); ! 683: ! 684: /* initialize menu */ ! 685: CheckMenus(hwndFrame); ! 686: ! 687: /* message loop */ ! 688: while (WinGetMsg(hab, &qmsg, NULL, 0, 0)) ! 689: WinDispatchMsg(hab, &qmsg); ! 690: ! 691: /* clean up */ ! 692: WinDestroyWindow(hwndFrame); ! 693: WinDestroyMsgQueue(hmq); ! 694: WinTerminate(hab); ! 695: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.