Mixed-mode library Assembly bug and managed C ++

Source: Internet
Author: User
Tags microsoft c
Recently, C ++ has developed a framework program supporting plug-in development. Later, I found that the function is beyond my expectation and I really want to borrow C # controls, WPF, and ActiveX from the plug-in. I have read several articles in codeproject, embedded the WPF control in the plug-in dialog box, and modified the project properties to support CLR. Compilation passed. Everything is normal .... however, an error is reported during running. net Runtime Library plug-in fails to load, the error is: "attemp to use msil code from this Assembly during native code initialization", Goole a moment, found that for me such a only understand C ++, it is not easy for C # colleagues to solve the whole problem. My plug-in extends the dynamic link library for MFC.. Net controls are compiled with the CLR option and run fails (. net ActiveX should not be affected.) paste the well-known Dr Dobbs article. If any of them can solve the problem in a simple way, please do not hesitate to inform me. Thank you! Mixed-mode library Assembly bug and managed C ++

By Richard Grimes, September 01,200 3

0 comments

The mixed-mode Assembly bug occurs when managed code runs in a DLL entrypoint (typically, dllmain ). vs. NET 2003 will issue a warning for this bug, but code containing it will still compile. richard takes a look at the inner workings of. net Framework to explain what causes this warning and gives some advice on how you can handle it.

 

Download the code for this issue

 

In the May editionWindows developer, John dorsey talked about a recent bug discovered in the Microsoft C ++ Compiler ("from the editor ,"WDN, May 2003). This month, I will take a deeper look at this issue and explain some of the background.

 

The mixed-mode Assembly bug occurs when managed code runs in a DLL entrypoint (typically,Dllmain). The entrypoint is only intended for simple initialization and the msdn documentation gives a list of the functions that are considered to be unsafe to call, but even the most trivial. net initialization code is dangerous to perform because using the framework is likely to entail one of these unsafe actions. the bug typically manifests itself in the DLL routine hanging, and if you are lucky it will be detected by the operating system, which will shut down the errant process.

 

This bug shows itself in specific, nontypical situations, but it is a potentially serious bug. the Visual C ++ team shoshould be credited for bringing the bug to light and for providing a workaround to prevent the problem from occurring in most of the Code. it is important to note that the remedial work carried out by the visual C ++ team did not appear in the Betas for Visual C ++. NET 2003, but does appear in the released version. consequently, texts written about visual C ++. NET 2003 during the Visual Studio beta cycle (including my own book from Microsoft Press) do not mention the issue. as you'll see later, if you do not take steps to try to prevent the bug, the compiler will be issue a warning but will continue to compile the code. I welcome the chance to be able to explain ain what causes this warning and to describe what to do about the problem.

 

 

The mixed-mode compiler bug

 

The mixed-mode loader bug appears only in code written in managed C ++. this is a direct consequence of the powerful nature of the language. to extend the familiar maxim: managed C ++ gives you a fully loaded Magnum, whereas the other. net versions ages merely hand you a half empty water pistol; both will allow you to shoot yourself in the foot, but only managed C ++ allows you to do serious damage.

 

One of the great features of managed C ++ is that it allows you to mix managed and unmanaged code in the same source file using a technology called it just works! (Ijw, so named because it justDoes). Ijw is applied automatically when you use unmanaged code in your managed C ++ source; you do not have to instruct the compiler to apply it. when the compiler sees that your code will access unmanaged code, it adds the thunks needed to perform the transitions between the managed and unmanaged worlds and it embeds appropriate native code in your assembly. ijw is a wonderful facility because it means that you can continue to use the C ++ static libraries, template libraries, and header files that you have developed previusly (and more importantly, code that you have gone through the expense of testing), as well as DLLs and COM objects developed in C ++ or other ages.

 

When you access unmanaged code through ijw, your Assembly becomes mixed mode, so-called because the Assembly contains both x86 code and Microsoft intermediate language (msil ). assemblies created with other. net versions that use unmanaged code are not mixed mode because they use platform invoke or com InterOP to access the unmanaged code, and these technologies do not embed unmanaged code within the Assembly.

 

 

