Exemplo n.º 1
0
void* StartClrBootstrap( wchar_t* dllPath,
                         wchar_t* userData,
                         wchar_t* appBase,
                         wchar_t* privatePath )
{
    // Bind to the CLR runtime..
    ICLRRuntimeHost *pClrHost = NULL;
    HRESULT clrResult = CorBindToRuntimeEx(
                            NULL, L"wks", 0, CLSID_CLRRuntimeHost,
                            IID_ICLRRuntimeHost, (PVOID*)&pClrHost);

    // Push the big START button shown above
    clrResult = pClrHost->Start();

    // Create parameters to pass into Bootstrap assembly
    // Bootstrapper is responsible for setting up the
    // apppath and privatebin parameters for the AppDomain
    wstring outParam = FormParameter(dllPath, appBase, privatePath, userData);

    // Get path to Bootstrap assembly
    wstring pathBootstrap = PathResolve(L"BootStrapper.dll");

    // Okay, the CLR is up and running in this (previously native) process.
    // Now call a method on our managed C# class library.
    DWORD retCode = 0;
    clrResult = pClrHost->ExecuteInDefaultAppDomain (
                    pathBootstrap.c_str(),
                    L"BootStrapper.Main",
                    L"Initialize",
                    outParam.c_str(),
                    &retCode
                );

    return (void*)retCode;
}
Exemplo n.º 2
0
extern "C" __declspec(dllexport) HRESULT Inject(_In_ LPCSTR param)
{
    DWORD pReturnValue;
    ICLRMetaHost*    pMetaHost       = NULL;
    ICLRRuntimeInfo* pRuntimeInfo    = NULL;
    ICLRRuntimeHost* pClrRuntimeHost = NULL;

    if (CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)) != S_OK)
    {
        return -1;
    }
    if (pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo)) != S_OK)
    {
        return -1;
    }
    if (pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pClrRuntimeHost)) != S_OK)
    {
        return -1;
    }

    if (pClrRuntimeHost->Start() != S_OK)
    {
        return -1;
    }

    WCHAR wsz[MAX_PATH];
    MultiByteToWideChar(CP_ACP, 0, param, -1, wsz, MAX_PATH);

    HRESULT hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(
        wsz,
        L"FakePacketSender.InjectedEntry",
        L"Run",
        wsz,
        &pReturnValue);

    if (hr = pClrRuntimeHost->Stop() != S_OK)
    {
        _com_error err(hr);
        MessageBox(0, err.ErrorMessage(), L"Error", 0);
        return -1;
    }

    if (hr != S_OK)
    {
        _com_error err(hr);
        MessageBox(0, err.ErrorMessage(), L"Error", 0);
        return -1;
    }

    pMetaHost->Release();
    pRuntimeInfo->Release();
    pClrRuntimeHost->Release();

    return hr;
}
Exemplo n.º 3
0
extern "C" DLL_EXPORT int STDMETHODCALLTYPE
CallExecuteInDefaultAppDomain(LPCWSTR pwzAssemblyPath,
                        LPCWSTR pwzTypeName,
                        LPCWSTR pwzMethodName,
                        LPCWSTR pwzArgument,
                        DWORD   *pReturnValue)
{
    ICLRRuntimeHost* host = GetCLRRuntimeHost();

    if (!host)
        return E_FAIL;

    auto result = host->ExecuteInDefaultAppDomain(pwzAssemblyPath, pwzTypeName, pwzMethodName, pwzArgument, pReturnValue);

    host->Release();

    return result;
}
Exemplo n.º 4
0
DllExport void LoadManagedProject(const wchar_t * managedDllLocation)
{
	HRESULT hr;

	// Secure a handle to the CLR v4.0
	ICLRRuntimeHost* pClr = StartCLR(L"v4.0.30319");
	if (pClr != NULL)
	{
		OutputDebugStringW(L"[Bootstrap] Attempting exec in default app domain\n");
		DWORD result;
		hr = pClr->ExecuteInDefaultAppDomain(
			managedDllLocation,
			L"KeeFarceDLL.KeeFarce",
			L"EntryPoint",
			L"Argument",
			&result);
	}
	else {
		OutputDebugStringW(L"[Bootstrap] could not spawn CRL\n");
	}
}
Exemplo n.º 5
0
EASYHOOK_NT_INTERNAL CompleteManagedInjection(LPREMOTE_INFO InInfo)
{
/*
Description:

    Loads the NET runtime into the calling process and invokes the
    managed injection entry point (EasyHook.InjectionLoader.Main).
*/
    ICLRMetaHost*           MetaClrHost = NULL;
    ICLRRuntimeInfo*        RuntimeInfo = NULL;
    ICLRRuntimeHost*        RuntimeClrHost = NULL;

	DWORD					ErrorCode = 0;
    WCHAR                   ParamString[MAX_PATH];
    REMOTE_ENTRY_INFO       EntryInfo;
    HMODULE                 hMsCorEE = LoadLibraryA("mscoree.dll");
    PROC_CorBindToRuntime*  CorBindToRuntime = (PROC_CorBindToRuntime*)GetProcAddress(hMsCorEE, "CorBindToRuntime"); // .NET 2.0/3.5 framework creation method
    PROC_CLRCreateInstance* CLRCreateInstance = (PROC_CLRCreateInstance*)GetProcAddress(hMsCorEE, "CLRCreateInstance"); // .NET 4.0+ framework creation method
    DWORD                   RetVal;
    bool                    UseCorBindToRuntime = false;

    if(CorBindToRuntime == NULL && CLRCreateInstance == NULL)
        UNMANAGED_ERROR(10); // mscoree.dll does not exist or does not expose either of the framework creation methods

    UseCorBindToRuntime = CLRCreateInstance == NULL;

    // invoke user defined entry point
    EntryInfo.HostPID = InInfo->HostProcess;
    EntryInfo.UserData = InInfo->UserData;
    EntryInfo.UserDataSize = InInfo->UserDataSize;

    if (!UseCorBindToRuntime)
    {
        // Attempt to use .NET 3.5/4 runtime object creation method rather than deprecated .NET 2.0 CorBindToRuntime
        if (FAILED(CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&MetaClrHost)))
        {
            // Failed to get a MetaHost instance
            DEBUGOUT("Failed to retrieve CLRMetaHost instance");
            UseCorBindToRuntime = true;
        }
        else
        {
            /*
                It is possible to create a ICLRRuntimeInfo object based on the runtime required by InInfo->UserLibrary
                however this requires that the assembly containing the "EasyHook.InjectionLoader" (usually EasyHook.dll)
                must be targetting the same framework version as the assembly(ies) to be injected. This is because the
                first CLR assembly injected into the target process in this case is usually the EasyHook.dll assembly.

                So instead we are providing a specific .NET version (for now v4.0.30319), this will need to be
                passed as a parameter in the future.
            */
            // TODO: add documentation about what happens when injecting into a managed process where the .NET framework is already loaded
            LPCWSTR frameworkVersion = L"v4.0.30319"; // TODO: .NET version string to be passed in "InInfo"
            if (FAILED(MetaClrHost->GetRuntime(
                frameworkVersion, 
                IID_ICLRRuntimeInfo,
                (LPVOID*)&RuntimeInfo)))
            {
                // .NET version requested is not available
                DEBUGOUT("Failed to retrieve runtime info for framework version: %s", frameworkVersion);
                UseCorBindToRuntime = true;
            }
            else
            {
                if (FAILED(RuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&RuntimeClrHost)))
                {
                    // Failed to create the requested CLR host version
                    // TODO: add documentation about why this might happen - e.g. does this happen if an older framework version is already loaded by the target?
                    DEBUGOUT("Failed to create CLR host for framework version: %s", frameworkVersion);
                    UseCorBindToRuntime = true;
                }
                else
                {
                    RuntimeClrHost->Start();

                    // Enable support for running older .NET v2 assemblies within .NET v4 (e.g. EasyHook.dll is .NET 2.0)
	                if (FAILED(RuntimeInfo->BindAsLegacyV2Runtime()))
                        DEBUGOUT("Unable to BindAsLegacyV2Runtime");
                }
            }
        }
    }

    if (UseCorBindToRuntime)
    {
        // load NET-Runtime and execute user defined method
        if(!RTL_SUCCESS(CorBindToRuntime(NULL, NULL, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void**)&RuntimeClrHost)))
        {
            DEBUGOUT("CorBindToRuntime failed");
            UNMANAGED_ERROR(11);
        }
        
        RuntimeClrHost->Start();
    }

    /*
        Test library code.
        This is because if once we have set the remote signal, there is no way to
        notify the host about errors. If the following call succeeds, then it will
        also do so some lines later... If not, then we are still able to report an error.

        The EasyHook managed injection loader will establish a connection to the
        host, so that further error reporting is still possible after we set the event!
    */
    RtlLongLongToUnicodeHex((LONGLONG)&EntryInfo, ParamString);

    if(!RTL_SUCCESS(RuntimeClrHost->ExecuteInDefaultAppDomain(
            InInfo->UserLibrary,
            L"EasyHook.InjectionLoader",
            L"Main",
            ParamString,
            &RetVal)))
    {
        if (UseCorBindToRuntime)
            UNMANAGED_ERROR(12); // We already tried the CorBindToRuntime method and it has failed

        // Running the assembly in the .NET 4.0 Runtime did not work;
        // Stop and attempt to run it in the .NET 2.0/3.5 Runtime
        if(RuntimeClrHost != NULL)
            RuntimeClrHost->Release();
        
        if(!RTL_SUCCESS(CorBindToRuntime(NULL, NULL, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void**)&RuntimeClrHost)))
            UNMANAGED_ERROR(11);
        
        RuntimeClrHost->Start();
        
        RtlLongLongToUnicodeHex((LONGLONG)&EntryInfo, ParamString);
        if(!RTL_SUCCESS(RuntimeClrHost->ExecuteInDefaultAppDomain(InInfo->UserLibrary, L"EasyHook.InjectionLoader", L"Main", ParamString, &RetVal)))
            UNMANAGED_ERROR(14); // Execution under both .NET 4 and .NET 2/3.5 failed (new ErrorCode: 14, for EasyHook 2.7)
    }

    if(!RetVal)
        UNMANAGED_ERROR(13);

    // set and close event
    if(!SetEvent(InInfo->hRemoteSignal))
        UNMANAGED_ERROR(22);

    CloseHandle(InInfo->hRemoteSignal);

    InInfo->hRemoteSignal = NULL;

    // execute library code (no way for error reporting, so we dont need to check)
    RuntimeClrHost->ExecuteInDefaultAppDomain(
        InInfo->UserLibrary,
        L"EasyHook.InjectionLoader",
        L"Main",
        ParamString,
        &RetVal);

