//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
//
// ClrHost.cpp : Definiuje punkt wejścia dla aplikacji konsoli
//

#include "stdafx.h"

void ReportError(HRESULT hr)
{
	LPVOID lpMsgBuf;
	DWORD bOk = FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		hr,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Domyślny język
		(LPTSTR) &lpMsgBuf,
		0,
		NULL); 

	if (bOk)
	{
		_ftprintf(stderr, _T("\t%s\n"), (LPTSTR)((LPTSTR)lpMsgBuf));
		LocalFree( lpMsgBuf );
	}
	else
	{
		_ftprintf(stderr, _T("Nieznany błąd\n"));
	}

}

void Start()
{
	printf("Niezarządzany kod hosta został uruchomiony...\n");
	//
	// Deklaracja zmiennych przekazywanych do CorBindToRuntime.
	//
	LPWSTR pszVer = L"v1.0.3705";
	LPWSTR pszFlavor = L"wks";
	ICorRuntimeHost *pHost = NULL;

	//
	// CorBindToRuntime jest głównym api używanym przez hosty do załadowania CLR do procesu.
	// Oprócz wersji i "svr vs wks" wybrano "no
	// domain neutral code ("single domain host").
	// 
	HRESULT hr = CorBindToRuntimeEx(
                      //wersja
                      pszVer,       
                      // svr lub wks                        
                      pszFlavor,    
                      // neutralność i ustawienia gc
                      STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC, 
                      CLSID_CorRuntimeHost, 
                      IID_ICorRuntimeHost, 
                      (void **)&pHost);
	if (!SUCCEEDED(hr))
	{
		printf("Działanie CorBindToRuntime nie powiodło się\n");
		ReportError(hr);
		return;
	}

	wchar_t buffer[128];
	DWORD dwBytes;
	hr = GetCORVersion(buffer, sizeof(buffer), &dwBytes);


	wprintf(L"Wersja %ls CLR została załadowana...\n", buffer);

	ICorConfiguration *pConfig = NULL;
	hr = pHost->GetConfiguration(&pConfig);
	// Wywołanie metod konfiguracji.
	// Jak można uzyskać egzemplarz IGCHostControl?
	// IGCHostControl *pGCHostControl = NULL;
	// pConfig->SetGCHostControl();
	pConfig->Release();

	// pHost->CreateLogicalThreadState() ??
	// pHost->CreateEvidence() create evidence or IIdentity?
	// pHost->CurrentDomain(IUnknown *pAppDomain) ??
	// pHost->DeleteLogicalThreadState() ??
	// pHost->LocksHeldByLogicalThread(DWORD *pCount) ??
	// pHost->MapFile(HANDLE hFile, HMODULE *hMapAddress) ??
	// pHost->SwitchInLogicalThreadState(DWORD **pFiberCookie) ??
	// pHost->SwitchOutLogicalThreadState(DWORD **pFiberCookie) ??

	//
	// Uruchomienie CLR.
	//
	pHost->Start();
	//
	// Uzyskanie wskaźnika do domyślnej domeny procesu.
	//
	_AppDomain *pDefaultDomain = NULL;
	IUnknown   *pAppDomainPunk = NULL;

	hr = pHost->GetDefaultDomain(&pAppDomainPunk);
	assert(pAppDomainPunk); 

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

	GetCurrentDirectory(sizeof(buffer), buffer);
	wprintf(L"Aktualny katalog \"%ls\"\n", buffer);

	BSTR bstrVal;
	pDefaultDomain->get_BaseDirectory(&bstrVal);
	wprintf(L"Katalog bazowy \"%ls\"\n", (LPWSTR)bstr_t(bstrVal,false));
	pDefaultDomain->get_DynamicDirectory(&bstrVal);
	wprintf(L"Katalog dynamiczny \"%ls\"\n", (LPWSTR)bstr_t(bstrVal,false));
	pDefaultDomain->get_FriendlyName(&bstrVal);
	wprintf(L"Przyjazna nazwa \"%ls\"\n", (LPWSTR)bstr_t(bstrVal,false));
	pDefaultDomain->get_RelativeSearchPath(&bstrVal);
	wprintf(L"Relatywna ścieżka wyszukiwania \"%ls\"\n", (LPWSTR)bstr_t(bstrVal,false));
	//
	// Załadowanie zarządzanej części hosta do domyślnej domeny.
	//
	ICorRun *pMgdHost = NULL;
	_ObjectHandle *pObjHandle = NULL;
	
	hr = pDefaultDomain->CreateInstance(
			_bstr_t("MgdHost"), 
			_bstr_t("ClrHost.MgdHost.HostProcessRequest"),
			&pObjHandle); 
	if(FAILED(hr))
		return;
	printf("Zarządzany kod hosta został utworzony...\n");

	assert(pObjHandle);

	VARIANT v;
	VariantInit(&v);
	hr = pObjHandle->Unwrap(&v);

	assert(v.pdispVal);
	hr = v.pdispVal->QueryInterface(__uuidof(ICorRun), 
                                    (void**) &pMgdHost);
	if(FAILED(hr))
		return;
	assert(pMgdHost);

	pMgdHost->Run(_bstr_t("DiningPhilosophersDomain"), _bstr_t("DiningPhilosophers.exe"));

	//
	// Zwolnienie zasobów
	//
	pAppDomainPunk->Release();
	pDefaultDomain->Release();
	pMgdHost->Release();

	//
	// Zatrzymanie CLR.  Po usunięciu CLR z procesu nie jest możliwe jego ponowne
	// załadowanie.
	//
	pHost->Stop();
}

int main(int argc, char* argv[])
{
	try
	{
		CoInitialize(NULL);
		Start();
	}
	catch(...)
	{
	}
	CoUninitialize();
}

