Showing posts with label MFC. Show all posts
Showing posts with label MFC. Show all posts

Wednesday, November 28, 2007

Calculate the execution time of a function

Calculate the execution time of a function :
we can use timeGetTime() fn available in mmsystem.h .
For using this function,
we have to include the winmm.lib


DWORD dwStartTime = timeGetTime();
StretchBlt(); // which we want to find the execution time
DWORD dwEndTime = timeGetTime();

dwEndTime - dwStartTime is the execution time.

we can also use QueryPerformanceCounter() fn to find the execution time.

Monday, November 26, 2007

While using CString in a DLL, I got an error Because it is a win32 DLL.

While using CString in a DLL, I got an error Because it is a win32 DLL.

Solution :

I modified the project ->settings->General Tab-> Use MFC in a Shared DLL or Use MFC in a Static Library...

Afterwards I included the #include in "stdafx.h" header file, now the problem is solved...

Friday, November 23, 2007

Design time combo box problem in MFC Dialog application

I got the combo box problem in VC++ 6 dialog application as follows :

1.I added the Combo box with DropList property in a dialog application
2.I added the "control" member variable from the classwizard.
3.Within OnInitDialog() fn, I added the string to the combo box.
( m_cbo.AddString("Sundar");m_cbo.AddString('Sundar");)
But the added string is not displayed at runtime.

if I set the selected index for the combo box,

m_cbo.SetCurSel(0) , the selected index string "sundar" alone displayed in a combo box at
runtime.


4. To identify the problem, I created the Dynamic combo box by code ...

and Added the string , all the strings are displayed in a combo box.

But For Design time combo box, it didnt display the text.

unsupported operation was attempted error

In an MFC Dialog based application,
I didnt have a linker error but I got an error at runtime as follows;

"An unsupported operation was attempted."

Then the application is closed...


Reason for the Error :

I have added the Combo box in a MFC dialog application and added the control variable for it.

wizard will add the code as follows : DDX_Control(pDX, IDC_cboDeviceList, m_cboDevice);

later for some reason, I deleted the combo box from the dialog.

But the following code is still in my dialog application

DDX_Control(pDX, IDC_cboDeviceList, m_cboDevice);

and causes the error "the unsupported operation was attempted"

Tuesday, September 18, 2007

MFC message maps

MFC Message Maps:
MFC provides an alternative to the switch statement used in traditional Windows programs to handle messages sent to a window. A mapping from messages to member-functions may be defined so that when a message is to be handled by a window, the appropriate member function is called automatically.

This message-map facility is designed to be similar to virtual functions but has additional benefits not possible with C++ virtual functions.


Defining a Message Map:

1.The DECLARE_MESSAGE_MAP macro declares three members for a class.

A private array of AFX_MSGMAP_ENTRY entries called _messageEntries,


A protected AFX_MSGMAP structure called messageMap that points to the _messageEntries array


A protected virtual function called GetMessageMap that returns the address of messageMap.
This macro should be placed in the declaration of any class using message maps. By convention, it is at the end of the class declaration. For example:

class CMyWnd : public CMyParentWndClass
{
// my stuff...

protected:
//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};


The message map's table is defined with a set of macros that expand to message map
entries. A table begins with a BEGIN_MESSAGE_MAP macro call, which defines the class that is handled by this message map and the parent class to which unhandled messages are passed. The
table ends with the END_MESSAGE_MAP macro call.

Between these two macro calls is an entry for each message to be handled by this message map. Every standard Windows message has a macro of the form ON_WM_xxx (where xxx is the name of the message) that generates an entry for that message.

CObject services

CObject Services :
Cobject provides basic services needed for all the classes.

1.Supports serialization
2.run-time class information
3.Dynamic Object Creation
4.Object diagnostic output (Debugging)
5.Compatibility with collection classes

The first-level macros, DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC, permit run-time access to the class name
and its position in the hierarchy. This, in turn, allows meaningful diagnostic dumping and dynamic Object creation.

The second-level macros, DECLARE_SERIAL and IMPLEMENT_SERIAL, include all the functionality of the first-level macros,
and they enable an object to be “serialized” to and from an “archive.”

MFC collection classes are derived from CObject So we can store any class( derived from CObject) into the collection class.



Serialization Example :
----------------------------------

class CMyObject : public CObject
{
// ...Member functions
public:
CMyObject() { }
virtual void Serialize( CArchive& ar ) { }

// Implementation
protected:
DECLARE_SERIAL( CMyObject )
};


class COtherObject : public CObject
{
// ...Member functions
public:
COtherObject() { }
virtual void Serialize( CArchive& ar ) { }

// Implementation
protected:
DECLARE_SERIAL( COtherObject )
};


class CCompoundObject : public CObject
{
// ...Member functions
public:
CCompoundObject();
virtual void Serialize( CArchive& ar );

// Implementation
protected:
CMyObject m_myob; // Embedded object
COtherObject* m_pOther; // Object allocated in constructor
CObject* m_pObDyn; // Dynamically allocated object
//..Other member data and implementation

DECLARE_SERIAL( CCompoundObject )
};

IMPLEMENT_SERIAL(CMyObject,CObject,1)
IMPLEMENT_SERIAL(COtherObject,CObject,1)
IMPLEMENT_SERIAL(CCompoundObject,CObject,1)


CCompoundObject::CCompoundObject()
{
m_pOther = new COtherObject; // Exact type known and object already
//allocated.
m_pObDyn = NULL; // Will be allocated in another member function
// if needed, could be a derived class object.
}

void CCompoundObject::Serialize( CArchive& ar )
{
CObject::Serialize( ar ); // Always call base class Serialize.
m_myob.Serialize( ar ); // Call Serialize on embedded member.
m_pOther->Serialize( ar ); // Call Serialize on objects of known exact type.

// Serialize dynamic members and other raw data
if ( ar.IsStoring() )
{
ar << m_pObDyn;
// Store other members
}
else
{
ar >> m_pObDyn; // Polymorphic reconstruction of persistent
// object
//load other members
}
}





RunTimeClass Information Support Example :
====================================


// in .H file
class CPerson : public CObject
{
DECLARE_DYNAMIC( CPerson )
public:
CPerson(){};

// other declaration
};


// in .CPP file
IMPLEMENT_DYNAMIC( CPerson, CObject )


void SomeFunction(void)
{
CObject* pMyObject = new CPerson;

if(pMyObject->IsKindOf( RUNTIME_CLASS( CPerson ) ) )
{
//if IsKindOf is true, then cast is all right
CPerson* pmyPerson = (CPerson*) pMyObject ;
...
delete pmyPerson;
}
...
delete [MyObject];
}


Dynamic Object Creation :
-----------------------------------


To dynamically create an object given its run-time class

Use the following code to dynamically create an object by using the CreateObject function of the CRuntimeClass.
Note that on failure, CreateObject returns NULL instead of raising an exception:

CRuntimeClass* pRuntimeClass = RUNTIME_CLASS( CMyClass );
CObject* pObject = pRuntimeClass->CreateObject();
ASSERT( pObject->IsKindOf( RUNTIME_CLASS( CMyClass ) ) );


Object diagnostic output :
------------------------------------

Dumping Object Contents :

This topic explains how to get a diagnostic dump of the contents ofour objects.

When deriving a class from CObject, we have the option to override the Dump member function and write a textual representation of the object’s member variables to a dump context which is similar to an I/O stream. Like an I/O stream, you can use the insertion (<<) operator to send data to a CDumpContext.

we do not have to override Dump when you derive a class from CObject. However, if we use other diagnostic features for debugging, providing the capability for dumping an object and viewing its contents is very helpful and highly recommended.

Note Before we can dump objects, we must enable diagnostic tracing so we can see the results of our dump in the debugger.


To override the Dump member function

Call the base class version of Dump to dump the contents of a base class object.


Write a textual description and value for each member variable of your derived class.
The declaration of the Dump function in the class declaration looks like:

class CPerson : public CObject
{
public:
#ifdef _DEBUG
virtual void Dump( CDumpContext& dc ) const;
#endif

CString m_firstName;
CString m_lastName;
// etc. ...
};

Note Since object dumping only makes sense when you are debugging your program, the declaration of the Dump function is bracketed with an #ifdef _DEBUG / #endif block.

In the following example from an implementation file for the class CPerson, the Dump function’s first statement calls the Dump member function for its base class. It then writes a short description of each member variable along with the member’s value to the diagnostic stream.

#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
// call base class function first
CObject::Dump( dc );

// now do the stuff for our specific class
dc << "last name: " << m_lastName << "\n"
<< "first name: " << m_firstName << "\n";
}
#endif

Note Again, notice that the definition of the Dump function is bracketed by #ifdef _DEBUG / #endif directives. If you refer to afxDump in a program linked with the nondebug libraries, you will get unresolved externals errors at link time.

To send Dump output to afxDump

You must supply a CDumpContext argument to specify where the dump output will go when you call the Dump function for an object.
MFC supplies a predefined CDumpContext object named afxDump that you will normally use for routine object dumping.
The following example shows how to use afxDump:

CPerson* pMyPerson = new CPerson;
// set some fields of the CPerson object...
//...
// now dump the contents
#ifdef _DEBUG
pMyPerson->Dump( afxDump );
#endif

In Windows NT, afxDump output is sent to the debugger, if present. Otherwise, you won’t get any afxDump output.

Note afxDump is defined only in the debug version of MFC.

Monday, August 06, 2007

Slider Control

1.Today I worked with slider control with horizontal attribute
change it as to horizontal and in order to receive the slider control messages we have to add the
HSCROLL or VSCROLL message

I added the following message :

ON_WM_HSCROLL() added it within BEGIN_MESSAGE () macro.

next I added the fn like

afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) ;

while using Slider control, the SCROLL message is posted...
There are two main notification codes are available.

That are
TB_THUMBTRACK //Slider movement (the user dragged the slider)
TB_THUMBPOSITION//WM_LBUTTONUP following a TB_THUMBTRACK notification message


void MFCDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CSliderCtrl * pControl = (CSliderCtrl*) pScrollBar);

