home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / mapi / remote.srv / adminui.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  18.5 KB  |  604 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. //  File Name 
  4. //      ADMINUI.CPP
  5. //
  6. //  Description
  7. //
  8. //  Author
  9. //      Irving De la Cruz
  10. //
  11. //  Revision: 1.7
  12. //
  13. // Written for Microsoft Windows Developer Support
  14. // Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved.
  15. //
  16. #include "ADMIN.H"
  17.  
  18. #define MAXCOLUMNS        7
  19.  
  20. extern "C"
  21. {
  22.     void WINAPI InitListViewColumns
  23.                         ();
  24.     BOOL WINAPI CalcStringEllipsis
  25.                         (HDC                        hdc,
  26.                          LPTSTR                     szString,
  27.                          int                        cchMax,
  28.                          UINT                       uColWidth);
  29.     void WINAPI DrawItemColumn
  30.                         (HDC                        hdc,
  31.                          LPTSTR                     szText,
  32.                          LPRECT                     prcClip);
  33. };
  34.  
  35. UINT g_uColumns[MAXCOLUMNS] =
  36. {
  37.     100,
  38.     125,
  39.     175,
  40.     75,
  41.     175,
  42.     80,
  43.     80
  44. };
  45.  
  46. void WINAPI InitListViewColumns()
  47. {
  48.     TCHAR achTemp[256];
  49.     LV_COLUMN lvc;
  50.     
  51.     // Initialize the LV_COLUMN structure
  52.     lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  53.     lvc.fmt = LVCFMT_LEFT;
  54.     lvc.pszText = achTemp;
  55.  
  56.     // Add the columns
  57.     for (int iCol = 0; iCol < MAXCOLUMNS; iCol++)
  58.     {
  59.         lvc.iSubItem = iCol;
  60.         lvc.cx = g_uColumns[iCol];
  61.         LoadString (ghInstance, IDS_COLUMN1 + iCol, achTemp, sizeof(achTemp));
  62.         ListView_InsertColumn (ghListView, iCol, &lvc);
  63.     }
  64. }
  65.  
  66. #define TOP_CLEAR               30
  67. #define V_BORDER_SIZE           5
  68. #define H_BORDER_SIZE           3
  69. #define CONTROL_HEIGHT          403
  70. #define TREEVIEW_WIDTH          215
  71. #define TREEVIEW_HEIGHT         CONTROL_HEIGHT
  72. #define LISTVIEW_WIDTH          560
  73. #define LISTVIEW_HEIGHT         CONTROL_HEIGHT
  74.  
  75. ///////////////////////////////////////////////////////////////////////////////
  76. //    CreateListView()
  77. //
  78. //    Parameters
  79. //      
  80. //    Purpose
  81. //      
  82. //    Return Value
  83. //      
  84. BOOL WINAPI CreateListView (HWND hOwnerWnd, HIMAGELIST hImages)
  85. {
  86.     RECT rc;
  87.     GetClientRect (hOwnerWnd, &rc);
  88.     ghListView = CreateWindowEx (0,
  89.                                  WC_LISTVIEW,
  90.                                  TEXT(""),
  91.                                  WS_VISIBLE | WS_CHILD | WS_BORDER |
  92.                                  LVS_REPORT | LVS_OWNERDRAWFIXED |
  93.                                  LVS_SHOWSELALWAYS | LVS_SINGLESEL |
  94.                                  LVS_SORTASCENDING,
  95.                                  V_BORDER_SIZE + TREEVIEW_WIDTH + V_BORDER_SIZE,
  96.                                  TOP_CLEAR, 
  97.                                  LISTVIEW_WIDTH,
  98.                                  LISTVIEW_HEIGHT,
  99.                                  hOwnerWnd,
  100.                                  (HMENU)IDC_LISTVIEW, 
  101.                                  ghInstance, 
  102.                                  NULL);
  103.     if (ghListView == NULL)
  104.     {
  105.         return FALSE;
  106.     }
  107.     ListView_SetImageList (ghListView, hImages, LVSIL_SMALL);
  108.     InitListViewColumns();
  109.     return TRUE;
  110. }
  111.  
  112. ///////////////////////////////////////////////////////////////////////////////
  113. //    CreateTreeView()
  114. //
  115. //    Parameters
  116. //      
  117. //    Purpose
  118. //      
  119. //    Return Value
  120. //      
  121. BOOL WINAPI CreateTreeView (HWND hOwnerWnd, HIMAGELIST hImages)
  122. {
  123.     ghTreeView = CreateWindowEx (0,
  124.                                  WC_TREEVIEW,
  125.                                  TEXT(""),
  126.                                  WS_BORDER | WS_CHILD | WS_VISIBLE |
  127.                                  TVS_HASLINES | TVS_HASBUTTONS |
  128.                                  TVS_LINESATROOT | TVS_SHOWSELALWAYS |
  129.                                  TVS_DISABLEDRAGDROP,
  130.                                  V_BORDER_SIZE,
  131.                                  TOP_CLEAR,
  132.                                  TREEVIEW_WIDTH,
  133.                                  TREEVIEW_HEIGHT,
  134.                                  hOwnerWnd,
  135.                                  (HMENU)IDC_TREEVIEW,
  136.                                  ghInstance,
  137.                                  NULL);
  138.     if (ghTreeView == NULL)
  139.     {
  140.         return FALSE;
  141.     }
  142.     TreeView_SetImageList (ghTreeView, hImages, TVSIL_NORMAL);
  143.     return TRUE;
  144. }
  145.  
  146. ///////////////////////////////////////////////////////////////////////////////
  147. //    CreateStatusBar()
  148. //
  149. //    Parameters
  150. //      
  151. //    Purpose
  152. //      
  153. //    Return Value
  154. //      
  155. BOOL WINAPI CreateStatusBar (HWND hOwnerWnd)
  156. {
  157.     ghStatusBar = CreateStatusWindow (WS_BORDER | WS_CHILD | WS_VISIBLE,
  158.                                       TEXT("Ready"),
  159.                                       hOwnerWnd,
  160.                                       IDC_STATUSBAR);
  161.     if (ghStatusBar == NULL)
  162.     {
  163.         return FALSE;
  164.     }
  165.     return TRUE;
  166. }
  167.  
  168. // Define this enumaration for easier array indexing
  169. enum
  170. {
  171.     SPACE_1,
  172.     ADD_USER,
  173.     ADD_DL,
  174.     SPACE_2,
  175.     SPACE_3,
  176.     DELETE_OBJ,
  177.     SPACE_4,
  178.     SPACE_5,
  179.     STOP_HAND,
  180.     NUMBER_OF_TB_BUTTONS,
  181.     NUMBER_OF_SEPARATORS = 5
  182. };
  183.  
  184. ///////////////////////////////////////////////////////////////////////////////
  185. //    CreateToolBar()
  186. //
  187. //    Parameters
  188. //      
  189. //    Purpose
  190. //      
  191. //    Return Value
  192. //      
  193. BOOL WINAPI CreateToolBar (HWND hOwnerWnd)
  194. {
  195.     // Initialize each of the buttons with the appropiate values
  196.     TBBUTTON tbButton[NUMBER_OF_TB_BUTTONS] = { 0 };
  197.  
  198.     tbButton[SPACE_1].fsStyle   = TBSTYLE_SEP;
  199.     tbButton[SPACE_2].fsStyle   = TBSTYLE_SEP;
  200.     tbButton[SPACE_3].fsStyle   = TBSTYLE_SEP;
  201.     tbButton[SPACE_4].fsStyle   = TBSTYLE_SEP;
  202.     tbButton[SPACE_5].fsStyle   = TBSTYLE_SEP;
  203.  
  204.     tbButton[ADD_USER].iBitmap   = 0;
  205.     tbButton[ADD_USER].idCommand = IDM_FILE_NEW_MAILBOX;
  206.     tbButton[ADD_USER].fsState   = TBSTATE_ENABLED;
  207.     tbButton[ADD_USER].fsStyle   = TBSTYLE_BUTTON;
  208.  
  209.     tbButton[ADD_DL].iBitmap      = 1;
  210.     tbButton[ADD_DL].idCommand    = IDM_FILE_NEW_DISTRIBUTIONLIST;
  211.     tbButton[ADD_DL].fsState      = TBSTATE_ENABLED;
  212.     tbButton[ADD_DL].fsStyle      = TBSTYLE_BUTTON;
  213.  
  214.     tbButton[DELETE_OBJ].iBitmap  = 2;
  215.     tbButton[DELETE_OBJ].idCommand= IDM_FILE_DELETE;
  216.     tbButton[DELETE_OBJ].fsState  = TBSTATE_ENABLED;
  217.     tbButton[DELETE_OBJ].fsStyle  = TBSTYLE_BUTTON;
  218.  
  219.     tbButton[STOP_HAND].iBitmap  = 3;
  220.     tbButton[STOP_HAND].idCommand= IDC_CANCEL_REMOTE_CALL;
  221.     tbButton[STOP_HAND].fsState  = TBSTATE_INDETERMINATE;
  222.     tbButton[STOP_HAND].fsStyle  = TBSTYLE_BUTTON;
  223.  
  224.     // Create the toolbar with the most commonly used functions
  225.     ghToolBar = CreateToolbarEx (hOwnerWnd,
  226.                                  WS_VISIBLE | WS_CHILD | WS_BORDER | TBSTYLE_TOOLTIPS,
  227.                                  IDC_TOOLBAR,
  228.                                  // The number of images is the number of
  229.                                  // buttons minus the separators
  230.                                  NUMBER_OF_TB_BUTTONS - NUMBER_OF_SEPARATORS,
  231.                                  ghInstance,
  232.                                  IDB_TOOLBAR,
  233.                                  tbButton,
  234.                                  NUMBER_OF_TB_BUTTONS,
  235.                                  16, 16, 16, 32,
  236.                                  sizeof(TBBUTTON));
  237.     if (NULL == ghToolBar)
  238.     {
  239.         return FALSE;
  240.     }
  241.     return TRUE;
  242. }
  243.  
  244. ///////////////////////////////////////////////////////////////////////////////
  245. //    UpdateHeaderWidth()
  246. //
  247. //    Parameters
  248. //      
  249. //    Purpose
  250. //      
  251. //    Return Value
  252. //      
  253. void WINAPI UpdateHeaderWidth (HWND hWndHeader, int iItem)
  254. {
  255.     HD_ITEM hdi;
  256.     // Get the column width from the control
  257.     hdi.mask = HDI_WIDTH;
  258.     if (Header_GetItem (hWndHeader, iItem, &hdi))
  259.     {
  260.         g_uColumns[iItem] = hdi.cxy;
  261.     }
  262.     // Force the ListView control to repaint
  263.     InvalidateRect (ghListView, NULL, TRUE);
  264. }
  265.  
  266. ///////////////////////////////////////////////////////////////////////////////
  267. //    DrawListViewItem()
  268. //
  269. //    Parameters
  270. //      
  271. //    Purpose
  272. //      
  273. //    Return Value
  274. //      
  275. void WINAPI DrawListViewItem (LPDRAWITEMSTRUCT pDIS)
  276. {
  277.     TCHAR szBuffer[512];
  278.     LV_ITEM lvi;
  279.     int cxImage = 0, cyImage = 0;
  280.     UINT uFirstColWidth;
  281.     RECT rcClip;
  282.     int iColumn = 1;
  283.     UINT uiFlags = ILD_TRANSPARENT;
  284.  
  285.     // Get the item image to be displayed
  286.     lvi.mask = LVIF_IMAGE | LVIF_STATE | LVIF_TEXT;
  287.     lvi.iItem = pDIS->itemID;
  288.     lvi.iSubItem = 0;
  289.     lvi.pszText = szBuffer;
  290.     lvi.cchTextMax = 512;
  291.     ListView_GetItem (pDIS->hwndItem, &lvi);
  292.  
  293.     // Check to see if this item is selected
  294.     if (pDIS->itemState & ODS_SELECTED)
  295.     {
  296.         // Set the text background and foreground colors
  297.         SetTextColor (pDIS->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  298.         SetBkColor (pDIS->hDC, GetSysColor(COLOR_HIGHLIGHT));
  299.  
  300.         // Also add the ILD_BLEND50 so the images come out selected
  301.         uiFlags |= ILD_BLEND50;
  302.     }
  303.     else
  304.     {
  305.         // Set the text background and foreground colors to the standard window colors
  306.         SetTextColor (pDIS->hDC, GetSysColor(COLOR_WINDOWTEXT));
  307.         SetBkColor (pDIS->hDC, GetSysColor(COLOR_WINDOW));
  308.     }
  309.  
  310.     // Get the image list and draw the image
  311.     ImageList_Draw (g_hImages,
  312.                     lvi.iImage,
  313.                     pDIS->hDC,
  314.                     pDIS->rcItem.left,
  315.                     pDIS->rcItem.top,
  316.                     uiFlags);
  317.     // Find out how big the image we just drew was
  318.     ImageList_GetIconSize (g_hImages, &cxImage, &cyImage);
  319.     
  320.     // Calculate the width of the first column after the image width.  If
  321.     // There was no image, then cxImage will be zero.
  322.     uFirstColWidth = g_uColumns[0] - cxImage;
  323.  
  324.     // Set up the new clipping rect for the first column text and draw it
  325.     rcClip.left = pDIS->rcItem.left + cxImage;
  326.     rcClip.right = pDIS->rcItem.left + g_uColumns[0];
  327.     rcClip.top = pDIS->rcItem.top;
  328.     rcClip.bottom = pDIS->rcItem.bottom;
  329.  
  330.     DrawItemColumn (pDIS->hDC, szBuffer, &rcClip);
  331.     szBuffer[0] = 0;
  332.  
  333.     lvi.mask = LVIF_TEXT;
  334.     lvi.iSubItem++;
  335.     ListView_GetItem (pDIS->hwndItem, &lvi);
  336.     rcClip.left = rcClip.right;
  337.     rcClip.right = rcClip.left + g_uColumns[1];
  338.     DrawItemColumn(pDIS->hDC, szBuffer, &rcClip);
  339.     szBuffer[0] = 0;
  340.  
  341.     rcClip.left = rcClip.right;
  342.     rcClip.right = rcClip.left + g_uColumns[2];
  343.     lvi.iSubItem++;
  344.     ListView_GetItem (pDIS->hwndItem, &lvi);
  345.     DrawItemColumn(pDIS->hDC, szBuffer, &rcClip);
  346.     szBuffer[0] = 0;
  347.  
  348.     rcClip.left = rcClip.right;
  349.     rcClip.right = rcClip.left + g_uColumns[3];
  350.     lvi.iSubItem++;
  351.     ListView_GetItem (pDIS->hwndItem, &lvi);
  352.     DrawItemColumn(pDIS->hDC, szBuffer, &rcClip);
  353.     szBuffer[0] = 0;
  354.  
  355.     rcClip.left = rcClip.right;
  356.     rcClip.right = rcClip.left + g_uColumns[4];
  357.     lvi.iSubItem++;
  358.     ListView_GetItem (pDIS->hwndItem, &lvi);
  359.     DrawItemColumn(pDIS->hDC, szBuffer, &rcClip);
  360.     szBuffer[0] = 0;
  361.  
  362.     rcClip.left = rcClip.right;
  363.     rcClip.right = rcClip.left + g_uColumns[5];
  364.     lvi.iSubItem++;
  365.     ListView_GetItem (pDIS->hwndItem, &lvi);
  366.     DrawItemColumn(pDIS->hDC, szBuffer, &rcClip);
  367.     szBuffer[0] = 0;
  368.  
  369.     rcClip.left = rcClip.right;
  370.     rcClip.right = rcClip.left + g_uColumns[6];
  371.     lvi.iSubItem++;
  372.     ListView_GetItem (pDIS->hwndItem, &lvi);
  373.     DrawItemColumn(pDIS->hDC, szBuffer, &rcClip);
  374.     szBuffer[0] = 0;
  375.  
  376.     // If we changed the colors for the selected item, undo it
  377.     if (pDIS->itemState & ODS_SELECTED)
  378.     {
  379.         // Set the text background and foreground colors
  380.         SetTextColor (pDIS->hDC, GetSysColor(COLOR_WINDOWTEXT));
  381.         SetBkColor (pDIS->hDC, GetSysColor(COLOR_WINDOW));
  382.     }
  383.  
  384.     // If the item is focused, now draw a focus rect around the entire row
  385.     if (pDIS->itemState & ODS_FOCUS)
  386.     {
  387.         // Adjust the left edge to exclude the image
  388.         rcClip = pDIS->rcItem;
  389.         rcClip.left += cxImage;
  390.  
  391.         // Draw the focus rect
  392.         DrawFocusRect (pDIS->hDC, &rcClip);
  393.     }
  394. }
  395.  
  396. ///////////////////////////////////////////////////////////////////////////////
  397. //    DrawItemColumn()
  398. //
  399. //    Parameters
  400. //      
  401. //    Purpose
  402. //      
  403. //    Return Value
  404. //      
  405. void WINAPI DrawItemColumn (HDC hdc, LPTSTR szText, LPRECT prcClip)
  406. {
  407.     TCHAR szString[256];
  408.     // Check to see if the string fits in the clip rect.  If not, truncate
  409.     // the string and add "...".
  410.     lstrcpy(szString, szText);
  411.     CalcStringEllipsis (hdc, szString, 256, prcClip->right - prcClip->left);
  412.     ExtTextOut (hdc,
  413.                 prcClip->left + 2,
  414.                 prcClip->top + 1,
  415.                 ETO_CLIPPED | ETO_OPAQUE,
  416.                 prcClip,
  417.                 szString,
  418.                 lstrlen(szString),
  419.                 NULL);
  420. }
  421.  
  422. ///////////////////////////////////////////////////////////////////////////////
  423. //    CalcStringEllipsis()
  424. //
  425. //    Parameters
  426. //      
  427. //    Purpose
  428. //      
  429. //    Return Value
  430. //      
  431. BOOL WINAPI CalcStringEllipsis (HDC     hdc,
  432.                                 LPTSTR  szString,
  433.                                 int     cchMax,
  434.                                 UINT    uColWidth)
  435. {
  436.     const TCHAR szEllipsis[] = TEXT("...");
  437.     SIZE   sizeString;
  438.     SIZE   sizeEllipsis;
  439.     int    cbString;
  440.     LPTSTR lpszTemp;
  441.     BOOL   fSuccess = FALSE;
  442.     
  443.     // Adjust the column width to take into account the edges
  444.     uColWidth -= 4;
  445.     __try
  446.     {
  447.         // Allocate a string for us to work with.  This way we can mangle the
  448.         // string and still preserve the return value
  449.         lpszTemp = (LPTSTR)HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cchMax);
  450.         if (!lpszTemp)
  451.         {
  452.             __leave;
  453.         }
  454.         lstrcpy (lpszTemp, szString);
  455.  
  456.         // Get the width of the string in pixels
  457.         cbString = lstrlen(lpszTemp);
  458.         if (!GetTextExtentPoint32 (hdc, lpszTemp, cbString, &sizeString))
  459.         {
  460.             __leave;
  461.         }
  462.  
  463.         // If the width of the string is greater than the column width shave
  464.         // the string and add the ellipsis
  465.         if ((ULONG)sizeString.cx > uColWidth)
  466.         {
  467.             if (!GetTextExtentPoint32 (hdc,
  468.                                        szEllipsis,
  469.                                        lstrlen(szEllipsis),
  470.                                        &sizeEllipsis))
  471.             {
  472.                 __leave;
  473.             }
  474.  
  475.             while (cbString > 0)
  476.             {
  477.                 lpszTemp[--cbString] = 0;
  478.                 if (!GetTextExtentPoint32 (hdc, lpszTemp, cbString, &sizeString))
  479.                 {
  480.                     __leave;
  481.                 }
  482.  
  483.                 if ((ULONG)(sizeString.cx + sizeEllipsis.cx) <= uColWidth)
  484.                 {
  485.                     // The string with the ellipsis finally fits, now make sure
  486.                     // there is enough room in the string for the ellipsis
  487.                     if (cchMax >= (cbString + lstrlen(szEllipsis)))
  488.                     {
  489.                         // Concatenate the two strings and break out of the loop
  490.                         lstrcat (lpszTemp, szEllipsis);
  491.                         lstrcpy (szString, lpszTemp);
  492.                         fSuccess = TRUE;
  493.                         __leave;
  494.                     }
  495.                 }
  496.             }
  497.         }
  498.         else
  499.         {
  500.             // No need to do anything, everything fits great.
  501.             fSuccess = TRUE;
  502.         }
  503.     }
  504.     __finally
  505.     {
  506.         // Free the memory
  507.         HeapFree (GetProcessHeap(), 0, (LPVOID)lpszTemp);
  508.         return fSuccess;
  509.     }
  510. }
  511.  
  512. ///////////////////////////////////////////////////////////////////////////////
  513. //    ErrorHandler()
  514. //
  515. //    Parameters
  516. //      
  517. //    Purpose
  518. //      
  519. //    Return Value
  520. //      
  521. void WINAPI ErrorHandler (HWND hOwnerWnd, HRESULT hError)
  522. {
  523.     LPTSTR szSystemMessage = NULL;
  524.     TCHAR achBuffer[1024];
  525.     DWORD dwSystemError;
  526.     // If the facility is RPC remove it before calling FormatMessage(). That
  527.     // Win32 API does not recognizes the facility.
  528.     if (HRESULT_FACILITY(hError) == FACILITY_RPC)
  529.     {
  530.         dwSystemError = LOWORD(hError);
  531.     }
  532.     else
  533.     {
  534.         dwSystemError = hError;
  535.     }
  536.     // Get an error description from the system
  537.     DWORD dwChars = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
  538.                                    NULL, 
  539.                                    dwSystemError, 
  540.                                    MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 
  541.                                    (LPTSTR)&szSystemMessage,
  542.                                    0, 
  543.                                    NULL);
  544.     // Check to see if FormatMessage() returned us an error
  545.     if (0 == dwChars)
  546.     {
  547.         szSystemMessage = TEXT("Generic Error");
  548.     }
  549.     else
  550.     {
  551.         LPTSTR pStr = &szSystemMessage[lstrlen (szSystemMessage)];
  552.         while ('\r' != *pStr)
  553.         {
  554.             pStr--;
  555.         }
  556.         *pStr = 0;
  557.     }
  558.     ASSERT (dwChars < 1024);
  559.     // Add the error code to the string returned and then show it.
  560.     switch (HRESULT_FACILITY(hError))
  561.     {
  562.         case FACILITY_WIN32 :
  563.             wsprintf (achBuffer,
  564.                       TEXT("%s (Win32 Error %d)"),
  565.                       szSystemMessage,
  566.                       LOWORD(hError));
  567.             break;
  568.         case FACILITY_STORAGE :
  569.             wsprintf (achBuffer,
  570.                       TEXT("%s (IStorage Error %08X)"),
  571.                       szSystemMessage,
  572.                       hError);
  573.             break;
  574.         case FACILITY_ITF :
  575.             wsprintf (achBuffer,
  576.                       TEXT("%s (Interface Error %08X)"),
  577.                       szSystemMessage,
  578.                       hError);
  579.             break;
  580.         case FACILITY_RPC :
  581.             wsprintf (achBuffer,
  582.                       TEXT("%s (RPC Error %d - %08X)"),
  583.                       szSystemMessage,
  584.                       LOWORD(hError),
  585.                       hError);
  586.             break;
  587.         default :
  588.             wsprintf (achBuffer,
  589.                       TEXT("%s (Unknown Facility(%d) Error %d - %08X)"),
  590.                       szSystemMessage,
  591.                       HRESULT_FACILITY(hError),
  592.                       hError,
  593.                       hError);
  594.             break;
  595.     }
  596.     if (dwChars)
  597.     {
  598.         LocalFree ((HLOCAL)szSystemMessage);
  599.     }
  600.     MessageBox (hOwnerWnd, achBuffer, TEXT("WINDS Administrator - Error"), MB_ICONSTOP | MB_OK);
  601. }
  602.  
  603. // End of file for ADMINUI.CPP
  604.