Home Dashboard Directory Help
Search

MFC Suitable for COM development ? (dllmain violations) by OReubens


Status: 

Closed
 as Fixed Help for as Fixed


1
0
Sign in
to vote
Type: Bug
ID: 636860
Opened: 1/19/2011 8:51:41 AM
Access Restriction: Public
0
Workaround(s)
view
0
User(s) can reproduce this bug

Description

According to the docs for the DllMain function:
http://msdn.microsoft.com/en-us/library/ms682583(VS.85).aspx
and the "best practices" document
http://go.microsoft.com/FWLink/?LinkId=84138

What you can do inside the DllMain, and by extention, what you can do in the constructors and destructors of global objects is severly limited.

We have an MFC based COM server. In it's history it has occasionally caused various issues at some of our customers but we never really managed to pinpoint it.
In a recent build of the product, however we're getting crashes in the component almost each time.

Spending lots of time debugging trying to track down the issue, it appears to be a memory overwrite in a section of memory that is *ONLY* touched via calls to ::LoadLibrary and ::FreeLibrary.

Knowing about the dllmain issues, we started to dive into our code to attempt to figure out problem points.

One obvious violation is that EVERY MFC extention dll violates the "best practices" by using CRT's new to allocate CDynLinkLibrary. in doing so it also violates the "Don't call dll's other than Kernel32".
What's causing these particular calls to MFC (CDynLinkLibrary constructor and AfxInitExtentionModule (and the matching term)), and call to new to be guaranteed safe ?

The MFC document on initializing extension DLL's
http://msdn.microsoft.com/en-us/library/aa279248(v=vs.60).aspx
Says it is guaranteed, although it's unclear why. I can only guess it's saying that assuming that you have an application that uses MFC and loads your etention DLL. if you have an COM object that uses an extention DLL, that's created from an application that doesn't use MFC what still gives you that guarantee ?

Furthering on the issue however, there's a massive amount of the DllMain and Best practices guideline violations buried inside the MFC dll itself.

In afxglobals.cpp, there's this 'little' hidden item.
AFX_GLOBAL_DATA afxGlobalData;

This is a global object constructed during DllMain (well the CRT version of DllMain at least) and is destroyed there as well.

The constructor violates the DllMain 'ruels' because:
- It does LoadLibrary and FreeLibrary several times
- it calls GetProcAddress
- It uses AfxActLoadLibrary/AfxActFreeLibrary, which is synchronisation (also not allowed)
- It calls functions from user32.dll (GetSystemMetrics, GetSysColor, EnumDisplayMonitors, ...)
- It calls functions from Gdi32.dll (CWindowDC constructor/destructor, DeleteObject, CreateFontIndirect, CreateStockObject, CreatePen, CreateBrush...)
- it calls functions from Shell32.dll (SHAppBarMessage)

Is all the above guaranteed to be safe when MFC dll is being loaded and unloaded dynamically as a result of it being part of the implicit linking of a COM dll ?

The crash we're having now would seem to indicate that at least "something" is going wrong somewhere in the LoadLibrary/FreeLibrary sequences.


I'm having the suspicion that at current MFC is intended only for implicit linking to applications, but in-process COM servers that are loading MFC extension DLL's aren't supported ?

This is in VS2008, we can't yet swap to VS2010 (we still need to support Win2000).
But as far as I had a chance to look at the MFC100 code, this is still more or less the same.
Details
Sign in to post a comment.
Posted by Jared_Burkeen on 6/11/2012 at 9:02 AM
We are running into this issue with our NSP (Name Service Provider) that loads our Dll.
Specifically, the loading of our DLL by our NSP is causing iTunes to hang when iTunes is launched.

Below is the stack when we run into this issue:

