Semicolon is a new monthly column from Microsoft's Visual C++ .NET Product Manager - our very own Nick Hodapp. The column is intended to be a direct line to Microsoft for you to ask those questions that no-one else can answer. If you've ever wondered about the internal machinations of the VC++ compiler, or ever wanted to know why a particular design decision was made, or want the inside story on a curly Visual C++ question that has been stumping you then post your questions here. Each installment will cover the best or the most interesting question. The rules are simple: Make it interesting, make it about Visual C++, and don't ever mention Visual Basic.
<FONT size=+1>I have some sad news and some glad news. As you certainly know by now, Microsoft is on the cusp of releasing Visual C++ .NET 2003 – a 10-year anniversary edition with lots of great new features including ISO C++ conformance and support for Windows Forms. The sad news is that we very recently discovered a bug in the product that we were unable to fix prior to release. While the bug doesn't affect every user, or even half our users, Microsoft is taking this concern seriously in order to address the needs of C++ developers. The glad news is that we found the bug and are delivering a temporary workaround fix until a permanent solution is available.
The “Loader Lock issue” only affects C++ developers using the mixed-mode assembly feature. It has the potential to cause application deadlock in very particular scenarios. It does not pertain to Visual C++ developers writing strictly MFC, ATL, or even traditional Win32 code. It does not pertain to C# or Visual Basic developers as they do not have such a feature available to them.
It is important to note that while Microsoft is taking this issue seriously, the bug actually surfaces quite infrequently and affects developers only in very specific scenarios. I want to tell you about the bug so that you can learn whether it will affect you and your programs. This text is not intended to be a complete technical breakdown of the problem. Rather, these words are an introduction. Microsoft is also releasing technical documentation to satiate your need as a developer for such information.
If you are a C++ programmer using Visual C++ .NET 2002 or the new Visual C++ .NET 2003 to build managed components (assembly DLLs) to run on the .NET Common Language Runtime (CLR), you are one of the customers whom this will affect. This bug manifests itself strictly in assemblies containing both native x86 and managed MSIL code, and it exists in both Visual C++ .NET versions 2002 and 2003. Presently, this is not a bug that could be addressed by a service-pack. Its existence is due to an architectural bug.
Very few customers external to Microsoft have notified us of being bitten by the bug. (We have provided these customers with the information required to work around the bug.) This is not surprising as the bug surfaces relatively infrequently. However, in the interest of creating robust applications we encourage you to familiarize yourselves with the recommended workaround procedure and determine whether it should be applied to your code.
Internally several Microsoft teams have now seen the bug manifest only under testing scenarios involving severe system stress, or in C++ mixed-mode assemblies that have been digitally signed. It took our team three years to both discover the issue and realize its seriousness and scope.
The bug is not a security-class bug. It causes application deadlock when it occurs.
Today the bug occurs rarely. It is serious because it could begin to occur more frequently as changes are made to the Common Language Runtime (CLR), to Windows, or as the mixed-mode assembly technology is more widely adopted. The bug occurs somewhat unpredictably.
If you are an experienced Win32 developer you are likely aware of the restrictions placed on the code you write in the
DllMain entry-point function.
DllMain is called by the Windows loader to initialize the DLL. Documentation in the Windows SDK has long prescribed that
DllMain code shall not call API's outside of Kernel32, shall not launch threads, shall not load other DLLs, etc. However, in mixed-mode DLLs that have a
DllMain entry point defined, it is likely that some action will provoke the CLR to execute some of these illicit APIs during
DllMain. When this happens the potential exists for deadlock to occur.
Mixed-mode DLLs that link to the CRT or other libraries may have a "hidden"
We have taken steps to enable the Visual C++ .NET 2003 compiler to alert developers that they may be susceptible to the problem. If you attempt to compile a mixed-mode assembly that has a
DllMain defined, the compiler/linker will emit a warning saying this is an unsafe practice and that the assembly should be linked using the "/noentry" switch. Of course, if the assembly links to the C-runtime (a very common scenario), specifying "/noentry" will have the affect of a second warning being emitted. This circular set of warnings is designed and intended to point developers to documentation about this issue, and the steps required to correct it. The warning messages themselves will contain links to the appropriate documentation.
The workaround steps involve changes to your application's code. You will have to compile your assembly-DLL project with
/noentry, and you will have to run your DLL initialization code in a function other than
DllMain. The technical documentation for this bug describes the specific steps that need to be taken.
While this fix is relatively simple, the reason we were unable to implement a complete fix in Visual C++ .NET 2003 is because it involves architectural changes to several distinct technologies, including the C++ compiler and the CLR. Introducing such changes this late in our product cycle would have produced ripples that would potentially have destabilized other features in the release, and caused significant delays. Since this bug is localized to a subset of .NET developers, and further to a subset of C++ developers, we've decided to continue shipping with the issue. In our eyes these workaround steps aren't ideal, but should suffice in the pinch situation we're in. Here is the even better news: In the next major release of Visual C++ we intend to implement a more appetizing fix, which will involve disallowing any managed-code from ever executing during
DllMain (enforced by the compiler and the CLR). This fix, with some additional details, will correct the problem completely. To prepare your applications now, the recommendation is never to architect or write explicitly managed-code in
DllMain. A new “standard” entry point will be invented specifically to initialize managed code and data.
I’m sure by now many of you are wondering what sets this bug apart from any other bug you’ve ever encountered in a Microsoft product. Why wouldn’t Microsoft just quietly release the documentation about the bug and let developers figure it all out on their own? For one, this is truly a “compiler bug”. Some developers are quick to jump to the conclusion that a bug in their application is actually due to a “compiler bug”. Sometimes this turns out to be true, but most times it isn’t. But it is truly painful when there is a compiler bug, especially if you’re one of those developers who isn’t prone to suspecting the toolset. The Loader Lock issue is a compiler-level bug, and we don’t want you to lose any sleep over it. Fix your code to work around what is, admittedly, our issue, and move on.
Secondarily, I think this is a good chance to remind C++ developers about the often overlooked set of rules which pertain to writing code in DllMain. If Microsoft’s own architects and developers inadvertently overlooked these rules, then I’ll bet some of you have too.
That said, I want you all to spend 15 minutes to a) reread the documentation about what is permissible code in DllMain(), and b) inspect your own application’s
DllMain() functions. If you find offending code, and if fixing it corrects a known bug in your application, let me know. Make the story interesting enough and I’ll send you a t-shirt.
The 2nd piece of homework, and the point of all this, is to a) read and understand the technical documentation about this bug, b) determine if it affects your code c) fix it if it does.
Given that you’ve read this far, I’d like to now make an about-turn and make a short appeal on behalf of continued and new usage of the mixed-mode assembly feature. As I’ve noted in the previous 1200 words, the feature does have a bug that requires special attention to avoid. Does that render the feature useless? Should you avoid creating native/managed assemblies in C++? The answer to both questions is ‘no’.
The primary impetus of this feature is to enable the integration of existing C++-based applications with fully managed applications and to enable existing C++ libraries to be callable from managed code. Secondarily it enables you to extend the life and functionality of existing C++ code by enabling seamless consumption of managed libraries – much in the way C++ developers 6 years ago began consuming COM and ActiveX components in purely Win32 applications. There are simply too many valuable pre-made classes and interfaces in the .NET Framework to ignore. If you’re up for a 3rd piece of homework (extra credit), I challenge you to scan through the .NET Framework documentation for an interesting class, then consume that class in a C++ program. Pretty powerful technology, eh?
I hope that you won’t write-off using this feature simply because it has a blemish.
Semicolon was on a 3 month hiatus this winter and I hope not to have it disappear for so long again. I’d love to have something more positive to talk about next time, so if you have any burning questions that you’d like me to write about, by all means send them my way.