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); }


原文地址:https://www.cnblogs.com/aicro/p/2555249.html