/* midimon.c - WinMain() and WndProc() functions for MIDIMon, along * with some initialization and error reporting functions. * * MIDIMon is a Windows with Multimedia application that records and displays * incoming MIDI information. It uses a low-level callback function to * get timestamped MIDI input. The callback function puts the incoming * MIDI event information (source device, timestamp, and raw MIDI * data) in a circular input buffer and notifies the application by posting * a MM_MIDIINPUT message. When the application processes the MM_MIDIINPUT * message, it removes the MIDI event from the input buffer and puts it in * a display buffer. Information in the display buffer is converted to * text and displayed in a scrollable window. Incoming MIDI data can be sent * to the MIDI Mapper if the user chooses. Filtering is provided for the * display buffer, but not for data sent to the Mapper. * * (C) Copyright Microsoft Corp. 1991. All rights reserved. * * You have a royalty-free right to use, modify, reproduce and * distribute the Sample Files (and/or any modified version) in * any way you find useful, provided that you agree that * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. * */ #include <windows.h> #include <mmsystem.h> #include <stdio.h> #include "midimon.h" #include "about.h" #include "circbuf.h" #include "display.h" #include "prefer.h" #include "instdata.h" #include "callback.h" #include "filter.h" HANDLE hInst; // Instance handle for application char szAppName[20]; // Application name HWND hMainWnd; // Main window handle HMIDIOUT hMapper = 0; // Handle to MIDI Mapper UINT wNumDevices = 0; // Number of MIDI input devices opened BOOL bRecordingEnabled = 1; // Enable/disable recording flag LONG nNumBufferLines = 0; // Number of lines in display buffer RECT rectScrollClip; // Clipping rectangle for scrolling LPCIRCULARBUFFER lpInputBuffer; // Input buffer structure LPDISPLAYBUFFER lpDisplayBuffer; // Display buffer structure PREFERENCES preferences; // User preferences structure EVENT incomingEvent; // Incoming MIDI event structure MIDIINCAPS midiInCaps[MAX_NUM_DEVICES]; // Device capabilities structures HMIDIIN hMidiIn[MAX_NUM_DEVICES]; // MIDI input device handles // Callback instance data pointers LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAX_NUM_DEVICES]; // Display filter structure FILTER filter = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Virtual key to scroll message translation structure KEYTOSCROLL keyToScroll [] = { VK_HOME, WM_VSCROLL, SB_TOP, 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_LINEUP, VK_RIGHT, WM_HSCROLL, SB_LINEDOWN }; #define NUMKEYS (sizeof (keyToScroll) / sizeof (keyToScroll[0])) // /* WinMain - Entry point for MIDIMon. */ int PASCAL WinMain(hInstance,hPrevInstance,lpszCmdLine,cmdShow) HANDLE hInstance,hPrevInstance; LPSTR lpszCmdLine; int cmdShow; { MSG msg; UINT wRtn; PREFERENCES preferences; char szErrorText[256]; unsigned int i; UNREFERENCED_PARAMETER(lpszCmdLine); hInst = hInstance; /* Get preferred user setup. */ getPreferences(&preferences); /* Initialize application. */ LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName)); if (hPrevInstance || !InitFirstInstance(hInstance)) return 0; /* Create a display window. */ hMainWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, preferences.iInitialX, preferences.iInitialY, preferences.iInitialW, preferences.iInitialH, (HWND)NULL, (HMENU)NULL, hInstance, (LPSTR)NULL); if (!hMainWnd) return 1; /* Hide scroll bars for now. */ SetScrollRange(hMainWnd, SB_VERT, 0, 0, FALSE); SetScrollRange(hMainWnd, SB_HORZ, 0, 0, FALSE); /* Show the display window. */ ShowWindow(hMainWnd, cmdShow); UpdateWindow(hMainWnd); /* Get the number of MIDI input devices. Then get the capabilities of * each device. We don't use the capabilities information right now, * but we could use it to report the name of the device that received * each MIDI event. */ wNumDevices = midiInGetNumDevs(); if (!wNumDevices) { Error("There are no MIDI input devices."); PostQuitMessage(0); } for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) { wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &midiInCaps[i], sizeof(MIDIINCAPS)); if(wRtn) { midiInGetErrorText(wRtn, (LPSTR)szErrorText, sizeof(szErrorText)); Error(szErrorText); } } /* Allocate a circular buffer for low-level MIDI input. This buffer * is filled by the low-level callback function and emptied by the * application when it receives MM_MIDIINPUT messages. */ lpInputBuffer = AllocCircularBuffer( (DWORD)(INPUT_BUFFER_SIZE * sizeof(EVENT))); if (lpInputBuffer == NULL) { Error("Not enough memory available for input buffer."); return 1; } /* Allocate a display buffer. Incoming events from the circular input * buffer are put into this buffer for display. */ lpDisplayBuffer = AllocDisplayBuffer((DWORD)(DISPLAY_BUFFER_SIZE)); if (lpDisplayBuffer == NULL) { Error("Not enough memory available for display buffer."); FreeCircularBuffer(lpInputBuffer); return 1; } /* Open all MIDI input devices after allocating and setting up * instance data for each device. The instance data is used to * pass buffer management information between the application and * the low-level callback function. It also includes a device ID, * a handle to the MIDI Mapper, and a handle to the application's * display window, so the callback can notify the window when input * data is available. A single callback function is used to service * all opened input devices. */ for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) { if ((lpCallbackInstanceData[i] = AllocCallbackInstanceData()) == NULL) { Error("Not enough memory available."); FreeCircularBuffer(lpInputBuffer); FreeDisplayBuffer(lpDisplayBuffer); return 1; } lpCallbackInstanceData[i]->hWnd = hMainWnd; lpCallbackInstanceData[i]->dwDevice = i; lpCallbackInstanceData[i]->lpBuf = lpInputBuffer; lpCallbackInstanceData[i]->hMapper = hMapper; wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[i], i, (DWORD)midiInputHandler, (DWORD)lpCallbackInstanceData[i], CALLBACK_FUNCTION); if(wRtn) { FreeCallbackInstanceData(lpCallbackInstanceData[i]); midiInGetErrorText(wRtn, (LPSTR)szErrorText, sizeof(szErrorText)); Error(szErrorText); } } /* Start MIDI input. */ for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) { if (hMidiIn[i]) midiInStart(hMidiIn[i]); } /* Standard Windows message processing loop. We don't drop out of * this loop until the user quits the application. */ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } /* Stop, reset, close MIDI input. Free callback instance data. */ for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) { if (hMidiIn[i]) { midiInStop(hMidiIn[i]); midiInReset(hMidiIn[i]); midiInClose(hMidiIn[i]); FreeCallbackInstanceData(lpCallbackInstanceData[i]); } } /* Close the MIDI Mapper, if it's open. */ if (hMapper) midiOutClose(hMapper); /* Free input and display buffers. */ FreeCircularBuffer(lpInputBuffer); FreeDisplayBuffer(lpDisplayBuffer); return (msg.wParam); } // /* WndProc - Main window procedure function. */ LRESULT FAR PASCAL WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HFONT hFont; HBRUSH hBrush; //!! HPEN hPen; HDC hDC; TEXTMETRIC tm; static BOOL bWindowCreated = 0; static LONG wChar, hChar; static LONG maxClientWidth; static LONG wClient, hClient; static LONG nVscrollMax = 0; static LONG nHscrollMax = 0; static LONG nVscrollPos = 0; static LONG nHscrollPos = 0; static LONG nNumCharsPerLine = 0; static LONG nNumDisplayLines = 0; static LONG nNumDisplayChars = 0; BOOL bNeedToUpdate = FALSE; LONG nVscrollInc, nHscrollInc; LONG nPaintBeg, nPaintEnd; LONG i; SIZE size; char szDisplayTextBuffer[120]; switch(message) { case WM_CREATE: hDC = GetDC(hWnd); /* Set the font we want to use. */ hFont = GetStockObject(ANSI_FIXED_FONT); SelectObject(hDC, hFont); /* Get text metrics and calculate the number of characters * per line and the maximum width required for the client area. */ GetTextMetrics(hDC, &tm); wChar = (LONG) tm.tmAveCharWidth; hChar = (LONG) (tm.tmHeight + tm.tmExternalLeading); nNumCharsPerLine = sizeof(LABEL) - 1; GetTextExtentPoint(hDC, szDisplayTextBuffer, sprintf(szDisplayTextBuffer, LABEL), &size); maxClientWidth = size.cx; ReleaseDC(hWnd, hDC); bWindowCreated = 1; break; case WM_SIZE: hClient = (LONG) HIWORD(lParam); wClient = (LONG) LOWORD(lParam); /* Get new client area and adjust scroll clip rectangle. */ GetClientRect(hWnd, (LPRECT)&rectScrollClip); rectScrollClip.top += hChar; /* Calculate new display metrics. We subtract 1 from * nNumDisplayLines to allow room for the label line. */ nNumDisplayLines = hClient / hChar - 1; nNumDisplayChars = wClient / wChar; /* Calculate and set new scroll bar calibrations. */ nVscrollMax = (LONG) max(0, nNumBufferLines - nNumDisplayLines); nVscrollPos = (LONG) min(nVscrollPos, nVscrollMax); nHscrollMax = (LONG) max(0, nNumCharsPerLine - nNumDisplayChars); nHscrollPos = (LONG) 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); break; case WM_GETMINMAXINFO: /* Limit the maximum width of the window. */ if(bWindowCreated) { ((LPPOINT)lParam)[4].x = maxClientWidth + (2 * GetSystemMetrics(SM_CXFRAME)) + (GetSystemMetrics(SM_CXVSCROLL)); } // *((LPWORD)lParam + 8) = maxClientWidth + // (2 * GetSystemMetrics(SM_CXFRAME)) + // (GetSystemMetrics(SM_CXVSCROLL)); break; case WM_COMMAND: /* Process menu messages. */ CommandMsg(hWnd, wParam, lParam); break; case WM_VSCROLL: /* Determine how much to scroll vertically. */ switch (LOWORD(wParam)) { 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 = min (-1, -nNumDisplayLines); break; case SB_PAGEDOWN: nVscrollInc = (LONG) max(1, nNumDisplayLines); break; case SB_THUMBTRACK: nVscrollInc = ((LONG)HIWORD(wParam) - nVscrollPos); break; default: nVscrollInc = 0; } /* Limit the scroll range and do the scroll. We use the * rectScrollClip rectangle because we don't want to scroll * the entire window, only the part below the display label line. */ if(nVscrollInc = max(-nVscrollPos, min(nVscrollInc, nVscrollMax - nVscrollPos))) { nVscrollPos += nVscrollInc; ScrollWindow(hWnd, 0, -hChar * nVscrollInc, (LPRECT)&rectScrollClip, (LPRECT)&rectScrollClip); UpdateWindow(hWnd); SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE); } break; case WM_HSCROLL: /* Determine how much to scroll horizontally. */ switch (LOWORD(wParam)) { case SB_LINEUP: nHscrollInc = -1; break; case SB_LINEDOWN: nHscrollInc = 1; break; case SB_PAGEUP: nHscrollInc = (LONG) min(-1, -nNumDisplayChars); break; case SB_PAGEDOWN: nHscrollInc = (LONG) max(1, nNumDisplayChars); break; case SB_THUMBTRACK: nHscrollInc = ((LONG)HIWORD(wParam) - nHscrollPos); break; default: nHscrollInc = 0; } /* Limit the scroll range and to the scroll. */ if(nHscrollInc = max(-nHscrollPos, min(nHscrollInc, nHscrollMax - nHscrollPos))) { nHscrollPos += nHscrollInc; ScrollWindow(hWnd, -wChar * nHscrollInc, 0, NULL, NULL); UpdateWindow(hWnd); SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE); } break; case WM_KEYDOWN: /* Translate keystrokes to scroll message. */ for (i = 0; i < NUMKEYS; i++) if (wParam == keyToScroll[i].wVirtKey) { SendMessage(hWnd, keyToScroll[i].iMessage, keyToScroll[i].wRequest, 0L); break; } break; case WM_PAINT: BeginPaint(hWnd, &ps); hBrush = CreateSolidBrush(GetSysColor(COLOR_APPWORKSPACE)); FillRect(ps.hdc, &ps.rcPaint, hBrush); DeleteObject(hBrush); /* Set up text attributes. */ hFont = GetStockObject(ANSI_FIXED_FONT); SelectObject(ps.hdc, hFont); SetBkMode(ps.hdc, TRANSPARENT); /* Put up the display label if we're asked to repaint the * top line of the screen. */ if(ps.rcPaint.top < hChar) { TextOut(ps.hdc, wChar * (0 - nHscrollPos), 0, szDisplayTextBuffer, sprintf(szDisplayTextBuffer, LABEL)); MoveToEx(ps.hdc, wChar * (0 - nHscrollPos), hChar - 1, NULL); LineTo(ps.hdc, wClient, hChar - 1); ps.rcPaint.top = hChar; } /* Calculate the beginning and ending line numbers that we need * to paint. These line numbers refer to lines in the display * buffer, not to lines in the display window. */ nPaintBeg = max (0, nVscrollPos + ps.rcPaint.top / hChar - 1); nPaintEnd = min(nNumBufferLines, nVscrollPos + ps.rcPaint.bottom / hChar + 1); /* Get the appropriate events from the display buffer, convert * to a text string and paint the text on the display. */ for (i = nPaintBeg; i < nPaintEnd; i++) { GetDisplayEvent(lpDisplayBuffer, (LPEVENT)&incomingEvent, i); TextOut(ps.hdc, wChar * (0 - nHscrollPos), hChar * (1 - nVscrollPos + i), szDisplayTextBuffer, GetDisplayText(szDisplayTextBuffer, (LPEVENT)&incomingEvent)); } EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; case MM_MIDIINPUT: /* This is a custom message sent by the low level callback * function telling us that there is at least one MIDI event * in the input buffer. We empty the input buffer, and put * each event in the display buffer, if it's not filtered. * If the input buffer is being filled as fast we can empty * it, then we'll stay in this loop and not process any other * Windows messages, or yield to other applications. We need * something to restrict the amount of time we spend here... */ while(GetEvent(lpInputBuffer, (LPEVENT)&incomingEvent)) { if(!bRecordingEnabled) continue; if(!CheckEventFilter((LPEVENT)&incomingEvent, (LPFILTER)&filter)) { AddDisplayEvent(lpDisplayBuffer, (LPEVENT)&incomingEvent); ++nNumBufferLines; nNumBufferLines = min(nNumBufferLines, DISPLAY_BUFFER_SIZE - 1); bNeedToUpdate = TRUE; } } /* Recalculate vertical scroll bar range, and force * the display to be updated. */ if (bNeedToUpdate) { nVscrollMax = (LONG) max(0, nNumBufferLines - nNumDisplayLines); nVscrollPos = nVscrollMax; SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE); SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE); InvalidateRect(hMainWnd, (LPRECT)&rectScrollClip, 0); UpdateWindow(hMainWnd); } break; default: return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; } // /* CommandMsg - Processes WM_COMMAND messages. * * Params: hWnd - Handle to the window receiving the message. * wParam - Parameter of the WM_COMMAND message. * lParam - Parameter of the WM_COMMAND message. * * Return: void */ VOID CommandMsg( HWND hWnd, WPARAM wParam, LPARAM lParam) { PREFERENCES preferences; RECT rectWindow; UINT wRtn; HMENU hMenu; unsigned int i; char szErrorText[80]; WORD wCommand; UNREFERENCED_PARAMETER(lParam); wCommand = LOWORD(wParam); /* Process any WM_COMMAND messages we want */ switch (wCommand) { case IDM_ABOUT: About(hInst, hWnd); break; case IDM_EXIT: PostMessage(hWnd, WM_CLOSE, 0, 0l); break; case IDM_SENDTOMAPPER: /* We use hMapper as a toggle between sending events to the * Mapper and not sending events. */ if(hMapper) { /* Close the Mapper and reset hMapper. Uncheck the menu item. * Clear Mapper handle in the instance data for each device. */ wRtn = midiOutClose(hMapper); if(wRtn != 0) { midiOutGetErrorText(wRtn, (LPSTR)szErrorText, sizeof(szErrorText)); Error(szErrorText); } hMapper = 0; for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) lpCallbackInstanceData[i]->hMapper = hMapper; DoMenuItemCheck(hWnd, wCommand, FALSE); } else { /* Open the MIDI Mapper, put the Mapper handle in the instance * data for each device and check the menu item. */ wRtn = midiOutOpen((LPHMIDIOUT) &hMapper, (UINT) MIDIMAPPER, 0L, 0L, 0L); if(wRtn != 0) { // error opening Mapper midiOutGetErrorText(wRtn, (LPSTR)szErrorText, sizeof(szErrorText)); Error(szErrorText); hMapper = 0; } else { // Mapper opened successfully for (i=0; (i<wNumDevices) && (i<MAX_NUM_DEVICES); i++) lpCallbackInstanceData[i]->hMapper = hMapper; DoMenuItemCheck(hWnd, wCommand, TRUE); } } break; case IDM_SAVESETUP: /* Save the current location and size of the display window * in the MIDIMON.INI file. */ GetWindowRect(hMainWnd, (LPRECT)&rectWindow); preferences.iInitialX = rectWindow.left; preferences.iInitialY = rectWindow.top; preferences.iInitialW = rectWindow.right - rectWindow.left; preferences.iInitialH = rectWindow.bottom - rectWindow.top; setPreferences((LPPREFERENCES) &preferences); break; case IDM_STARTSTOP: /* Toggle between recording into the display buffer and not * recording. Toggle the menu item between "Start" to "Stop" * accordingly. */ hMenu = GetMenu(hWnd); if(bRecordingEnabled) { ModifyMenu(hMenu, IDM_STARTSTOP, MF_BYCOMMAND, IDM_STARTSTOP, "&Start"); bRecordingEnabled = 0; } else { ModifyMenu(hMenu, IDM_STARTSTOP, MF_BYCOMMAND, IDM_STARTSTOP, "&Stop"); bRecordingEnabled = 1; } DrawMenuBar(hWnd); break; case IDM_CLEAR: /* Reset the display buffer, recalibrate the scroll bars, * and force an update of the display. */ ResetDisplayBuffer(lpDisplayBuffer); nNumBufferLines = 0; SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE); InvalidateRect(hWnd, (LPRECT)&rectScrollClip, 0); UpdateWindow(hWnd); break; /* Set up filter structure for MIDI channel filtering. */ case IDM_FILTCHAN0: case IDM_FILTCHAN1: case IDM_FILTCHAN2: case IDM_FILTCHAN3: case IDM_FILTCHAN4: case IDM_FILTCHAN5: case IDM_FILTCHAN6: case IDM_FILTCHAN7: case IDM_FILTCHAN8: case IDM_FILTCHAN9: case IDM_FILTCHAN10: case IDM_FILTCHAN11: case IDM_FILTCHAN12: case IDM_FILTCHAN13: case IDM_FILTCHAN14: case IDM_FILTCHAN15: filter.channel[wCommand - IDM_FILTCHAN0] = !filter.channel[wCommand - IDM_FILTCHAN0]; DoMenuItemCheck(hWnd, wCommand, filter.channel[wCommand - IDM_FILTCHAN0]); break; /* Setup filter structure for MIDI event filtering. */ case IDM_NOTEOFF: filter.event.noteOff = !filter.event.noteOff; DoMenuItemCheck(hWnd, wCommand, filter.event.noteOff); break; case IDM_NOTEON: filter.event.noteOn = !filter.event.noteOn; DoMenuItemCheck(hWnd, wCommand, filter.event.noteOn); break; case IDM_POLYAFTERTOUCH: filter.event.keyAftertouch = !filter.event.keyAftertouch; DoMenuItemCheck(hWnd, wCommand, filter.event.keyAftertouch); break; case IDM_CONTROLCHANGE: filter.event.controller = !filter.event.controller; DoMenuItemCheck(hWnd, wCommand, filter.event.controller); break; case IDM_PROGRAMCHANGE: filter.event.progChange = !filter.event.progChange; DoMenuItemCheck(hWnd, wCommand, filter.event.progChange); break; case IDM_CHANNELAFTERTOUCH: filter.event.chanAftertouch = !filter.event.chanAftertouch; DoMenuItemCheck(hWnd, wCommand, filter.event.chanAftertouch); break; case IDM_PITCHBEND: filter.event.pitchBend = !filter.event.pitchBend; DoMenuItemCheck(hWnd, wCommand, filter.event.pitchBend); break; case IDM_CHANNELMODE: filter.event.channelMode = !filter.event.channelMode; DoMenuItemCheck(hWnd, wCommand, filter.event.channelMode); break; case IDM_SYSTEMEXCLUSIVE: filter.event.sysEx = !filter.event.sysEx; DoMenuItemCheck(hWnd, wCommand, filter.event.sysEx); break; case IDM_SYSTEMCOMMON: filter.event.sysCommon = !filter.event.sysCommon; DoMenuItemCheck(hWnd, wCommand, filter.event.sysCommon); break; case IDM_SYSTEMREALTIME: filter.event.sysRealTime = !filter.event.sysRealTime; DoMenuItemCheck(hWnd, wCommand, filter.event.sysRealTime); break; case IDM_ACTIVESENSE: filter.event.activeSense = !filter.event.activeSense; DoMenuItemCheck(hWnd, wCommand, filter.event.activeSense); break; default: break; } } // /* InitFirstInstance - Performs initializaion for the first instance * of the application. * * Params: hInstance - Instance handle. * * Return: Returns 1 if there were no errors. Otherwise, returns 0. */ BOOL InitFirstInstance(hInstance) HANDLE hInstance; { WNDCLASS wc; /* Define the class of window we want to register. */ wc.lpszClassName = szAppName; wc.style = CS_HREDRAW | CS_VREDRAW; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(hInstance,"Icon"); wc.lpszMenuName = "Menu"; wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1); wc.hInstance = hInstance; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; if(!RegisterClass(&wc)) return FALSE; return TRUE; } // /* DoMenuItemCheck - Checks and unchecks menu items. * * Params: hWnd - Window handle for window associated with menu items. * wMenuItem - The menu ID for the menu item. * newState - The new checked/unchecked state of the menu item. * * Return: void */ void DoMenuItemCheck( HWND hWnd, WORD wMenuItem, BOOL newState) { HMENU hMenu; hMenu = GetMenu(hWnd); CheckMenuItem(hMenu, wMenuItem, (newState ? MF_CHECKED: MF_UNCHECKED)); } /* Error - Beeps and shows an error message. * * Params: szMsg - Points to a NULL-terminated string containing the * error message. * * Return: Returns the return value from the MessageBox() call. * Since this message box has only a single button, the * return value isn't too meaningful. */ int Error(szMsg) LPSTR szMsg; { MessageBeep(0); return MessageBox(hMainWnd, szMsg, szAppName, MB_OK); }