/* Copyright (c) Microsoft Corporation. Licensed under the MIT License. */ /*************************************************************************** Author: ShonK Project: Kauai Reviewed: Copyright (c) Microsoft Corporation Windows base application class. ***************************************************************************/ #include "frame.h" ASSERTNAME WIG vwig; /*************************************************************************** WinMain for any frame work app. Sets up vwig and calls FrameMain. ***************************************************************************/ int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pszs, int wShow) { vwig.hinst = hinst; vwig.hinstPrev = hinstPrev; vwig.pszCmdLine = GetCommandLine(); vwig.wShow = wShow; vwig.lwThreadMain = LwThreadCur(); FrameMain(); return 0; } /*************************************************************************** Shutdown immediately. ***************************************************************************/ void APPB::Abort(void) { _ShutDownViewer(); FatalAppExit(0, PszLit("Fatal Error Termination")); } /*************************************************************************** Do OS specific initialization. ***************************************************************************/ bool APPB::_FInitOS(void) { AssertThis(0); STN stnApp; PSZ pszAppWndCls = PszLit("APP"); // get the app name GetStnAppName(&stnApp); // register the window classes if (vwig.hinstPrev == hNil) { WNDCLASS wcs; wcs.style = CS_BYTEALIGNCLIENT | CS_OWNDC; wcs.lpfnWndProc = _LuWndProc; wcs.cbClsExtra = 0; wcs.cbWndExtra = 0; wcs.hInstance = vwig.hinst; wcs.hIcon = LoadIcon(NULL, IDI_APPLICATION); wcs.hCursor = LoadCursor(NULL, IDC_ARROW); wcs.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcs.lpszMenuName = 0; wcs.lpszClassName = pszAppWndCls; if (!RegisterClass(&wcs)) return fFalse; wcs.lpfnWndProc = _LuMdiWndProc; wcs.lpszClassName = PszLit("MDI"); if (!RegisterClass(&wcs)) return fFalse; } if ((vwig.hwndApp = CreateWindow(pszAppWndCls, stnApp.Psz(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hNil, hNil, vwig.hinst, pvNil)) == hNil) { return fFalse; } if (hNil == (vwig.hdcApp = GetDC(vwig.hwndApp))) return fFalse; //set a timer, so we can idle regularly. if (SetTimer(vwig.hwndApp, 0, 1, pvNil) == 0) return fFalse; vwig.haccel = LoadAccelerators(vwig.hinst, MIR(acidMain)); ShowWindow(vwig.hwndApp, vwig.wShow); return fTrue; } /*************************************************************************** Get the next event from the OS event queue. Return true iff it's a real event (not just an idle type event). ***************************************************************************/ bool APPB::_FGetNextEvt(PEVT pevt) { AssertThis(0); AssertVarMem(pevt); GetMessage(pevt, hNil, 0, 0); switch (pevt->message) { case WM_TIMER: return fFalse; case WM_MOUSEMOVE: // dispatch these so real Windows controls can receive them, // but return false so we can do our idle stuff - including // our own mouse moved stuff. _DispatchEvt(pevt); return fFalse; } return fTrue; } /*************************************************************************** The given GOB is tracking the mouse. See if there are any relevant mouse events in the system event queue. Fill in *ppt with the location of the mouse relative to pgob. Also ensure that GrfcustCur() will return the correct mouse state. ***************************************************************************/ void APPB::TrackMouse(PGOB pgob, PT *ppt) { AssertThis(0); AssertPo(pgob, 0); AssertVarMem(ppt); EVT evt; PTS pts; for (;;) { if (!PeekMessage(&evt, hNil, 0, 0, PM_REMOVE | PM_NOYIELD)) { GetCursorPos(&pts); break; } if (FIn(evt.message, WM_MOUSEFIRST, WM_MOUSELAST + 1)) { pts = evt.pt; break; } //toss key events if (!FIn(evt.message, WM_KEYFIRST, WM_KEYLAST + 1)) { TranslateMessage(&evt); DispatchMessage(&evt); } } ppt->xp = pts.x; ppt->yp = pts.y; pgob->MapPt(ppt, cooGlobal, cooLocal); } /*************************************************************************** Dispatch an OS level event to someone that knows what to do with it. ***************************************************************************/ void APPB::_DispatchEvt(PEVT pevt) { AssertThis(0); AssertVarMem(pevt); CMD cmd; if (hNil != vwig.hwndClient && TranslateMDISysAccel(vwig.hwndClient, pevt) || hNil != vwig.haccel && TranslateAccelerator(vwig.hwndApp, vwig.haccel, pevt)) { return; } switch (pevt->message) { case WM_KEYDOWN: case WM_CHAR: if (_FTranslateKeyEvt(pevt, (PCMD_KEY)&cmd) && pvNil != vpcex) vpcex->EnqueueCmd(&cmd); ResetToolTip(); break; case WM_KEYUP: case WM_DEADCHAR: case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_SYSCHAR: case WM_SYSDEADCHAR: ResetToolTip(); // fall thru default: TranslateMessage(pevt); DispatchMessage(pevt); break; } } /*************************************************************************** Translate an OS level key down event to a CMD. This returns false if the key maps to a menu item. ***************************************************************************/ bool APPB::_FTranslateKeyEvt(PEVT pevt, PCMD_KEY pcmd) { AssertThis(0); AssertVarMem(pevt); AssertVarMem(pcmd); EVT evt; ClearPb(pcmd, size(*pcmd)); pcmd->cid = cidKey; if (pevt->message == WM_KEYDOWN) { TranslateMessage(pevt); if (PeekMessage(&evt, pevt->hwnd, 0, 0, PM_NOREMOVE) && WM_CHAR == evt.message && PeekMessage(&evt, pevt->hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)) { Assert(evt.message == WM_CHAR, 0); pcmd->ch = evt.wParam; } else pcmd->ch = chNil; pcmd->vk = pevt->wParam; } else { pcmd->vk = vkNil; pcmd->ch = pevt->wParam; } pcmd->grfcust = GrfcustCur(); pcmd->cact = SwLow(pevt->lParam); return fTrue; } /*************************************************************************** Look at the next system event and if it's a key, fill in the *pcmd with the relevant info. ***************************************************************************/ bool APPB::FGetNextKeyFromOsQueue(PCMD_KEY pcmd) { AssertThis(0); AssertVarMem(pcmd); EVT evt; for (;;) { if (!PeekMessage(&evt, hNil, 0, 0, PM_NOREMOVE) || !FIn(evt.message, WM_KEYFIRST, WM_KEYLAST + 1) || !PeekMessage(&evt, evt.hwnd, evt.message, evt.message, PM_REMOVE)) { break; } if (hNil != vwig.hwndClient && TranslateMDISysAccel(vwig.hwndClient, &evt) || hNil != vwig.haccel && TranslateAccelerator(vwig.hwndApp, vwig.haccel, &evt)) { break; } switch (evt.message) { case WM_CHAR: case WM_KEYDOWN: if (!_FTranslateKeyEvt(&evt, pcmd)) goto LFail; return fTrue; default: TranslateMessage(&evt); DispatchMessage(&evt); break; } } LFail: TrashVar(pcmd); return fFalse; } /*************************************************************************** Flush user generated events from the system event queue. ***************************************************************************/ void APPB::FlushUserEvents(ulong grfevt) { AssertThis(0); EVT evt; while ((grfevt & fevtMouse) && PeekMessage(&evt, hNil, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) || (grfevt & fevtKey) && PeekMessage(&evt, hNil, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) { } } /*************************************************************************** Get our app window out of the clipboard viewer chain. ***************************************************************************/ void APPB::_ShutDownViewer(void) { if (vwig.hwndApp != hNil) ChangeClipboardChain(vwig.hwndApp, vwig.hwndNextViewer); vwig.hwndNextViewer = hNil; } /*************************************************************************** Main window procedure (a static method). ***************************************************************************/ LRESULT CALLBACK APPB::_LuWndProc(HWND hwnd, uint wm, WPARAM wParam, LPARAM lw) { AssertNilOrPo(vpappb, 0); long lwRet; if (pvNil != vpappb && vpappb->_FFrameWndProc(hwnd, wm, wParam, lw, &lwRet)) { return lwRet; } return DefFrameProc(hwnd, vwig.hwndClient, wm, wParam, lw); } /*************************************************************************** Handle Windows messages for the main app window. Return true iff the default window proc should _NOT_ be called. ***************************************************************************/ bool APPB::_FFrameWndProc(HWND hwnd, uint wm, WPARAM wParam, LPARAM lw, long *plwRet) { AssertThis(0); AssertVarMem(plwRet); PGOB pgob; RC rc; PT pt; long xp, yp; long lwT; long lwStyle; *plwRet = 0; switch (wm) { default: return _FCommonWndProc(hwnd, wm, wParam, lw, plwRet); case WM_CREATE: Assert(vwig.hwndApp == hNil, 0); vwig.hwndNextViewer = SetClipboardViewer(hwnd); vwig.hwndApp = hwnd; return fTrue; case WM_CHANGECBCHAIN: if ((HWND)wParam == vwig.hwndNextViewer) vwig.hwndNextViewer = (HWND)lw; else if (hNil != vwig.hwndNextViewer) SendMessage(vwig.hwndNextViewer, wm, wParam, lw); return fTrue; case WM_DRAWCLIPBOARD: if (hNil != vwig.hwndNextViewer) SendMessage(vwig.hwndNextViewer, wm, wParam, lw); if (vwig.hwndApp != hNil && GetClipboardOwner() != vwig.hwndApp) vpclip->Import(); return fTrue; case WM_DESTROY: _ShutDownViewer(); vwig.hwndApp = hNil; PostQuitMessage(0); return fTrue; case WM_SIZE: // make sure the style bits are set correctly lwT = lwStyle = GetWindowLong(vwig.hwndApp, GWL_STYLE); if (_fFullScreen && wParam == SIZE_MAXIMIZED) { // in full screen mode, set popup and nuke the system menu stuff lwStyle |= WS_POPUP; lwStyle &= ~(WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); } else { // in non-full screen mode, clear popup and set the system menu stuff lwStyle &= ~WS_POPUP; lwStyle |= (WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); } if (lwT != lwStyle) SetWindowLong(vwig.hwndApp, GWL_STYLE, lwStyle); return _FCommonWndProc(hwnd, wm, wParam, lw, plwRet); case WM_PALETTECHANGED: if ((HWND)wParam == hwnd) return fTrue; // fall thru case WM_QUERYNEWPALETTE: *plwRet = GPT::CclrSetPalette(hwnd, fTrue) > 0; return fTrue; case WM_GETMINMAXINFO: BLOCK { long dypFrame, dypScreen, dypExtra; MINMAXINFO *pmmi; pmmi = (MINMAXINFO *)lw; *plwRet = DefFrameProc(hwnd, vwig.hwndClient, wm, wParam, (long)pmmi); dypFrame = GetSystemMetrics(SM_CYFRAME); dypScreen = GetSystemMetrics(SM_CYSCREEN); dypExtra = 0; FGetProp(kpridFullScreen, &lw); if (lw) dypExtra = GetSystemMetrics(SM_CYCAPTION); pmmi->ptMaxPosition.y = -dypFrame - dypExtra; pmmi->ptMaxSize.y = pmmi->ptMaxTrackSize.y = dypScreen + 2 * dypFrame + dypExtra; _FCommonWndProc(hwnd, wm, wParam, (long)pmmi, &lw); } return fTrue; case WM_CLOSE: if (pvNil != vpcex) vpcex->EnqueueCid(cidQuit); return fTrue; case WM_QUERYENDSESSION: if (!_fQuit) Quit(fFalse); *plwRet = _fQuit; return fTrue; case WM_COMMAND: if (GET_WM_COMMAND_HWND(wParm, lw) != hNil) break; lwT = GET_WM_COMMAND_ID(wParam, lw); if (!FIn(lwT, wcidMinApp, wcidLimApp)) break; if (pvNil != vpmubCur) vpmubCur->EnqueueWcid(lwT); else if (pvNil != vpcex) vpcex->EnqueueCid(lwT); return fTrue; case WM_INITMENU: if (vpmubCur != pvNil) { vpmubCur->Clean(); return fTrue; } break; // these are for automated testing support... case WM_GOB_STATE: if (pvNil != (pgob = GOB::PgobFromHidScr(lw))) *plwRet = pgob->LwState(); return fTrue; case WM_GOB_LOCATION: *plwRet = -1; if (pvNil == (pgob = GOB::PgobFromHidScr(lw))) return fTrue; pgob->GetRcVis(&rc, cooLocal); if (rc.FEmpty()) return fTrue; pt.xp = pt.yp = 0; pgob->MapPt(&pt, cooLocal, cooGlobal); for (lwT = 0; lwT < 256; lwT++) { for (yp = rc.ypTop + (lwT & 0x0F); yp < rc.ypBottom; yp += 16) { for (xp = rc.xpLeft + (lwT >> 4); xp < rc.xpRight; xp += 16) { if (pgob->FPtIn(xp, yp) && pgob == GOB::PgobFromPtGlobal(xp + pt.xp, yp + pt.yp)) { pt.xp += xp; pt.yp += yp; GOB::PgobScreen()->MapPt(&pt, cooGlobal, cooLocal); *plwRet = LwHighLow((short)pt.xp, (short)pt.yp); return fTrue; } } } } return fTrue; case WM_GLOBAL_STATE: *plwRet = GrfcustCur(); return fTrue; case WM_CURRENT_CURSOR: if (pvNil != _pcurs) *plwRet = _pcurs->Cno(); else *plwRet = cnoNil; return fTrue; case WM_GET_PROP: if (!FGetProp(lw, plwRet)) *plwRet = wParam; return fTrue; case WM_SCALE_TIME: *plwRet = vpusac->LuScale(); vpusac->Scale(lw); return fTrue; case WM_GOB_FROM_PT: pt.xp = wParam; pt.yp = lw; GOB::PgobScreen()->MapPt(&pt, cooLocal, cooGlobal); if (pvNil != (pgob = GOB::PgobFromPtGlobal(pt.xp, pt.yp))) *plwRet = pgob->Hid(); return fTrue; case WM_FIRST_CHILD: if (pvNil != (pgob = GOB::PgobFromHidScr(lw)) && pvNil != (pgob = pgob->PgobFirstChild())) { *plwRet = pgob->Hid(); } return fTrue; case WM_NEXT_SIB: if (pvNil != (pgob = GOB::PgobFromHidScr(lw)) && pvNil != (pgob = pgob->PgobNextSib())) { *plwRet = pgob->Hid(); } return fTrue; case WM_PARENT: if (pvNil != (pgob = GOB::PgobFromHidScr(lw)) && pvNil != (pgob = pgob->PgobPar())) { *plwRet = pgob->Hid(); } return fTrue; case WM_GOB_TYPE: if (pvNil != (pgob = GOB::PgobFromHidScr(lw))) *plwRet = pgob->Cls(); return fTrue; case WM_IS_GOB: if (pvNil != (pgob = GOB::PgobFromHidScr(lw))) *plwRet = pgob->FIs(wParam); return fTrue; } return fFalse; } /*************************************************************************** MDI window proc (a static method). ***************************************************************************/ LRESULT CALLBACK APPB::_LuMdiWndProc(HWND hwnd, uint wm, WPARAM wParam, LPARAM lw) { AssertNilOrPo(vpappb, 0); long lwRet; if (pvNil != vpappb && vpappb->_FMdiWndProc(hwnd, wm, wParam, lw, &lwRet)) { return lwRet; } return DefMDIChildProc(hwnd, wm, wParam, lw); } /*************************************************************************** Handle MDI window messages. Returns true iff the default window proc should _NOT_ be called. ***************************************************************************/ bool APPB::_FMdiWndProc(HWND hwnd, uint wm, WPARAM wParam, LPARAM lw, long *plwRet) { AssertThis(0); AssertVarMem(plwRet); PGOB pgob; long lwT; *plwRet = 0; switch (wm) { default: return _FCommonWndProc(hwnd, wm, wParam, lw, plwRet); case WM_GETMINMAXINFO: *plwRet = DefMDIChildProc(hwnd, wm, wParam, lw); _FCommonWndProc(hwnd, wm, wParam, lw, &lwT); return fTrue; case WM_CLOSE: if ((pgob = GOB::PgobFromHwnd(hwnd)) != pvNil) vpcex->EnqueueCid(cidCloseWnd, pgob); return fTrue; case WM_MDIACTIVATE: GOB::ActivateHwnd(hwnd, GET_WM_MDIACTIVATE_FACTIVATE(hwnd, wParam, lw)); break; } return fFalse; } /*************************************************************************** Common stuff between the two window procs. Returns true if the default window proc should _NOT_ be called. ***************************************************************************/ bool APPB::_FCommonWndProc(HWND hwnd, uint wm, WPARAM wParam, LPARAM lw, long *plwRet) { AssertThis(0); AssertVarMem(plwRet); PGOB pgob; PT pt; PSCB pscb; RC rc; HDC hdc; PAINTSTRUCT ps; HRGN hrgn; *plwRet = 0; switch (wm) { case WM_PAINT: if (IsIconic(hwnd)) break; // make sure the palette is selected and realized.... // theoretically, we shouldn't have to do this, but because // of past and present Win bugs, we do it to be safe. GPT::CclrSetPalette(hwnd, fFalse); // invalidate stuff that we have marked internally (may as well // draw everything that needs drawn). InvalMarked(hwnd); // NOTE: BeginPaint has a bug where it returns in ps.rcPaint the // bounds of the update region intersected with the current clip region. // This causes us to not draw everything we need to. To fix this we // save, open up, and restore the clipping region around the BeginPaint // call. hdc = GetDC(hwnd); if (hNil == hdc) goto LFailPaint; if (FCreateRgn(&hrgn, pvNil) && 1 != GetClipRgn(hdc, hrgn)) FreePhrgn(&hrgn); SelectClipRgn(hdc, hNil); if (!BeginPaint(hwnd, &ps)) { ReleaseDC(hwnd, hdc); LFailPaint: Warn("Painting failed"); break; } // Since we use CS_OWNDC, these DCs should be the same... Assert(hdc == ps.hdc, 0); rc = RC(ps.rcPaint); UpdateHwnd(hwnd, &rc); EndPaint(hwnd, &ps); // don't call the default window proc - or it will clear anything // that got invalidated while we were drawing (which can happen // in a multi-threaded pre-emptive environment). return fTrue; case WM_SYSCOMMAND: if (wParam == SC_SCREENSAVE && !FAllowScreenSaver()) return fTrue; break; case WM_GETMINMAXINFO: if (pvNil != (pgob = GOB::PgobFromHwnd(hwnd))) { MINMAXINFO *pmmi = (MINMAXINFO far *)lw; pgob->GetMinMax(&rc); pmmi->ptMinTrackSize.x = LwMax(pmmi->ptMinTrackSize.x, rc.xpLeft); pmmi->ptMinTrackSize.y = LwMax(pmmi->ptMinTrackSize.y, rc.ypTop); pmmi->ptMaxTrackSize.x = LwMin(pmmi->ptMaxTrackSize.x, rc.xpRight); pmmi->ptMaxTrackSize.y = LwMin(pmmi->ptMaxTrackSize.y, rc.ypBottom); } return fTrue; case WM_SIZE: if (pvNil != (pgob = GOB::PgobFromHwnd(hwnd))) pgob->SetRcFromHwnd(); break; case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: ResetToolTip(); if (pvNil != (pgob = GOB::PgobFromHwnd(hwnd)) && pvNil != (pgob = pgob->PgobFromPt(SwLow(lw), SwHigh(lw), &pt))) { long ts; // compute the multiplicity of the click - don't use Windows' // guess, since it can be wrong for our GOBs. It's even wrong // at the HWND level! (Try double-clicking the maximize button). ts = GetMessageTime(); if (_pgobMouse == pgob && FIn(ts - _tsMouse, 0, GetDoubleClickTime())) { _cactMouse++; } else _cactMouse = 1; _tsMouse = ts; if (_pgobMouse != pgob && pvNil != _pgobMouse) { AssertPo(_pgobMouse, 0); vpcex->EnqueueCid(cidRollOff, _pgobMouse); } _pgobMouse = pgob; _xpMouse = klwMax; pgob->MouseDown(pt.xp, pt.yp, _cactMouse, GrfcustCur()); } else _pgobMouse = pvNil; break; case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: ResetToolTip(); break; case WM_SETCURSOR: if (LOWORD(lw) != HTCLIENT) return fFalse; RefreshCurs(); return fTrue; case WM_HSCROLL: case WM_VSCROLL: pscb = (PSCB)CTL::PctlFromHctl(GET_WM_HSCROLL_HWND(wParam, lw)); if (pvNil != pscb && pscb->FIs(kclsSCB)) { pscb->TrackScroll(GET_WM_HSCROLL_CODE(wParam, lw), GET_WM_HSCROLL_POS(wParam, lw)); } break; case WM_ACTIVATEAPP: _Activate(FPure(wParam)); break; } return fFalse; } #ifdef DEBUG /*************************************************************************** Debug initialization. ***************************************************************************/ bool APPB::_FInitDebug(void) { AssertThis(0); return fTrue; } // passes the strings to the assert dialog proc STN *_rgpstn[3]; /*************************************************************************** Dialog proc for assert. ***************************************************************************/ BOOL CALLBACK _FDlgAssert(HWND hdlg, UINT msg, WPARAM w, LPARAM lw) { switch (msg) { case WM_INITDIALOG: SetDlgItemText(hdlg, 3, _rgpstn[0]->Psz()); SetDlgItemText(hdlg, 4, _rgpstn[1]->Psz()); SetDlgItemText(hdlg, 5, _rgpstn[2]->Psz()); return fTrue; case WM_COMMAND: switch (GET_WM_COMMAND_ID(w, lw)) { default: break; case 0: case 1: case 2: EndDialog(hdlg, GET_WM_COMMAND_ID(w, lw)); return fTrue; } break; } return fFalse; } MUTX _mutxAssert; /*************************************************************************** The assert proc. Returning true breaks into the debugger. ***************************************************************************/ bool APPB::FAssertProcApp(PSZS pszsFile, long lwLine, PSZS pszsMsg, void *pv, long cb) { const long kclwChain = 10; STN stn0, stn1, stn2; int tmc; PSZ psz; long cact; long *plw; long ilw; long rglw[kclwChain]; _mutxAssert.Enter(); if (_fInAssert) { _mutxAssert.Leave(); return fFalse; } _fInAssert = fTrue; _rgpstn[0] = &stn0; _rgpstn[1] = &stn1; _rgpstn[2] = &stn2; // build the main assert message with file name and line number if (pszsMsg == pvNil || *pszsMsg == 0) psz = PszLit("Assert (%s line %d)"); else { psz = PszLit("Assert (%s line %d): %s"); stn2.SetSzs(pszsMsg); } if (pvNil != pszsFile) stn1.SetSzs(pszsFile); else stn1 = PszLit("Some Header file"); stn0.FFormatSz(psz, &stn1, lwLine, &stn2); // call stack - follow the EBP chain.... __asm { mov plw,ebp } for (ilw = 0; ilw < kclwChain; ilw++) { if (pvNil == plw || IsBadReadPtr(plw, 2 * size(long)) || *plw <= (long)plw) { rglw[ilw] = 0; plw = pvNil; } else { rglw[ilw] = plw[1]; plw = (long *)*plw; } } for (cact = 0; cact < 2; cact++) { // format data if (pv != pvNil && cb > 0) { byte *pb = (byte *)pv; long cbT = cb; long ilw; long lw; STN stnT; stn2.SetNil(); for (ilw = 0; ilw < 20 && cb >= 4; cb -= 4, pb += 4, ++ilw) { CopyPb(pb, &lw, 4); stnT.FFormatSz(PszLit("%08x "), lw); stn2.FAppendStn(&stnT); } if (ilw < 20 && cb > 0) { lw = 0; CopyPb(pb, &lw, cb); stnT.FFormatSz((cb <= 2) ? PszLit("%04x") : PszLit("%08x"), lw); stn2.FAppendStn(&stnT); } } else stn2.SetNil(); if (cact == 0) { pv = rglw; cb = size(rglw); stn1 = stn2; } } OutputDebugString(stn0.Psz()); OutputDebugString(PszLit("\n")); if (stn1.Cch() > 0) { OutputDebugString(stn1.Psz()); OutputDebugString(PszLit("\n")); } if (stn2.Cch() > 0) { OutputDebugString(stn2.Psz()); OutputDebugString(PszLit("\n")); } if (LwThreadCur() != vwig.lwThreadMain) { // can't use a dialog - it may cause grid - lock long sid; ulong grfmb; stn0.FAppendSz(PszLit("\n")); stn0.FAppendStn(&stn1); stn0.FAppendSz(PszLit("\n")); stn0.FAppendStn(&stn2); grfmb = MB_SYSTEMMODAL | MB_YESNO | MB_ICONHAND; sid = MessageBox(hNil, stn0.Psz(), PszLit("Thread Assert! (Y = Ignore, N = Debugger)"), grfmb); switch (sid) { default: tmc = 0; break; case IDNO: tmc = 1; break; } } else { // run the dialog tmc = DialogBox(vwig.hinst, PszLit("AssertDlg"), vwig.hwndApp, &_FDlgAssert); } _fInAssert = fFalse; _mutxAssert.Leave(); switch (tmc) { case 0: // ignore return fFalse; case 1: // break into debugger return fTrue; case 2: // abort Abort(); // shouldn't return Debugger(); break; } return fFalse; } #endif //DEBUG /*************************************************************************** Put an alert up. Return which button was hit. Returns tYes for yes or ok; tNo for no; tMaybe for cancel. ***************************************************************************/ bool APPB::TGiveAlertSz(PSZ psz, long bk, long cok) { AssertThis(0); AssertSz(psz); long sid; ulong grfmb; HWND hwnd; grfmb = MB_APPLMODAL; switch (bk) { default: BugVar("bad bk value", &bk); //fall through case bkOk: grfmb |= MB_OK; break; case bkOkCancel: grfmb |= MB_OKCANCEL; break; case bkYesNo: grfmb |= MB_YESNO; break; case bkYesNoCancel: grfmb |= MB_YESNOCANCEL; break; } switch (cok) { default: BugVar("bad cok value", &cok); //fall through case cokNil: break; case cokInformation: grfmb |= MB_ICONINFORMATION; break; case cokQuestion: grfmb |= MB_ICONQUESTION; break; case cokExclamation: grfmb |= MB_ICONEXCLAMATION; break; case cokStop: grfmb |= MB_ICONSTOP; break; } hwnd = GetActiveWindow(); if (hNil == hwnd) hwnd = vwig.hwndApp; sid = MessageBox(hwnd, psz, PszLit(""), grfmb); switch (sid) { default: case IDYES: case IDOK: return tYes; case IDCANCEL: return tMaybe; case IDNO: return tNo; } }