// Borland C++ - (C) Copyright 1991, 1992 by Borland International //******************************************************************* // // program - Msgwnd.c // purpose - A windows program to receive and display messages. // This illustrates how DDE messages can be sent from // one application and received by another. // // To use this program, build MSGWND and TSTAPP. There // are project files for this. When you run TSTAPP, MSGWND // will be started automatically. // //******************************************************************* #define STRICT #include #include #include #include #include #include #include #include #include #include #include #include "msgwnd.h" #include "msgproc.h" #include "ddesrvr.h" #define MAX_QRT_LEN 100 typedef struct scrollkeys { WORD wVirtkey; int iMessage; WORD wRequest; } SCROLLKEYS; SCROLLKEYS key2scroll [] = { { VK_HOME, WM_COMMAND, IDM_HOME }, { VK_END, WM_VSCROLL, SB_BOTTOM }, { VK_PRIOR, WM_VSCROLL, SB_PAGEUP }, { VK_NEXT, WM_VSCROLL, SB_PAGEDOWN }, { VK_UP, WM_VSCROLL, SB_LINEUP }, { VK_DOWN, WM_VSCROLL, SB_LINEDOWN }, { VK_LEFT, WM_HSCROLL, SB_PAGEUP }, { VK_RIGHT, WM_HSCROLL, SB_PAGEDOWN } }; # define NUMKEYS (sizeof key2scroll / sizeof key2scroll[0]) // data initialized by first instance typedef struct tagSETUPDATA { char szAppName[10]; // name of application } SETUPDATA; SETUPDATA SetUpData; // Data that can be referenced throughout the // program but not passed to other instances HINSTANCE hInst; // hInstance of application HWND hWndMain; // hWnd of main window int xChar, yChar, yCharnl; int xClient, yClient; LOGFONT cursfont; HFONT holdsfont; HFONT hnewsfont; RECT wrect; // window scroll/paint stuff int nVscrollMax, nHscrollMax; int nVscrollPos, nHscrollPos; int numlines; int maxwidth; int nVscrollInc, nHscrollInc; int nPaintBeg, nPaintEnd; int nPageMaxLines; int plines; // for scroll print RECT rect; int blanklen; char blanks[256]; // to keep lines # define MAX_KEEP 50 HANDLE hkeep[MAX_KEEP + 1]; int hwm_keep; // function prototypes int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int cmdShow); BOOL InitMsgwnd(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int cmdShow); BOOL InitMsgwndFirst(HINSTANCE hInstance); BOOL InitMsgwndEvery(HINSTANCE hInstance, int cmdShow); void CloseMsgwnd(HWND hWnd); BOOL CALLBACK _export About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK _export MsgwndWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void SetupScroll(HWND hWnd); void MsgwndPaint(HWND hWnd); void ScrollPrint(HWND hWnd, char *str); void ClearKeep(void); //******************************************************************* // WinMain - Msgwnd main // // paramaters: // hInstance - The instance of this instance of this // application. // hPrevInstance - The instance of the previous instance // of this application. This will be 0 // if this is the first instance. // lpszCmdLine - A long pointer to the command line that // started this application. // cmdShow - Indicates how the window is to be shown // initially. ie. SW_SHOWNORMAL, SW_HIDE, // SW_MIMIMIZE. // // returns: // wParam from last message. // //******************************************************************* int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int cmdShow) { MSG msg; BOOL brc; brc = InitMsgwnd(hInstance, hPrevInstance, lpszCmdLine, cmdShow); if (brc == FALSE) return(0); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return(msg.wParam); } #if !defined(__WIN32__) //******************************************************************* // InitMsgwndAdded - done only for added instances of Msgwnd // // paramaters: // hPrevInstance - The instance of the previous instance // of this application. // // returns: // FALSE to indicate multiple instances are not allowed. // //******************************************************************* BOOL InitMsgwndAdded(HINSTANCE hPrevInstance) { // get the results of the initialization of first instance GetInstanceData(hPrevInstance, (BYTE*) &SetUpData, sizeof(SETUPDATA)); return(FALSE); // We are not allowing multiple MsgWnd apps. } #endif //******************************************************************* //******************************************************************* // InitMsgwnd - init the Msgwnd application // // paramaters: // hInstance - The instance of this instance of this // application. // hPrevInstance - The instance of the previous instance // of this application. This will be 0 // if this is the first instance. // lpszCmdLine - A long pointer to the command line that // started this application. // cmdShow - Indicates how the window is to be shown // initially. ie. SW_SHOWNORMAL, SW_HIDE, // SW_MIMIMIZE. // // returns: // TRUE if successful. // or // FALSE if failure. // //******************************************************************* # pragma argsused BOOL InitMsgwnd(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int cmdShow) { BOOL brc; #if defined(__WIN32__) brc = InitMsgwndFirst(hInstance); #else if (! hPrevInstance) brc = InitMsgwndFirst(hInstance); else brc = InitMsgwndAdded(hPrevInstance); #endif if (brc == TRUE) brc = InitMsgwndEvery(hInstance, cmdShow); return(brc); } //******************************************************************* // InitMsgwndFirst - done only for first instance of Msgwnd // // paramaters: // hInstance - The instance of this instance of this // application. // // returns: // TRUE if successful. // or // FALSE if failure. // //******************************************************************* BOOL InitMsgwndFirst(HINSTANCE hInstance) { WNDCLASS wcMsgwndClass; LoadString(hInstance, IDS_NAME, (LPSTR) SetUpData.szAppName, 10); // fill in window class information wcMsgwndClass.lpszClassName = SetUpData.szAppName; wcMsgwndClass.hInstance = hInstance; wcMsgwndClass.lpfnWndProc = MsgwndWndProc; wcMsgwndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wcMsgwndClass.hIcon = LoadIcon(hInstance, SetUpData.szAppName); wcMsgwndClass.lpszMenuName = (LPSTR) SetUpData.szAppName; wcMsgwndClass.hbrBackground = GetStockObject(WHITE_BRUSH); wcMsgwndClass.style = CS_HREDRAW | CS_VREDRAW; wcMsgwndClass.cbClsExtra = 0; wcMsgwndClass.cbWndExtra = 0; // register the class if (!RegisterClass(&wcMsgwndClass)) return(FALSE); return(TRUE); } //******************************************************************* // InitMsgwndEvery - done for every instance of Msgwnd // // paramaters: // hInstance - The instance of this instance of this // application. // cmdShow - Indicates how the window is to be shown // initially. ie. SW_SHOWNORMAL, SW_HIDE, // SW_MIMIMIZE. // // returns: // TRUE if successful. // //******************************************************************* BOOL InitMsgwndEvery(HINSTANCE hInstance, int cmdShow) { TEXTMETRIC tm; HDC hDC; char *cp; char szWinLocStr[40]; int x, y, w, h; hInst = hInstance; // save for use by window procs // Get last location and size of Msgwnd from WIN.INI file. GetProfileString("Msgwnd", "Window", "0,160,500,150", szWinLocStr, sizeof(szWinLocStr)); // Decode location and size of window from profile string. cp = szWinLocStr; x = (int) strtol(cp, &cp, 10); cp++; y = (int) strtol(cp, &cp, 10); cp++; w = (int) strtol(cp, &cp, 10); cp++; h = (int) strtol(cp, &cp, 10); // Create our window. hWndMain = CreateWindow( SetUpData.szAppName, // window class name SetUpData.szAppName, // window title WS_OVERLAPPEDWINDOW | // type of window WS_HSCROLL | WS_VSCROLL, x, // x window location y, // y w, // cx and size h, // cy NULL, // no parent for this window NULL, // use the class menu hInstance, // who created this window NULL // no parms to pass on ); // Get window display context. hDC = GetDC(hWndMain); // Build fixed screen font. cursfont.lfHeight = 6; cursfont.lfWidth = 6; cursfont.lfEscapement = 0; cursfont.lfOrientation = 0; cursfont.lfWeight = FW_NORMAL; cursfont.lfItalic = FALSE; cursfont.lfUnderline = FALSE; cursfont.lfStrikeOut = FALSE; cursfont.lfCharSet = ANSI_CHARSET; cursfont.lfOutPrecision = OUT_DEFAULT_PRECIS; cursfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; cursfont.lfQuality = DEFAULT_QUALITY; cursfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; strcpy((char *)cursfont.lfFaceName, "System"); hnewsfont = CreateFontIndirect((LPLOGFONT) &cursfont); // Install fixed font in display context so text metrics will reflect // that font. holdsfont = SelectObject(hDC, hnewsfont); // Get text metrics to be used when painting the window. GetTextMetrics(hDC, &tm); xChar = tm.tmAveCharWidth; yChar = tm.tmHeight + tm.tmExternalLeading; yCharnl = tm.tmHeight; // Init a blank line. blanklen = 255; memset(blanks, ' ', blanklen); // Release the display context. ReleaseDC(hWndMain, hDC); ShowWindow(hWndMain, cmdShow); UpdateWindow(hWndMain); return(TRUE); } //******************************************************************* // CloseMsgwnd - done at termination of every instance of Msgwnd // // paramaters: // hWnd - The callers window handle. // //******************************************************************* void CloseMsgwnd(HWND hWnd) { RECT wrect; char szWinLocStr[40]; int w, h; // Clear out kept strings. ClearKeep(); // Get location and size of our window on the screen so we can // come back up in the same spot next time we are invoked. GetWindowRect(hWnd, (LPRECT) &wrect); w = wrect.right - wrect.left; h = wrect.bottom - wrect.top; // Make a string with our window location and size. sprintf(szWinLocStr, "%d,%d,%d,%d", wrect.left, wrect.top, w, h); // Save in WIN.INI file. WriteProfileString("Msgwnd", "Window", szWinLocStr); // Delete font DeleteObject(hnewsfont); } //******************************************************************* // About - handle about dialog messages // // paramaters: // hDlg - The window handle for this dialog box // message - The message number // wParam - The WPARAM parameter for this message // lParam - The LPARAM parameter for this message // // returns: // Depends on message. // //******************************************************************* # pragma argsused BOOL CALLBACK _export About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_INITDIALOG) return(TRUE); else if (message == WM_COMMAND) { switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDOK: EndDialog(hDlg, TRUE); return(TRUE); default: return(TRUE); } } return(FALSE); } //******************************************************************* //******************************************************************* // MsgwndWndProc - every message for this instance will come here // // Handle the messages for this application. // // paramaters: // hWnd - The window handle for this message // message - The message number // wParam - The WPARAM parameter for this message // lParam - The LPARAM parameter for this message // // returns: // Depends on message. // //******************************************************************* LRESULT CALLBACK _export MsgwndWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { DLGPROC lpproc; int i; HWND hClientWnd; switch (message) { case WM_DDE_INITIATE: // If this message is recieved, it is being sent by another // applicaton that is looking for a server on a particular // application/topic combination. // Get the handle of this potential client. hClientWnd = (HWND) wParam; // Go see if we support the application and topic he wants. DDEServerInit(hWnd, hClientWnd, lParam, "msgwnd", "screen"); break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDM_QUIT: // User selected Quit on menu PostMessage(hWnd, WM_CLOSE, 0, 0L); break; case IDM_HOME: // Used to implement home to topleft from keyboard. SendMessage(hWnd, WM_HSCROLL, GET_WM_HSCROLL_MPS (SB_TOP, 0, 0)); SendMessage(hWnd, WM_VSCROLL, GET_WM_VSCROLL_MPS (SB_TOP, 0, 0)); break; case IDM_ABOUT: // Display about box. lpproc = (DLGPROC)MakeProcInstance((FARPROC)About, hInst); DialogBox(hInst, MAKEINTRESOURCE(ABOUT), hWnd, lpproc); #pragma warn -eff FreeProcInstance((FARPROC)lpproc); #pragma warn .eff break; default: break; } break; case WM_SIZE: // Save size of window client area. yClient = HIWORD(lParam); xClient = LOWORD(lParam); // Go setup scroll ranges and file display area based upon // client area size. SetupScroll(hWnd); break; case WM_VSCROLL: // React to the various vertical scroll related actions. switch (GET_WM_VSCROLL_CODE(wParam, lParam)) { case SB_TOP: nVscrollInc = -nVscrollPos; break; case SB_BOTTOM: nVscrollInc = nVscrollMax - nVscrollPos; break; case SB_LINEUP: nVscrollInc = -1; break; case SB_LINEDOWN: nVscrollInc = 1; break; case SB_PAGEUP: nVscrollInc = -max(1, yClient / yChar); break; case SB_PAGEDOWN: nVscrollInc = max(1, yClient / yChar); break; case SB_THUMBPOSITION: nVscrollInc = GET_WM_VSCROLL_POS(wParam, lParam) - nVscrollPos; break; case SB_THUMBTRACK: nVscrollInc = GET_WM_VSCROLL_POS(wParam, lParam) - nVscrollPos; break; default: nVscrollInc = 0; } nVscrollInc = max(-nVscrollPos, min(nVscrollInc, nVscrollMax - nVscrollPos)); if (nVscrollInc) { nVscrollPos += nVscrollInc; ScrollWindow(hWnd, 0, -yChar * nVscrollInc, NULL, NULL); SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE); UpdateWindow(hWnd); } break; case WM_HSCROLL: // React to the various horizontal scroll related actions. switch (GET_WM_HSCROLL_CODE(wParam, lParam)) { case SB_LINEUP: nHscrollInc = -1; break; case SB_LINEDOWN: nHscrollInc = 1; break; case SB_PAGEUP: nHscrollInc = -8; break; case SB_PAGEDOWN: nHscrollInc = 8; break; case SB_THUMBPOSITION: nHscrollInc = GET_WM_HSCROLL_POS(wParam, lParam) - nHscrollPos; break; case SB_THUMBTRACK: nHscrollInc = GET_WM_HSCROLL_POS(wParam, lParam) - nHscrollPos; break; default: nHscrollInc = 0; } nHscrollInc = max(-nHscrollPos, min(nHscrollInc, nHscrollMax - nHscrollPos)); if (nHscrollInc) { nHscrollPos += nHscrollInc; ScrollWindow(hWnd, -xChar * nHscrollInc, 0, NULL, NULL); SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE); UpdateWindow(hWnd); } break; case WM_KEYDOWN: // Translate various keydown messages to appropriate horizontal // and vertical scroll actions. for (i = 0; i < NUMKEYS; i++) { if (wParam == key2scroll[i].wVirtkey) { SendMessage(hWnd, key2scroll[i].iMessage, key2scroll[i].wRequest, 0L); break; } } break; case WM_PAINT: // Go paint the client area of the window with the appropriate // part of the selected file. MsgwndPaint(hWnd); break; case WM_DESTROY: // This is the end if we were closed by a DestroyWindow call. CloseMsgwnd(hWnd); // take any necessary wrapup action. PostQuitMessage(0); // this is the end... break; case WM_QUERYENDSESSION: // If we return TRUE we are saying it's ok with us to end the // windows session. CloseMsgwnd(hWnd); // take any necessary wrapup action. return((long) TRUE); // we agree to end session. case WM_CLOSE: // Tell windows to destroy our window. DestroyWindow(hWnd); break; default: // Let windows handle all messages we choose to ignore. return(DefWindowProc(hWnd, message, wParam, lParam)); } return(0L); } //******************************************************************* // SetupScroll - setup scroll ranges // // Setup the vertical and horizontal scroll ranges and positions // of the applicatons main window based on: // // numlines - The maximum number of lines to display. // maxwidth - The maximum width of any line to display. // // The resulting variables, nVscrollPos and nPageMaxLines, are used // by the function MsgwndPaint to determine what part of the selected // file to display in the window. // // paramaters: // hWnd - The callers window handle. // //******************************************************************* void SetupScroll(HWND hWnd) { // numlines established during open nVscrollMax = max(0, numlines - yClient / yChar); nVscrollPos = min(nVscrollPos, nVscrollMax); nHscrollMax = max(0, maxwidth - xClient / xChar); nHscrollPos = min(nHscrollPos, nHscrollMax); SetScrollRange (hWnd, SB_VERT, 0, nVscrollMax, FALSE); SetScrollPos (hWnd, SB_VERT, nVscrollPos, TRUE); SetScrollRange (hWnd, SB_HORZ, 0, nHscrollMax, FALSE); SetScrollPos (hWnd, SB_HORZ, nHscrollPos, TRUE); nPageMaxLines = min(numlines, yClient / yChar); rect.left = 0; rect.top = 0; rect.right = xClient; rect.bottom = yClient; blanklen = rect.right / xChar + 1; } //******************************************************************* // MsgwndPaint - paint the main window // // This function is responsible for redisplaying a portion of the saved // strings. Which strings it displays depends on the current scroll // position. // // paramaters: // hWnd - The callers window handle // //******************************************************************* void MsgwndPaint(HWND hWnd) { PAINTSTRUCT ps; HDC hDC; char currec[256]; int ypos; LPSTR lcp; int i; int ndone; // Get display context. BeginPaint(hWnd, (LPPAINTSTRUCT) &ps); hDC = ps.hdc; // Select fixed font. SelectObject(hDC, hnewsfont); // Setup scroll ranges. SetupScroll(hWnd); // See if we have any lines to show. if (hwm_keep) { // Y position of bottom line in client area. ypos = rect.bottom - yChar; // Index into keep list of first line (from bottom) to show. i = nVscrollMax - nVscrollPos; ndone = 1; while (ndone) { lcp = GlobalLock(hkeep[i]); if (lcp) { // We must fill line with blanks to width of window // or else some previous longet text might show through. _fstrcpy(currec, blanks); // Line to show. _fstrncpy(currec, lcp, _fstrlen(lcp)); // Send to window. TextOut(hDC, xChar * (-nHscrollPos + 0), ypos, currec, blanklen); // New Y is one character height higher. ypos -= yChar; GlobalUnlock(hkeep[i]); } // Index of next keep string to show. i++; // No use drawing lines beyond top of client area. // They would not show, so don't wast the energy. if (ypos < -yChar) ndone = 0; // Have we done all of the lines? if (i > (hwm_keep - 1)) ndone = 0; } } EndPaint(hWnd, (LPPAINTSTRUCT) &ps); } //******************************************************************* // ScrollPrint - saves string sent to it and cause it to display // // This function gets a string and saves it in a list of strings. // The oldest string is deleted when the list reaches its maximum // size. // // Depending on the scroll position at the time the string is recieved // one of two methods of displaying the string will be used: // // If the scroll position was at the bottom, the window is scrolled up // one line, the bottom line is made invalid, and windows is told to // repaint. This will cause only the new string to be output, resulting // in minimum redraw on the screen. // // If the scroll position was not at the bottom of the window because // the user had been looking at previous messages, windows will be told // to redraw the entire window starting with the new string. // // paramaters: // hWnd - The window to put the message in. // str - the string to print in window. // //******************************************************************* void ScrollPrint(HWND hWnd, char *str) { int i; int lstr; LPSTR lcp; RECT trect; // If our keep stack is full free oldest member. if (hwm_keep >= MAX_KEEP) GlobalFree(hkeep[hwm_keep]); // Move all handles to make room for new one. for (i = hwm_keep; i > 0; i--) hkeep[i] = hkeep[i - 1]; // If keep stack not yet full add one to high watter mark. if (hwm_keep < MAX_KEEP) hwm_keep++; // Make sure we know how many saved lines there are. numlines = hwm_keep; // Length of new string. lstr = strlen(str); // Is it longer than any previous string. if (lstr > maxwidth) maxwidth = lstr; // Get storage to save it. hkeep[0] = GlobalAlloc(GMEM_MOVEABLE, (DWORD) (lstr + 1)); if (hkeep[0]) { // Lock it down to get address. lcp = GlobalLock(hkeep[0]); if (lcp) { // Save string. _fstrcpy(lcp, str); GlobalUnlock(hkeep[0]); } } // See what we have to do to display it efficently. if (!(nVscrollMax - nVscrollPos)) { // We are scrolled to bottom of list. // Scroll contents of window up one character hehght. ScrollWindow(hWnd, 0, -yChar, &rect, &rect); // Set scroll position to last line nVscrollPos = numlines - yChar / yClient; // Tell windows to repaint only the bottom line of window. GetClientRect(hWnd, &trect); trect.top = trect.bottom - yChar; InvalidateRect(hWnd, &trect, TRUE); } else { // We are not scrolled to bottom of list. // Set scroll position to last line. nVscrollPos = numlines - yChar / yClient; // Tell windows to repaint the entire window. InvalidateRect(hWnd, NULL, TRUE); } } //******************************************************************* // ClearKeep - free all saved strings //******************************************************************* void ClearKeep() { int i; for (i = 0; i < hwm_keep; i++) { GlobalFree(hkeep[i]); hkeep[i] = 0; } // Reset counters. numlines = hwm_keep = 0; } //******************************************************************* //******************************************************************* // DDEExecuteCommand - execute a command send by client // // paramaters: // lpCommand - command from client to be executed. // // commands supported: // // print - print a line in window. // [print ("line to print")] // // clear - clear the window. // [clear] // // returns: // 1 - if command successful. // 0 - if not successful. // //******************************************************************* int DDEExecuteCommand(LPSTR lpCommand) { int nRc; LPSTR lcp; LPSTR lcp1; LPSTR lcp2; char buf[256]; // Init return code. nRc = 0; // Find start of command. lcp = _fstrchr(lpCommand, '['); if (lcp) { // See what the command is. lcp++; if (!_fstrncmp(lcp, "print", 5)) // Print a line to window. { // Find string between quotes (does not support embeded quotes) lcp1 = _fstrchr(lcp, '\"'); if (lcp1) { lcp1++; lcp2 = _fstrchr(lcp1, '\"'); if (lcp2) { *lcp2 = 0; _fstrcpy(buf, lcp1); // Print the string to window. ScrollPrint(hWndMain, buf); // Increment line count. plines++; nRc = 1; } } } else if (!_fstrncmp(lcp, "clear", 5)) // Clear window. { // Delete saved strings. ClearKeep(); // Clear the window. InvalidateRect(hWndMain, NULL, TRUE); UpdateWindow(hWndMain); // Reset line count. plines = 0; nRc = 1; } } return(nRc); } //******************************************************************* // DDEData - get data requested by client // // paramaters: // szItem - name of data item requested. // // items supported: // // lines - the number lines printed to window. // // returns: // Size of data item. // or // Zero if no data item. // //******************************************************************* int DDEData(char *szItem, HANDLE *hData) { int nRc; int len; char buf[30]; LPSTR lpData; // Init return code. nRc = 0; // See what data item is being requested. if (!stricmp(szItem, "lines")) { // Format response. sprintf(buf, "Lines = %d", plines); // Get length of response. len = strlen(buf); // Get memory for response. *hData = GlobalAlloc(GMEM_MOVEABLE, (DWORD) (len + 1)); if (*hData) { lpData = GlobalLock(*hData); if (lpData) { // Copy response to memory. _fstrcpy(lpData, buf); GlobalUnlock(*hData); // Set return code to length of data returned. nRc = len; } else { GlobalFree(*hData); *hData = 0; } } } return(nRc); } //*******************************************************************