Q115088: PRB: A _USRDLL with No CWinApp Object Fails to Load

Article: Q115088
Product(s): Microsoft C Compiler
Version(s): winnt:1.0,2.0,2.1,4.0
Operating System(s): 
Keyword(s): kbDLL kbMFC kbVC kbVC100 kbVC150 kbVC200 kbVC400 kbGrpDSMFCATL
Last Modified: 07-MAY-2001

-------------------------------------------------------------------------------
The information in this article applies to:

- The Microsoft Foundation Classes (MFC), used with:
   - 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 
-------------------------------------------------------------------------------

SYMPTOMS
========

If you build an MFC _USRDLL using Visual C++, version 1.0, without defining a
CWinApp object, when its LibMain() function is called you will get the following
message in the debugger Output Window:

  <unknown application>: File appcore.cpp, Line 602, Assertion Failed!
  AfxAbort called

If you are using Visual C++, version 1.0 for Windows NT, you will get the
following message box when DllMain() is called:

  <unknown application>: File appcore.cpp, Line 559, Assertion Failed!

If you are using Visual C++, version 1.5, you will get the following message when
LibMain() is called:

  <unknown application>: File afxwin1.inl, Line 25, Assertion Failed!
  AfxAbort called

If you are running a Windows-based application, the call to LibMain() will fail
without warning, unless you are running under the debugger. Under Windows NT,
you will get the warning shown previously when calling DllMain() (if you are
running a debug version of your DLL), followed by message boxes from the system
stating that the application caused a problem.

In order to support resource-only DLLs, the assertions described above were
removed in Microsoft Visual C++ 32-bit Edition, versions 2.0, 2.1, 2.2, and 4.0.
Using those versions, calling a CWinApp member function through the pointer
returned by AfxGetApp() will result in an access violation if there is no
CWinApp object defined in the DLL.

CAUSE
=====

When an attempt is made to load a DLL for the first time, the DLL[ASCII 146]s
entry point (LibMain() for Windows, DllMain() for Windows NT) is called to
initialize its instance. For a _USRDLL, the entry point calls AfxWinInit(),
which calls AfxGetApp(). If no CWinApp object has been instantiated in the DLL,
AfxGetApp() returns "NULL". AfxGetApp() cannot access the calling application's
CWinApp (if the calling application is an MFC application). This causes the
assertion.

This behavior is by design, because most of the classes provided by MFC rely on
the instantiation of a CWinApp object. Even some simple objects such as an
AfxMessageBox make calls to AfxGetApp(). If no CWinApp object exists [and your
DLL uses any GUI objects that rely on calls to AfxWinInit() and AfxWinTerm()],
assertions and program failures will occur.

RESOLUTION
==========

Be sure to instantiate an object of type CWinApp or a class derived from
CWinApp. If it is important that you not have a CWinApp object in your _USRDLL,
see the "MORE INFORMATION" section of this article, below, for suggestions.

NOTE: Visual C++ 4.0 automatically generates the code containing the declaration,
definition, and instantiation of the CWinApp derived class for the DLL, so the
following sections do not apply.

MORE INFORMATION
================

It is not necessary for your DLL to call AfxWinInit() and AfxWinTerm() for it to
use the MFC collection classes and the simple class types such as CString. If
you wish to use only these MFC classes in your DLL, you can omit the
instantiation of a CWinApp object, but you also must write your own LibMain() or
DllMain() function for the DLL.

For an example of the code that you may want to include in your entry point, you
can look at what MFC normally does in AfxWinInit().

NOTE: SetCurrentHandles() is called, so afxCurrentInstanceHandle and
afxCurrentResourceHandle get set equal to m_hInstance. You may need to set these
values yourself.

Sample Code for Windows
-----------------------

The code below shows one possible implementation of LibMain(). For the sake of
pointing out the initialization portion, LibMain() calls MyInit(), which
consists of a very simplified simulation of AfxWinInit().

     // Compile options needed: /D "_USRDLL"

     #include <afxwin.h>

     BOOL AFXAPI MyInit(HINSTANCE);

     extern "C" int FAR PASCAL LibMain(HINSTANCE hInstance,
        WORD wDataSegment, WORD wHeapSize, LPSTR lpszCmdLine)
     {
        // Initialize DLL's instance(/module) not the app's
        if (!MyInit(hInstance))
        {
           return 0;    // Init Failed
        }

     // nothing to run
        return 1;   // ok
     }

     BOOL AFXAPI MyInit(HINSTANCE hInstance)
     {
        afxCurrentInstanceHandle = hInstance;
        afxCurrentResourceHandle = hInstance;

     // one instance initialization stuff goes here

     // Handle critical errors
        ::SetErrorMode(SEM_FAILCRITICALERRORS);

        return TRUE;
     }

Sample Code for Windows NT
--------------------------

The code below shows one possible implementation of DllMain(). For the sake of
pointing out the initialization portion, DllMain() calls MyInit(), which
consists of a very simplified simulation of AfxWinInit().

     // Compile options needed: /D "_USRDLL"

     #include <afxwin.h>

     BOOL AFXAPI MyInit(HINSTANCE);

     extern "C" int APIENTRY
     DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
     {
             if (dwReason == DLL_PROCESS_ATTACH)
             {
                     // Initialize DLL's instance(/module) not the app's
                     if (!MyInit(hInstance))
                     {
                             return 0;       // Init Failed
                     }

             }
             return 1;   // ok
     }

     BOOL AFXAPI MyInit(HINSTANCE hInstance)
     {
             afxCurrentInstanceHandle = hInstance;
             afxCurrentResourceHandle = hInstance;

             // one instance initialization stuff goes here

             // Handle critical errors
             ::SetErrorMode(SEM_FAILCRITICALERRORS);

             return TRUE;
     }

Additional query words: 1.00 1.50 2.00 2.10 2.50 4.00

======================================================================
Keywords          : kbDLL kbMFC kbVC kbVC100 kbVC150 kbVC200 kbVC400 kbGrpDSMFCATL 
Technology        : kbAudDeveloper kbMFC
Version           : winnt:1.0,2.0,2.1,4.0
Issue type        : kbprb

=============================================================================