switch(nSBCode)
{
case TB_THUMBTRACK://Slider movement (the user dragged the slider)
{
break;
}
case TB_THUMBPOSITION:
{
//WM_LBUTTONUP following a TB_THUMBTRACK notification message
//StopTimer();
break;
}

}


}

Wednesday, August 01, 2007

User Defined message creation

The user defined message can be created by the user and used in MFC application :

ForExample if we want to post our own message, we have to create our own message .

Each and every message have a unique number. Using this number, windows identifies the message.



Normally some messages are predefined. Ex : WM_LBUTTONDOWN



it is having the unique ID . using this ID, the windows takes appropriate action for left button down event.





if we are creating our own message, that will not be mingle with the existing ID.



Assume that WM_LBUTTONDOWN message ID number is 0x0008 .



if we are creating our own message with this ID, the windows will takes appropriate action for the LButtonDown.



To overcome this we have to use it as follows :



#define WM_USERMSG WM_APP + 1

#define WM_USERMSG2 WM_APP + 2



and so on.



How can we handle our own message in an application ?





For Adding handler to the user defined message , MFC has ON_COMMAND() message handler.





Here is the testing of custom message :



#define WM_USERMSG WM_APP + 1



How to send custom message to the application ?...



::SendMessage(

HWND, // Parent window

WM_USERMSG, // User defined message

1, // wparam value...

2 // lparam value



) ;