ABORT_ERROR:

    // release resources
    if(MetaClrHost != NULL)
        MetaClrHost->Release();
    if(RuntimeInfo  != NULL)
        RuntimeInfo->Release();
    if(RuntimeClrHost != NULL)
        RuntimeClrHost->Release();

    if(hMsCorEE != NULL)
        FreeLibrary(hMsCorEE);

    return ErrorCode;
}
/// <summary>
/// Starts the dot net runtime.
/// </summary>
/// <remarks>http://www.codingthewheel.com/archives/how-to-inject-a-managed-assembly-dll/</remarks>
HRESULT StartTheDotNetRuntime(_In_ LPCTSTR lpCommand)
{
	FILE *file;
	fopen_s(&file, logPath, "a+");

	fprintf(file, "binding runtime.\r\n");
	fflush(file);

    fprintf(file, "Loading the .NET runtime host.\n");
	fflush(file);
      
	ICLRMetaHost *pMetaHost = NULL;
	auto result = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
	if (FAILED(result))
	{
		fprintf(file, "Error: failed to create CLR instance.\n");
		fflush(file);
		
		return result;
	}
 
	fprintf(file, "Loading the .NET runtime.\n");
	fflush(file);

	ICLRRuntimeInfo *pRuntimeInfo = NULL;
	result = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
	if (FAILED(result))
	{
		fprintf(file, "Error: failed to create CLR instance.\n");
		fflush(file);

		pMetaHost->Release();
		return result;
	}
 
	fprintf(file, "Acquiring the .NET runtime.\n");
	fflush(file);

	ICLRRuntimeHost *pClrRuntimeHost = NULL;
	result = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pClrRuntimeHost));
	if (FAILED(result))
	{
		fprintf(file, "Error: failed to acquire CLR runtime.\n");
		fflush(file);

		pMetaHost->Release();
		return result;
	}

	fprintf(file, "Starting the .NET runtime.\n");
	fflush(file);

	result = pClrRuntimeHost->Start();
	if (FAILED(result))
	{
		fprintf(file, "Error: failed to start CLR runtime.\n");
		fflush(file);

		pClrRuntimeHost->Release();
		pMetaHost->Release();
		return result;
	}

	fprintf(file, "Executing payload assembly.\n");
	fflush(file);
    DWORD dwRet = 0;
    result = pClrRuntimeHost->ExecuteInDefaultAppDomain(
            assemblyPath,
            classFqn, methodName, parameter, &dwRet);
	if (FAILED(result))
	{
		fprintf(file, "Error: unable to execute example code.\n");
		fflush(file);

		pClrRuntimeHost->Stop();
		pClrRuntimeHost->Release();
		pMetaHost->Release();
		return result;
	}

	fprintf(file, "Stopping the .NET runtime.\n");
	fflush(file);

	pClrRuntimeHost->Stop();

	fprintf(file, "Releasing the .NET runtime.\n");
	fflush(file);

	pClrRuntimeHost->Release();
	pMetaHost->Release();

	fclose(file);

	return ERROR_SUCCESS;
}
Exemplo n.º 7
0
int _tmain(int argc, _TCHAR* argv[])
{
	// Bind to the runtime.
	ICLRRuntimeHost *pClrHost = NULL;
	HRESULT hrCorBind = CorBindToRuntimeEx(
		NULL,   // Load the latest CLR version available
		L"wks", // Workstation GC ("wks" or "svr" overrides)
		0,      // No flags needed
		CLSID_CLRRuntimeHost,
		IID_ICLRRuntimeHost,
		(PVOID*)&pClrHost);
    CheckFail(hrCorBind, "Bind to runtime failed (0x%x)");

	// Construct our host control object.
    DHHostControl *pHostControl = new DHHostControl(pClrHost);
	if (!pHostControl)
        Fail("Host control allocation failed");
	pClrHost->SetHostControl(pHostControl);

	// Now, begin the CLR.
	HRESULT hrStart = pClrHost->Start();
    if (hrStart == S_FALSE)
        _ASSERTE(!L"Runtime already started; probably OK to proceed");
    else
        CheckFail(hrStart, "Runtime startup failed (0x%x)");

    // Construct the shim path (i.e. shim.exe).
	WCHAR wcShimPath[MAX_PATH];
    if (!GetCurrentDirectoryW(MAX_PATH, wcShimPath))
        CheckFail(HRESULT_FROM_WIN32(GetLastError()), "GetCurrentDirectory failed (0x%x)");
    wcsncat_s(wcShimPath, sizeof(wcShimPath) / sizeof(WCHAR), L"\\shim.exe", MAX_PATH - wcslen(wcShimPath) - 1);

    // Gather the arguments to pass to the shim.
    LPWSTR wcShimArgs = NULL;
    if (argc > 1)
    {
        SIZE_T totalLength = 1; // 1 is the NULL terminator
        for(int i = 1; i < argc; i++)
        {
            // TODO: add characters for quotes around args w/ spaces inside them
            if (i != 1)
                totalLength++; // add a space between args
            totalLength += _tcslen(argv[i]) + 1;
		}

        wcShimArgs = new WCHAR[totalLength];
        wcShimArgs[0] = '\0';
 
        for(int i = 1; i < argc; i++)
        {
            if (i != 1)
                wcscat_s(wcShimArgs, totalLength, L" ");
            wcsncat_s(wcShimArgs, totalLength, argv[i], wcslen(argv[i]));
		}
	}

    if (wcShimArgs == NULL)
        Fail("Missing program path (host.exe <exePath>)\r\n");

	// And execute the program...
    DWORD retVal;
    HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
        wcShimPath,
        L"Shim",
        L"Start",
        wcShimArgs,
        &retVal);
    CheckFail(hrExecute, "Execution of shim failed (0x%x)\r\n");

    if (wcShimArgs)
        delete wcShimArgs;

    // Stop the CLR and cleanup.
    pHostControl->ShuttingDown();
    pClrHost->Stop();
    pClrHost->Release();

	return retVal;
}
Exemplo n.º 8
0
EASYHOOK_NT_INTERNAL CompleteManagedInjection(LPREMOTE_INFO InInfo)
{
    /// Region: Temporary workaround for InInfo->Assemblies not being received correctly
    InInfo->AssembliesCount = 3;
    InInfo->Assemblies = new RhAssemblyInfo[InInfo->AssembliesCount];
    InInfo->Assemblies[0].FullName = L"ProcMonInject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d34a061f079be347, ProcessorArchitecture=MSIL";
    InInfo->Assemblies[0].AssemblyLoadPath = L"D:/Documenten/Documenten/Projects/EasyHook/EasyHook - CLRHostingAPI - vs2008/Debug/x64/ProcMonInject.dll";
    InInfo->Assemblies[0].AssemblyDebugInfoPath = NULL;
    InInfo->Assemblies[1].FullName = L"ProcessMonitor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d34a061f079be347, ProcessorArchitecture=MSIL";
    InInfo->Assemblies[1].AssemblyLoadPath = L"D:/Documenten/Documenten/Projects/EasyHook/EasyHook - CLRHostingAPI - vs2008/Debug/x64/ProcessMonitor.exe";
    InInfo->Assemblies[1].AssemblyDebugInfoPath = NULL;
    InInfo->Assemblies[2].FullName = L"EasyHook, Version=2.5.0.0, Culture=neutral, PublicKeyToken=4b580fca19d0b0c5, ProcessorArchitecture=MSIL";
    InInfo->Assemblies[2].AssemblyLoadPath = L"D:/Documenten/Documenten/Projects/EasyHook/EasyHook - CLRHostingAPI - vs2008/Debug/x64/EasyHook.dll";
    InInfo->Assemblies[2].AssemblyDebugInfoPath = NULL;
    /// Endregion

/*
Description:

    Loads the NET runtime into the calling process and invokes the
    managed injection entry point.
*/

#ifdef _NET4
    ICLRMetaHost*	        pMetaClrHost = NULL;
    ICLRRuntimeInfo*        pRuntimeInfo = NULL;
#endif
    REMOTE_ENTRY_INFO       EntryInfo;
	ICLRRuntimeHost*	    pClr = NULL;
    WCHAR                   ParamString[MAX_PATH];
    DWORD                   ErrorCode = 0;
    HMODULE                 hMsCorEE = LoadLibraryA("mscoree.dll");
    PROC_CorBindToRuntime*  CorBindToRuntime = (PROC_CorBindToRuntime*)GetProcAddress(hMsCorEE, "CorBindToRuntime");

    if(CorBindToRuntime == NULL)
        UNMANAGED_ERROR(10);

    // Invoke user defined entry point
    EntryInfo.HostPID = InInfo->HostProcess;
    EntryInfo.UserData = InInfo->UserData;
    EntryInfo.UserDataSize = InInfo->UserDataSize;

    RtlLongLongToUnicodeHex((LONGLONG)&EntryInfo, ParamString);

    // Load the Common Language Runtime
    if(!RTL_SUCCESS(CorBindToRuntime(NULL, NULL, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void**)&pClr)))
        UNMANAGED_ERROR(11);
    if (!RTL_SUCCESS(AssociateCustomCLRHost(pClr, InInfo->Assemblies, InInfo->AssembliesCount)))
        UNMANAGED_ERROR(14);
	pClr->Start();

    /*
		Test library code.
		This is because if once we have set the remote signal, there is no way to
		notify the host about errors. If the following call succeeds, then it will
		also do so some lines later... If not, then we are still able to report an error.

        The EasyHook managed injection loader will establish a connection to the
        host, so that further error reporting is still possible after we set the event!
	*/
    RtlLongLongToUnicodeHex((LONGLONG)&EntryInfo, ParamString);

    HRESULT result = pClr->ExecuteInDefaultAppDomain(
                            InInfo->UserLibrary, 
                            L"EasyHook.InjectionLoader", L"Main", 
                            ParamString, &ErrorCode);
	if(!RTL_SUCCESS(result))
	{
        // Test failed!
#ifdef _NET4
        // Target CLR might be .NET 4.0, try to load this CLR instead.
		if(NULL != pClr)
	        pClr->Release();
		if(FAILED(CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (void**)&pMetaClrHost)))
			UNMANAGED_ERROR(11);
		if(FAILED(pMetaClrHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (void**)&pRuntimeInfo)))
			UNMANAGED_ERROR(11);
		if(FAILED(pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void**) &pClr)))
			UNMANAGED_ERROR(11);
        AssociateCustomCLRHost(pClr);
		pClr->Start();
		RtlLongLongToUnicodeHex((LONGLONG)&EntryInfo, ParamString);
		if(!RTL_SUCCESS(RuntimeClrHost->ExecuteInDefaultAppDomain(InInfo->UserLibrary, L"EasyHook.InjectionLoader", L"Main", ParamString, &RetVal)))
