Calling A .NET Managed Method from Native Code

Article translations Article translations
Close Close
Article ID: 953836 - View products that this article applies to.
Expand all | Collapse all
Source: Microsoft Support

RAPID PUBLISHING

RAPID PUBLISHING ARTICLES PROVIDE INFORMATION DIRECTLY FROM WITHIN THE MICROSOFT SUPPORT ORGANIZATION. THE INFORMATION CONTAINED HEREIN IS CREATED IN RESPONSE TO EMERGING OR UNIQUE TOPICS, OR IS INTENDED SUPPLEMENT OTHER KNOWLEDGE BASE INFORMATION.

Action



This is a sample native console application which will query the registry to verify if a specific version of the .NET Framework is installed. It will then use the CorBindToRuntimeEx API to load a specific version of the Common Language Runtime (CLR). Finally, it uses the Interfaces and methods exposed in mscorlib.tlb to load an assembly and type and execute methods.

Create a new C++ Console application named CheckCLR and replace the code in CheckCLR.cpp with the following code:

/*
    CheckCLR.cpp - A Native Code CLR Host Sample

    CheckCLR is a native console application which queries the registry to
    determine if a specific version of the .NET runtime is installed on the
    machine. It then loads the specific CLR required.

    It will then load a managed console app and execute the Main method.
    It then loads a managed assembly and executes a method.
*/
#include "stdafx.h"
#include <windows.h>
#include <mscoree.h>
#include <assert.h>
#include <stdio.h>
#include <tchar.h>

// Import mscorlib typelib. Using 1.0 for maximum backwards compatibility
#import "C:\windows\Microsoft.NET\Framework\v1.0.3705\mscorlib.tlb" auto_rename
// Link with mscoree.dll import lib.
#pragma comment(lib,"mscoree.lib")

using namespace mscorlib;

int _tmain(int argc, _TCHAR* argv[])
{
    //
    // Query 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5\Install' DWORD value
    // See http://support.microsoft.com/kb/318785/ for more information on .NET runtime versioning information
    //
    HKEY key = NULL;
    DWORD lastError = 0;
    lastError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5"),0,KEY_QUERY_VALUE,&key);
    if(lastError!=ERROR_SUCCESS) {
        _putts(TEXT("Error opening HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5"));
        return 1;
    }

    DWORD type;
    BYTE data[4];
    DWORD len = sizeof(data);
    lastError = RegQueryValueEx(key,TEXT("Install"),NULL,&type,data,&len);
 
    if(lastError!=ERROR_SUCCESS) {
        RegCloseKey(key);
        _putts(TEXT("Error querying HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5\\Install"));
        return 2;
    }

    RegCloseKey(key);

    // Was Install DWORD key value == 1 ??
    if(data[0]==1)
        _putts(TEXT(".NET Framework 3.5 is installed"));
    else {
        _putts(TEXT(".NET Framework 3.5 is NOT installed"));
        return 3;
    }

    // 
    // Load the runtime the 3.5 Runtime (CLR version 2.0)
    //
    LPWSTR pszVer = L"v2.0.50727";  // .NET Fx 3.5 needs CLR 2.0
    LPWSTR pszFlavor = L"wks";
    ICorRuntimeHost *pHost = NULL;

    HRESULT hr = CorBindToRuntimeEx( pszVer,       
                                                       pszFlavor,    
                                                       STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC, 
                                                       CLSID_CorRuntimeHost, 
                                                       IID_ICorRuntimeHost,
                                                       (void **)&pHost);

    if (!SUCCEEDED(hr)) {
        _tprintf(TEXT("CorBindToRuntimeEx failed 0x%x\n"),hr);
        return 1;
    }
 
    _putts(TEXT("Loaded version 2.0.50727 of the CLR\n"));
 
    pHost->Start(); // Start the CLR

    //
    // Get a pointer to the default domain in the CLR
    //
    _AppDomainPtr pDefaultDomain = NULL;
    IUnknownPtr   pAppDomainPunk = NULL;

    hr = pHost->GetDefaultDomain(&pAppDomainPunk);
    assert(pAppDomainPunk); 
 
    hr = pAppDomainPunk->QueryInterface(__uuidof(_AppDomain),(void**) &pDefaultDomain);
    assert(pDefaultDomain);

    //
    // Load an Exe Assembly and call Main()
    //
    _bstr_t bstrExeName = L"ConsoleApplication1.exe";
    try {
        hr = pDefaultDomain->ExecuteAssembly_2(bstrExeName);
    }
    catch(_com_error& error) {
        _tprintf(TEXT("ERROR: %s\n"),(_TCHAR*)error.Description());
        goto exit;
    }

    /*
        Load a type from a DLL Assembly and call it

        Doing the same thing from native code as this C# code:

            System.Runtime.Remoting.ObjectHandle objptr;
            objptr = AppDomain.CurrentDomain.CreateInstanceFrom("ClassLibrary1.dll",
                                                                                           "ClassLibrary1.Class1");
            object obj = objptr.Unwrap();
            Type t = obj.GetType();
            t.InvokeMember("Test",
                                   BindingFlags.InvokeMethod,
                                   null,
                                   obj,
                                   new object[0]);
    */
    try {
        _ObjectHandlePtr pObjectHandle; 
        _ObjectPtr pObject; 
        _TypePtr pType;
        SAFEARRAY* psa;

        // Create an instance of a type from an assembly
        pObjectHandle = pDefaultDomain->CreateInstanceFrom(L"ClassLibrary1.dll",     // no path -- local directory
                                                                                      L"ClassLibrary1.Class1");
  
        variant_t vtobj = pObjectHandle->Unwrap();                                     // Get an _Object (as variant) from the _ObjectHandle
        vtobj.pdispVal->QueryInterface(__uuidof(_Object),(void**)&pObject);  // QI the variant for the Object iface
        pType = pObject->GetType();                                                         // Get the _Type iface
        psa = SafeArrayCreateVector(VT_VARIANT,0,0);                                // Create a safearray (0 length)
        pType->InvokeMember_3("Test",                                                     // Invoke "Test" method on pType
                                            BindingFlags_InvokeMethod,
                                            NULL,
                                            vtobj,
                                            psa );
        SafeArrayDestroy(psa);                                                                   // Destroy safearray
    }
    catch(_com_error& error) {
        _tprintf(TEXT("ERROR: %s\n"),(_TCHAR*)error.Description());
        goto exit;
    }

exit:
    pHost->Stop();
    pHost->Release();

    return 0;
}

