home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / ctlevent.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  16KB  |  626 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #include <stdarg.h>
  13.  
  14. #ifdef AFXCTL_CORE2_SEG
  15. #pragma code_seg(AFXCTL_CORE2_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. #define new DEBUG_NEW
  24.  
  25. #pragma warning(disable: 4706) // assignment within conditional
  26.  
  27. /////////////////////////////////////////////////////////////////////////////
  28. // Stock event mask
  29.  
  30. #define STOCKEVENT_CLICK            0x00000001
  31. #define STOCKEVENT_DBLCLICK         0x00000002
  32. #define STOCKEVENT_KEYDOWN          0x00000004
  33. #define STOCKEVENT_KEYPRESS         0x00000008
  34. #define STOCKEVENT_KEYUP            0x00000010
  35. #define STOCKEVENT_MOUSEDOWN        0x00000020
  36. #define STOCKEVENT_MOUSEMOVE        0x00000040
  37. #define STOCKEVENT_MOUSEUP          0x00000080
  38. #define STOCKEVENT_ERROR            0x00000100
  39. #define STOCKEVENT_READYSTATECHANGE 0x00000200
  40.  
  41. #define STOCKEVENTS_MOUSE       0x000000A3  // Click, DblClick, MouseDown, MouseUp
  42.  
  43. AFX_STATIC_DATA const DWORD _afxStockEvents[] =
  44. {
  45.     STOCKEVENT_CLICK,               // -600
  46.     STOCKEVENT_DBLCLICK,            // -601
  47.     STOCKEVENT_KEYDOWN,             // -602
  48.     STOCKEVENT_KEYPRESS,            // -603
  49.     STOCKEVENT_KEYUP,               // -604
  50.     STOCKEVENT_MOUSEDOWN,           // -605
  51.     STOCKEVENT_MOUSEMOVE,           // -606
  52.     STOCKEVENT_MOUSEUP,             // -607
  53.     STOCKEVENT_ERROR,               // -608
  54.     STOCKEVENT_READYSTATECHANGE,    // -609
  55. };
  56.  
  57. void COleControl::InitStockEventMask()
  58. {
  59.     const AFX_EVENTMAP* pEventMap = GetEventMap();
  60.     const AFX_EVENTMAP_ENTRY* pEntry;
  61.     ASSERT(pEventMap != NULL);
  62.  
  63.     // If stock event mask is already initialized, we're outta here.
  64.     if (*pEventMap->lpStockEventMask != (DWORD)-1)
  65.         return;
  66.  
  67.     AfxLockGlobals(CRIT_STOCKMASK);
  68.  
  69.     if (*pEventMap->lpStockEventMask == (DWORD)-1)
  70.     {
  71.         const AFX_EVENTMAP* pEventMapTop = pEventMap;
  72.         DWORD dwStockEventMask = 0;
  73.  
  74.         while (pEventMap != NULL)
  75.         {
  76.             pEntry = pEventMap->lpEntries;
  77.             while (pEntry != NULL && pEntry->pszName != NULL)
  78.             {
  79.                 int nIndex = DISPID_CLICK - pEntry->dispid;
  80.                 DWORD dwFlag;
  81.                 if ((pEntry->flags & afxEventStock) && (nIndex >= 0) &&
  82.                     (nIndex < _countof(_afxStockEvents)) &&
  83.                     (dwFlag = _afxStockEvents[nIndex]) != 0)
  84.                 {
  85.                     dwStockEventMask |= dwFlag;
  86.                 }
  87.  
  88.                 ++pEntry;
  89.             }
  90.             // check base class
  91.             pEventMap = pEventMap->lpBaseEventMap;
  92.         }
  93.  
  94.         *pEventMapTop->lpStockEventMask = dwStockEventMask;
  95.     }
  96.  
  97.     AfxUnlockGlobals(CRIT_STOCKMASK);
  98. }
  99.  
  100. /////////////////////////////////////////////////////////////////////////////
  101. // Event map operations
  102.  
  103. const AFX_EVENTMAP* COleControl::GetEventMap() const
  104. {
  105.     return &eventMap;
  106. }
  107.  
  108. const AFX_EVENTMAP_ENTRY* COleControl::GetEventMapEntry(
  109.         LPCTSTR pszName,
  110.         DISPID* pDispid) const
  111. {
  112.     ASSERT(pszName != NULL);
  113.     ASSERT(pDispid != NULL);
  114.  
  115.     const AFX_EVENTMAP* pEventMap = GetEventMap();
  116.     const AFX_EVENTMAP_ENTRY* pEntry;
  117.     DISPID dispid = MAKELONG(1, 0);
  118.  
  119.     while (pEventMap != NULL)
  120.     {
  121.         pEntry = pEventMap->lpEntries;
  122.  
  123.         // Scan entries in this event map
  124.  
  125.         if (pEntry != NULL)
  126.         {
  127.             while (pEntry->pszName != NULL)
  128.             {
  129.                 if (lstrcmp(pEntry->pszName, pszName) == 0)
  130.                 {
  131.                     if (pEntry->dispid != DISPID_UNKNOWN)
  132.                         dispid = pEntry->dispid;
  133.  
  134.                     *pDispid = dispid;
  135.                     return pEntry;
  136.                 }
  137.  
  138.                 ++pEntry;
  139.                 ++dispid;
  140.             }
  141.         }
  142.  
  143.         // If we didn't find it, go to the base class's event map
  144.  
  145.         pEventMap = pEventMap->lpBaseEventMap;
  146.         dispid = MAKELONG(1, HIWORD(dispid)+1);
  147.     }
  148.  
  149.     // If we reach here, the event isn't supported
  150.  
  151.     return NULL;
  152. }
  153.  
  154. void COleControl::FireEventV(DISPID dispid, BYTE* pbParams,
  155.     va_list argList)
  156. {
  157.     COleDispatchDriver driver;
  158.  
  159.     POSITION pos = m_xEventConnPt.GetStartPosition();
  160.     LPDISPATCH pDispatch;
  161.     while (pos != NULL)
  162.     {
  163.         pDispatch = (LPDISPATCH)m_xEventConnPt.GetNextConnection(pos);
  164.         ASSERT(pDispatch != NULL);
  165.         driver.AttachDispatch(pDispatch, FALSE);
  166.         TRY
  167.             driver.InvokeHelperV(dispid, DISPATCH_METHOD, VT_EMPTY, NULL,
  168.                 pbParams, argList);
  169.         END_TRY
  170.         driver.DetachDispatch();
  171.     }
  172. }
  173.  
  174. void AFX_CDECL COleControl::FireEvent(DISPID dispid, BYTE* pbParams, ...)
  175. {
  176.     va_list argList;
  177.     va_start(argList, pbParams);
  178.     FireEventV(dispid, pbParams, argList);
  179.     va_end(argList);
  180. }
  181.  
  182. /////////////////////////////////////////////////////////////////////////////
  183. // Helper function for stock events
  184.  
  185. short AFXAPI _AfxShiftState()
  186. {
  187.     BOOL bShift = (GetKeyState(VK_SHIFT) < 0);
  188.     BOOL bCtrl  = (GetKeyState(VK_CONTROL) < 0);
  189.     BOOL bAlt   = (GetKeyState(VK_MENU) < 0);
  190.  
  191.     return (short)(bShift + (bCtrl << 1) + (bAlt << 2));
  192. }
  193.  
  194. /////////////////////////////////////////////////////////////////////////////
  195. // Window message handlers for stock events
  196.  
  197. void COleControl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  198. {
  199.     HWND hWndSave = m_hWnd;
  200.     USHORT nCharShort = (USHORT)nChar;
  201.     KeyDown(&nCharShort);
  202.     if ((m_hWnd == hWndSave) && (nCharShort != 0))
  203.         DefWindowProc(WM_SYSKEYDOWN, nCharShort, MAKELONG(nRepCnt, nFlags));
  204. }
  205.  
  206. void COleControl::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
  207. {
  208.     HWND hWndSave = m_hWnd;
  209.     USHORT nCharShort = (USHORT)nChar;
  210.     KeyUp(&nCharShort);
  211.     if ((m_hWnd == hWndSave) && (nCharShort != 0))
  212.         DefWindowProc(WM_SYSKEYUP, nCharShort, MAKELONG(nRepCnt, nFlags));
  213. }
  214.  
  215. void COleControl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  216. {
  217.     HWND hWndSave = m_hWnd;
  218.     USHORT nCharShort = (USHORT)nChar;
  219.     KeyDown(&nCharShort);
  220.     if ((m_hWnd == hWndSave) && (nCharShort != 0))
  221.         DefWindowProc(WM_KEYDOWN, nCharShort, MAKELONG(nRepCnt, nFlags));
  222. }
  223.  
  224. void COleControl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
  225. {
  226.     HWND hWndSave = m_hWnd;
  227.     USHORT nCharShort = (USHORT)nChar;
  228.     KeyUp(&nCharShort);
  229.     if ((m_hWnd == hWndSave) && (nCharShort != 0))
  230.         DefWindowProc(WM_KEYUP, nCharShort, MAKELONG(nRepCnt, nFlags));
  231. }
  232.  
  233. void COleControl::KeyUp(USHORT* pnChar)
  234. {
  235.     if (GetStockEventMask() & STOCKEVENT_KEYUP)
  236.     {
  237.         USHORT nShiftState = _AfxShiftState();
  238.         FireKeyUp(pnChar, nShiftState);
  239.  
  240.         // If handler set *pnChar to zero, cancel further processing.
  241.         if (*pnChar != 0)
  242.             OnKeyUpEvent(*pnChar, nShiftState);
  243.     }
  244. }
  245.  
  246. void COleControl::KeyDown(USHORT* pnChar)
  247. {
  248.     if (GetStockEventMask() & STOCKEVENT_KEYDOWN)
  249.     {
  250.         USHORT nShiftState = _AfxShiftState();
  251.         FireKeyDown(pnChar, nShiftState);
  252.  
  253.         // If handler set *pnChar to zero, cancel further processing.
  254.         if (*pnChar != 0)
  255.             OnKeyDownEvent(*pnChar, nShiftState);
  256.     }
  257. }
  258.  
  259. AFX_STATIC void AFXAPI _AfxPostTrailByte(CWnd* pWnd, BYTE bTrailByte)
  260. {
  261.     // Force new trail byte to the front of the queue.
  262.     pWnd->PostMessage(WM_QUEUE_SENTINEL);
  263.     pWnd->PostMessage(WM_CHAR, bTrailByte);
  264.     MSG msg;
  265.     while (::PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE) &&
  266.         (msg.message != WM_QUEUE_SENTINEL))
  267.     {
  268.         ::PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
  269.     }
  270.  
  271.     ASSERT(msg.message == WM_QUEUE_SENTINEL);
  272.     ASSERT(msg.hwnd == pWnd->m_hWnd);
  273. }
  274.  
  275. UINT COleControl::OnGetDlgCode()
  276. {
  277.     // If we're firing KeyPress, prevent the container from stealing WM_CHAR.
  278.     return (IsSubclassedControl() ? CWnd::OnGetDlgCode() : 0) |
  279.         ((GetStockEventMask() & STOCKEVENT_KEYPRESS) ? DLGC_WANTCHARS : 0);
  280. }
  281.  
  282. void COleControl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
  283. {
  284.     USHORT nCharShort = (USHORT)nChar;
  285.     USHORT nCharSave = nCharShort;
  286.     BOOL bLeadByte = IsDBCSLeadByte((BYTE)nCharShort);
  287.     MSG msg;
  288.  
  289.     if (GetStockEventMask() & STOCKEVENT_KEYPRESS)
  290.     {
  291.         if (bLeadByte)
  292.         {
  293.             // We have the lead-byte of a DBCS character.  Peek for the
  294.             // next WM_CHAR message, which will contain the other byte.
  295.  
  296.             BOOL bMessage;
  297.             VERIFY(bMessage = ::PeekMessage(&msg, m_hWnd, WM_CHAR, WM_CHAR,
  298.                 PM_NOYIELD | PM_NOREMOVE));
  299.  
  300.             // Combine the bytes to form the DBCS character.
  301.  
  302.             if (bMessage)
  303.                 nCharShort = (USHORT)((nCharShort << 8) | msg.wParam);
  304.         }
  305.  
  306.         HWND hWndSave = m_hWnd;
  307.         nCharSave = nCharShort;
  308.         FireKeyPress(&nCharShort);
  309.  
  310.         // If handler set nCharShort to zero, cancel further processing.
  311.         if (nCharShort != 0)
  312.             OnKeyPressEvent(nCharShort);
  313.  
  314.         if (m_hWnd != hWndSave)
  315.             return;
  316.     }
  317.  
  318.     if (nCharShort != 0)
  319.     {
  320.         if (nCharSave != nCharShort)
  321.         {
  322.             nChar = nCharShort;
  323.  
  324.             // Event handler has changed the character.
  325.  
  326.             BOOL bNewLeadByte = IsDBCSLeadByte(HIBYTE(nCharShort));
  327.  
  328.             if (bLeadByte)
  329.             {
  330.                 if (bNewLeadByte)
  331.                 {
  332.                     // Event handler changed character from DBCS to DBCS:
  333.                     // Remove the old trail byte and post the new one.
  334.  
  335.                     VERIFY(::PeekMessage(&msg, m_hWnd, WM_CHAR, WM_CHAR,
  336.                         PM_NOYIELD | PM_REMOVE));
  337.                     _AfxPostTrailByte(this, LOBYTE(nCharShort));
  338.                     nChar = HIBYTE(nCharShort);
  339.                 }
  340.                 else
  341.                 {
  342.                     // Event handler changed character from DBCS to SBCS:
  343.                     // Remove the second byte from the queue, and forward
  344.                     // along the new single-byte character.
  345.  
  346.                     VERIFY(::PeekMessage(&msg, m_hWnd, WM_CHAR, WM_CHAR,
  347.                         PM_NOYIELD | PM_REMOVE));
  348.                 }
  349.             }
  350.             else
  351.             {
  352.                 if (bNewLeadByte)
  353.                 {
  354.                     // Event handler changed character from SBCS to DBCS:
  355.                     // Post the new trail byte.
  356.  
  357.                     _AfxPostTrailByte(this, LOBYTE(nCharShort));
  358.                     nChar = HIBYTE(nCharShort);
  359.                 }
  360.             }
  361.         }
  362.  
  363.         DefWindowProc(WM_CHAR, nChar, MAKELONG(nRepCnt, nFlags));
  364.     }
  365.  
  366.     if (bLeadByte)
  367.     {
  368.         // Cleanup after processing a DBCS character:
  369.         // Remove the next WM_CHAR message (containing the second byte) from
  370.         // the message queue, UNLESS we're subclassing an Edit, ListBox, or
  371.         // ComboBox control.
  372.  
  373.         TCHAR szClassName[10];
  374.         if ((!::GetClassName(m_hWnd, szClassName, 10)) ||  // didn't get class
  375.             (lstrcmpi(szClassName, _T("Edit")) &&           // not Edit
  376.              lstrcmpi(szClassName, _T("ListBox")) &&        // not ListBox
  377.              lstrcmpi(szClassName, _T("ComboBox"))))        // not ComboBox
  378.         {
  379.             VERIFY(::PeekMessage(&msg, m_hWnd, WM_CHAR, WM_CHAR,
  380.                 PM_NOYIELD | PM_REMOVE));
  381.         }
  382.     }
  383. }
  384.  
  385. void COleControl::OnKeyPressEvent(USHORT)
  386. {
  387.     // Can be overridden by subclass
  388. }
  389.  
  390. void COleControl::OnKeyDownEvent(USHORT, USHORT)
  391. {
  392.     // Can be overridden by subclass
  393. }
  394.  
  395. void COleControl::OnKeyUpEvent(USHORT, USHORT)
  396. {
  397.     // Can be overridden by subclass
  398. }
  399.  
  400. void COleControl::ButtonDown(USHORT iButton, UINT, CPoint point)
  401. {
  402.     DWORD dwStockEventMask = GetStockEventMask();
  403.     if ((dwStockEventMask & STOCKEVENTS_MOUSE) || m_bPendingUIActivation)
  404.     {
  405.         if (m_iButtonState == 0)
  406.             SetCapture();
  407.  
  408.         m_iButtonState |= iButton;
  409.  
  410.         if (dwStockEventMask & STOCKEVENT_MOUSEDOWN)
  411.             FireMouseDown(iButton, _AfxShiftState(), point.x, point.y);
  412.  
  413.         m_iDblClkState &= ~iButton;
  414.     }
  415. }
  416.  
  417. void COleControl::ButtonUp(USHORT iButton, UINT, CPoint point)
  418. {
  419.     if (m_iButtonState != 0)
  420.     {
  421.         m_iButtonState &= ~iButton;
  422.  
  423.         if (m_iButtonState == 0)
  424.             ReleaseCapture();
  425.  
  426.         DWORD dwStockEventMask = GetStockEventMask();
  427.  
  428.         if (dwStockEventMask & STOCKEVENT_MOUSEUP)
  429.             FireMouseUp(iButton, _AfxShiftState(), point.x, point.y);
  430.  
  431.         if ((dwStockEventMask & STOCKEVENT_CLICK) &&
  432.             !(m_iDblClkState & iButton))
  433.         {
  434.             CRect rect;
  435.             GetClientRect(&rect);
  436.             if (rect.PtInRect(point))
  437.                 OnClick(iButton);
  438.         }
  439.  
  440.         m_iDblClkState &= ~iButton;
  441.     }
  442. }
  443.  
  444. void COleControl::ButtonDblClk(USHORT iButton, UINT, CPoint)
  445. {
  446.     DWORD dwStockEventMask = GetStockEventMask();
  447.     if (dwStockEventMask & STOCKEVENTS_MOUSE)
  448.     {
  449.         SetCapture();
  450.         m_iButtonState |= iButton;
  451.  
  452.         if (dwStockEventMask & STOCKEVENT_DBLCLICK)
  453.         {
  454.             FireDblClick();
  455.             m_iDblClkState |= iButton;
  456.         }
  457.     }
  458. }
  459.  
  460. void COleControl::OnMouseMove(UINT /*nFlags*/, CPoint point)
  461. {
  462.     if (GetStockEventMask() & STOCKEVENT_MOUSEMOVE)
  463.     {
  464.         HWND hWndSave = m_hWnd;
  465.         FireMouseMove((short)m_iButtonState, _AfxShiftState(), point.x, point.y);
  466.         if (m_hWnd != hWndSave)
  467.             return;
  468.     }
  469.     Default();
  470. }
  471.  
  472. void COleControl::OnLButtonDown(UINT nFlags, CPoint point)
  473. {
  474.     OnButtonDown(LEFT_BUTTON, nFlags, point);
  475. }
  476.  
  477. void COleControl::OnLButtonUp(UINT nFlags, CPoint point)
  478. {
  479.     OnButtonUp(LEFT_BUTTON, nFlags, point);
  480. }
  481.  
  482. void COleControl::OnLButtonDblClk(UINT nFlags, CPoint point)
  483. {
  484.     OnButtonDblClk(LEFT_BUTTON, nFlags, point);
  485. }
  486.  
  487. void COleControl::OnMButtonDown(UINT nFlags, CPoint point)
  488. {
  489.     OnButtonDown(MIDDLE_BUTTON, nFlags, point);
  490. }
  491.  
  492. void COleControl::OnMButtonUp(UINT nFlags, CPoint point)
  493. {
  494.     OnButtonUp(MIDDLE_BUTTON, nFlags, point);
  495. }
  496.  
  497. void COleControl::OnMButtonDblClk(UINT nFlags, CPoint point)
  498. {
  499.     OnButtonDblClk(MIDDLE_BUTTON, nFlags, point);
  500. }
  501.  
  502. void COleControl::OnRButtonDown(UINT nFlags, CPoint point)
  503. {
  504.     OnButtonDown(RIGHT_BUTTON, nFlags, point);
  505. }
  506.  
  507. void COleControl::OnRButtonUp(UINT nFlags, CPoint point)
  508. {
  509.     OnButtonUp(RIGHT_BUTTON, nFlags, point);
  510. }
  511.  
  512. void COleControl::OnRButtonDblClk(UINT nFlags, CPoint point)
  513. {
  514.     OnButtonDblClk(RIGHT_BUTTON, nFlags, point);
  515. }
  516.  
  517. void COleControl::OnButtonDown(USHORT nButton, UINT nFlags, CPoint point)
  518. {
  519.     HWND hWndSave = m_hWnd;
  520.     if (nButton == LEFT_BUTTON)
  521.         SetFocus();
  522.     ButtonDown(nButton, nFlags, point);
  523.     if (m_hWnd != hWndSave)
  524.         return;
  525.  
  526.     Default();
  527. }
  528.  
  529. void COleControl::OnButtonUp(USHORT nButton, UINT nFlags, CPoint point)
  530. {
  531.     HWND hWndSave = m_hWnd;
  532.     Default();
  533.     ButtonUp(nButton, nFlags, point);
  534.     if (m_hWnd != hWndSave)
  535.         return;
  536.  
  537.     if (m_bInPlaceActive && !m_bUIActive && m_bPendingUIActivation)
  538.     {
  539.         m_bPendingUIActivation = FALSE;
  540.         HWND hWndFocus = ::GetFocus();
  541.         if (hWndFocus == m_hWnd || ::IsChild(m_hWnd, hWndFocus))
  542.             OnActivateInPlace(TRUE, NULL);
  543.     }
  544. }
  545.  
  546. void COleControl::OnButtonDblClk(USHORT nButton, UINT nFlags, CPoint point)
  547. {
  548.     HWND hWndSave = m_hWnd;
  549.     ButtonDblClk(nButton, nFlags, point);
  550.     if (m_hWnd != hWndSave)
  551.         return;
  552.  
  553.     Default();
  554. }
  555.  
  556. void COleControl::OnCancelMode()
  557. {
  558.     CWnd::OnCancelMode();
  559.  
  560.     if ((m_iButtonState != 0) || (m_iDblClkState != 0))
  561.     {
  562.         ReleaseCapture();
  563.         m_iButtonState = 0;
  564.         m_iDblClkState = 0;
  565.     }
  566. }
  567.  
  568. void COleControl::OnClick(USHORT /*iButton*/)
  569. {
  570.     // May be overridden by subclass
  571.  
  572.     if (GetStockEventMask() & STOCKEVENT_CLICK)
  573.         FireClick();
  574. }
  575.  
  576. /////////////////////////////////////////////////////////////////////////////
  577. // Error event
  578.  
  579. #define ERROR_PARAMS \
  580.      EVENT_PARAM(VTS_I2 VTS_PBSTR VTS_SCODE VTS_BSTR VTS_BSTR VTS_I4 VTS_PBOOL)
  581.  
  582. void COleControl::FireError(SCODE scode, LPCTSTR lpszDescription, UINT nHelpID)
  583. {
  584.     USES_CONVERSION;
  585.  
  586.     ExternalAddRef();   // "Insurance" addref -- keeps control alive.
  587.  
  588.     BSTR bstrDescription = ::SysAllocString(T2COLE(lpszDescription));
  589.     LPCTSTR lpszSource = AfxGetAppName();
  590.     LPCTSTR lpszHelpFile = _T("");
  591.  
  592.     if (nHelpID != 0)
  593.         lpszHelpFile = AfxGetApp()->m_pszHelpFilePath;
  594.  
  595.     if (lpszHelpFile == NULL)
  596.         lpszHelpFile = _T("");
  597.  
  598.     BOOL bCancelDisplay = FALSE;
  599.  
  600.     FireEvent(DISPID_ERROREVENT, ERROR_PARAMS, (WORD)SCODE_CODE(scode),
  601.         &bstrDescription, scode, lpszSource, lpszHelpFile, (DWORD)nHelpID,
  602.         &bCancelDisplay);
  603.  
  604.     if (!bCancelDisplay)
  605.         DisplayError(scode, OLE2CT(bstrDescription), lpszSource, lpszHelpFile, nHelpID);
  606.  
  607.     ::SysFreeString(bstrDescription);
  608.  
  609.     ExternalRelease();
  610. }
  611.  
  612. void COleControl::DisplayError(SCODE /*scode*/, LPCTSTR lpszDescription,
  613.     LPCTSTR lpszSource, LPCTSTR /*lpszHelpFile*/, UINT /*nHelpID*/)
  614. {
  615.     // May be overridden by subclass.
  616.  
  617.     MessageBox(lpszDescription, lpszSource);
  618. }
  619.  
  620. /////////////////////////////////////////////////////////////////////////////
  621. // Force any extra compiler-generated code into AFX_INIT_SEG
  622.  
  623. #ifdef AFX_INIT_SEG
  624. #pragma code_seg(AFX_INIT_SEG)
  625. #endif
  626.