the user can pass their own data through wparam and lparam parameter.





in win32 application we can handle this message in the window procedure as follows :



switch( uMsg)

{

case WM_USERMSG :

int i = (int) wparam; // value will be 1

int j = (int) lparam; // value will be 2 for the above program...

break;



}



In MFC we can add handler to the user defined message using ON_COMMAND() macro..



afx_msg LRESULT OnUserMessage(WPRAM wp, LPARAM lp);



ON_COMMAND( WM_USERMSG, OnUserMessage)





LRESULT CMFCDlg:: OnUserMessage(WPRAM wp, LPARAM lp)

{

int i = (int)wp; // value will be 1

int j = (int) lp; // value will be 2 // if we used the SendMessage(hwnd,WM_USERMSG, 1,2)



retrurn 0;

}

Tuesday, June 26, 2007

Mastering VC++

Look at the MFC Source in the following site and understand them....

http://www.codeproject.com/useritems/OpenSource_VC_MFC.asp
http://win32assembly.online.fr/tutorials.html

Thursday, May 10, 2007

Color Progress control

Color progress bar control :
---------------------------------------------


After adding the progress bar control and add the "control" member variable to the progress bar as m_ColorProgressbar


No need for the OnCtlColor() fn.
Progress bar control supports color without OnCtlColor() fn.