#endif
			UNMANAGED_ERROR(12);
	}

	if(!ErrorCode)
        UNMANAGED_ERROR(13);

	// set and close event
	if(!SetEvent(InInfo->hRemoteSignal))
		UNMANAGED_ERROR(22);
	CloseHandle(InInfo->hRemoteSignal);

	InInfo->hRemoteSignal = NULL;

	// Execute library code (no way for error reporting, so we dont need to check)
	pClr->ExecuteInDefaultAppDomain(
        InInfo->UserLibrary, 
        L"EasyHook.InjectionLoader", 
        L"Main", 
        ParamString, 
        &ErrorCode);

ABORT_ERROR:

	// Release Resources
	if(NULL != pClr)
		pClr->Release();

#ifdef _NET4
    if (NULL != pMetaClrHost)
        pMetaClrHost->Release();
    if (NULL != pRuntimeInfo)
        pRuntimeInfo->Release();
#endif

	return ErrorCode;
}
Exemplo n.º 9
0
VOID StartAssembly(vector<wstring> const& params) 
{
	ICLRMetaHost *pMetaHost = NULL;
    HRESULT hr;
    hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost,
        (LPVOID*)&pMetaHost);

    if (SUCCEEDED(hr))
    {
        IEnumUnknown *peunkRuntimes;
        hr = pMetaHost->EnumerateInstalledRuntimes(&peunkRuntimes);
        if (SUCCEEDED(hr))
        {
            // *** FINDING LATEST RUNTIME ***
            IUnknown *punkRuntime;
            ICLRRuntimeInfo *prtiLatest = NULL;
            WCHAR szLatestRuntimeVersion[MAX_PATH];
            while (peunkRuntimes->Next(1, &punkRuntime, NULL) == S_OK)
            {
                ICLRRuntimeInfo *prtiCurrent;
                hr = punkRuntime->QueryInterface(IID_PPV_ARGS(&prtiCurrent));
                if (SUCCEEDED(hr))
                {
                    if (!prtiLatest)
                    {
                        hr = prtiCurrent->QueryInterface(IID_PPV_ARGS(&prtiLatest));
                        if (SUCCEEDED(hr))
                        {
                            DWORD cch = ARRAYSIZE(szLatestRuntimeVersion);
                            hr = prtiLatest->GetVersionString(szLatestRuntimeVersion, &cch);
                        }
                    }
                    else
                    {
                        WCHAR szCurrentRuntimeVersion[MAX_PATH];
                        DWORD cch = ARRAYSIZE(szCurrentRuntimeVersion);
                        hr = prtiCurrent->GetVersionString(szCurrentRuntimeVersion, &cch);
                        if (SUCCEEDED(hr))
                        {
                            if (wcsncmp(szLatestRuntimeVersion, szCurrentRuntimeVersion, cch) < 0)
                            {
                                hr = prtiCurrent->GetVersionString(szLatestRuntimeVersion, &cch);
                                if (SUCCEEDED(hr))
                                {
                                    prtiLatest->Release();
                                    hr = prtiCurrent->QueryInterface(IID_PPV_ARGS(&prtiLatest));
                                }
                            }
                        }
                    }
                    prtiCurrent->Release();
                }
                punkRuntime->Release();
            }
            peunkRuntimes->Release();

            // *** STARTING CLR ***
            if (SUCCEEDED(hr))
            {
                ICLRRuntimeHost *prth;
                hr = prtiLatest->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&prth);
                if (SUCCEEDED(hr))
                {
					hr = prth->Start();
					if (SUCCEEDED(hr))
					{
						DWORD dwRet = 0;
						hr = prth->ExecuteInDefaultAppDomain(params.at(0).c_str(), params.at(1).c_str(), 
							params.at(2).c_str(), params.at(3).c_str(), &dwRet);
						// hr = 0x80131513 (System.MissingMethodException)

						if (SUCCEEDED(hr))
						{
							// *** Success ***
							MessageBox(GetDesktopWindow(), L"Successfully called managed function.", L"Success", MB_OK);
						}
						hr = prth->Stop();
					}
					prth->Release();
                }
            } 
        }
        pMetaHost->Release();
    }
}