ntdll!ZwWaitForSingleObject+0x15
ntdll!RtlIntegerToUnicodeString+0x20b
GDI32!CreateFontIndirectExW+0x29
GDI32!CreateFontIndirectA+0x4a
nmsvc!CFont::CreateFontIndirectA+0x13 [f:\dd\vctools\vc7libs\ship\atlmfc\include\afxwin1.inl @ 162]
nmsvc!AFX_GLOBAL_DATA::UpdateFonts+0x465 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxglobals.cpp @ 430]
nmsvc!AFX_GLOBAL_DATA::AFX_GLOBAL_DATA+0x5cc [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxglobals.cpp @ 279]
nmsvc!`dynamic initializer for 'afxGlobalData''+0xf [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxglobals.cpp @ 196]
nmsvc!_initterm+0x1c [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 873]
nmsvc!_cinit+0x6e [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 288]
nmsvc!_CRT_INIT+0xae [f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c @ 132]
nmsvc!__DllMainCRTStartup+0x9a [f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c @ 324]
nmsvc!_DllMainCRTStartup+0x21 [f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c @ 293]
ntdll!RtlQueryEnvironmentVariable+0x241
ntdll!LdrResSearchResource+0xb4d
ntdll!LdrResSearchResource+0xa10
ntdll!LdrLoadDll+0x7b
KERNELBASE!LoadLibraryExW+0x1f1
KERNELBASE!LoadLibraryExA+0x26
kernel32!LoadLibraryA+0x31
nmNsp!loadParserDll+0xeb [c:\source\windows-client\client_dll\parser.cpp @ 69]
nmNsp!NSPStartup+0x246 [c:\source\windows-client\nsp_dll\nspmain.cpp @ 591]


Where is the QFE for the reported DllMain issue? I checked here, but couldn't locate the fix:

http://connect.microsoft.com/VisualStudio/Downloads

I have MSDN account, do I need to use up an incident just to get this fix?
Posted by Microsoft on 2/2/2011 at 1:57 PM
Hello,

Thanks for the report. I think the previous post from Microsoft must have been meant for a different issue. I understood the issue and it has been fixed in MFC for the next major release of Visual Studio. All GDI32 and USER32 API calls have been moved from the AFX_GLOBAL_DATA constructor to a separate Initialize method which is called from AfxWinInit (before any of the global data is needed).

Note that this fix is only for a future release so if you need a fix for VS2008 or VS2010 you will need to pursue a QFE via our customer service representatives.

Pat Brenner
Visual C++ Libraries Development
Posted by OReubens on 1/20/2011 at 2:04 AM
There's probably a misunderstanding somewhere since I don't get the reason to start a 2nd VS. The comment at the end "this is in VC 2008", merely was intended to say that the COM server component was built using VS2008. We can't yet swap to building with VS2010 (we haven't even installed VS2010) since we still need to support Win2000.

The issue is the COM server component. And it can cause crashes in any application using said component.

It's a MFC regular DLL that links to several MFC extension DLL's. Thus MFC ends up getting loaded and unloaded into the address space of the host application as this host application creates and releases objects.

afxGlobalData being a global object, it's constructor and destructor is being called from DllMain. There's a lot of stuff happening in that ctor/dtor that --according to the DllMain help-- shouldn't be there.

We're still debugging, but for now, that is the biggest and most obvious violation of the DllMain 'rules'. Is all of that happening via the DllMain of the MFC dll guaranteed to be safe ? From what I understand of it all, that would only be 'guaranteed' safe if MFC is implicitely loaded from an application. But what if it's all happening as a result of loadlibrary (via COM object creation) ???

The actual error/crash is happening somewhere inside the call to loadlibrarywhen the COM server is being unloaded. It appears that some system managed chunk of memory is being accessed after it has already been freed. Sometimes this causes an actual crash if said memory has also been uncommitted.

Stack trace at the crash looks like this:
>    ntdll.dll!_LdrpUpdateLoadCount2@8() + 0x33 bytes    
    ntdll.dll!_LdrpUpdateLoadCount2@8() + 0xaa bytes    
    ntdll.dll!_LdrpUpdateLoadCount2@8() + 0xaa bytes    
    ntdll.dll!_LdrpUpdateLoadCount2@8() + 0xaa bytes    
    ntdll.dll!_LdrpUpdateLoadCount2@8() + 0xaa bytes    
    ntdll.dll!_LdrpUnloadDll@8() - 0x51f5 bytes    
    ntdll.dll!_LdrUnloadDll@4() + 0x46 bytes    
    kernel32.dll!_FreeLibrary@4() + 0x81 bytes    
    ole32.dll!CClassCache::CDllPathEntry::CFinishObject::Finish() + 0x25 bytes    
    ole32.dll!CClassCache::CFinishComposite::Finish() - 0x110df bytes    
    ole32.dll!CClassCache::FreeUnused() + 0x99 bytes    
    ole32.dll!_CoFreeUnusedLibrariesEx@8() + 0x2e bytes    
    ole32.dll!_CoFreeUnusedLibraries@0() + 0x9 bytes    
    VBE6.DLL!06178ddc()     

I'll happily provide dumps and more information. Just wanted to verify on the MFC global data issue, and fact that it isn't an issue inside the VS editor itself, it's about using MFC dll for in-process COM server.
Posted by Microsoft on 1/20/2011 at 12:17 AM
Thanks for reporting the issue.
In order to fix the issue, we must first reproduce the issue in our labs. We are unable to reproduce the issue with the steps you provided.

It may help if you provide us with a mini dump file and call stack. You can use the following steps to get a mini dump file:
1. Start Visual Studio.
2. Start another instance of VS.
3. In the second instance click Tools | Attach to Process...
4. In the list of processes locate devenv.exe.
5. Click Select... and explicitly choose 'Native' and 'Managed' code.
6. Click OK and OK to close Select dialog and Attach to Process dialog.
7. Go back to the first instance of VS and repro the crash\hang.
8. Upon the crash\hang, control should go to the second instance of VS.
9. In the second instance click Debug | Save Mini Dump (without heap).

If you are running the VB profile you will not see the Save Dump As menu item. To add this menu item:
a. Select Tools -> Customize
b. Select the Commands tab
c. Select Debug from the Menu bar dropdown
d. Click Add Command...
e. Select Debug from the Categories list.
f. Find the Save Dump As entry in the Commands window.
g. Click OK (the Save Dump As... command is added to the top of the Debug menu).
h. Click Close

You can get detailed steps about how to get the dump file and call stack at http://blogs.msdn.com/debugger/archive/2009/12/30/what-is-a-dump-and-how-do-i-create-one.aspx

Please name the zipped file Feedback-636860.zip.
You can upload the file to workspace:

https://sftasia.one.microsoft.com/choosetransfer.aspx?key=7c8336de-83fb-4481-96e0-d7f7f88e35d4
Password: F^p#w)54n)*d)

It would be greatly appreciated if you could provide us with that information as quickly as possible. If we do not hear back from you within 7 days, we will close this issue.

Thanks again for your efforts and we look forward to hearing from you.

Posted by Microsoft on 1/19/2011 at 8:59 AM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(http://support.microsoft.com)
Sign in to post a workaround.