class CAppDialog : public CDialog
{
public:
virtual void OnOK();
afx_msg void OnTimer(UINT nIDEvent);
};

BEGIN_MESSAGE_MAP()
ON_WM_TIMER()
END_MESSAGE_MAP()



#define PROG_TIMER 2011


void CAppDialog::OnOK()
{
m_ColorProgressbar.SetRange(0,100);
COLORREF m_BackColor = RGB(255,255,255); //white
COLORREF m_BarColor = RGB(255,140,0); //Orange
m_ColorProgressbar.SendMessage(PBM_SETBKCOLOR,0,m_BackColor);
m_ColorProgressbar.SendMessage(PBM_SETBARCOLOR,0,m_BarColor);
SetTimer(PROG_TIMER,100,NULL);
}

void CAppDialog::OnTimer(UINT nIDEvent)
{
if(nIDEvent == PROG_TIMER)
{
m_ColorProgressbar.StepIt();
}
CDialog::OnTimer(nIDEvent);
}

How do I draw a bitmap in the background of a dialog?

How do I draw a bitmap in the background of a dialog?
The trick is to override the OnEraseBkgnd handler. Here's one way of doing it:

BOOL CTestbed2Dlg::OnEraseBkgnd(CDC* pDC)
{
//if u load the bitmap in pDC using BitBlt() fn
}

Wednesday, April 04, 2007

Handling multiple events using WaitForMultipleObjects() fn

m_hMsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hThreadStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hThread = CreateThread(NULL, 2048, (LPTHREAD_START_ROUTINE)pr_threadStat, (VOID *)this, NULL, &dwID);


DWORD ThreadProc(LPVOID lpParam)
{
CMwsServlet* p = (CMwsServlet*)lpParam;

p->thread();

}

void classname ::thread()
{

HANDLE hEvents[] = { m_hThreadStopEvent, m_hMsgEvent};
DWORD dwObject;
bool bNeedMoreData = false;
// int i, nCondition;

// Main thread loop
while(true)
{

dwObject = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
if (dwObject == 0)
{
return; // Stop event: leave process
}

// If servlet event
if (dwObject == 1)
{
IMWSServletMsgPtr p = m_pServlet->GetNextMessage();

OutputDebugString("Message from Servlet: '");
OutputDebugString(m_pServlet->FormatMessage(p));
OutputDebugString("'\n");
}
}
}

Monday, April 02, 2007

Kernel Objects Vs GDI objects

kernel objects :

1.several types of kernel objects, such as access token objects, event objects, file objects, file-mapping objects, I/O completion port objects, job objects, mailslot objects, mutex objects, pipe objects, process objects, semaphore objects, thread objects, and waitable timer

2.Each kernel object is simply a memory block allocated by the kernel and is accessible only by the kernel. This memory block is a data structure whose members maintain information about the object.

3.Kernel objects are owned by the kernel, not by a process.

4.kernel object can outlive the process that created it.

5.The kernel knows how many processes are using a particular kernel object because each object contains a usage count.

