CLR寄宿——C++调用C# dll


在看到CLR寄宿的内容的时候,研究了一下非托管C++启用CLR的过程。在MSDN sample gallary中看到了一个很好的例子。直接把代码贴到下面(略作了一些修改)。生怕以后找不到了。另外,从C++传送到C#方法的参数只试验过int, double, wchar_t*(字符串)。其他的没有做过实验。


C++代码 

//
//    FUNCTION: RuntimeHostV4Demo1(PCWSTR, PCWSTR)
//
//    Invoking way: RuntimeHostV4Demo1(L"v4.0.30319", L"CSNET2ClassLibrary", L"CSNET2ClassLibrary.CSSimpleObject");
//
//    PURPOSE: The function demonstrates using .NET Framework 4.0 Hosting 
//    Interfaces to host a .NET runtime, and use the ICorRuntimeHost interface
//    that was provided in .NET v1.x to load a .NET assembly and invoke its 
//    type. 
//    
//    If the .NET runtime specified by the pszVersion parameter cannot be 
//    loaded into the current process, the function prints ".NET runtime <the 
//    runtime version> cannot be loaded", and return.
//    
//    If the .NET runtime is successfully loaded, the function loads the 
//    assembly identified by the pszAssemblyName parameter. Next, the function 
//    instantiates the class (pszClassName) in the assembly, calls its 
//    ToString() member method, and print the result. Last, the demo invokes 
//    the public static function 'int GetStringLength(string str)' of the class 
//    and print the result too.
//
//    PARAMETERS:
//    * pszVersion - The desired DOTNETFX version, in the format “vX.X.XXXXX”. 
//      The parameter must not be NULL. It’s important to note that this 
//      parameter should match exactly the directory names for each version of
//      the framework, under C:\Windows\Microsoft.NET\Framework[64]. The 
//      current possible values are "v1.0.3705", "v1.1.4322", "v2.0.50727" and 
//      "v4.0.30319". Also, note that the “v” prefix is mandatory.
//    * pszAssemblyName - The display name of the assembly to be loaded, such 
//      as "CSClassLibrary". The ".DLL" file extension is not appended.
//    * pszClassName - The name of the Type that defines the method to invoke.
//
//    RETURN VALUE: HRESULT of the demo.
//
HRESULT RuntimeHostV4Demo1(PCWSTR pszVersion, PCWSTR pszAssemblyName, 
    PCWSTR pszClassName)
{
    HRESULT hr;

    ICLRMetaHost *pMetaHost = NULL;
    ICLRRuntimeInfo *pRuntimeInfo = NULL;

     //  ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting interfaces
    
//  supported by CLR 4.0. Here we demo the ICorRuntimeHost interface that 
    
//  was provided in .NET v1.x, and is compatible with all .NET Frameworks. 
    ICorRuntimeHost *pCorRuntimeHost = NULL;

    IUnknownPtr spAppDomainThunk = NULL;
    _AppDomainPtr spDefaultAppDomain = NULL;

     //  The .NET assembly to load.
    bstr_t bstrAssemblyName(pszAssemblyName);
    _AssemblyPtr spAssembly = NULL;

     //  The .NET class to instantiate.
    bstr_t bstrClassName(pszClassName);
    _TypePtr spType = NULL;
    variant_t vtObject;
    variant_t vtEmpty;

     //  The static method in the .NET class to invoke.
    bstr_t bstrStaticMethodName(L " GetStringLength ");
    SAFEARRAY *psaStaticMethodArgs = NULL;
    variant_t vtStringArg1(L " HelloWorld ");
    variant_t vtStringArg2( 18);
    variant_t vtStringArg3( 123.321);
    variant_t vtLengthRet;

     //  The instance method in the .NET class to invoke.
    bstr_t bstrMethodName(L " ToString ");
    SAFEARRAY *psaMethodArgs = NULL;
    variant_t vtStringRet;

     //  
    
//  Load and start the .NET runtime.
    
//  

    wprintf(L " Load and start the .NET runtime %s \n ", pszVersion);

    hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
     if (FAILED(hr))
    {
        wprintf(L " CLRCreateInstance failed w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Get the ICLRRuntimeInfo corresponding to a particular CLR version. It 
    
//  supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
    hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo));
     if (FAILED(hr))
    {
        wprintf(L " ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Check if the specified runtime can be loaded into the process. This 
    
//  method will take into account other runtimes that may already be 
    
//  loaded into the process and set pbLoadable to TRUE if this runtime can 
    
//  be loaded in an in-process side-by-side fashion. 
    BOOL fLoadable;
    hr = pRuntimeInfo->IsLoadable(&fLoadable);
     if (FAILED(hr))
    {
        wprintf(L " ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     if (!fLoadable)
    {
        wprintf(L " .NET runtime %s cannot be loaded\n ", pszVersion);
         goto Cleanup;
    }

     //  Load the CLR into the current process and return a runtime interface 
    
//  pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting  
    
//  interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost 
    
//  interface that was provided in .NET v1.x, and is compatible with all 
    
//  .NET Frameworks. 
    hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, 
        IID_PPV_ARGS(&pCorRuntimeHost));
     if (FAILED(hr))
    {
        wprintf(L " ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Start the CLR.
    hr = pCorRuntimeHost->Start();
     if (FAILED(hr))
    {
        wprintf(L " CLR failed to start w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  
    
//  Load the NET assembly. Call the static method GetStringLength of the 
    
//  class CSSimpleObject. Instantiate the class CSSimpleObject and call 
    
//  its instance method ToString.
    
//  

    
//  The following C++ code does the same thing as this C# code:
    
//  
    
//    Assembly assembly = AppDomain.CurrentDomain.Load(pszAssemblyName);
    
//    object length = type.InvokeMember("GetStringLength", 
    
//        BindingFlags.InvokeMethod | BindingFlags.Static | 
    
//        BindingFlags.Public, null, null, new object[] { "HelloWorld", 18, 123.321 });
    
//    object obj = assembly.CreateInstance("CSClassLibrary.CSSimpleObject");
    
//    object str = type.InvokeMember("ToString", 
    
//        BindingFlags.InvokeMethod | BindingFlags.Instance | 
    
//        BindingFlags.Public, null, obj, new object[] { });

    
//  Get a pointer to the default AppDomain in the CLR.
    hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
     if (FAILED(hr))
    {
        wprintf(L " ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

    hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
     if (FAILED(hr))
    {
        wprintf(L " Failed to get default AppDomain w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Load the .NET assembly.
    wprintf(L " Load the assembly %s\n ", pszAssemblyName);
    hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
     if (FAILED(hr))
    {
        wprintf(L " Failed to load the assembly w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Get the Type of CSSimpleObject.
    hr = spAssembly->GetType_2(bstrClassName, &spType);
     if (FAILED(hr))
    {
        wprintf(L " Failed to get the Type interface w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Call the static method of the class: 
    
//    public static int GetStringLength(string str);

    
//  Create a safe array to contain the arguments of the method. The safe 
    
//  array must be created with vt = VT_VARIANT because .NET reflection 
    
//  expects an array of Object - VT_VARIANT. There is only one argument, 
    
//  so cElements = 1.
    psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT,  03);
    LONG index =  0;
    hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg1);
    index =  1;
    hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg2);
    index =  2;
    hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg3);
     if (FAILED(hr))
    {
        wprintf(L " SafeArrayPutElement failed w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Invoke the "GetStringLength" method from the Type interface.
    hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast<BindingFlags>(
        BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public), 
        NULL, vtEmpty, psaStaticMethodArgs, &vtLengthRet);
     if (FAILED(hr))
    {
        wprintf(L " Failed to invoke GetStringLength w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Print the call result of the static method.
    wprintf(L " Call %s.%s(\"%s\") => %d\n "
        static_cast<PCWSTR>(bstrClassName), 
        static_cast<PCWSTR>(bstrStaticMethodName), 
        static_cast<PCWSTR>(vtStringArg1.bstrVal), 
        vtLengthRet.lVal);

     //  Instantiate the class.
    hr = spAssembly->CreateInstance(bstrClassName, &vtObject);
     if (FAILED(hr))
    {
        wprintf(L " Assembly::CreateInstance failed w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Call the instance method of the class.
    
//    public string ToString();

    
//  Create a safe array to contain the arguments of the method.
    psaMethodArgs = SafeArrayCreateVector(VT_VARIANT,  00);

     //  Invoke the "ToString" method from the Type interface.
    hr = spType->InvokeMember_3(bstrMethodName, static_cast<BindingFlags>(
        BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public),
        NULL, vtObject, psaMethodArgs, &vtStringRet);
     if (FAILED(hr))
    {
        wprintf(L " Failed to invoke ToString w/hr 0x%08lx\n ", hr);
         goto Cleanup;
    }

     //  Print the call result of the method.
    wprintf(L " Call %s.%s() => %s\n "
        static_cast<PCWSTR>(bstrClassName), 
        static_cast<PCWSTR>(bstrMethodName), 
        static_cast<PCWSTR>(vtStringRet.bstrVal));

Cleanup:

     if (pMetaHost)
    {
        pMetaHost->Release();
        pMetaHost = NULL;
    }
     if (pRuntimeInfo)
    {
        pRuntimeInfo->Release();
        pRuntimeInfo = NULL;
    }
     if (pCorRuntimeHost)
    {
         //  Please note that after a call to Stop, the CLR cannot be 
        
//  reinitialized into the same process. This step is usually not 
        
//  necessary. You can leave the .NET runtime loaded in your process.
        
// wprintf(L"Stop the .NET runtime\n");
        
// pCorRuntimeHost->Stop();

        pCorRuntimeHost->Release();
        pCorRuntimeHost = NULL;
    }

     if (psaStaticMethodArgs)
    {
        SafeArrayDestroy(psaStaticMethodArgs);
        psaStaticMethodArgs = NULL;
    }
     if (psaMethodArgs)
    {
        SafeArrayDestroy(psaMethodArgs);
        psaMethodArgs = NULL;
    }

     return hr; }

 

C#代码 

namespace CSNET2ClassLibrary
{
     public  class CSSimpleObject
    {
         ///   <summary>
        
///  Constructor
        
///   </summary>
         public CSSimpleObject()
        {
        }

         private  float fField = 0F;

         ///   <summary>
        
///  This is a public Property. It allows you to get and set the value 
        
///  of a float field.
        
///   </summary>
         public  float FloatProperty
        {
             get {  return fField; }
             set
            {
                 //  Fire the event FloatPropertyChanging
                 bool cancel =  false;
                 if (FloatPropertyChanging !=  null)
                {
                    FloatPropertyChanging(value,  out cancel);
                }

                 //  If the change is not canceled, make the change.
                 if (!cancel)
                {
                    fField = value;
                }
            }
        }

         ///   <summary>
        
///  Returns a String that represents the current Object. Here, we 
        
///  return the string form of the float field fField.
        
///   </summary>
        
///   <returns> the string form of the float field fField. </returns>
         public  override  string ToString()
        {
             return  this.fField.ToString( " F2 ");
        }

         ///   <summary>
        
///  This is a public static method. It returns the number of 
        
///  characters in a string.
        
///   </summary>
        
///   <param name="str"> a string </param>
        
///   <returns> the number of characters in the string </returns>
         public  static  int GetStringLength( string str,  string length)
        {
             return (str.Length + Int32.Parse(length));
        }

         public  static  int GetStringLength( string str,  int length,  double somevalue)
        {
            Console.WriteLine(str +  "   " + length.ToString() +  "   " + somevalue.ToString() );
             return (str.Length + length + ( int)somevalue);
        }

         ///   <summary>
        
///  This is an event. The event is fired when the float property is 
        
///  set.
        
///   </summary>
         public  event PropertyChangingEventHandler FloatPropertyChanging;
    }


     ///   <summary>
    
///  Property value changing event handler
    
///   </summary>
    
///   <param name="NewValue"> the new value of the property </param>
    
///   <param name="Cancel">
    
///  Output whether the change should be cancelled or not.
    
///   </param>
     public  delegate  void PropertyChangingEventHandler( object NewValue,  out  bool Cancel); }



免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM