Teach Yourself Visual C++ 6 in 21 Days
- B -
Answers (day 11-21)
Day 11
Quiz
- 1. What are the five base classes that are used in MDI applications?
- The CWinApp-derived class, the CMDIFrameWnd-derived class, the CMDIChildWnd-derived
class, the CDocument-derived class, and the CView-derived class.
- 2. Why do you have to place the ON_COMMAND_RANGE message map entry outside
the section maintained by the Class Wizard?
- The Class Wizard doesn't understand the ON_COMMAND_RANGE message map entry and
thus would either remove or corrupt it.
- 3. What argument does ON_COMMAND_RANGE pass to the event function?
- The ID of the event message.
- 4. What event message should you use to display a pop-up menu?
- WM_CONTEXTMENU.
Exercise
Add the pull-down and context menus for the width, using the same pen widths as
yesterday.
Follow these steps:
- 1. Add the Width handling code as in yesterday's exercise.
- 2. Add the Width menu entries using the same settings as yesterday.
- 3. Open the Day11Doc.h header file.
- 4. Scroll down toward the bottom of the header file until you find the
protected section where the AFX_MSG message map is declared (search for //{{AFX_MSG(CDay11Doc)).
- 5. Add the function declarations in Listing B.17 before the line that
you searched for. (The string that you searched for is the beginning marker for the
Class Wizard maintained message map. Anything you place between it and the end marker,
//}}AFX_MSG, is likely to be removed or corrupted by the Class Wizard.)
LISTING B.17. THE EVENT-HANDLER DECLARATIONS IN DayllDoc.H.
.
.
.
1: #ifdef _DEBUG
2: virtual void AssertValid() const;
3: virtual void Dump(CDumpContext& dc) const;
4: #endif
5:
6: protected:
7:
8: // Generated message map functions
9: protected:
10: afx_msg void OnColorCommand(UINT nID);
11: afx_msg void OnWidthCommand(UINT nID);
12: afx_msg void OnUpdateColorUI(CCmdUI* pCmdUI);
13: afx_msg void OnUpdateWidthUI(CCmdUI* pCmdUI);
14: //{{AFX_MSG(CDay11Doc)
15: // NOTE - the ClassWizard will add and remove member functions //here.
16: // DO NOT EDIT what you see in these blocks of generated //code !
17: //}}AFX_MSG
18: DECLARE_MESSAGE_MAP()
19: private:
20: UINT m_nColor;
21: CObArray m_oaLines;
22: };
- 6. Open the Day11Doc.cpp source-code file.
- 7. Search for the line BEGIN_MESSAGE_MAP and add the lines in Listing
B.18 just after it. It's important that this code be between the BEGIN_MESSAGE_MAP
line and the //{{AFX_MSG_MAP line. If these commands are between the //{{AFX_MSG_MAP
and //}}AFX_MSG_MAP lines, then the Class Wizard will remove or corrupt them.
LISTING B.18. THE EVENT-HANDLER MESSAGE MAP ENTRIES IN Day11Doc.cpp.
1://///////////////////////////////////////////////////////////////////// 2: // CDay11Doc
3:
4: IMPLEMENT_DYNCREATE(CDay11Doc, CDocument)
5:
6: BEGIN_MESSAGE_MAP(CDay11Doc, CDocument)
7: ON_COMMAND_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnColorCommand)
8: ON_COMMAND_RANGE(ID_WIDTH_VTHIN, ID_WIDTH_VTHICK, OnWidthCommand)
9: ON_UPDATE_COMMAND_UI_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, ÂOnUpdateColorUI)
10: ON_UPDATE_COMMAND_UI_RANGE(ID_WIDTH_VTHIN, ID_WIDTH_VTHICK, ÂOnUpdateWidthUI)
11: //{{AFX_MSG_MAP(CDay11Doc)
12: // NOTE - the ClassWizard will add and remove mapping macros //here.
13: // DO NOT EDIT what you see in these blocks of generated //code!
14: //}}AFX_MSG_MAP
15: END_MESSAGE_MAP()
16:
17: const COLORREF CDay11Doc::m_crColors[8] = {
18: RGB( 0, 0, 0), // Black
19: RGB( 0, 0, 255), // Blue
.
.
.
- 8. Scroll to the bottom of the file and add the two event message handler
functions in Listing B.19.
LISTING B.19. THE WIDTH MENU EVENT HANDLER FUNCTIONS.
1: void CDay11Doc::OnWidthCommand(UINT nID)
2: {
3: // Set the current width
4: m_nWidth = nID - ID_WIDTH_VTHIN;
5: }
6:
7: void CDay11Doc::OnUpdateWidthUI(CCmdUI* pCmdUI)
8: {
9: // Determine if the menu entry should be checked
10: pCmdUI->SetCheck(GetWidth() == pCmdUI->m_nID ? 1 : 0);
11: }
- 9. Open the IDR_CONTEXTMENU in the Menu Designer.
- 10. In the Width cascading menu, add the width menu entries just like
you did for the IDR_DAY11TYPE menu, using the same property settings. You can select
the ID from the drop-down list of IDs if you would rather search for them instead
of type.
Day 12
Quiz
- 1. How do you tie a toolbar button to a menu entry that triggers that
same function?
- Give the toolbar button the same object ID as the menu entry.
- 2. How do you make sure that a toolbar can be docked with the frame window?
- Both must have docking enabled on the same sides (using the EnableDocking function)
in the OnCreate function of the frame class.
- 3. How can you remove the Num Lock status indicator from the status bar?
- Remove the ID_INDICATOR_NUM from the indicators table near the top of the main
frame source code file.
- 4. Why do you have to edit the resource file to add a combo box to a toolbar?
- You need to add a separator to the toolbar as a placeholder in the toolbar. The
toolbar designer will do its best to prevent you from adding the separators, assuming
that they are a mistake.
Exercises
- 1. Add another pane to the status bar to display the current width selected.
- Add an entry to the strings table with an ID of ID_INDICATOR_WIDTH and a caption
of VERY THICK.
- Add another entry to the status bar indicators table at the beginning of CMainFrame.cpp:
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_WIDTH,
ID_INDICATOR_COLOR,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
- Add a new member function to the CToolbarDoc class. Specify the function type
as afx_msg void, the function definition as OnUpdateIndicatorWidth (CCmdUI *pCmdUI),
and the access as protected. Edit the function as follows:
void CToolbarDoc::OnUpdateIndicatorWidth(CCmdUI *pCmdUI)
{
CString strWidth;
// What is the current width?
switch (m_nWidth)
{
case 0: // Very Thin
strWidth = "VERY THIN";
break;
case 1: // Thin
strWidth = "THIN";
break;
case 2: // Medium
strWidth = "MEDIUM";
break;
case 3: // Thick
strWidth = "THICK";
break;
case 4: // Very Thick
strWidth = "VERY THICK";
break;
}
// Enable the status bar pane
pCmdUI->Enable(TRUE);
// Set the text of the status bar pane
// to the current width
pCmdUI->SetText(strWidth);
}
- Edit the CToolbarDoc message map, adding the ON_UPDATE_COMMAND_UI message handler
entry as follows:
////////////////////////////////////////////////////////////////
// CToolbarDoc
IMPLEMENT_DYNCREATE(CToolbarDoc, CDocument)
BEGIN_MESSAGE_MAP(CToolbarDoc, CDocument)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_WIDTH,
ÂOnUpdateIndicatorWidth)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_COLOR,
ÂOnUpdateIndicatorColor)
//{{AFX_MSG_MAP(CToolbarDoc)
ON_UPDATE_COMMAND_UI(ID_WIDTH_VTHIN, OnUpdateWidthVthin)
.
.
ON_COMMAND(ID_WIDTH_VTHIN, OnWidthVthin)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
- 2. Add a button to the main toolbar that can be used to toggle the color
toolbar on and off, as in Figure 12.7.
- Open the IDR_MAINFRAME toolbar in the toolbar designer. Paint an icon for the
blank button at the end of the toolbar. Double-click the button to open its properties
dialog. Specify the button ID as ID_VIEW_COLORBAR and enter an appropriate prompt
for the button. Recompile and run your application, and the color toolbar toggle
should be working on the main toolbar.
Day 13
Quiz
- 1. What two macros do you have to add to a class to make it serializable?
- DECLARE_SERIAL and IMPLEMENT_SERIAL.
- 2. How can you determine whether the CArchive object is reading from or
writing to the archive file?
- You call the IsStoring or IsLoading functions.
- 3. What arguments do you need to pass to the IMPLEMENT_SERIAL macro?
- The class name, the base class name, and the version number.
- 4. What class do you need to inherit the view class from to be able to
use the dialog designer to create a form for the main window in an SDI or MDI application?
- CFormView.
- 5. What type of file does the CArchive write to by default?
- CFile.
Exercise
Add a couple of radio buttons to the form to specify the person's sex, as in Figure
13.5. Incorporate this change into the CPerson class to make the field persistent.
In the window designer, add the two radio buttons and the static text prompt.
Specify the control properties in Table B.2.
TABLE B.2. CONTROL PROPERTY SETTINGS.
Object |
Property |
Setting |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Sex: |
Radio Button |
ID |
IDC_RMALE |
|
Caption |
Mal&e |
|
Group |
Checked |
Radio Button |
ID |
IDC_RFEMALE |
|
Caption |
&Female |
Move the mnemonic in the First button from &First to Fi&rst to prevent
a conflict with the new radio buttons.
Attach a variable to the new radio buttons as in Table B.3.
TABLE B.3. CONTROL VARIABLES.
Object |
Name |
Category |
Type |
IDC_RMALE |
m_iSex |
Value |
int |
Increment the version number in the IMPLEMENT_SERIAL macro in the CPerson class.
Add a new member variable to the CPerson class. Specify the type as int, the name
as m_iSex, and the access as private. Update the CPerson constructor function, adding
the m_iSex variable to the initializations as in line 8 of Listing B.20.
LISTING B.20. THE MODIFIED CPerson CONSTRUCTOR.
1: CPerson::CPerson()
2: {
3: // Initialize the class variables
4: m_iMaritalStatus = 0;
5: m_iAge = 0;
6: m_bEmployed = FALSE;
7: m_sName = "";
8: m_iSex = 0;
9: }
Add the inline functions to the CPerson class declaration to set and get the value
of this new variable, as in lines 9 and 15 of Listing B.21.
LISTING B.21. THE MODIFIED CPerson CLASS DECLARATION.
1: class CPerson : public CObject
2: {
3: DECLARE_SERIAL (CPerson)
4: public:
5: // Functions for setting the variables
6: void SetEmployed(BOOL bEmployed) { m_bEmployed = bEmployed;}
7: void SetMaritalStat(int iStat) { m_iMaritalStatus = iStat;}
8: void SetAge(int iAge) { m_iAge = iAge;}
9: void SetSex(int iSex) { m_iSex = iSex;}
10: void SetName(CString sName) { m_sName = sName;}
11: // Functions for getting the current settings of the variables
12: BOOL GetEmployed() { return m_bEmployed;}
13: int GetMaritalStatus() { return m_iMaritalStatus;}
14: int GetAge() {return m_iAge;}
15: int GetSex() {return m_iSex;}
16: CString GetName() {return m_sName;}
17: CPerson();
18: virtual ~CPerson();
19:
20: private:
21: BOOL m_bEmployed;
22: int m_iMaritalStatus;
23: int m_iAge;
24: CString m_sName;
25: };
Update the Serialize function in the CPerson class to include the m_iSex variable
as in lines 9 and 12 of Listing B.22.
LISTING B.22. THE MODIFIED CPerson.Serialize FUNCTION.
1: void CPerson::Serialize(CArchive &ar)
2: {
3: // Call the ancestor function
4: CObject::Serialize(ar);
5:
6: // Are we writing?
7: if (ar.IsStoring())
8: // Write all of the variables, in order
9: ar << m_sName << m_iAge << m_iMaritalStatus << m_bEmployed <<
Âm_iSex;
10: else
11: // Read all of the variables, in order
12: ar >> m_sName >> m_iAge >> m_iMaritalStatus >> m_bEmployed >>
Âm_iSex;
13:
14: }
Modify the PopulateView function in the view object to include the Sex variable
in the data exchange, as in line 19 of Listing B.23.
LISTING B.23. THE MODIFIED CSerializeView.POPULATEVIEW FUNCTION.
1: void CSerializeView::PopulateView()
2: {
3: // Get a pointer to the current document
4: CSerializeDoc* pDoc = GetDocument();
5: if (pDoc)
6: {
7: // Display the current record position in the set
8: m_sPosition.Format("Record %d of %d", pDoc->GetCurRecordNbr(),
9: pDoc->GetTotalRecords());
10: }
11: // Do we have a valid record object?
12: if (m_pCurPerson)
13: {
14: // Yes, get all of the record values
15: m_bEmployed = m_pCurPerson->GetEmployed();
16: m_iAge = m_pCurPerson->GetAge();
17: m_sName = m_pCurPerson->GetName();
18: m_iMaritalStatus = m_pCurPerson->GetMaritalStatus();
19: m_iSex = m_pCurPerson->GetSex();
20: }
21: // Update the display
22: UpdateData(FALSE);
23: }
Add an event handler for the clicked event of both new radio buttons, using the
same function for both event handlers. Update the record object's field using the
Set function, as in Listing B.24.
LISTING B.24. THE CSerializeView.OnSex FUNCTION.
1: void CSerializeView::OnSex()
2: {
3: // TODO: Add your control notification handler code here
4:
5: // Sync the data in the form with the variables
6: UpdateData(TRUE);
7: // If we have a valid person object, pass the data changes to it
8: if (m_pCurPerson)
9: m_pCurPerson->SetSex(m_iSex);
10: // Get a pointer to the document
11: CSerializeDoc * pDoc = GetDocument();
12: if (pDoc)
13: // Set the modified flag in the document
14: pDoc->SetModifiedFlag();
15: }
Day 14
Quiz
- 1. What does ODBC stand for?
- Open Database Connector.
- 2. What functions can you use to navigate the record set in a CRecordset
object?
- Move, MoveNext, MovePrev, MoveFirst, MoveLast, and SetAbsolutePosition.
- 3. What view class should you use with an ODBC application?
- CRecordView.
- 4. What sequence of functions do you need to call to add a new record
to a record set?
- AddNew, Update, and Requery.
- 5. What function do you need to call before the fields in the CRecordset
object can be updated with any changes?
- Edit.
Exercise
Add a menu entry and dialog to let the user indicate the record number to move
to, and then move to that record.
- 1. Create a new dialog, designing the dialog layout as in Figure B.1.
Configure the controls as in Table B.4.
FIGURE B.1.
The Move To dialog layout.
TABLE B.4. DIALOG PROPERTY SETTINGS.
Object |
Property |
Setting |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Move to record: |
Edit Box |
ID |
IDC_ERECNBR |
- 2. Open the Class Wizard. Create a new class for the new dialog. Give
the new class the name CMoveToDlg. After you create the new class, add a variable
to the Edit Box control. Specify the variable type as long and the name as m_lRowNbr.
- 3. Add another menu entry to the main application menu. Specify the menu
properties as in Table B.5.
TABLE B.5. MENU PROPERTY SETTINGS.
Object |
Property |
Setting |
Menu Entry |
ID |
IDM_RECORD_MOVE |
|
Caption |
&Move To... |
|
Prompt |
Move to a specific record\nMove To |
- 4. Open the Class Wizard and add an event-handler function for the COMMAND
message for this new menu to the view class. Edit this function, adding the code
in Listing B.25.
LISTING B.25. THE CDbOdbcView OnRecordMove FUNCTION.
1: void CTestdb5View::OnRecordMove()
2: {
3: // TODO: Add your command handler code here
4: // Create an instance of the Move To dialog
5: CMoveToDlg dlgMoveTo;
6: // Get the row number to move to
7: if (dlgMoveTo.DoModal() == IDOK)
8: {
9: // Get a pointer to the record set
10: CRecordset* pSet = OnGetRecordset();
11: // Make sure that there are no outstanding changes to be saved
12: if (pSet->CanUpdate() && !pSet->IsDeleted())
13: {
14: pSet->Edit();
15: if (!UpdateData())
16: return;
17:
18: pSet->Update();
19: }
20: // Set the new position
21: pSet->SetAbsolutePosition(dlgMoveTo.m_lRowNbr);
22: // Update the form
23: UpdateData(FALSE);
24: }
25: }
- 5. Include the header file for the new dialog in the view class source
code, as in line 10 of Listing B.26.
LISTING B.26. THE CDbOdbcView INCLUDES.
1: // DbOdbcView.cpp : implementation of the CDbOdbcView class
2: //
3:
4: #include "stdafx.h"
5: #include "DbOdbc.h"
6:
7: #include "DbOdbcSet.h"
8: #include "DbOdbcDoc.h"
9: #include "DbOdbcView.h"
10: #include "MoveToDlg.h"
- 6. Add a toolbar button for the new menu entry.
Day 15
Quiz
- 1. What does ADO stand for?
- ActiveX Data Objects.
- 2. What does ADO use for database access?
- OLE DB.
- 3. What are the objects in ADO?
- Connection, Command, Parameter, Error, Recordset, and Field.
- 4. How do you initialize the COM environment?
::CoInitialize(NULL);
- 5. How do you associate a Connection object with a Command object?
pCmd->ActiveConnection = pConn;
- 6. How do you associate a Command object with and populate a Recordset
object?
- One of two ways:
_RecordsetPtr pRs;
pRs = pCmd->Execute();
- Or
_RecordsetPtr pRs;
pRs.CreateInstance(__uuidof(Recordset));
pRs->PutRefSource(pCmd);
Exercise
Enable and disable the navigation menus and toolbar buttons based on whether the
recordset is at the beginning of file (BOF) or end of file (EOF, renamed to EndOfFile).
Add event-handler functions to the document class for the navigation menu entries'
UPDATE_COMMAND_UI event message. Edit these functions, adding the code in Listing
B.27 to the functions for the First and Previous menus, and the code in Listing B.28
to the functions for the Last and Next menus.
LISTING B.27. THE CDbAdoDoc OnUpdateDataFirst FUNCTION.
1: void CDbAdoDoc::OnUpdateDataFirst(CCmdUI* pCmdUI)
2: {
3: // TODO: Add your command update UI handler code here
4: // Does the record set exist?
5: if (m_pRs)
6: {
7: // Are we at the BOF?
8: if (m_pRs->BOF)
9: pCmdUI->Enable(FALSE);
10: else
11: pCmdUI->Enable(TRUE);
12: }
13: }
LISTING B.28. THE CDbAdoDoc OnUpdateDataLast FUNCTION.
1: void CDbAdoDoc::OnUpdateDataLast(CCmdUI* pCmdUI)
2: {
3: // TODO: Add your command update UI handler code here
4: // Does the record set exist?
5: if (m_pRs)
6: {
7: // Are we at the EOF?
8: if (m_pRs->EndOfFile)
9: pCmdUI->Enable(FALSE);
10: else
11: pCmdUI->Enable(TRUE);
12: }
13: }
Day 16
Quiz
- 1. When do you want to create a new MFC class?
- When you need to create a new class that is inherited from an existing MFC class.
- 2. When you make changes to a library file, what do you have to do to
the applications that use the library file?
- They all have to be relinked.
- 3. What are the different types of classes that you can create?
- MFC, generic, and form.
- 4. When you package some functionality in a library file, what do you
need to give to other programmers who want to use your library module?
- The LIB library file and the header files for the objects in the module.
- 5. What are two of the basic principles in object-oriented software design?
- Encapsulation and inheritance. The third principle is polymorphism, which was
not discussed today.
Exercises
Separate the CLine class into a different library module from the drawing class
so that you have two library modules instead of one. Link them into the test application.
- 1. Create a new project. Specify that the project is a Win32 Static Library
project. Give the project a suitable name, such as Line.
- 2. Specify that the project contain support for MFC and precompiled headers.
- 3. Copy the Line.cpp and Line.h files into the project directory. Add
both of these files to the project. Compile the library module.
- 4. Open the original library module project. Delete the Line.cpp and Line.h
files from the project. Edit the include statement at the top of the drawing object
source-code file to include the Line.h file from the Line module project directory,
as on line 9 of Listing B.29. Recompile the project.
LISTING B.29. THE CModArt INCLUDES AND COLOR TABLE.
1: // ModArt.cpp: implementation of the CModArt class.
2: //
3: //////////////////////////////////////////////////////////////////////
4:
5: #include <stdlib.h>
6: #include <time.h>
7:
8: #include "stdafx.h"
9: #include "..\Line\Line.h"
10: #include "ModArt.h"
- 5. Open the test application project. Add the Line library file to the
project. Build the project.
Day 17
Quiz
- 1. What kind of DLL do you have to create to make classes in the DLL available
to applications?
- An MFC extension DLL.
- 2. What do you have to add to the class to export it from a DLL?
- The AFX_EXT_CLASS macro in the class declaration.
- 3. What kind of DLL can be used with other programming languages?
- A regular DLL.
- 4. If you make changes in a DLL, do you have to recompile the applications
that use the DLL?
- Normally, no. Only if changes were made in the exported interface for the DLL
do you need to recompile the applications that use the DLL.
- 5. What function does the LIB file provide for a DLL?
- The LIB file contains stubs of the functions in the DLL, along with the code
to locate and pass the function call along to the real function in the DLL.
Exercises
- 1. Separate the line class into its own MFC extension DLL and use it with
the second (regular) DLL.
- Create a new project. Specify that the project is an AppWizard (DLL) project,
and give the project a suitable name, such as LineDll.
- Specify that the DLL will be an MFC extension DLL.
- After generating the project skeleton, copy the line source code and header files
into the project directory. Add these files into the project.
- Edit the CLine class declaration, adding the AFX_EXT_CLASS macro to the class
declaration.
- Compile the DLL. Copy the DLL into the debug directory for the test application.
- Open the regular DLL project. Delete the line source code and header files from
the project in the File View of the workspace pane. Add the line DLL LIB file to
the project. Edit the drawing functionality source-code file, changing the line class
header include to include the version in the CLine DLL project directory, as in Listing
B.30.
LISTING B.30. THE CModArt INCLUDES.
1: // ModArt.cpp: implementation of the CModArt class.
2: //
3: /////////////////////////////////////////////////////////////////////
4:
5: #include <stdlib.h>
6: #include <time.h>
7:
8: #include "stdafx.h"
9: #include "..\LineDll\Line.h"
10: #include "ModArt.h"
- Compile the project. Copy the DLL into the test application project debug directory.
- Run the test application.
- 2. Alter the line class DLL so that it uses a consistent line width for
all lines.
- Open the line class DLL project that you created in the previous exercise. Edit
the class constructor, replacing the initialization of the m_nWidth variable with
a constant value, as in Listing B.31.
LISTING B.31. THE CLine CONSTRUCTOR.
1: CLine::CLine(CPoint ptFrom, CPoint ptTo, UINT nWidth, COLORREF crColor)
2: {
3: m_ptFrom = ptFrom;
4: m_ptTo = ptTo;
5: m_nWidth = 1;
6: m_crColor = crColor;
7: }
- Compile the DLL. Copy the DLL into the test application project debug directory.
Run the test application.############################
Day 18
Quiz
- 1. When is the OnIdle function called?
- When the application is idle and there are no messages in the application message
queue.
- 2. How can you cause the OnIdle function to be repeatedly called while
the application is sitting idle?
- Returning a value of TRUE will cause the OnIdle function to continue to be called
as long as the application remains idle.
- 3. What is the difference between an OnIdle task and a thread?
- An OnIdle task executes only when the application is idle and there are no messages
in the message queue. A thread executes independently of the rest of the application.
- 4. What are the four thread synchronization objects?
- Critical sections, mutexes, semaphores, and events.
- 5. Why shouldn't you specify a higher than normal priority for the threads
in your application?
- The rest of the threads and processes running on the computer will receive a
greatly reduced amount of processor time.
Exercises
- 1. If you open a performance monitor on your system while the application
that you built today is running, you'll find that even without any of the threads
running, the processor usage remains 100 percent, as in Figure 18.11. The OnIdle
function is continuously being called even when there is nothing to be done.
- Modify the OnIdle function so that if there's nothing to be done, neither of
the OnIdle tasks are active. Then, the OnIdle function will not continue to be called
until one of these threads is active, at which time it should be continuously called
until both threads are once again turned off. This will allow the processor to drop
to a minimal utilization, as in Figure 18.12.
- Edit the OnIdle function as in Listing B.32.
LISTING B.32. THE MODIFIED CTaskingApp OnIdle FUNCTION.
1: BOOL CTaskingApp::OnIdle(LONG lCount)
2: {
3: // TODO: Add your specialized code here and/or call the base class
4:
5: // Call the ancestor's idle processing
6: BOOL bRtn = CWinApp::OnIdle(lCount);
7:
8: // Get the position of the first document template
9: POSITION pos = GetFirstDocTemplatePosition();
10: // Do we have a valid template position?
11: if (pos)
12: {
13: // Get a pointer to the document template
14: CDocTemplate* pDocTemp = GetNextDocTemplate(pos);
15: // Do we have a valid pointer?
16: if (pDocTemp)
17: {
18: // Get the position of the first document
19: POSITION dPos = pDocTemp->GetFirstDocPosition();
20: // Do we have a valid document position?
21: if (dPos)
22: {
23: // Get a pointer to the document
24: CTaskingDoc* pDocWnd =
25: (CTaskingDoc*)pDocTemp->GetNextDoc(dPos);
26: // Do we have a valid pointer?
27: if (pDocWnd)
28: {
29: // Get the position of the view
30: POSITION vPos = pDocWnd->GetFirstViewPosition();
31: // Do we have a valid view position?
32: if (vPos)
33: {
34: // Get a pointer to the view
35: CTaskingView* pView =
Â(CTaskingView*)pDocWnd->GetNextView(vPos);
36: // Do we have a valid pointer?
37: if (pView)
38: {
39: // Should we spin the first idle thread?
40: if (pView->m_bOnIdle1)
41: {
42: // Spin the first idle thread
43: pDocWnd->DoSpin(0);
44: bRtn = TRUE;
45: }
46: // Should we spin the second idle thread?
47: if (pView->m_bOnIdle2)
48: {
49: // Spin the second idle thread
50: pDocWnd->DoSpin(2);
51: bRtn = TRUE;
52: }
53: }
54: }
55: }
56: }
57: }
58: }
59: return bRtn;
60: }
- 2. When starting the independent threads, give one of the threads a priority
of THREAD_PRIORITY_NORMAL and the other a priority of THREAD_PRIORITY_LOWEST.
- Edit the SuspendSpinner function as in Listing B.33.
LISTING B.33. THE MODIFIED CTaskingDoc SuspendSpinner FUNCTION.
1: void CTaskingDoc::SuspendSpinner(int nIndex, BOOL bSuspend)
2: {
3: // if suspending the thread
4: if (!bSuspend)
5: {
6: // Is the pointer for the thread valid?
7: if (m_pSpinThread[nIndex])
8: {
9: // Get the handle for the thread
10: HANDLE hThread = m_pSpinThread[nIndex]->m_hThread;
11: // Wait for the thread to die
12: ::WaitForSingleObject (hThread, INFINITE);
13: }
14: }
15: else // We are running the thread
16: {
17: int iSpnr;
18: int iPriority;
19: // Which spinner to use?
20: switch (nIndex)
21: {
22: case 0:
23: iSpnr = 1;
24: iPriority = THREAD_PRIORITY_NORMAL;
25: break;
26: case 1:
27: iSpnr = 3;
28: iPriority = THREAD_PRIORITY_LOWEST;
29: break;
30: }
31: // Start the thread, passing a pointer to the spinner
32: m_pSpinThread[nIndex] = AfxBeginThread(ThreadFunc,
33: (LPVOID)&m_cSpin[iSpnr], iPriority);
34: }
35: }
Day 19
Quiz
- 1. What are the three aspects of a control that are visible to the container
application?
- Properties, methods, and events.
- 2. Why do you need to design a property page for your control?
- To provide the user with the ability to set the properties of the control.
- 3. What are the four types of properties that a control might have?
- Ambient, extended, stock, and custom.
- 4. What happens to the parameters that are passed to the methods of a
control?
- They are marshaled into a standardized, machine-independent structure.
- 5. What tool can you use to test your controls?
- The ActiveX Control Test Container.
Exercises
- 1. Add a method to your control to enable the container application to
trigger the generation of a new squiggle drawing.
- Open the Class Wizard to the Automation tab. Click the Add Method button. Enter
a method name, such as GenNewDrawing, and specify the return type as void. Click
OK to add the method. Edit the method, adding the code in Listing B.34.
LISTING B.34. THE CSquiggleCtrl GenNewDrawing FUNCTION.
1: void CSquiggleCtrl:: GenNewDrawing()
2: {
3: // TODO: Add your specialized code here and/or call the base class
4: // Set the flag so a new drawing will be generated
5: m_bGenNewDrawing = TRUE;
6: // Invalidate the control to trigger the OnDraw function
7: Invalidate();
8: }
- 2. Add a method to your control to save a squiggle drawing. Use the CFile::modeWrite
and CArchive::store flags when creating the CFile and CArchive objects.
- Open the Class Wizard to the Automation tab. Click the Add Method button. Enter
a method name, such as SaveDrawing, and specify the return type as BOOL. Add a single
parameter, sFileName, with a type of LPCTSTR. Click OK to add the method. Edit the
method, adding the code in Listing B.35.
LISTING B.35. THE CSquiggleCtrl SaveDrawing FUNCTION.
1: BOOL CSquiggleCtrl::SaveDrawing(LPCTSTR sFileName)
2: {
3: // TODO: Add your dispatch handler code here
4: try
5: {
6: // Create a CFile object
7: CFile lFile(sFileName, CFile::modeWrite);
8: // Create a CArchive object to store the file
9: CArchive lArchive(&lFile, CArchive::store);
10: // Store the file
11: m_maDrawing.Serialize(lArchive);
12: }
13: catch (CFileException err)
14: {
15: return FALSE;
16: }
17: return TRUE;
18: }
Day 20
Quiz
- 1. What are the two things that a client application must know to be able
to connect to a server application?
- The network address (or name) of the computer and the port on which the server
is listening.
- 2. What CAsyncSocket function is used to enable a server application to
detect connection efforts by client applications?
- Listen.
- 3. What CAsyncSocket member function is called to signal that data has
arrived through a socket connection?
- OnReceive.
- 4. What function is called to signal that a connection has been established?
- OnConnect.
- 5. What function do you use to send a message through a socket connection
to the application on the other end?
- Send.
Exercises
The server application that you wrote can handle only a single connection at a
time. If a second application tries to open a connection to it while it has an existing
connection to an application, the server application will crash. The server tries
to accept the second connection into the socket that is already connected to the
first client application. Add a third socket object to the application that will
be used to reject additional client connections until the first client closes the
connection.
Follow these steps:
- 1. Add a member variable to the dialog class (CSockDlg). Specify the variable
type as BOOL, the name as m_bConnected, and the access as private.
- 2. Initialize the variable as FALSE in the OnInitDialog function.
- 3. Set the variable to TRUE in the OnAccept dialog function once the connection
has been accepted.
- 4. Set the variable to FALSE in the OnClose dialog function.
- 5. Modify the OnAccept dialog function as in Listing B.36.
LISTING B.36. THE MODIFIED CSockDlg OnAccept FUNCTION.
1: void CSockDlg::OnAccept()
2: {
3: if (m_bConnected)
4: {
5: // Create a rejection socket
6: CAsyncSocket sRjctSock;
7: // Create a message to send
8: CString strMsg = "Too many connections, try again later.";
9: // Accept using the rejection socket
10: m_sListenSocket.Accept(sRjctSock);
11: // Send the rejection message
12: sRjctSock.Send(LPCTSTR(strMsg), strMsg.GetLength());
13: // Close the socket
14: sRjctSock.Close();
15: }
16: else
17: {
18: // Accept the connection request
19: m_sListenSocket.Accept(m_sConnectSocket);\
20: // Mark the socket as connected
21: m_bConnected = TRUE;
22: // Enable the text and message controls
23: GetDlgItem(IDC_EMSG)->EnableWindow(TRUE);
24: GetDlgItem(IDC_BSEND)->EnableWindow(TRUE);
25: GetDlgItem(IDC_STATICMSG)->EnableWindow(TRUE);
26: }
27: }
Day 21
Quiz
- 1. What does the CHtmlView class encapsulate for use in Visual C++ applications?
- The Internet Explorer Web browser.
- 2. How can you get the URL for the current Web page from the CHtmlView
class?
- GetLocationURL().
- 3. What command is triggered for the frame class when the user presses
the Enter key in the edit box on the dialog bar?
- IDOK.
- 4. What functions can you call to navigate the browser to the previous
and the next Web pages?
- GoBack() and GoForward().
- 5. How can you stop a download in progress?
- With the Stop() function.
Exercises
- 1. Add the GoSearch function to the menu and toolbar.
- Add a menu entry to the Go menu. Specify the menu entry properties in Table B.6.
TABLE B.6. MENU PROPERTY SETTINGS.
Object |
Property |
Setting |
Menu Entry |
ID |
IDM_GO_SEARCH |
|
Caption |
&Search |
|
Prompt |
Search the Web\nSearch |
- Using the Class Wizard, add an event-handler function to the view class on the
IDM_GO_SEARCH ID for the COMMAND event message. Edit the code as in Listing B.37.
LISTING B.37. THE CWebBrowseView OnGoSearch FUNCTION.
1: void CWebBrowseView::OnGoSearch()
2: {
3: // TODO: Add your command handler code here
4:
5: // Go to the search page
6: GoSearch();
7: }
- Add a toolbar button for the menu ID IDM_GO_SEARCH.
- 2. Add the GoHome function to the menu and toolbar.
- Add a menu entry to the Go menu. Specify the menu entry properties in Table B.7.
TABLE B.7. MENU PROPERTY SETTINGS.
Object |
Property |
Setting |
Menu Entry |
ID |
IDM_GO_START |
|
Caption |
S&tart Page |
|
Prompt |
Go to the start page\nHome |
- Using the Class Wizard, add an event-handler function to the view class on the
IDM_GO_START ID for the COMMAND event message. Edit the code as in Listing B.38.
LISTING B.38. THE CWebBrowseView OnGoStart FUNCTION.
1: void CWebBrowseView::OnGoStart()
2: {
3: // TODO: Add your command handler code here
4:
5: // Go to the start page
6: GoHome();
7: }
- Add a toolbar button for the menu ID IDM_GO_START.
- 3. Disable the Stop toolbar button and menu entry when the application
is not downloading a Web page.
- Using the Class Wizard, add an event handler to the view class for the IDM_VIEW_STOP
object ID on the UPDATE_COMMAND_UI event message. Edit the function, adding the code
in Listing B.39.
LISTING B.39. THE CWebBrowseView OnUpdateViewStop FUNCTION.
1: void CWebBrowseView::OnUpdateViewStop(CCmdUI* pCmdUI)
2: {
3: // TODO: Add your command update UI handler code here
4:
5: // Enable the button if busy
6: pCmdUI->Enable(GetBusy());
7: }
© Copyright, Macmillan Computer Publishing. All
rights reserved.
|