6.usage count is one of the data members common to all kernel object types. When an object is first created, its usage count is set to 1. Then when another process gains access to an existing kernel object, the usage count is incremented. When a process terminates, the kernel automatically decrements the usage count for all the kernel objects the process still has open. If the object's usage count goes to 0, the kernel destroys the object. This ensures that no kernel object will remain in the system if no processes are referencing the object.

7.Kernel objects can be protected with a security descriptor.

8.A security descriptor describes who created the object, who can gain access to or use the object, and who is denied access to the object. Security descriptors are usually used when writing server applications; you can ignore this feature of kernel objects if you are writing client-side applications.

9.In addition to kernel objects, your application might use other types of objects, such as menus, windows, mouse cursors, brushes, and fonts. These objects are User objects or Graphics Device Interface (GDI) objects, not kernel objects.


10.How can we identify the object is kernel object or GDI Object ?

The easiest way to determine whether an object is a kernel object is to examine the function that creates the object. Almost all functions that create kernel objects have a parameter that allows you to specify security attribute information, as did the CreateFileMapping function shown earlier.

11.When a process is initialized, the system allocates a handle table for it. This handle table is used only for kernel objects, not for User objects or GDI objects. The details of how the handle table is structured and managed are undocumented.


12.The structure of a process's handle table


Index Pointer to Kernel Object Memory Block Access Mask (DWORD of Flag Bits) Flags (DWORD of Flag Bits)
1 0x???????? 0x???????? 0x????????
2 0x???????? 0x???????? 0x????????
… … …

13.When a process first initializes, its handle table is empty.

14. Difference between CMutex and CSemaphore ?

Shared memory with Single lock is called CMutex
Shared memory with multi lock is called CSemaphore.

15.Closing a Kernel Object - BOOL CloseHandle(HANDLE hobj);

16.Changing a Handle's Flags - call the SetHandleInformation function

Tuesday, March 27, 2007

Bottom up DIB and Top down DIB

The origin for a bottom-up DIB is the lower-left corner of the bitmap; the origin for a top-down DIB is the upper-left corner.

I faced the problem like this
I have drawn the image using DrawText() fn... it succeeds... But it didnt work well...
I used the hBackDC to DrawText()... On hBackDC I selected the hBackBitmap object.
hBackBitmap holds the image size as 320 * 240 bitmap...
using DrawText() I displayed the image in out of region in hBackBitmap...
it wont get any error.
Even though the x,y values exceeds, DrawText( ) fails...

I must start the basics in CSource filter...

Monday, March 26, 2007

Simple MFC application

//Simple MFC application program

// Define a window class derived from CWnd
class CHelloWindow : public CWnd
{
public:
CHelloWindow()
{
CreateEx(WS_EX_CLIENTEDGE,
AfxRegisterWndClass(0, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1)),
_T("Hello World!"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, 0);
}
};