/*** End of file CheckCLR.cpp ***/

Create a C# Console Application named ConsoleApplication1. Add a Console.WriteLine call in the Main method and output a simple string message. Build ConsoleApplication1. Copy the ConsoleApplication1.exe build target to the ouput directory where CheckCLR.exe is built. Alternatively, you can change the code above to have the full path name to where ConsoleApplication1.exe is built.

Create a C# Class Library named ClassLibrary1. In the Class1 class, add a public method named Test. In Test, add a Console.WriteLine call and output a simple string message. Build ClassLibrary1.dll. Copy the ClassLibrary1.dll build target to the outut directory where CheckCLR.exe is built. Alternatively, you can change the code above to have the full path name to where ClassLibrary1.dll is built.

After building CheckCLR, you should should be able to run CheckCLR.exe from a command prompt and see the output of the Console.WriteLine calls:

    .NET Framework 3.5 is installed
    Loaded version 2.0.50727 of the CLR


    Hello World from ConsoleApplication1.Main
    Hello World from ClassLibrary1.Class1


If running CheckCLR.exe under the debugger from your Visual C++ project, you may get could not load file or assembly error messages. In that case, copy ConsoleApplication1.exe and ClassLibrary1.dll to the folder containing the CheckCLR.sln solution file. The debugger uses that folder as the application default directory.

 

 

 

DISCLAIMER

MICROSOFT AND/OR ITS SUPPLIERS MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY, RELIABILITY OR ACCURACY OF THE INFORMATION CONTAINED IN THE DOCUMENTS AND RELATED GRAPHICS PUBLISHED ON THIS WEBSITE (THE “MATERIALS”) FOR ANY PURPOSE. THE MATERIALS MAY INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS AND MAY BE REVISED AT ANY TIME WITHOUT NOTICE.

TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, MICROSOFT AND/OR ITS SUPPLIERS DISCLAIM AND EXCLUDE ALL REPRESENTATIONS, WARRANTIES, AND CONDITIONS WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO REPRESENTATIONS, WARRANTIES, OR CONDITIONS OF TITLE, NON INFRINGEMENT, SATISFACTORY CONDITION OR QUALITY, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, WITH RESPECT TO THE MATERIALS.

Properties

Article ID: 953836 - Last Review: May 31, 2008 - Revision: 1.0
APPLIES TO
  • Microsoft Visual C++ 2008 Express Edition
  • Microsoft Visual C++ 2005 Express Edition
  • Microsoft Visual C# 2008 Express Edition
  • Microsoft Visual C# 2005 Express Edition
Keywords: 
kbnomt kbrapidpub KB953836

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com