Article: Q105286
Product(s): Microsoft C Compiler
Version(s): winnt:1.0,2.0,2.1,4.0
Operating System(s):
Keyword(s): kbDLL kbMFC kbVC100 kbVC150 kbVC200 kbVC400 kbDSupport kbGrpDSMFCATL
Last Modified: 07-MAY-2001
-------------------------------------------------------------------------------
The information in this article applies to:
- The Microsoft Foundation Classes (MFC), used with:
- Microsoft C/C++ for MS-DOS, version 7.0
- Microsoft Visual C++ for Windows, 16-bit edition, versions 1.0, 1.5, 1.51, 1.52
- Microsoft Visual C++, 32-bit Editions, versions 1.0, 2.0, 2.1, 4.0
-------------------------------------------------------------------------------
SUMMARY
=======
The Microsoft Foundation Class (MFC) Libraries create temporary objects that are
used inside of message handler functions. In MFC applications, these temporary
objects are automatically cleaned up in the CWinApp::OnIdle() function that is
called in between processing messages. However, in MFC dynamic-link libraries
(DLLs) built using the _USRDLL model, the OnIdle() function is not automatically
called. As a result, temporary objects are not automatically cleaned up. To
clean up temporary objects, the DLL must explicitly call OnIdle(1) periodically.
MORE INFORMATION
================
MFC maintains a set of maps that associate windows' handles with MFC objects.
When the FromHandle() function is called, it checks the map to see whether an
MFC object exists that is associated with the handle. If there is, FromHandle()
returns a pointer to the object; if not, FromHandle() creates a temporary
object. These temporary objects are cleaned up when CWinApp::OnIdle() is called
with a count of 1.
In an MFC application, the OnIdle() function is called when the message loop is
idle. It is called inside of a ::PeekMessage() loop that checks for new
messages. The ::PeekMessage() loop is in the function CWinApp::Run(). The
OnIdle() function is passed an "lCount" parameter, which indicates how many
times OnIdle() has been called since the last message. When OnIdle() is called
with an lCount equal to 1, the temporary objects are cleaned up. However,
because an MFC _USRDLL DLL does not contain a main message loop, OnIdle() is not
automatically called and the temporary objects are not cleaned up. To clean up
the temporary objects, the DLL must call OnIdle() explicitly.
In general, it can be difficult to determine the best time to call OnIdle()- -it
needs to be called often enough that temporary objects do not accumulate, and it
needs to be called when no temporary objects are in use. Below are three
strategies for calling OnIdle() in _USRDLL DLLs:
1. Call the OnIdle() function at the beginning of each exported function. This
will clean up the temporary objects each time the DLL is entered. This method
works as long as the DLL doesn't re-enter itself by calling other functions
in the DLL that also clean up temporary objects when they exit. Also, the
function in the DLL must not yield to Windows by calling functions such as
CWnd::MessageBox(), CDialog::DoModal(), and ::PeekMessage(). These functions
allow other applications to process messages. If any of these other
applications were called back in to the DLL, it would cause the temporary
objects to be cleaned up.
2. Have the DLL export a function to be used only for cleaning up temporary
objects. Then have each application that uses the DLL call this function when
its main message loop is idle. This will allow functions in the DLL to call
other functions in the DLL with out prematurely cleaning up the temporary
objects. Note, however, that this approach is not safe if the DLL's
functions, such as CWnd::MessageBox(), CDialog::DoModal(), and
::PeekMessage(), yield to Windows. The DLLTRACE sample shipped with Visual
C++ demonstrates this approach.
3. Maintain a counter that indicates whether the DLL is in the middle of a
function. At the beginning of each exported function, increment the counter
by 1; at the end of the function, decrement the counter by 1. If at the end
of the function the counter is zero, then it is safe to call OnIdle(). The
following code demonstrates one way to implement this approach:
Sample Code
-----------
static DWORD dwEntryCount = 0;
void LockTemporaryObjects()
{
InterlockedIncrement (&dwEntryCount);
}
void UnlockTemporaryObjects()
{
if (dwEntryCount == 0)
return;//Keep us from going negative
if (0 == InterlockedDecrement((&dwEntryCount))
{
AfxGetApp()->CWinApp::OnIdle(0); // Updates UI objects
AfxGetApp()->CWinApp::OnIdle(1); // Free's tempory objects
}
}
The LockTemporaryObjects() and UnlockTemporaryObjects() function are then used as
follows:
Sample Code
-----------
void PASCAL FAR EXPORT MyDllRoutine()
{
TRY
{
LockTemporaryObjects();
// Do work
UnlockTemporaryObjects();
}
CATCH_ALL(e)
{
// Good idea to deal with exceptions in _USRDLL anyway
UnlockTemporaryObjects();
}
END_CATCH_ALL
}
These approaches are easy to implement for functions that the DLL explicitly
exports; however, it is also possible to enter the DLL through the window
procedure used by MFC windows created in the DLL. If the DLL creates a window
that has a long lifetime, such as a modeless dialog box or a frame window, then
you may want to clean up temporary objects in between calls to the this window's
window procedure. You can do this by overriding the WindowProc() function for
the window object. This function is called once for each message that the window
processes. Because it is very common for message handlers to call functions that
end up sending more messages to the window, you need to use the counter method
mentioned above to make sure that you aren't cleaning up temporary objects that
are still being used. The following code fragment demonstrates how this could be
done for a modeless dialog box in a class derived from CDialog:
CMyDialog::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
LockTemporaryObjects();
LRESULT lResult = CDialog::WindowProc( message, wParam, lParam);
UnlockTemporaryObjects();
return lResult;
}
Additional query words:
======================================================================
Keywords : kbDLL kbMFC kbVC100 kbVC150 kbVC200 kbVC400 kbDSupport kbGrpDSMFCATL
Technology : kbAudDeveloper kbMFC
Version : winnt:1.0,2.0,2.1,4.0
Issue type : kbhowto
=============================================================================