// Define an application class derived from CWinApp
class CHelloApp : public CWinApp
{
public:
virtual BOOL InitInstance()
{
m_pMainWnd = new CHelloWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
};

CHelloApp HelloApp; // HelloApp's constructor initializes and runs the app

Sunday, March 25, 2007

CMutex with CSingleLock application

1.CSingleLock with CMutex.... MFC...

One thread incremented the counter , another one thread displays the value in the dialog box....

I have to synchronize the things...


Description :

1. CMutex object is available as a member in dialog application...

2. we developed the CCounterThread(increment the value) and CDisplayThread

used to update the value to the dialog box...

CCounterThread and CDisplayThread classes are derived from CWinThread...


The Dialog box have the string value for the counter (displayed in Text Box)...

within the counter thread, we incremented the counter and displayed the value in DisplayThread...

class CCounterThread : public CWinThread
{
public:
BOOL m_bDone;
CCounterThread() { m_bDone = FALSE; }
virtual BOOL InitInstance () { return TRUE; }
virtual int Run();
CMutexDlg* dlg; //Set this dialog at runtime...
};

int CCounterThread ::Run ()
{
int i =0;
CSingleLock sLock(&dlg->m_MutexObject);

while(!m_bDone)
{

sLock.Lock();
dlg->m_Number = i; // m_Number is mapped to the dialog's Edit box...
sLock.UnLock();

}

SendMessage(dlg,WM_CLOSE,0,0);

}
CMutexDlg has CCounterThread and CDisplayThread object, CMutex object...
CCounterThread and CDisplayThread must have the Dialog (CMutexDlg) object..


I derived the CWinThread class...

The CWinThread derived class has overriding Run() fn...

Thursday, March 22, 2007

File Change Notifications

File Change Notifications

HANDLE parameter passed to ::WaitForSingleObject can be a "file change notification handle." The Win32 API includes a
function named ::FindFirstChangeNotification that returns a handle you can use to wake a blocked thread whenever a change
occurs in a specified directory or its subdirectories—for example, when a file is renamed or deleted or a new directory is
created.

Changes to the file system are instantly reflected in the left or right pane. The most efficient way to do it is to start a
background thread and have it block on one or more file change notification handles. Here's what the thread function for a
thread that monitors drive C: might look like:

UINT ThreadFunc (LPVOID pParam)
{
HWND hwnd = (HWND) pParam; // Window to notify
HANDLE hChange = ::FindFirstChangeNotification (_T ("C:\\"),
TRUE, FILE_NOTIFY_CHANGE_FILE_NAME ¦ FILE_NOTIFY_CHANGE_DIR_NAME);

if (hChange == INVALID_HANDLE_VALUE) {
TRACE (_T ("Error: FindFirstChangeNotification failed\n"));
return (UINT) -1;
}

while (...) {
::WaitForSingleObject (hChange, INFINITE);
::PostMessage (hwnd, WM_USER_CHANGE_NOTIFY, 0, 2);
::FindNextChangeNotification (hChange); // Reset
}
::FindCloseChangeNotification (hChange);
return 0;
}




The first parameter passed to ::FindFirstChangeNotification identifies the directory you want to monitor, the second
specifies whether you want to monitor just that directory (FALSE) or that directory and all its subdirectories (TRUE), and
the third specifies the kinds of changes that the thread should be notified of. In this example, the thread will be awakened
when a file is created, renamed, or deleted anywhere on the C: drive (FILE_NOTIFY_CHANGE_FILE_NAME) or when a directory is
created, renamed, or deleted (FILE_NOTIFY_CHANGE_DIR_NAME). When the thread is awakened, it posts a user-defined message to
the window whose handle was passed in pParam. The message's lParam holds a drive number (2 for drive C:). The window that
receives the message—presumably the application's top-level frame window—can respond to the message by updating its views.
Keep in mind that a thread awakened by a file change notification doesn't receive any information about the nature of the
change or about where in the directory tree the change occurred, so it must scan the file system if it wants to determine
what caused the file change notification.

It's also possible to structure the thread so that it monitors not just one drive, but several. All you would have to do is
call ::FindFirstChangeNotification once per drive to acquire a separate file change notification handle for each drive and
use ::WaitForMultipleObjects to block on all the file change notifications simultaneously. ::WaitForMultipleObjects is the
Win32 API equivalent of CMultiLock::Lock. Passing FALSE in the third parameter to a call to ::WaitForMultipleObjects tells
the system to wake the thread when any one of the objects that the thread is blocking on becomes signaled.

AlphaBlend() fn notes

In VC98, AlphaBlend() fn is not added to CDC class..we can make use of

AlphaBlend() fn with msimg32.lib . msimg32.lib contains AlphaBlend() fn..

#define AC_SRC_OVER 0x0
#define AC_SRC_ALPHA 0x1

How can we access the HDC from CDC class ?...

CDC class has m_hDC member which is a type of HDC for CDC...

Saturday, March 10, 2007

MFC Array Classes

Type-Specific MFC Array Classes

Class Name Data Type
CByteArray 8-bit bytes (BYTEs)
CWordArray 16-bit words (WORDs)
CDWordArray 32-bit double words (DWORDs)
CUIntArray Unsigned integers (UINTs)
CStringArray CStrings
CPtrArray void pointers
CObArray CObject pointers


Once you learn to use one of these array classes, you can use the others too, because all share a common set of member functions. The following example declares an array of 10 UINTs and initializes it with the numbers 1 through 10:

CUIntArray array;
array.SetSize (10);
for (int i=0; i<10; i++)
array[i] = i + 1;




You can use the same approach to declare an array of CStrings and initialize it with textual representations of the integers 1 through 10:

CStringArray array;
array.SetSize (10);
for (int i=0; i<10; i++) {
CString string;
string.Format (_T ("%d"), i);
array[i] = string;
}




In both cases, SetSize sizes the array to hold 10 elements. In both cases, the overloaded [] operator calls the array's SetAt function, which copies a value to an element at a specified location in the array. And in both cases, the code asserts if the array's bounds are violated. The bounds check is built into the code for SetAt:

ASSERT(nIndex >= 0 && nIndex < m_nSize);




You can see this code for yourself in the MFC source code file Afxcoll.inl.

You can insert items into an array without overwriting the items that are already there by using the InsertAt function. Unlike SetAt, which simply assigns a value to an existing array element, InsertAt makes room for the new element by moving elements above the insertion point upward in the array. The following statements initialize an array with the numbers 1 through 4 and 6 through 10, and then insert a 5 between the 4 and the 6:

CUIntArray array;
array.SetSize (9);
for (int i=0; i<4; i++)
array[i] = i + 1;
for (i=4; i<9; i++)
array[i] = i + 2;
array.InsertAt (4, 5); // Insert a 5 at index 4.




You can also pass a third parameter to InsertAt specifying the number of times the item should be inserted or pass a pointer to another array object in parameter 2 to insert an entire array. Note that this example sets the array size to 9, not 10, yet no assertion occurs when InsertAt is called. That's because InsertAt is one of a handful of array functions that automatically grow the array as new items are added. Dynamically sized arrays are discussed in the next section.

Values can be retrieved from an MFC array using standard array addressing syntax. The following example reads back the UINTs written to the CUIntArray in the previous example:

for (int i=0; i<10; i++)
UINT nVal = array[i];




Used this way, the [] operator calls the array's GetAt function, which retrieves a value from a specified position in the array—with bounds checking, of course. If you'd prefer, you can call GetAt directly rather than use the [] operator.

To find out how many elements an array contains, call the array's GetSize function. You can also call GetUpperBound, which returns the 0-based index of the array's upper bound—the number of elements in the array minus 1.

MFC's array classes provide two functions for removing elements from an array: RemoveAt and RemoveAll. RemoveAt removes one or more items from the array and shifts down any items above the ones that were removed. RemoveAll empties the array. Both functions adjust the array's upper bounds to reflect the number of items that were removed, as the following example demonstrates:

// Add 10 items.
CUIntArray array;
array.SetSize (10);
for (int i=0; i<10; i++)
array[i] = i + 1;

// Remove the item at index 0.
array.RemoveAt (0);
TRACE (_T ("Count = %d\n"), array.GetSize ()); // 9 left.

// Remove items 0, 1, and 2.
array.RemoveAt (0, 3);
TRACE (_T ("Count = %d\n"), array.GetSize ()); // 6 left.

// Empty the array.
array.RemoveAll ();
TRACE (_T ("Count = %d\n"), array.GetSize ()); // 0 left.




The Remove functions delete elements, but they don't delete the objects that the elements point to if the elements are pointers. If array is a CPtrArray or a CObArray and you want to empty the array and delete the objects referenced by the deleted pointers, rather than write

array.RemoveAll ();




you should write this:

int nSize = array.GetSize ();
for (int i=0; i delete array[i];
array.RemoveAll ();




Failure to delete the objects whose addresses are stored in a pointer array will result in memory leaks. The same is true of MFC lists and maps that store pointers.

Dynamic Array Sizing
Besides being bounds-checked, the MFC array classes also support dynamic sizing. You don't have to predict ahead of time how many elements a dynamically sized array should have because the memory set aside to store array elements can be grown as elements are added and shrunk as elements are removed.

One way to dynamically grow an MFC array is to call SetSize. You can call SetSize as often as needed to allocate additional memory for storage. Suppose you initially size an array to hold 10 items but later find that it needs to hold 20. Simply call SetSize a second time to make room for the additional items:

// Add 10 items.
CUIntArray array;
array.SetSize (10);
for (int i=0; i<10; i++)
array[i] = i + 1;

// Add 10 more.
array.SetSize (20);
for (i=10; i<20; i++)
array[i] = i + 1;




When an array is resized this way, the original items retain their values. Thus, only the new items require explicit initialization following a call to SetSize.

Another way to grow an array is to use SetAtGrow instead of SetAt to add items. For example, the following code attempts to use SetAt to add 10 items to an array of UINTs:

CUIntArray array;
for (int i=0; i<10; i++)
array.SetAt (i, i + 1);




This code will assert the first time SetAt is called. Why? Because the array's size is 0 (note the absence of a call to SetSize), and SetAt doesn't automatically grow the array to accommodate new elements. Change SetAt to SetAtGrow, however, and the code works just fine:

CUIntArray array;
for (int i=0; i<10; i++)
array.SetAtGrow (i, i + 1);




Unlike SetAt, SetAtGrow automatically grows the array's memory allocation if necessary. So does Add, which adds an item to the end of the array. The next example is functionally identical to the previous one, but it uses Add instead of SetAtGrow to add elements to the array:

CUIntArray array;
for (int i=0; i<10; i++)
array.Add (i + 1);




Other functions that automatically grow an array to accommodate new items include InsertAt, Append (which appends one array to another), and Copy, which, as the name implies, copies one array to another.

MFC grows an array by allocating a new memory buffer and copying items from the old buffer to the new one. If a grow operation fails because of insufficient memory, MFC throws an exception. To trap such errors when they occur, wrap calls that grow an array in a try block accompanied by a catch handler for CMemoryExceptions:

try {
CUIntArray array;
array.SetSize (1000); // Might throw a CMemoryException.

}
catch (CMemoryException* e) {
AfxMessageBox (_T ("Error: Insufficient memory"));
e->Delete (); // Delete the exception object.
}




This catch handler displays an error message warning the user that the system is low on memory. In real life, more extensive measures might be required to recover gracefully from out-of-memory situations.

Because a new memory allocation is performed every time an array's size is increased, growing an array too frequently can adversely impact performance and can also lead to memory fragmentation. Consider the following code fragment:

CUIntArray array;
for (int i=0; i<100000; i++)
array.Add (i + 1);




These statements look innocent enough, but they're inefficient because they require thousands of separate memory allocations. That's why MFC lets you specify a grow size in SetSize's optional second parameter. The following code initializes the array more efficiently because it tells MFC to allocate space for 10,000 new UINTs whenever more memory is required:

CUIntArray array;
array.SetSize (0, 10000);
for (int i=0; i<100000; i++)
array.Add (i + 1);

MFC collection classes

Three MFC collection classes are
1.CArray
2.CList
3.CMap


CArray usage...
The first CArray template parameter specifies the type of data stored in the array; the second specifies how the type is represented in parameter lists. You could use CPoints instead of CPoint references, but references are more efficient when the size of the item exceeds the size of a pointer.

class CPoint3D
{
public:
CPoint3D ()
{
x = y = z = 0;
}
CPoint3D (int xPos, int yPos, int zPos)
{
x = xPos;
y = yPos;
z = zPos;
}
int x, y, z;
};


void Test()
{

CArray array;

// Populate the array, growing it as needed.
for (int i=0; i<10; i++)
array.SetAtGrow (i, CPoint3D (i*10, 0, 0));

// Enumerate the items in the array.
int nCount = array.GetSize ();

for (i=0; i {
CPoint3D point = array[i];
TRACE (_T ("x=%d, y=%d, z=%d\n"), point.x, point.y, point.z);
}

}

typedef CArray CUIntArray;