[Guide] Loading .NET 4.0 CLR menu

User Tag List

Results 1 to 8 of 8
  1. #1
    PyGuy's Avatar Corporal
    Reputation
    14
    Join Date
    Jan 2011
    Posts
    20
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [Guide] Loading .NET 4.0 CLR

    There are several excellent posts about loading the .NET CLR from a C/C++ application or dll. However, Microsoft changed the methods and procedure slightly for .NET 4.0, so I'm writing this to bridge the gap.

    First you will need to start the CLR for the version of .NET you wish to run. In order to call this correctly you will need to find the exact version number of .NET on your machine. The easy way is to simply look in the C:\Windows\Microsoft.NET\Framework directory and copy the full text of the directory with the highest number. On my machine this results in "v4.0.30319". There are more robust ways of obtaining this string of course.

    Pass that string into this function to start the CLR:
    Code:
    /// <summary>
    /// Returns a pointer to a running CLR host of the specified version
    /// </summary>
    /// <param name="dotNetVersion">The exact version number of the CLR you want to
    /// run. This can be obtained by looking in the C:\Windows\Microsoft.NET\Framework
    /// directory and copy the name of the last directory.</param>
    /// <returns>A running CLR host or NULL. You are responsible for calling Release() on it.</returns>
    ICLRRuntimeHost* GetRuntimeHost(LPCWSTR dotNetVersion)
    {
    	ICLRMetaHost* metaHost = NULL;
    	ICLRRuntimeInfo* info = NULL;
    	ICLRRuntimeHost* runtimeHost = NULL;
    
    	// Get the CLRMetaHost that tells us about .NET on this machine
    	if (S_OK == CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost))
    	{
    		// Get the runtime information for the particular version of .NET
    		if (S_OK == metaHost->GetRuntime(dotNetVersion, IID_ICLRRuntimeInfo, (LPVOID*)&info))
    		{
    			// Get the actual host
    			if (S_OK == info->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost))
    			{
    				// Start it. This is okay to call even if the CLR is already running
    				runtimeHost->Start();
    			}
    		}
    	}
    	if (NULL != info)
    		info->Release();
    	if (NULL != metaHost)
    		metaHost->Release();
    
    	return runtimeHost;
    }
    Great! Now you've got a running CLR. Its time to run a function. The function must have a particular signature: public static int Function(string param). Here's the code to call that function:
    Code:
    /// <summary>
    /// Executes some code in the CLR. The function must have the signature: public static int Function(string param)
    /// </summary>
    /// <param name="host">A started instance of the CLR</param>
    /// <param name="assemblyPath">The full path to your compiled code file.
    /// i.e. "C:\MyProject\MyCode.dll"</param>
    /// <param name="typeName">The full type name of the class to be called, including the
    /// namespace. i.e. "MyCode.MyClass"</param>
    /// <param name="function">The name of the function to be called. i.e. "MyFunction"</param>
    /// <param name="param">A string parameter to pass to the function.</param>
    /// <returns>The integer return code from the function or -1 if the function did not run</returns>
    int ExecuteClrCode(ICLRRuntimeHost* host, LPCWSTR assemblyPath, LPCWSTR typeName,
    	LPCWSTR function, LPCWSTR param)
    {
    	if (NULL == host)
    		return -1;
    
    	DWORD result = -1;
    	if (S_OK != host->ExecuteInDefaultAppDomain(assemblyPath, typeName, function, param, &result))
    		return -1;
    
    	return result;
    }
    Let's put it all together and run something. Let's suppose you create a new C# dll with the code below and install it as C:\MyLibrary.dll
    Code:
    using System;
    using System.Windows;
    
    namespace MyLibrary
    {
       class MyClass
       {
          public static int ShowMessage(string message)
          {
             MessageBox.Show(message);
             return 1;
          }
       }
    }
    From you C/C++ code you can now show the message box like so (assuming your version of .NET 4.0 is the same as mine):
    Code:
    ICLRRuntimeHost* host = GetRuntimeHost(L"v4.0.30319");
    ExecuteClrCode(host , L"C:\\MyLibrary.dll", L"MyLibrary.MyClass", L"ShowMessage", L"Hello World");
    // At some point you will need to call Release(). You can do it now or during cleanup code
    host->Release();
    Of course the ShowMessage function is rather trivial and non-helpful as there are easier ways to do that in C/C++, but you can do anything C# you need to from that function, including starting new threads, opening windows, launching processes, hooking other functions, etc. Its simply your doorway into .NET 4.0.

    Edit: I forgot the includes you'll need for the C/C++ code:
    Code:
    #include <metahost.h>
    #include <mscoree.h>
    Edit: Fixed resource leak
    Last edited by PyGuy; 02-23-2011 at 12:10 PM.

    [Guide] Loading .NET 4.0 CLR
  2. #2
    Jadd's Avatar 🐸 Premium Seller
    Reputation
    1511
    Join Date
    May 2008
    Posts
    2,432
    Thanks G/R
    81/333
    Trade Feedback
    1 (100%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Phew, I've been too lazy to look up the new CLR hosting methods for 4.0, so this is really helpful. Looks like it's time to make the switch! Thanks a lot

    +Rep.

  3. #3
    Bananenbrot's Avatar Contributor
    Reputation
    153
    Join Date
    Nov 2009
    Posts
    384
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Are we actually supposed to IUnkown::Release() those COM interfaces? Because then you are leaking COM objects.

    For getting the runtime version Assembly.ImageRuntimeVersion Property (System.Reflection) is epic if you already loaded the Assembly metadata for the library to inject.
    With the help of Assembly's members, you can also investigate the binary for a matching entrypoint via e.g. attributes:
    Code:
                if (entryPoint == null)
                {
                    try
                    {
                        entryPoint = Library.GetTypes().SelectMany(t =>
                            t.GetMethods().Where(mi =>
                                mi.HasAttribute<DllEntryPointAttribute>() &&
                                mi.IsPublic &&
                                mi.IsStatic &&
                                mi.ReturnType == typeof(int) &&
                                mi.GetParameters().Length == 1 &&
                                mi.GetParameters()[0].ParameterType == typeof(string)
                        )).First();
                    }
                    catch (ArgumentNullException ex)
                    {
                        throw new MissingAttributeException("No function matching the entry point signature and decorated with the DllEntryPointAttribute was found.\n"
                            + "Decorate your entry point function or adjust the signature!", ex);
                    }
                }

  4. #4
    _Mike's Avatar Contributor
    Reputation
    310
    Join Date
    Apr 2008
    Posts
    531
    Thanks G/R
    0/2
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Bananenbrot View Post
    Are we actually supposed to IUnkown::Release() those COM interfaces? Because then you are leaking COM objects.
    Yes you should*. (hint: RAII)
    Any assemblies you load will stay loaded and any managed threads you start will stay running even if you release the interfaces.

    *) Unless you want to repeatedly call ExecuteIn.. then release everything but the runtime host.
    Last edited by _Mike; 02-23-2011 at 11:46 AM.

  5. #5
    PyGuy's Avatar Corporal
    Reputation
    14
    Join Date
    Jan 2011
    Posts
    20
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Fixed resource leak. Thank you for pointing that out.

  6. #6
    Bananenbrot's Avatar Contributor
    Reputation
    153
    Join Date
    Nov 2009
    Posts
    384
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    #include <atlcomcli.h> and try this to be exception-safe:

    Code:
    CComPtr<ICLRRuntimeHost> GetRuntimeHost(LPCWSTR dotNetVersion)
    {
        CComPtr<ICLRMetaHost> metaHost;
        CComPtr<ICLRRuntimeInfo> info;
        CComPtr<ICLRRuntimeHost> runtimeHost;
    
        // Get the CLRMetaHost that tells us about .NET on this machine
        if (S_OK == CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, reinterpret_cast<LPVOID*>(&metaHost.p)))
        {
            // Get the runtime information for the particular version of .NET
            if (S_OK == metaHost->GetRuntime(dotNetVersion, IID_ICLRRuntimeInfo, reinterpret_cast<LPVOID*>(&info.p)))
            {
                // Get the actual host
                if (S_OK == info->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, reinterpret_cast<LPVOID*>(&runtimeHost.p)))
                {
                    // Start it. This is okay to call even if the CLR is already running
                    runtimeHost->Start();
                }
            }
        }
    
        return runtimeHost;
    }
    Just do some FAILED(hr)-handling and you are done.

    edit: Just changed naming conventions a second time to fit your's. Hopefully it's error free (not written in VS).

    edit2: http://en.wikipedia.org/wiki/Resourc...Initialization
    Last edited by Bananenbrot; 02-23-2011 at 12:34 PM.

  7. #7
    lanman92's Avatar Active Member
    Reputation
    50
    Join Date
    Mar 2007
    Posts
    1,033
    Thanks G/R
    0/1
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    All-in-one code framework by microsoft has this stuff in it. Has a program explaining how to load CLR v2.0 and v4.0. Other things in there are also pretty neat.

  8. #8
    Bananenbrot's Avatar Contributor
    Reputation
    153
    Join Date
    Nov 2009
    Posts
    384
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    While I really find this useful (sample code for COM is really nice), I don't think this is actually good C++ practice:
    Code:
    hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo));
        if (FAILED(hr))
        {
            wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
            goto Cleanup;
        }

Similar Threads

  1. Professions Guide [Lowered Net Cost]
    By loccedout in forum World of Warcraft Guides
    Replies: 4
    Last Post: 05-11-2009, 10:30 PM
  2. [Guide] Loading Screens
    By Vcertno in forum WoW ME Tools & Guides
    Replies: 2
    Last Post: 08-25-2008, 12:10 PM
  3. [Guide][VB.NET] Read a string from memory
    By Gothian in forum Programming
    Replies: 14
    Last Post: 08-18-2008, 04:39 PM
  4. [Guide][VB.NET] Reading a String From Memory
    By Gothian in forum WoW Memory Editing
    Replies: 14
    Last Post: 01-18-2008, 12:08 PM
All times are GMT -5. The time now is 03:21 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search