`

Managed Extensions for C++ Reference

阅读更多
Managed Extensions for C++ Reference
Converting Managed Extensions for C++ Projects from Pure Intermediate Language to Mixed Mode

 

 

 

Managed Extensions for C++ projects that are created as DLLs by default contain Microsoft intermediate language (MSIL) code that does not link to native C/C++ libraries such as the C run-time (CRT) library, ATL, or MFC, and does not use any static variables. Their code targets only the common language runtime.

This is done because linking with an entry point causes managed code to run during DllMain, which is not safe (see DllMain for the limited set of things you can do during its scope).

A DLL without an entry point has no way to initialize static variables except for very simple types such as integers. You should not normally have any static variables in a /NOENTRY DLL.

The ATL, MFC and CRT libraries all rely on static variables, so you also cannot use these libraries from within this DLL.

If your mixed-mode DLL needs to use statics or libraries that depend on statics (such as ATL, MFC, or CRT), then you must modify your DLL to have an explicit entry point.

To modify your DLL to have an explicit entry point, convert the managed DLL to mixed mode.

To convert the managed DLL to mixed mode

  1. Link with /NOENTRY. In Solution Explorer, right-click the project node and click Properties. In the project's Property Pages dialog box, click Linker, and then click Command Line. Add this switch to the Additional Options field.
  2. Link msvcrt.lib. In the project's Property Pages dialog box, click Linker, and then click Input. Add msvcrt.lib to the Additional Dependencies property.
  3. Remove nochkclr.obj. On the Input page (same page as previous step), remove nochkclr.obj from the Additional Dependencies property.
  4. Link in the CRT. On the Input page (same page as previous step), add __DllMainCRTStartup@12 to the Force Symbol References property.

Modifying Components That Consume the DLL for Manual Initializiation

After converting to mixed mode, you must modify components that consume the DLL for manual initialization, depending on the way that your DLL is implemented:

  • Your DLL is entered using DLL exports (__declspec(dllexport)), and your consumers cannot use managed code if they are linked statically or dynamically to your DLL.
  • Your DLL is a COM-based DLL.
  • Your DLL's consumers can use managed code, and your DLL contains either DLL exports or managed entry points.

To modify your DLL that is entered using DLL exports (__declspec(dllexport)) and consumers that cannot use managed code

  1. Add two new exports to your DLL:
    // init.cpp
        // Add these headers before the header with the using namespace System
        // directive, or add them in a .cpp file that does not have a
        // using namespace System directive.
        #include <windows.h>
        #include <_vcclrit.h>
        // Call this function before you call anything in this DLL.
        // It is safe to call from multiple threads, is not reference
        // counted, and is reentrancy safe.
        extern "C" __declspec(dllexport) void __stdcall DllEnsureInit(void)
        {
        // Do nothing else here. If you need extra initialization steps,
        // create static objects with constructors that perform
        // initialization.
        __crt_dll_initialize();
        // Do nothing else here.
        }
        // Call this function after this whole process is totally done
        // calling anything in this DLL. It is safe to call from multiple
        // threads, is not reference counted, and is reentrancy safe.
        // First call will terminate.
        extern "C" __declspec(dllexport) void __stdcall DllForceTerm(void)
        {
        // Do nothing else here. If you need extra terminate steps,
        // use atexit.
        __crt_dll_terminate();
        // Do nothing else here.
        }

    Add the following to the DLL .def file in the exports section:

    DllEnsureInit   PRIVATE
        DllForceTerm   PRIVATE

    Without these lines, if you have two DLLs that export functions, then the application linking to the DLL will have link errors. Typically, the exported functions will have the same names.

    In a multiconsumer case, each consumer can be linked statically or dynamically to your DLL.

  2. Your DLL can have several consumers.
  3. If the consumer is statically linked to the DLL, before the first use of your DLL or anything that depends on it in your application, add the following call:
    // Snippet 1
        typedef void (__stdcall *pfnEnsureInit)(void);
        typedef void (__stdcall *pfnForceTerm)(void);
        {
        // ... initialization code
        HMODULE hDll=::GetModuleHandle("mydll.dll");
        If(!hDll)
        {
        // exit, return; there is nothing else to do
        }
        pfnEnsureInit pfnDll=( pfnEnsureInit) ::GetProcAddress(hDll,
        "DllEnsureInit");
        if(!pfnDll)
        {
        // exit, return; there is nothing else to do
        }
        pfnDll();
        // ... more initialization code
        }
  4. After the last use of the DLL in your application, add the following code:
    // Snippet 2
        {
        // ... termination code
        HMODULE hDll=::GetModuleHandle("mydll.dll");
        If(!hDll)
        {
        // exit, return; there is nothing else to do
        }
        pfnForceTerm pfnDll=( pfnForceTerm) ::GetProcAddress(hDll,
        "DllForceTerm");
        if(!pfnDll)
        {
        // exit, return; there is nothing else to do
        }
        pfnDll();
        // ... more termination code
        }
  5. If the consumer is dynamically linked to the DLL, insert snippet 1 immediately after the first LoadLibrary for the DLL and insert snippet 2 immediately before the last FreeLibrary for the DLL.

To modify your DLL that is COM based

  • Modify the DLL export functions DllCanUnloadNow, DllGetClassObject, DllRegisterServer, and DllUnregisterServer as demonstrated in the following code:
    // Implementation of DLL Exports
        STDAPI DllCanUnloadNow(void)
        {
        HRESULT hrReturn=S_FALSE;
        // Function as usual
        // At this point hrReturn is S_OK if you can unload
        if(hrReturn == S_OK)
        {
        __crt_dll_terminate();
        }
        return hrReturn;
        }
        STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
        {
        // Do nothing here
        __crt_dll_initialize();
        // Continue with DllGetClassObject as before
        }
        STDAPI DllRegisterServer(void)
        {
        if ( !( __crt_dll_initialize() ) )
        {
        return E_FAIL;
        }
        // Call your registration code here
        HRESULT hr = <registration code>
        __crt_dll_terminate();
        return hr;
        }
        STDAPI DllUnregisterServer(void)
        {
        if ( !( __crt_dll_initialize() ) )
        {
        return E_FAIL;
        }
        // Call your unregistration code here
        HRESULT hr = <unregistration code>
        __crt_dll_terminate();
        return hr;
        }

To modify your DLL that contains consumers that use managed code and DLL exports or managed entry points.

  1. Implement a managed class with static member functions for initialization and termination. Add a .cpp file to your project, implementing a managed class with static members for initialization and termination:
    // ManagedWrapper.cpp
        // This code verifies that DllMain is not called by the Loader
        // automatically when linked with /noentry. It also checks some
        // functions that the CRT initializes.
        #include <windows.h>
        #include <stdio.h>
        #include <string.h>
        #include <stdlib.h>
        #include <math.h>
        #include "_vcclrit.h"
        #using <mscorlib.dll>
        using namespace System;
        public __gc class ManagedWrapper {
        public:
        static int minitialize() {
        int retval = 0;
        try {
        __crt_dll_initialize();
        } catch(System::Exception* e) {
        Console::WriteLine(e);
        retval = 1;
        }
        return retval;
        }
        static int mterminate() {
        int retval = 0;
        try {
        __crt_dll_terminate();
        } catch(System::Exception* e) {
        Console::WriteLine(e);
        retval = 1;
        }
        return retval;
        }
        };
        BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID
        lpvReserved) {
        Console::WriteLine(S"DllMain is called...");
        return TRUE;
        } /* DllMain */
  2. Call these functions before you refer to the DLL and after you have finished using it. Call the initialization and termination member functions in main:
    #using <mscorlib.dll>
        #using "ijwdll.dll"
        using namespace System;
        int main() {
        int retval = 0;
        retval += ManagedWrapper::minitialize();
        retval += ManagedWrapper::mterminate();
        return retval;
        }

 

 

 

创建 DLL 的 C++ 托管扩展项目默认包含 MSIL(微软中间语言)代码,这个代码并不与 C 运行时库(CRT),ATL 或 MFC 这样的本机 C/C++ 库链接,也不使用任何静态变量。其代码只面向公共语言运行时。
  之所以要这么做是因为带有入口点的链接导致 DllMain 期间运行托管代码,这样不安全(参见 DllMain 相关文档,了解在它执行期间你不能做那些事情)。
  不带 入口点的 DLL 无法初始化静态变量,非常简单的类型如整型除外。通常,在 /NOENTRY DLL 中,你不能有任何静态变量。
  ATL,MFC 和 CRT 库都依赖于静态变量,所以你也不能在该 DLL 中使用这些库。
如果你的混合模式 DLL必须使用静态变量或者依赖静态变量的库(如:ATL,MFC 或 CRT),那么你必须修改你的 DLL,使之具备外在入口点。
  为此,必须将托管 DLL 转换为混合模式。那么,

如何将将托管 DLL 转换为混合模式?

  1. 用 /NOENTRY 链接:在解决方案管理器中,在项目节点上单击右键并选择“属性”。在属性页对话框中选择“链接器”,然后选择 “命令行”。添加一个开关到“附加选项”编辑框。
  2. 链接 msvcrt.lib:在项目的属性页对话框中选择“连接器”,然后选择“输入”。将 msvcrt.lib 添加到“附加依赖项”。
  3. 删除 nochkclr.obj:在“输入”页面(与前一步骤相同的页面),从附加依赖项属性中删除 nochkclr.obj。
  4. 链接到 CRT:在“输入”页面(与前一步骤相同的页面),将 __DllMainCRTStartup@12 添加到“强制符号引用”属性中。

修改使用DLL的代码部分,进行手动初始化

  转换成混合模式之后,你必须修改使用DLL的代码部分,根据你的DLL实现方式进行手动初始化:

  • 你的 DLL 使用 __declspec(dllexport) 输出,并且 DLL 的使用者与 DLL 之间的链接不管是静态链接的还是动态的,那么 DLL 的使用者都无法使用托管代码。
  • 你的 DLL 是基于 COM 的 DLL
  • 你的 DLL 的调用者可以用托管代码,并且你的 DLL 包含 DLL 输出或托管入口点

用 __declspec(dllexport) 输出且调用者无法使用托管代码的 DLL 的修改方法:

  1. 向 DLL 添加两个新的输出:
    // init.cpp
    // 在 using namespace System 指令头之前添加这些头文件,
    // 或者在没有using namespace System 指令头的 .cpp 文件中添加它们
    #include <windows.h>
    #include <_vcclrit.h>
    
    // 在你调用任何该 DLL 中的东西之前调用该函数。
    // 从多线程中调用才安全,并非引用安全,而是重入安全
    
    extern "C" __declspec(dllexport) void __stdcall DllEnsureInit(void)
    {
    	// 在这里什么也不要做,如果你需要额外的初始化步骤,
    	// 创建带有构造函数的静态对象,在构造函数中完成初始化。
    	__crt_dll_initialize();
    	// 在这里什么也不要做。
    }
    
    // 在整个进程彻底调用完该 DLL 后调用该函数。从多线程中调用才安全。
    // 并非引用安全,而是重入安全。第一次调用将终止。
    
    extern "C" __declspec(dllexport) void __stdcall DllForceTerm(void)
    {
    	// 在这里什么也不要做,如果你需要额外的终止步骤,
    	// Do nothing else here. If you need extra terminate steps, 
    	// 使用 atexit.
    	__crt_dll_terminate();
    	// 在这里什么也不要做。
    }        
    将下面代码添加到 DLL .def 文件的 “exports” 部分:
    DllEnsureInit PRIVATE
    DllForceTerm PRIVATE

    如果没有这两行,那么当你有两个 DLL 都输出函数时,链接到该 DLL 的应用程序将会出现链接错误。典型的错误是输出的函数名字相同。
    在有多个DLL调用者时,每个调用者都可以和你 DLL 进行静态或动态链接。

  2. 你的 DLL 可以有多个调用者
  3. 如果调用者与该 DLL 静态链接,那么在应用程序中首次使用你的 DLL 或者依赖它的任何东西之前,要添加如下调用:
    // 代码段一
    
    typedef void (__stdcall *pfnEnsureInit)(void);
    typedef void (__stdcall *pfnForceTerm)(void);
    
    {
    	// ... 初始化代码
    	HMODULE hDll=::GetModuleHandle("mydll.dll");
    	If(!hDll)
    	{
    		// 退出,返回,再没有什么要做的了
    	}
    	pfnEnsureInit pfnDll=( pfnEnsureInit) ::GetProcAddress(hDll, 
    		"DllEnsureInit");
    	if(!pfnDll)
    	{
    		// 退出,返回,再没有什么要做的了
    	}
    	
    	pfnDll();
    	
    	// ... 更多的初始化代码
    }        
  4. 在应用程序最后一次使用 DLL 之后,添加下面的代码:
    // 代码段二
    
    {
    	// ... 终止代码
    	HMODULE hDll=::GetModuleHandle("mydll.dll");
    	If(!hDll)
    	{
    		// 退出,返回,再没有什么要做的了
    	}
    	pfnForceTerm pfnDll=( pfnForceTerm) ::GetProcAddress(hDll, 
    		"DllForceTerm");
    	if(!pfnDll)
    	{
    		// 退出,返回,再没有什么要做的了
    	}
    	
    	pfnDll();
    	
    	// ... 更多的终止代码
    }        
  5. 如果调用者是动态链接到此 DLL,将代码段一插入到第一次调用 LoadLibrary 之后,同时将代码段二插入到最后一次调用 FreeLibrary 之前。

基于 COM 的 DLL 的修改方法

  修改 DllCanUnloadNow,DllGetClassObject,DllRegisterServer 和 DllUnregisterServer 输出函数的方法如下:

// 实现 DLL 输出

STDAPI DllCanUnloadNow(void)
{
	HRESULT hrReturn=S_FALSE;
	// Function as usual
	// At this point hrReturn is S_OK if you can unload
	if(hrReturn == S_OK)
	{
		__crt_dll_terminate();
	}
	return hrReturn;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
	// 在这里什么也不要做。
	__crt_dll_initialize();
	// 像从前那样继续 DllGetClassObject
}

STDAPI DllRegisterServer(void)
{
	if ( !( __crt_dll_initialize() ) )
	{
		return E_FAIL;
	}
	// 在这里调用注册代码
	HRESULT hr = <registration code>
		__crt_dll_terminate();
	return hr;
}

STDAPI DllUnregisterServer(void)
{
	if ( !( __crt_dll_initialize() ) )
	{
		return E_FAIL;
	}
	// 在这里调用注销代码
	HRESULT hr = <unregistration code>
		__crt_dll_terminate();
	return hr;
}
      

你的 DLL 包含调用者,该调用者使用托管代码以及 DLL 输出或者托管入口点,修改方式如下:

 

  1. 实现一个带有静态成员函数的托管类,静态成员函数用于初始化终止例程。往项目中添加一个 .cpp 文件:
    // ManagedWrapper.cpp
    
    // 这个代码验证当使用 /NOENTRY 链接选项时,DllMain 没有被 Loader 自动调用。
    // 它也检查某些 CRT 初始化函数。
    
    #include <windows.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    #include "_vcclrit.h"
    
    #using <mscorlib.dll>
    using namespace System;
    
    public __gc class ManagedWrapper {
    public:
    	static int minitialize() {
    		int retval = 0;
    		try {
    			__crt_dll_initialize();
    		} catch(System::Exception* e) {
    			Console::WriteLine(e);
    			retval = 1;
    		}
    		return retval;
    	}
    	static int mterminate() {
    		int retval = 0;
    		try {
    			__crt_dll_terminate();
    		} catch(System::Exception* e) {
    			Console::WriteLine(e);
    			retval = 1;
    		}
    		return retval;
    	}
    };
    
    BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID 
    					lpvReserved) {
    	Console::WriteLine(S"DllMain is called...");
    	return TRUE;
    } /* DllMain */
    
  2. 在引用 DLL 之前和使用完 DLL 之后,在 main 中调用这些初始化例程和终止例程成员函数:
    #using <mscorlib.dll>
    #using "ijwdll.dll"
    
    using namespace System;
    
    int main() {
    	int retval = 0;
    	retval += ManagedWrapper::minitialize();
    	retval += ManagedWrapper::mterminate();
    	return retval;
    }        
分享到:
评论

相关推荐

    Managed Extensions for C++ Specification.doc

    《Managed Extensions for C++ 规范》文档详细阐述了Visual C++编程语言的管理扩展。这些扩展是为了支持通用类型系统和通用语言运行时而设计的一系列特性。它们与C++ ISO标准兼容,这意味着开发者可以在遵循标准的...

    Managed extensions for VCL for Delphi & C++Builder 6-2010 Cracked

    Managed VCL is the best components suite to work with .Net framework from Delphi and C++ Builder - feature named .Net interop. It is designed to provide a way to interact with applications written in ...

    Managed VCL for C++Builder

    Managed VCL是一款.Net Framework下的Delphi和C++ Builder组件程序To provide a consistent object-oriented programming environment whether object code is stored and executed locally, executed locally but ...

    .NET Programming with Visual C++: Tutorial, Reference, and Immediate Solutions

    CHAPTER 2: Managed Extensions to C++ CHAPTER 3:. NET System Namespace: Arrays, Strings, Delegates And Other Essential Classe CHAPTER 4: NET System Namespace: Attributes, Garbage Collector, and Other ...

    Visual.C++.NET托管扩展编程英文版

    Microsoft Visual C++ .NET is an existing language that has been extended to produce .NET code, and these extensions are called the Managed Extensions for C++. The Managed Extensions allow C++ ...

    Oracle Data Provider for .NET, Managed Driver for ODAC 12c Release 4

    Oracle Data Provider for .NET, Managed Driver for ODAC (Oracle Data Access Components) 12c Release 4 是Oracle公司为.NET Framework 4.5设计的一款数据访问驱动程序,它提供了全面的功能,使开发者能够构建...

    Visual C++_NET实践与提高-深入Windows编程

    书中的这部分内容会讲解如何使用C++/CLI编写.NET代码,以及Managed Extensions for C++的使用。 六、异常处理和内存管理 在.NET环境中,异常处理和内存管理与传统的C++有所不同。本书将详细介绍如何在.NET环境下...

    Mixing Native and Managed Types in C++.

    在C++编程中,混合使用原生(Native)和托管(Managed)类型是一个常见的需求,尤其是在处理跨平台或与现有代码库集成时。C++/CLI(C++ Common Language Infrastructure)是微软为.NET Framework提供的一种语言扩展...

    C#、托管C++、非托管C++编程

    2. **托管C++**:托管C++(也称为Managed Extensions for C++)是C++的一个扩展,允许开发者在.NET框架内编写代码。它结合了C++的语法和.NET的特性,如对象管理、垃圾回收以及对.NET类库的访问。托管C++是连接C++与...

    Microsoft?Visual?C++?.NET技术内幕_C++_visualc_

    本书主要聚焦于C++/CLI(Managed Extensions for C++)和MFC(Microsoft Foundation Classes)在.NET环境中的运用,旨在帮助开发者充分利用C++的强大功能,同时享受到.NET Framework带来的便利。 C++是一种静态类型...

    Begining Visual C++ 2005

    在.NET框架下,Visual C++ 2005引入了 Managed Extensions for C++,这是一种让C++开发者能够编写托管代码的方式,从而可以利用.NET的许多特性,如垃圾回收、类型安全和跨语言互操作性。然而,对于不习惯这些新特性...

    c++/cli language cli语言英文版

    12. **Managed Extensions for C++**:C++/CLI不仅提供了语言扩展,还引入了一套新的库,如System::命名空间,包含了许多.NET框架类。 13. **Metadata**:每个CLI组件都包含元数据,描述了组件的结构、类型和成员,...

    C++/CLI for C# Developers

    本书《Pro Visual C++2005 for C# Developers: Featuring C++/CLI》由 Dean C. Wills 编写,并得到了微软 Visual C++ 团队成员 Jason Shirk 的推荐序言。 #### 二、C++/CLI 与 Managed C++ 的区别 ##### 2.1 ...

    Visual C++ 2008和2010 入门经典源代码

    2008版的Visual C++引入了对.NET Framework 3.5的支持,这意味着开发者可以利用C++/CLI(Managed Extensions for C++)来编写.NET应用程序,同时保留了传统的本地C++编程能力。此外,2008版还增强了对C++0x标准...

    visual c++.net技术内幕

    在C++.NET中,最重要的变化之一是引入了Managed Extensions for C++,这使得C++开发者能够利用.NET Framework的全部功能,例如垃圾回收、类型安全和跨语言互操作性。书中的内容可能涵盖了这些概念,解释了如何在C++...

    Visual.C++.2010开发权威指南].尹成.扫描版.zip

    4. **CLR(Common Language Runtime)与C++/CLI**:Visual C++ 2010支持.NET框架,通过C++/CLI(Managed Extensions for C++)可以编写托管代码。这部分内容会涵盖如何在C++中利用.NET功能,如泛型、垃圾回收、类型...

    microsoft Visual C++ 2005 sp1.rar

    3. **.NET Framework支持**:Visual C++ 2005能够创建.NET Framework下的托管代码,支持C++/CLI(Managed Extensions for C++),使得开发者可以利用.NET平台的优势。 4. **C++ Standard Library**:完全支持C++...

    DirectX for Managed Code

    DirectX for Managed Code是微软为.NET开发者提供的一套API,旨在让C#和其他.NET语言的程序员能够充分利用DirectX的功能来创建高性能的图形和多媒体应用程序。DirectX是一个包含多个子组件的集合,主要用于游戏开发...

    Visual C++.NET 数据库开发指南

    在.NET环境中,C++可以使用Managed Extensions for C++(也称为MC++)或者现代的C++/CLI来编写.NET应用程序。本书可能首先介绍了这两种语言版本的基础知识,包括语法特性、类型系统以及垃圾回收机制,以确保读者能够...

Global site tag (gtag.js) - Google Analytics