Converting mixed-mode assemblies to pure mode

 

A pure-mode assembly is one that only contains msil. you can generate such an assembly with managed C ++ if you decide not to use ijw. however, it is not as simple as replacing ijw callto static import libraries with callto platform invoke because the compiler does extra work that you have to undo. this extra work performed med by the compiler is not immediately obvious because there are several conflicting features that the compiler is trying to achieve.

 

Mixed-mode library assemblies must haveDllmainEntry point to perform initialization for the unmanaged code. typically, such code will be required to initialize the C Runtime Library (CRT) to use the CRT functions or to be able to use global objects (the CRT is used to call constructors on global objects ). such actions are typical for native C ++ projects, so the compiler automatically assumes that you will need the CRT (there is another good reason for assuming this, as you'll see in a moment ). to do this, the compiler adds an unmanaged function called_ DllmaincrtstartupAnd indicates that this is an entrypoint for the DLL. This method does the appropriate initialization and then callyourDllmain, If it exists. Thus, even if you do not provideDllmain, You'll still get an unmanaged entrypoint.

 

If you provideDllmainFor your library assembly and compile it as managed code (in other words, you do not place# Pragma unmanagedAnd# Pragma managedAround the function), you are putting your library in a dangerous position. The reason is that the Code will be compiled to msil, which means that to call yourDllmain,. Net runtime must be running. if your DLL client is unmanaged, then the runtime will have to be started. if the Assembly is loaded on a Windows XP system, the OS will identify that the DLL it is loading is an assembly and it will start. net runtime automatically. on other operating systems, the address defined as the entry point for the DLL will be called (you can see the address of the DLL entrypoint by passing the name of the DLL to dumpbin with the/headers switch) and this entrypoint will simply call_ CordllmainFunction exported by mscoree. dll, which will initialize the. NET runtime and then call the code that is defined as the. NET entrypoint In the DLL (typically_ Dllmaincrtstartup). In both cases,. Net code will be executed in the context of the initialization of the DLL, a situation that is potentially dangerous.

 

Recognizing that mixed-mode assemblies are a potential problem, the visual C ++ team added an extra check when the code is compiled. if the Assembly is mixed mode and you do not take the steps to remove the entrypoint, the compiler will issue a warning lnk1_3:

 

Link: Warning lnk1_3: DLL containing objects compiled with/CLR is not linked with/noentry; image may not run correctly

 

 

 

If you do not use unmanaged code in your library assembly, then you do not need an entrypoint, so the solution is simply to tell the linker not to produce one. to do this, you use the/noentry linker switch. however, if you do this with a managed C ++ DLL project, you'll get a linker error lnk2019 complaining that there is an unresolved external symbol_ Main. Seasoned ATL developers will recognize this error: It occurs when ATL Code uses the CRT but is compiled_ Atl_min_crt. This ATL symbol indicates to the compiler that the CRT is not used; hence, the linker complains about the inconsistency. the solution with ATL is to remove the symbol or to remove the CRT code. since your library assembly has no CRT code, removing it from your library seems impossible. or is it?

 

 

Application domains and unmanaged CILS

 

To understand this error, You have to delve into another issue of. net runtime highlighted by unmanaged callthrough managed C ++ .. net code runs in an application domain in a process and a process can have multiple application domains. with the first version of the runtime, a bug occurred when a call was made to unmanaged code. the problem was that the runtime failed to make note of the application domain where the call occurred. consequently, when the call returned back to the managed World, the runtime did not know which application domain to return. most processes did not show this problem because most. NET applications only have a single application domain. however, Asp. net and Internet Explorer (when it hosts. net code) Both create application domains to isolate code.

 

To get around this problem, the first version of the runtime always returned back to the first application domain to be created in the process. version 1.1 of the runtime addressed this issue by storing an identifier of the source application domain. however, since this behavior differed from Version 1.0, the C ++ team offered a command-line switch (/CLR: initialappdomain) to get the old behavior. in addition, the compiler cocould not guarantee that a user wocould attempt to call a version 1.1 library on version 1.0 of the runtime. if this happened, it cocould cause severe problems because the msil to store the Application ID simply does not exist with the old version of the runtime. to prevent this the compiler automatically adds a call to a method called_ Check_common1_ageruntime_version, Which is implemented by the CRT (but more importantly, it is unmanaged code) and is called when the DLL is loaded.

 

If you know that your code will run on version 1.0 of the runtime, or if you want to get the version 1.0 behavior, you can use/CLR: initialappdomain and the runtime check will not be made-hence/noentry will link without a unresolved symbol error. if you use/CLR to get the version 1.1 behavior, then you'll have to take extra steps to remove the dependence on the unmanaged code to check the version of the runtime. microsoft provides a stub version_ Check_common1_ageruntime_versionIn the object file nochkclr. OBJ. however, although this is an empty version of the function, it is still unmanaged code and so you will get a mixed-mode library assembly. the solution here is to remove any references to nochkclr. OBJ in your build tool (Visual Studio. NET 2003 will add it to the additional dependencies of the linker input P roperty page) and to provide your own, nonnative, empty version of the runtime checker function:

 

// Global, managed Function

 

Void _ cdecl _ check_common1_ageruntime_version ()

 

{}

 

 

Mixed-mode library assemblies

 

If you decide that you need to generate a mixed-mode library assembly (for example, you want to call a static native library or template library ), then you will have to take steps to reduce the problem of the DLL loader bug. to do this, you need to use/noentry to remove the entrypoint, and remove nochkclr. OBJ from the linker so that you get the runtime version checking. removing the entrypoint means that you will have to force the linker to use_ Dllmaincrtstartup @ 12Symbol in its symbol table with the/include switch. To summarize, the switches that you will need are:

 

CL command line:/CLR/LD

 

Linker command line:/noentry

 

/Include :__ dllmaincrtstartup @ 12

 

 

 

However, there is still more work to do. the CRT is still not initialized and it is your responsibility to do this explicitly, in the context of your DLL. microsoft has provided a header (_ vcclrit. h) that has initialization and termination routines that you can use to call your DLL initialization code (_ Dllmaincrtstartup) In a threadsafe manner. These routines will be compiled as managed code, but they will always be called after the DLL has loaded.

 

These functions are called_ Crt_dll_initialize (), Which callthe_ DllmaincrtstartupWithDll_process_attach; And_ Crt_dll_terminate (), Which callthe_ DllmaincrtstartupWithDll_process_detach. How you give access to these routines depends on the capability of the DLL's client. if the process calling the DLL is managed, then the DLL shocould export a class with public static methods that call these routines. if the process is unmanaged, your DLL shocould export functions_ Declspec (dllexport)That call these routines (remember that managed C ++ global functions are compiled as msil ).

 

In both cases, the initialization function shocould be called before any call is made to other classes in the DLL, And the terminate function shocould be called after the last call to the DLL. this way, the initialization code in_ Dllmaincrtstartup(And any code that you have provided inDllmain) Will be called after the DLL has been loaded and initialized (hence, the loader deadlock issue will not occur) and after the. NET runtime has been initialized.

 

The mixed-mode library Assembly bug will not be eliminated until the next version of. net runtime. however, I am sure that you will agree that digging deeper into the causes of the bug gives fascinating insight into how DLLs are loaded and initialized, and into the inner workings of. net runtime.

 

 

References

 

A description of the mixed-mode loader bugs: http://msdn.microsoft.com/library/default.asp? Url =/library/enus/dv_vstechart/html/vcconmixeddllloadingproblem. asp.

 

Instructions for creating mixed-mode library assemblies without an entrypoint: http: // msdn example.

 

A description of the new/CLR: initialappdomain switch: http://www.windevnet.com/documents/win1039544102348/. W: d

 

Richard Grimes is an author and speaker on. net. his latest book, programming with managed extensions for Microsoft Visual C ++. net, updated for Visual C ++. NET 2003, is available now from Microsoft press. he can be contacted at richard@richardgrimes.com.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.