Designing and
Implementing Distributed Applications with Microsoft Visual C++
6.0
Deriving the Physical Design
MFC Framework
MFC is a set of pre-built
C++ classes that are interfaces to the Windows API. MFC also adds an
optional architecture that greatly simplifies development.
The main MFC elements are:
Documents
Document object is derived
from CDocument class, and provides methods to access application’s data.
(See also CDocument)
Views
Views determine how the user
interacts with the document’s data. There can be multiple views on the
same data. There are many kinds of views, each deriving from a different
class, like CView, CScrollView, CFormView, etc... (See also CView)
Frame Windows
Frame windows host view
windows. A frame can be the main frame or a child frame in the MDI
architecture. (See also CFrameWnd)
Document templates
A document template is the
link between an application’s document, view and frame window, allowing
them to act like a single entity. (See also Document Templates and the Document/View Creation
Process)
Threads
A thread is the basic unit
of execution in a Window-based application.
Application Object
The application object is
the backbone of an MFC application. It encapsulate the Win32 WinMain()
function. (See also CWinApp)
Differences between Windows 95, 98 and NT
Required DLLs
Not all the required DLLs
exist in each kind of OS, and not all the DLLs expose the same
functionalities.
Security
An application must check NT
permissions and has to display correct messages to the users.
Versioning
GetVersion returns 4 on all
these OS. Use GetVersionEx instead. Don’t assume 4 as a response because
on Win2000 the value is 5.
Large Drives
GetFreeDiskSpace doesn’t
work with FAT32 drives larger than 2Gb. Use GetFreeDiskSpaceEx instead
System Paths and registry keys
In every OS system paths and
registry keys are different. This also applies in different language
versions of the same OS. Use API to read correct paths and keys.
ANSI and Unicode
NT is a Unicode OS and Win9x
are ANSI. Don’t use Unicode on Win9x applications. If ANSI is used on NT a
wrapper translates everything to Unicode, slowing the system.
To use Unicode under WinNT a
UNICODE symbol has to be defined and the _T macro must be used before
every string. Also appropriate char types must be used.
(See also UNICODE programming summary)
Screen Coordinates
Under Windows 9x screen
coordinates are limited to 16 bit, staying in the range –32768 to 32767.
Under Windows NT/2000 screen coordinates are 32 bit. (See also CDC)
Platform SDK vs. MFC
Use Platform SDK only for
very small applications that use very few common controls or if you need
to use only functionality not supported by the MFC framework.
Use MFC in all the other
cases. If something is possible with MFC use it and do the rest with Platform SDK.
MFC Regular vs. MFC Extension DLLs
MFC regular DLLs can be used
in every Win32 programming environment because only C functions are
exported. C++ classes can be used inside the DLLs.
MFC extension DLLs can be
used only with C++ compatible compilers, because MFC derived classes,
member functions, C++ classes and so on can be exported. (See also Extension DLLs)
Message Routing
MFC objects can receive
messages if they have a message map. A message map contains messages ID
and pointers to handlers. When the object receives a message, it looks at
its own message map for a matching message. If the message is found the
handler is executed, otherwise the message is routed to the object at the
lower level.
If multiple handlers exist,
the higher is called. (See also Message Handling and Mapping Topics) The handling order is the following
SDI |
MDI |
View |
View |
Document |
Document |
|
Child Frame |
Main Frame |
Main Frame |
Application |
Application |
Document/View Architecture
In Document/View
architecture, an object manages data and another takes care of
presentation, allowing changing how the data are showed without changing
how the data are managed.
Single Document Interface
(SDI), Multiple Document Interface (MDI) and Dialog Based are the three
possible choices. In the SDI architecture there can be only one document opened.
(See also Document/View Architecture Topics)
MFC Drawing architecture
To draw on the screen,
Windows applications have to deal with GDI and device contexts (DC).
MFC supply a class, CDC, which encapsulates the device context. With CDC it’s
possible to draw lines, ellipses and so on. The most important methods are
MoveTo, LineTo, PolyLine, PolyLineTo, Arc, ArcTo, PolyBezier, PolyBezierTo,
PolyDraw, Chord, Ellipse, Pie, Polygon, Rectangle, RoundRect.
MFC also supplies a set of
graphic components like Pens, Brushes, Palettes, Bitmaps, and Fonts.
To draw a text with MFC it’s
necessary to set the attributes of the text before writing it.
SetTextColor, GetTextColor, SetBkMode, GetBkMode,
SetBkColor, GetBkColor, SetTextAlign and
GetTextAlign are used to set the attributes. TextOut, TabbedTextOut,
DrawText and ExtTextOut are used to write the text on the device. GDI translates logical
coordinates to physical coordinates using several mapping methods. There
are 8 mapping modes, MM_ISOTROPIC, MM_ANISOTROPIC, MM_HIENGLISH,
MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, MM_TEXT and MM_TWIPS.
MFC Printing and print-preview architecture
MFC functions useful for
printing support are embedded in all the classes derived from CView and
are OnPreparePrinting, DoPreparePrinting, OnBeginPrinting, OnEndPrinting,
OnEndPrintPreview, OnPrepareDC, OnDraw and the most important, OnPrint.
Overriding OnPrint allows
providing special printing functions, like header, footer and so on.
(See also Printing and Print Preview Topics)
Multithreading
Interface threads
Can receive and process
messages
Worker threads
Cannot receive messages. A
worker thread is only another path of execution for the application to do
background work.
(See also Multithreading Topics)
Process priorities
-
REALTIME_PRIORITY_CLASS Higher
-
HIGH_PRIORITY_CLASS
-
NORMAL_PRIORITY_CLASS Default
-
IDLE_PRIORITY_CLASS Lower
An application can change
his priority using SetPriorityClass()/GetPriorityClass()
Thread priorities
-
THREAD_PRIORITY_TIME_CRITICAL
-
THREAD_PRIORITY_TIME_HIGHEST
-
THREAD_PRIORITY_ABOVE_NORMAL
-
THREAD_PRIORITY_NORMAL
-
THREAD_PRIORITY_BELOW_NORMAL
-
THREAD_PRIORITY_LOWES
-
THREAD_PRIORITY_IDLE
An application can change
the priority of an individual thread using SetThreadPriority() /
GetThreadPriority()
Synchronization
(When clicking on the following links, if the message "You
are not authorized to view this page" appears, then click on the
appropriate subject on the tree path in the left hand pane of the browser.
CCriticalSection
Only a section of code can
be accessed at one time
CEvent
Can block a process from
accessing a resource until another thread allows it
CMutex
Used to lock a resource
shared by multiple threads
CSemaphore
Allows a limited number of
accesses to a resource
CSingleLock
Used to control access to
previous synchronization objects
CMultiLock
Can block up to 64 synchronization objects
Database
ODBC
ODBC was the standard to
access Relational DB. ODBC is still most popular, but Microsoft prefers
that new applications use OLE DB to access DB.
RDO is the object model
built over ODBC.
OLE DB
OLE DB is the new set of
APIs to access Relational and non-Relational data. The most important
benefit of using OLE DB is that the same model is used to access every
kind of data (with the right provider). An ODBC provider for OLE DB is
given for backward compatibility with every relational database.
ADO is the object model
built over OLE DB.
JET
JET is the database engine
used by Microsoft Access and is very popular for desktop databases. DAO is the object model
built over JET
Access methods
MFC
MFC can directly access ODBC
or DAO databases. (See also Database Topics (General))
ATL
ATL can access OLE DB
databases.
Platform SDK
The Platform SDK is the
hardest way to access a database, and can be used if the other two
libraries are not sufficient to do what the application need.
ADO and RDO object models
can be accessed only by importing the type libraries and by directly using
COM objects.
(See also Choosing an API to see differences between DB access
technologies)
Designing Properties, Methods and Events of ActiveX
Controls
ActiveX controls in MFC are
implemented with the COleControl class.
Properties
There are 9 stock properties
common to every ActiveX control already implemented in a control that
derives from COleControl base class
-
Appearance
-
BackColor
-
BorderStyle
-
Caption
-
Enabled
-
Font
-
ForeColor
-
hWnd
-
Text
There can be also custom
properties that can be implemented in four ways:
Methods
There are two stock methods
implemented by COleControl class
COleControl class doesn’t
support custom methods. To add a custom methods a programmer must use the
DISP_FUNCTION() macro, and add the ID of the statement in the primary
dispatch interface in the .ODL file
Events
These are the stock events
implemented by COleControl class
-
Click
-
DblClick
-
Error
-
KeyDown
-
KeyPress
-
KeyUp
-
MouseDown
-
MouseMove
-
MouseUp
-
ReadyStateChange
Like custom methods, custom
events are also not supported by COleControl class.
Custom events can be
implemented with the EVENT_CUSTOM macro. Also the .ODL file must be
modified to add the ID statement in the primary dispatch interface. Establishing the Development Environment
Visual C++ Installation
|
|
|
|
|
Windows 95 or
later, Windows NT 4.0 (SP 3 or later), Windows
2000 |
|
|
|
|
24 MB minimum, 32
MB recommended |
|
|
|
|
|
CD-ROM, mouse, VGA
monitor (SVGA recommended),
Internet Explorer
4.01 SP 1 |
|
|
Typical: 43 MB,
Full: 59 MB |
|
Typical: 57 MB,
Full: 493 MB |
|
|
|
|
|
|
|
|
|
Click to see a Comparison Chart of Features in Each Visual C++ Edition
Server Services Installation
After the installation of
Visual C++ (or Visual Studio), a set of server services can be installed
by using Add/Remove Programs from the Control Panel, or by launching the
setup again. On Windows 9x the
installation must follow this order
-
NT Option Pack
(for Win9x)
-
FrontPage 98
Server Extension
-
Data Access
Components 2.0
-
Visual InterDev
Server Components
-
Application
Performance Explorer
-
Visual Studio
Analyzer
-
Visual SourceSafe
Server and Visual FoxPro Server Samples (these can be installed if
needed and in any order)
Under Windows NT only two
options will appear: Launch BackOffice Inst. Wizard and Visual SourceSafe
Server. Under BackOffice all the tools of Windows 9x are included plus
On a server machine DCOM
must be enabled. On Windows 98 and Windows NT (4.x or later) DCOM is
already present, but under Windows 95 it has to be installed. It can be
downloaded from the Microsoft Web Site. (See also Microsoft DCOM95 for Windows 95)
MTS Configuration on client computers
MTS doesn’t need to be
installed on client computers, but packages have to be configured and DCOM
has to be enabled. Remember to install DCOM support under Windows 95.
When a package is installed
on a server with MTS it can be exported. Exporting a package will create a
“Clients folder” that contains all the files needed to install the package
on a client computer. (See also Exporting MTS Packages)
Remember to never execute
the client installer on the server.
Visual SourceSafe
Visual SourceSafe is a
source code control system that comes with Visual C++ and Visual Studio
Enterprise Ed. (See also VSS Start Page)
VSS Client is installed on
every developer’s machine, allowing access to the centralized database
installed and administered with VSS Server.
NetSetup is the best way to
install VSS over a network. It installs only VSS Client, not VSS Server.
It is stored on the same
directory on the server where VSS Server is installed, and can be accessed
over the network. No CD is necessary.
With NetSetup users can
install VSS Client without the help of an Administrator.
After the installation of
VSS Server, administrators have to configure it with VSS Administrator.
If the VSS Server directory
is shared it’s better to set the right read-write permissions to users. If project security is
enabled, a user can have four levels of access
-
read-only
-
check out/check in
-
add/rename/delete
-
destroy
If security is not enabled
there are only read-only and read-write permissions. VSS Administrator is
used to set access rights.
VSS Client allows to store
and retrieve files and to ensure that only one person at a time can modify
a file. Normally only one can checkout a file at a time, but this can be
changed.
Files can be shared among
multiple projects. Changes made to a file are seen by all the projects.
With branching a file can go
in two directions. Under the Paths tab it’s possible to see the history of
a branched file.
Get Last Version is the
command used to retrieve the last version of a file. Also an older version
can be retrieved from History of File/Project.
A file with Get Last Version
can be retrieved only if it doesn’t exists, or is in read-only state. If
the file is not in read-only mode, VSS assumes that the file is checked
out and doesn’t replace it.
Label is the command used to
mark all the files in the project. It’s useful to mark all the files
before a major release so that if the release needs to be restored files
can be immediately found.
Implementing the Navigation for the User Interface
MFC AppWizard
Under MFC AppWizard three
kinds of projects can be created: Single Document Interface, Multi
Document Interface and Dialog Based.
It’s possible to enable or
disable the support for the Document/View architecture.
|
|
|
|
It adds only the
AFXDB.H file and links all the libraries |
Database View
without File Support |
It gives the ability
to use the CRecordView derived view class but does not give any
serialization option |
Database View with
File Support |
It adds document
serialization |
|
|
|
|
It allows the
inclusion of Active Documents generated by other
servers |
|
It allows the
creation of documents that can be embedded in other
applications |
|
It allows the Active
Documents to run as a stand-alone application |
Both container and
server |
|
Other options can be set
like the support for ActiveX, Automation, Status Bar, Docking Toolbar, 3D
Controls, MAPI, Winsock, Printing support and Context Sensitive Help.
Also some advanced options
can be set like the name of the classes, the styles of the windows, and so
on.
Resource Editor
The Resource Editor can be
used to work with .RC files. There are editors for Toolbars, Menus,
Dialogs, Strings, Accelerator Keys, HTML Resources, Graphics and Binary
Files.
To place an accelerator key
directly in the Dialog Editor or in the Menu Editor, just add “&”
before the letter. (“Save &As” for example)
(See also Resource Editor Topics (Specific to Visual C++))
Toolbars with MFC
Toolbars can be created with
the Toolbar Resource Editor. It’s possible to create new toolbars, buttons
and separators, convert bitmaps into resources and edit existing toolbars
or buttons.
The “Prompt” edit box in the
“Toolbar Button Properties” allows you to set status bar text and tool
tip. For example, with the text “Opens an existing Document\nOpen”, when
the cursor is over the button, “Opens an existing Document” is displayed
in the status bar and the tool tip “Open” shows up under the button.
To add a custom toolbar to a
window a CToolBar protected member has to be added, and in the OnCreate
function the CreateEx function can be called. To enable tool tips the
CBRS_TOOLTIPS style has to be passed to the function.
(See also Toolbar Topics)
Status bar with MFC
To write in a status bar
there are different ways: CStatusBar::SetText() and CCmdUI::SetText()
(See also Status Bar Topics)
Class Wizard
Class Wizard can be used to
create new classes, adding member variables to existing classes, and to
manage message maps and message handlers.
Class Wizard can also be
used to associate a Resource to a Class. If it’s possible to map the
resource to a class the line Class Wizard will be in the context-menu of
the resource.
Property Sheet
A property sheet is a tabbed
dialog box. To create a property sheet a programmer must create a set of
dialogs with the resource editor and has to map them to a set of classes
derived from CPropertyPage.
Then a class derived from
CPropertySheet must be created. All the property page members must be
added in the .h of the property sheet class with statements like CPropPage1 m_PropPage1.
In the constructor every
property page must be added to the sheet with AddPage(&m_propPageX) for every page.
AddPage() is member of CPropertySheet.
Now creating the property
sheet and calling the DoModal will display the tabbed dialog box.
(See also Property Sheet Topics)
CFormView
CFormView is used to show
controls, usually used in dialog boxes, into normal Views.
(See also CFormView)
Process and Validate User Input
DDV or Dialog Data
Validation is the mechanism used to provide simple validation to data
using CString or numeric data types. It can be set by the ClassWizard or
directly with DDV_MaxChars, DDV_MinMaxInt or with all the other macros.
DDX or Dialog Data Exchange
is the mechanism used to transfer data from a document’s variables to a
dialog boxes and vice versa.
DoDataExchange is the
function that calls DDX and DDV macros.
First a DDX macro maps a
control to a variable, and then a DDV macro defines the rules that apply
to the variable. Every DDV macro refers to the previous DDX macro.
(See also Dialog Data Exchange and Validation)
UpdateData(FALSE) forces the
data to be transferred from the document to the dialog; UpdateData(TRUE)
forces the opposite direction. If some of the rules are violated
UpdateData returns FALSE.
ActiveX Controls
With the AppWizard setting
“ActiveX support” enabled, it’s possible to use ActiveX controls directly
in a project, using them like every other control.
To add an ActiveX control to
a project it’s necessary to use Add to Project/Components and Controls
from the Project menu. There it’s possible to select an ActiveX control
and use it.
A wrapper class will be
created and all the functions of the control become accessible.
ISAPI DLLs
ISAPI are extensions of the
Internet Information Server that can extend its functionality.
ISAPI Server extensions are
equivalent to the CGI. They are used to generate dynamic content in the
Web Site in response of a request of the ISAPI DLL.
ISAPI Filters can pre and
post process all the data sent from and to the Web Browser.
The AppWizard can create
ISAPI DLLs by selecting the appropriate items.
ISAPI Server related classes
are CHttpServer and CHttpServerContext.
ISAPI Filter related classes
are CHttpFilter and CHttpFilterContext.
(See also Internet Server API (ISAPI) Extensions)
Incorporate existing code into an application by using
wizards and scriptlets
If the structure of a group
of projects is the same, and also a part of the code is the same, the
generation of new projects can be standardized by using the Custom
AppWizard facility. Custom AppWizard allows creating new projects using an
existing project as a template. (See also Overview of Creating a Custom AppWizard)
Scriptlets cannot be used
directly in this version of Visual C++. The only way to use them is to
embed them in an HTML pages and to show the html page with CHtmlView or
with the Internet Explorer ActiveX.
Store and Retrieve settings from the registry
With the support of MFC
settings can be stored under HKEY_CURRENT_USER\Software\CompanyName\AppName\SectionName
SetRegistryKey() member
function of CWinApp derived classes is used to set the CompanyName.
AppName is the same used for
the Application in project settings.
SetProfileString(), GetProfileString(), SetProfileInt() and
GetProfileInt() can be used to
store and retrieve string and integer data from the registry.
SectionName is the first parameter of these functions.
RegisterShellFileTypes()
allows saving all the information about document types and associations
related to the application.
To write under other
registry keys, Win32 APIs are needed. The most common are RegCreateKeyEx,
RegOpenKeyEx, RegCloseKey, RegDeleteKey, RegSetValueEx, RegQueryValueEx
and RegDeleteValue
Display data from a data source
Serialization
is the process of allowing an object to
persist between runs of your program.
An object must derive from CObject, must use the macros DECLARE_SERIAL and IMPLEMENT_SERIAL and must
implement Serialize method. Serialize receives a CArchive in which
the data have to be stored.
CArchive is the class that
stays in the middle between the Serialize method and the CFile. CArchive
allows moving data in only one direction once they’re created. To move
data it’s possible to use “<<” or “>>”.
CFile is the class that
allows to write and to read a file. CStdioFile is derived from CFile and
allows access to text files.
CSocket and CAsyncSocket can
be used to use WinSock to send data across a network.
CSocket is simpler and
allows the programmer to create client/server applications, which
communicates with each other using sockets. CArchive is needed to manage
the communication process.
Displaying data from a
database to the screen (using CRecordSet to access the database) is
available the class CRecordView. A dialog template (shown inside the
CRecordView) contains controls that can be mapped to the fields of the
database.
Instantiate and Invoke a COM component
To instantiate and invoke a
COM component under Visual C++ there are two main ways.
The smartest way is to
import the DLL of the component and to let the environment take care about
all the implementation details.
This is done in this way:
#import “file name with
path.dll” no_namespace
Using no_namespace tells the
compiler to not add a namespace to the component. If there are many
components with the same function names, it’s better to use namespaces.
If the component has an
interface called IFoo, to use it a programmer can do:
IFooPtr pFoo(_uuidof(Foo));
pFoo->DoWhatYouWant();
Smart pointers are used to
call AddRef, Release, QueryInterface and so on.
(See also The #import Directive)
The other, classic, way is
to initialise COM with AfxOleInit() or CoInitializeEx(), identify the
ClsID of the component using ::CLSIDFromProgID(), and call
CoCreateInstance to create the component.
Then Release has to be
called when the component is not needed, and CoUninitialize() has to be
called at the end of the program.
Asynchronous Processing
Threads
Secondary threads can be
created to do background tasks or to interact with the user.
The function has to follow
the prototype UINT ThreadName(LPVOID paramName) and can be started with AfxBeginThread().
Here is an example:
UINT foo(LPVOID lpFoo)
{ ...... }
CWinThread* pFoo=
AfxBeginThread(foo, NULL);
Download ActiveX user interface controls
To download an ActiveX
control from a Web page the OBJECT tag is used, with the CLASSID tag that
specifies the ClsID of the control and the CODEBASE tag that specifies the
location of the control. With CODEBASE it is also possible to specify the
version of the control, so if a new version exists, the browser will not
use the control installed in the client machine, but will download the new
version.
The control must be supplied
in a .CAB file that contains the .OCX file and the .INF file that
specifies how to install the control. The .CAB file can also be signed to
ensure who is the maker of the control.
Implement online user assistance
Writing in the status bar
relevant information for the user is the first method to provide
assistance.
To write in the status bar
the best way is to use SetText(), like m_stBar.SetText(“Ok”)
Tool tips are supplied by
MFC for toolbar buttons and menus, but can be added to every control.
This can be obtained with
the help of CToolTipCtrl:
CToolTipCtrl* pToolTip;
pToolTip->Create(pDialogWnd); // pointer to the window
pToolTip->AddTool(pControl,”Tooltip...”) // pointer to the
control
On line Help and Context
Sensitive Help are the implemented by the framework (if selected in the
MFC AppWizard).
The WinHelp() member of CWinApp is used to call the standard
Windows Help.
To use HTML Help it’s
necessary to use run “hh.exe“ because at the moment there is no support
from the compiler. (See also HTML Help Start Page)
To link the help file to a
compiled HTML help (also on a Web Site) it’s possible to add a macro in
the source RTF file (See also To link from a topic in a WinHelp file to a topic in an HTML
Help file):
!execfile(hh.exe, ms-its:file
name.chm::/topic.htm)
Error Handling
Exceptions
are
objects that contain error conditions. Exceptions are produced by
functions when there are errors.
With MFC there are two ways
of catching errors: MFC macros and C++ exceptions.
MFC macros are only for
backward compatibility. (See also Exception Handling Topics (General))
CException
is the base class of every MFC exception. It
has two methods to report errors, GetErrorMessage() that retrieves the
error message and ReportError() that retrieves it and reports it to the
user.
try
{// piece of code that
could generate exceptions
}
catch (CMemoryException* memExc)
{ // first catch a memory
exception
memExc->Delete();
}
catch (CFileException* filExc)
{ // then a file
exception, it’s just a sample
filExc->Delete();
}
catch (CException* allExc)
{ // this catches all the
other exceptions...
allExc->Delete();
}
Remember to use Delete()
method of the exception to delete it, because it’s not sure that the
exception is on the heap or on the stack.
CException catches all the
exceptions, so it must be the last in the order, because C++ exceptions
are handled in the order they are declared.
An exception can also be
ignored, and the next handler will catch it. An unhandled exception can
cause the termination of the program.
Use an Active Document
Active Documents are
stand-alone applications hosted by other applications (like MS Office or
Internet Explorer).
Using Active Documents in VC
applications is very simple. By using MFC AppWizard it’s possible to
select the type of support needed. Creating and Managing COM components
Create a COM component
SDK
Using the SDK is the hardest
way to create a COM component. Everything must be programmed, the
registration of the component, the specifications of the interfaces in
.IDL, the ClassFactory, and the class that implements the component.
MFC
CCmdTarget is the class that
implements IUnknown and IDispatch. MFC is very heavy and should be used to
make COM components if it’s used very intensively. With the Wizard only
OLE Automation server and clients can be built easily.
ATL
ATL is the best choice to
implement a lightweight COM component. Using the Wizard is the fastest way
to make a COM component with ATL. Create ActiveX user interface controls
ATL
ATL generated controls are
very light. To create a control the “ATL COM AppWizard” should be used.
After that the programmer should use “New ATL Object”, “Full control”.
In the “ATL Object Wizard
Properties/Attributes” the programmer can choose the threading model, the
ability to support aggregation, dual interfaces, connection points, and
ISupportErrorInfo for the FreeThreaded Marshaler.
SDK
To implement a control
without ATL or MFC a programmer must implement IOleControl,
IOleControlSite and ISimpleFrameSite. After that the control must support
OLEIVERB_PROPERTIES and events. The control has to draw itself in the
container space.
MFC
Use the “MFC ActiveX Control
Wizard” to create the control. The control is based on COleControl that is
based on COleControlModule. MFC ActiveX controls are very heavy, and
should be used only if MFC is used extensively.
(See also ActiveX Controls: Overview)
Reuse Existing Components
(See also Object Reusability)
Containment
A component is “contained”
inside another component. When a request for an interface of the
“contained” component is requested, the call is sent to an interface of
the container that calls the inner interface. (See also Containment)
Aggregation
With aggregation the inner
interface is directly exposed to the client without a wrapper. The inner
component must support aggregation, because for every QueryInterface done
on the inner component it has to check its interfaces and the interfaces
of the outer component. (See also Aggregation)
Error Handling
IErrorInfo
Is supplied with the error
object by the OS (CreateErrorInfo API)
ICreateErrorInfo
Is supplied with the error
object by the OS (CreateErrorInfo API)
ISupportErrorInfo
Is used by the Automation
server to report errors to the client
(See also Error Handling Interfaces)
Log errors in an error log
Under Windows NT/2000 the
application error log can be used to store error messages of every
purpose. Use EventViewer to read the log. Use ReportEvent() Win32 API to write in the
application error log.
Create and use an Active Document
Active documents are
implemented with additional interfaces that manage views, so that objects
can function within containers and yet retain control over their display
and printing functions.
COleServerDoc is the
replacement of CDocument that supports Active Document creation.
IOleObject, IOleClientSite,
IOleDocumentView, IOleCommandTarget, and IPrint. IDataObject,
IPersistStorage, IOleInPlaceActiveObject, IOleInPlaceObject, IPersistFile
are interfaces needed on the server.
IOleInPlaceSite and
IOleInPlaceFrame are used on the container.
(See also Active Documents)
Create a COM component that can participate in a
transaction
To run under MTS the
component
-
must be a DLL
-
must support
IClassFactory
-
must support
Single-Threaded Apartment
-
must support
dual-interfaces
The component has to call
SetComplete or SetAbort (methods of IObjectContext) to signal that it has
finished.
The component should check
if security is enabled before using it.
The component should acquire
resources only when it needs them, and should release them as soon as
possible. This is done to optimize the usage of the resource, especially
if the resources are in a resource-pool.
(See also Transaction Server Component Requirements)
Sign a COM component
Signing a COM component is
useful to distribute components over the Internet, to be used in
compatible browser. Signing allows the client to know who has created the
component and if it was altered. (See also Signing and Marking ActiveX Controls)
Debug a COM component
To debug a COM component the
easiest way is to set a breakpoint in the component, build it in debug
mode, and register the component.
Then a programmer can load
the client into the IDE and start it in debug mode. When the client calls
the component, the breakpoint stops the execution and transfers it to the
debugger.
ATL based COM components can
also be debugged by using special macros.
_ATL_DEBUG_INTERFACES
is used to enable reference
count debugging.
_ATL_DEBUG_QI is used to enable QueryInterface debugging.
Apartment-Model Threading
Single-Threaded Apartment
COM calls are done by
sending messages to the window’s message queue. This technique allows
synchronizing concurrent calls to be serial. Legacy code often ignores
threading, and by default uses a single STA. (See also Single-threaded apartments)
To enter the STA the thread
must call CoInitializeEx(NULL,COINIT_APARTMENTTHREADED)
Multithreaded Apartment
COM calls are sent directly
to the object. The object has to deal with synchronization, because
multiple calls could arrive from multiple threads. (See also Multi-threaded apartments)
To enter the MTA the thread
must call
CoInitializeEx(NULL,COINIT_MULTITHREADED)
MTS Packages
A package is a collection of
related COM classes.
A server package is
executed as a process managed by MTS.
A library package is
executed in-process in the client address space.
(See also Packaging MTS Components)
Only DLL COM classes can be
used to make a package. Each DLL must have DllRegisterServer() to register
CLSID, ProgID, IID, and type libraries in the registry.
Every component must have a
type library.
MTS Explorer can be used to
create a package and to fit components into the package. (See also Creating MTS Packages)
Roles can be defined in MTS
Explorer to add security checks to a package. If security is enabled for
the package and the user is not in the right role the access to components
in the package is denied. (See also Enabling MTS Package Security)
Remember to set the
shutdown time of the component. If a component must run every time
it must have leave running when idle enabled. Creating Data Services
Accessing and manipulating data by using ad hoc queries
ODBC
To use ODBC in a Visual C++
application two MFC classes are necessary.
CRecordset is used to
interact with rows returned from the database in response of a query.
CRecordset is never used directly; it’s better to use a derived class.
CDatabase is used to attach
and to communicate with a database and is generally used when there are
more recordsets.
(See also ODBC and MFC)
ADO
ADO is the object model
built on the top of OLE DB. Remember to use ADO or OLE DB in every new
application. To use ADO with Visual C++ a programmer needs to import the
type library “msado15.dll”.
The object model exposed by
ADO is composed of six main objects, but not all are necessary to query
the database. For example a recordset can be obtained without opening a
connection and without sending a command, but using a connection allows
you to obtain more than one recordset, and using a command allows you to
send the same command without querying the metadata every time.
Connection
Used to maintain connection
information like cursor type, connection string, time-outs, default
database
Error
Used to report extended
error information. A collection of errors is used because one or more
errors could be returned.
Command
Contains information about a
command, like the query string, parameters, and so on.
Parameter
The Command object can
contain a collection of parameters. Each parameter type can be declared
from the programmer to improve performance, or can be discovered at
run-time.
Recordset
Is a set of rows returned
from a query, including cursors.
Field
Is used to contain a set of
information about a single column of data.
(See also Using ADO with Microsoft Visual C++)
DAO
DAO is the object model used
to access the Jet database engine. It’s supported by MFC with these
classes: CDaoWorkspace, CDaoDatabase, CDaoException, CDaoQueryDef,
CDaoRecordset, CDaoFieldExchange
Remember that CDaoRecordset
is never used directly; a derived class is used instead.
(See also DAO and MFC)
RDO
RDO is an object model built
over ODBC. It’s not supported by MFC, but can be used by importing the
type library and accessing the COM objects directly.
Access data with Stored Procedures
Using stored procedures to
access a SQL Server database gives these advantages
-
SQL statements are
pre-compiled and executes faster
-
Syntax is checked
at compile time, not every time
-
SP remains in the
cache of the database
-
Network traffic is
low because the code is in the database
-
SP can be shared
between applications, allowing code reuse
-
SP give better
security
(See also Stored Procedures)
Server Side Cursors vs. Client Side Cursors
Server side cursors allow
processing of the data entirely on the server, reducing network load.
Client side cursors allow
using disconnected recordset, sending more data over the network.
ADO will use server side
cursors by default. (See also Client-Side Cursors versus Server-Side Cursors)
Cursor Types
Forward-only (adOpenForwardOnly in ADO)
This cursor allows
navigating the recordset from the first to the last record.
Only MoveNext is supported.
It’s the fastest cursor. It’s useful for filling list boxes, for example.
Static (adOpenStatic in ADO)
It’s a snapshot of the
result set. Any update made after the creation of the cursors won’t be
reflected in the recordset. It can be scrolled in every direction.
Dynamic (adOpenDynamic in ADO)
It’s the most complete
cursor, but also the more expensive. Every change in the data is reflected
in the recordset.
Keyset (adOpenKeyset in ADO)
It’s like the dynamic
cursors, but is less expensive because every change to the data present in
the recordset is shown, but no new items are added to the recordset if
they are inserted into the database.
(See also Choosing a Cursor Type and Choosing a Cursor Library)
Handle database errors
The ADO Connection object
contains an error collection with these methods and properties
Count
Contains the number of
errors in the collection
Item
Is used to retrieve an error
from the collection
Clear
Is used to remove all the
errors from the collection
The Error object contains
these properties: Description, Number, HelpFile, HelpContext, Source,
SQLState, NativeError
(See also Errors Collection (ADO))
Manage Transactions
Remember ACID properties
-
Atomicity
-
Consistency
-
Isolation
-
Durability
(See also Transactions) To manage transactions with
ADO the connection object supplies these methods
-
BeginTrans
-
CommitTrans
-
RollbackTrans
Locking
Pessimistic locking is the
most secure locking, but it can slow down performance, because it puts a
lock on every used row and other users have to wait. It should be used
when there are high possibilities that more users are accessing the same
record.
Optimistic locking is used
when concurrent access to the same record is not common.
There are four ways to
implement locking:
Read-Only (adLockReadOnly in ADO)
The data can be read, but
cannot be changed. It’s good when users need only to retrieve data.
Pessimistic (adLockPessimistic in ADO)
The record is locked when
the operation starts and it’s unlocked when the operation is finished.
Optimistic (adLockOptimistic in ADO)
The record is locked only
when it’s updated. This can cause errors if the possibility of concurrent
access is high.
Batch Optimistic (adLockBatchOptimistic in ADO)
It’s like Optimistic Locking
but the update is done only when the UpdateBatch method of the Recordset
object is called.
(See also Managing Concurrency With Cursor Locks)
Creating a Physical Database
Implementing a Data Storage Architecture
There are primary data
files *.mdf and secondary data files *.ndf. Every database must
have one and only one primary data file, and could have many secondary
data files.
Log files contain data to
recover the database in case of failure. They have an extension of
*.ldf. Log files grow with every change in the database.
-
Filegroups
Data files can
be logically grouped into filegroups. Filegroups allows you to separate
files on different physical disks, to separate table data and indexes,
to perform incremental backups, etc...
Create DB, and create DB tables that enforce data integrity
and referential integrity
Data integrity
Normalization
-
2nd
Normal Form
Each column that is
not part of the primary key should depend on all columns that make a
composite Primary Key and not only depend on a subset of the composite
Primary key.
-
3rd
Normal Form
Columns that are not
covered by the Primary Key should not depend on each other.
Declarative Data Integrity
-
Identities
-
Constraints
-
Unique constraint
-
Default constraint
-
Check constraint
-
Defaults
-
Rules
Procedural Data Integrity
-
Stored procedures
-
Triggers
Indexes
Indexes allow the programmer
to increase the performance of the database.
There are two types of
indexes: clustered and non-clustered. With clustered indexes data are
stored in the same order of the index. With non-clustered indexes, index
data are stored separately from the table.
Indexes can be created with
CREATE INDEX.
(See also Creating an Index)
Populate the Database
To populate a DataBase SQL
Server supplies two external tools: DTS and BCP.
(See also Choosing a Tool to Import or Export Data)
DTS can copy data from an
OLE DB source to an OLE DB destination.
(See also Overview of Data Transformation Services)
BCP can copy data using the
ODBC bulk copy API.
BULK INSERT is the SQL
statement that does the same work as BCP.
Bulk copy is also an ODBC
extension of SQL Server 7.0. To use this extension from Visual C++ the
application needs to import ODBCSS.H, ODBCBCP.LIB, and ODBCBCP.DLL.
(See also Copying Data Using bcp or BULK INSERT) Testing and Debugging the Solution
Debugging Techniques
Debugging Support
Visual C++ includes
debugging support in the run-time library, enabled in the debug version of
the executable.
There are debug versions of
malloc, free, calloc, realloc, new and delete that are
useful to find memory leaks.
(See also Using C Run-Time Library Debugging Support)
Also in the IDE of the
compiler there are many debug features, like the integrated debugger with
advanced breakpoints, edit-and-continue, etc...
(See also VC Debugger)
Depends
Depends (and other tools
like QuickView and DumpBin) can be used to find the dependencies of and
executable to discover which dll, ocx or com components are used by an
application and which are missing or with a wrong version.
Spy++
Spy++ can be used to inspect
windows, processes, threads and messages.
(See also Spy++)
MFC Macros
ASSERT
ASSERT allows the programmer
to check for logic errors during the execution of a program. If the
condition is false the program stops and displays an error message. This
can be used to check for pre and post-conditions. ASSERT works only in
debug mode, cleaning the release mode of all the testing code.
ASSERT_VALID
ASSERT_VALID is used with
objects derived from CObject. It’s the same as ASSERT and it also calls
the AssertValid function of the object.
ASSERT_KINDOF
ASSERT_KINDOF checks if the
object is a member of the specified class.
TRACE
TRACE is used to send
strings on a dump device. Use TraceR.EXE to enable tracing.
DEBUG_NEW
DEBUG_NEW is useful to find
memory leaks. To use DEBUG_NEW the macro
#define new DEBUG_NEW
must be used. DEBUG_NEW logs
every memory allocations in a file. CMemoryState::DumpAllObjectsSince can
be used to view all allocated objects.
Elements of a test plan
Beta test
Beta test is the testing
made by users that are not part of the development process. Beta test is
useful to test the application under many different conditions like OS,
CPU, RAM, drivers, user level and different languages.
Regression test
Regression test is repeating
the same test to check that nothing is changed from the last version of
the application in areas not affected by the development process.
Unit test
Unit test is the testing
made by the developer on a small working part of an application. Unit test
can imply writing stubs to simulate other units not yet written. Use only
to try if a unit works.
Integration test
Integration test is used to
check if all the units can work together. Unit testing is not enough to
show if different units can work together.
Stress test
Stress test is needed
because an application can stop working if there is too much load. Stress
testing places the highest loads with the lowest amount of resources
available. This kind of test is useful to determine the minimum
requirements and the maximum load for an application. Deploying an Application
Creating a Setup program
Use QuickView, Depends or
DUMPBIN to determine dependencies.
Use InstallShield to create
the setup program.
Remember to include all
DLLs, COM and ActiveX components that are used by the application.
Setup created with
InstallShield will register all COM components into the registry.
Self-registering DLLs must
contain DllRegisterServer and DllUnregisterServer functions. These
functions are called to register the COM component into the registry.
Self-registering EXEs must
accept \RegServer and \UnregServer command line options. These are used to
register/unregister the component without running it.
(See also Redistributing Microsoft Visual C++ 6.0 Applications)
The setup program must be
called SETUP.EXE to comply with the Windows 98 Logo Program.
Register a DCOM component
Registered DCOM components
on the client machine must have the key \RemoteServerName in the registry
under HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID
RemoteServerName is a named
value that contains the server name that the client will contact for every
request of that object.
Configure DCOM on the client computer and on the server
computer
Running DCOMCNFG is the
easiest way to configure DCOM. On Win9x it requires the machine to have
access control to User Level in the Network Applet, and to be connected to
a WinNT domain.
DCOM has to be enabled both
on the client and on the server to work. This can be done with “Enable
Distributed COM on this computer” in Default Properties of DCOMCNFG or by
setting the HKEY_LOCAL_MACHINE\Software\Microsoft\OLE\EnableDCOM value to
1.
Using .cab files
Cabinet or .cab are
compressed files, used to distribute applications.
A cab file can be signed,
and can be deployed over a network.
To sign a cab file a digital
certificate is required.
A cab file can be embedded
in an HTML page (using the tag OBJECT and CODEBASE).
A cab file can contain an
.INF file to specify which components must be registered. Plan floppy disk, web and network deployment
InstallShield allows
selecting the media used to distribute the application.
An application can be
distributed over a set of floppy or compact disc, over a network or using
a Web-Based distribution. Compact disc distribution can also be made
automatic by providing an AUTORUN.INF file on the CD. A simple file is:
[AutoRun]
open=filename.exe
icon=filename.ico
where open is the file to
run and icon is the icon of the CD.
Compact disc is the default
method for InstallShield.
Evaluating Microsoft SMS
Microsoft System Management
Server can be used to deploy applications over a network.
A package is the basic unit
of software distribution under SMS.
The SMS installer can be
used to create a Package Definition File that can be distributed and
launched on every client to install the package.
(See also Advanced Desktop Management: Systems Management Server)
Uninstaller
InstallShield generates
automatically a program called UNINST.EXE that reads installations log
files (UNINST.ISU) and removes any items installed. UNINST.EXE doesn’t
remove hidden items except .GID, .FTG and .FTS files. UNINST.EXE also
doesn’t remove items added after the installation of the application,
including user’s documents.
To register an Uninstaller
(with a custom setup program), an entry must be added to the registry key
under
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CurrentVersion\Uninstall\AppName,
containing the path of the Uninstaller program.
(See also part 2.5 Support Add/Remove Programs properly of the
Application Specification for Microsoft Windows 2000 for Desktop
Applications)
(See also Removing "Ghosts" from the Add/Remove Programs Utility)
Zero Administration for Windows (ZAW)
Zero Administration for
Windows is an initiative to reduce work and costs associated with
installing and managing a Windows-based network environment.
ZAW includes functionalities
to automatically update systems and install applications, to cache
configuration information and to administer and lock systems from a
central point.
Windows Installer and
Systems Management Server are part of the ZAW initiative.
To implement a ZAW solution
the Zero Administration Kit is needed.
It can run on Windows NT and
Windows 9x.
An evolution of the ZAW is
available and is an important part of Windows 2000. Maintaining and Supporting an Application
Implement Static Load Balancing
Dynamic Load Balancing uses
an algorithm to determine the load on each server, and routes every
request to the server that can best serve it.
Static Load Balancing is a
manual process. Every client must be configured to point to a server
statically.
If MTS is used to host COM
components, a package file can be exported to simplify the component
installation on the client. MTS support only static load balancing. COM+
also supports dynamic load balancing (but is not part of this exam...).
Fix errors and prevent future errors
Logic errors (known also as
bugs) occur when the program can be compiled, but doesn’t work as
expected.
Syntax errors occur when the
compiler doesn’t understand what is written in the source code.
TRACE, ASSERT, SEH
(Structured Exception Handling) and C++ exceptions can be used to prevent
future errors and to trap run-time errors when they occur.
Deploy updates
Use InstallShield to deploy
application updates. It can be used to replace program files, components
and registry settings. Use Overwrite property to specify the conditions
under which files will or will not overwrite the user’s hard disk. |