//
// 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.
//
//  
// MetadataTablesPage.cpp : plik implementacji
//

#include "stdafx.h"

#include "AssemblyDoc.h"
#include "AssemblyView.h"
#include "MetadataTablesPage.h"
#include "CILOpcode.h"

#include <sstream>
#include <iostream>

// Okno dialogowe CMetadataTablesPage

IMPLEMENT_DYNAMIC(CMetadataTablesPage, CPropertyPage)
CMetadataTablesPage::CMetadataTablesPage()
	: CPropertyPage(CMetadataTablesPage::IDD)
{
}

CMetadataTablesPage::~CMetadataTablesPage()
{
}

void CMetadataTablesPage::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TABLETREE, m_ctrlTableTree);
}

BEGIN_MESSAGE_MAP(CMetadataTablesPage, CPropertyPage)
END_MESSAGE_MAP()

// Procedury obsługi wiadomości CMetadataTablesPage
BOOL CMetadataTablesPage::OnInitDialog()
{
	CPropertyPage::OnInitDialog();

	// Wypełnienie danymi
	CPropertySheet *pPropertySheet = STATIC_DOWNCAST(CPropertySheet, GetParent());
	CAssemblyView* pView = STATIC_DOWNCAST(CAssemblyView, pPropertySheet->GetParent());

	// Liczba tablic w bazie danych.
	ULONG cTables;
    cTables = pView->MetaDataTableCount();

	HTREEITEM treeRowItem;
    for (MetaDataConstantIterator it =  pView->MetaDataBegin();
		 it != pView->MetaDataEnd();
		 ++it)
    {
		if(it->second.Rows > 0)
		{
			treeRowItem = m_ctrlTableTree.InsertItem(it->second.Name.c_str());
			switch(it->first)
			{
			case Module:
				HandleModule(it->second, pView, treeRowItem);
				break;
			case TypeRef:
				HandleTypeRef(it->second, pView, treeRowItem);
				break;
			case TypeDef:
				HandleTypeDef(it->second, pView, treeRowItem);
				break;
			case FieldDef:
				HandleFieldDef(it->second, pView, treeRowItem);
				break;
			case MethodDef:
				HandleMethodDef(it->second, pView, treeRowItem);
				break;
			case ParamDef:
				HandleParamDef(it->second, pView, treeRowItem);
				break;
			case InterfaceImpl:
				HandleInterfaceImpl(it->second, pView, treeRowItem);
				break;
			case MemberRef:
				HandleMemberRef(it->second, pView, treeRowItem);
				break;
			case Constant:
				HandleConstant(it->second, pView, treeRowItem);
				break;
			case CustomAttribute:
				HandleCustomAttribute(it->second, pView, treeRowItem);
				break;
			case FieldMarshal:
				HandleFieldMarshal(it->second, pView, treeRowItem);
				break;
			case DeclSecurity:
				HandleDeclSecurity(it->second, pView, treeRowItem);
				break;
			case ClassLayout:
				HandleClassLayout(it->second, pView, treeRowItem);
				break;
			case StandAloneSig:
				HandleStandAloneSig(it->second, pView, treeRowItem);
				break;
			case EventMap:
				HandleEventMap(it->second, pView, treeRowItem);
				break;
			case Event:
				HandleEvent(it->second, pView, treeRowItem);
				break;
			case PropertyMap:
				HandlePropertyMap(it->second, pView, treeRowItem);
				break;
			case Property:
				HandleProperty(it->second, pView, treeRowItem);
				break;
			case MethodSemantics:
				HandleMethodSemantics(it->second, pView, treeRowItem);
				break;
			case ModuleRef:
				HandleModuleRef(it->second, pView, treeRowItem);
				break;
			case TypeSpec:
				HandleTypeSpec(it->second, pView, treeRowItem);
				break;
			case ImplMap:
				HandleImplMap(it->second, pView, treeRowItem);
				break;
			case FieldRVA:
				HandleFieldRVA(it->second, pView, treeRowItem);
				break;
			case Assembly:
				HandleAssembly(it->second, pView, treeRowItem);
				break;
			case AssemblyProcessor:
				HandleAssemblyProcessor(it->second, pView, treeRowItem);
				break;
			case AssemblyOS:
				HandleAssemblyOS(it->second, pView, treeRowItem);
				break;
			case AssemblyRef:
				HandleAssemblyRef(it->second, pView, treeRowItem);
				break;
			case AssemblyRefProcessor:
				HandleAssemblyRefProcessor(it->second, pView, treeRowItem);
				break;
			case AssemblyRefOS:
				HandleAssemblyRefOS(it->second, pView, treeRowItem);
				break;
			case File:
				HandleFile(it->second, pView, treeRowItem);
				break;
			case ExportedType:
				HandleExportedType(it->second, pView, treeRowItem);
				break;
			case ManifestResource:
				HandleManifestResource(it->second, pView, treeRowItem);
				break;
			case NestedClass:
				HandleNestedClass(it->second, pView, treeRowItem);
				break;
			default:
				break;
			}
		}
	}
	return TRUE;  // zwrócenie TRUE, chyba że ognisko ustawiono na element sterujący
}

void CMetadataTablesPage::HandleModule(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	HTREEITEM treeRowItem;
	PBYTE row;
	wchar_t lBuffer[256];
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		row = t.Address + t.RowSize * i;

		// Generacja jest zawsze zerowa, a więc można ją pominąć.
		row += 2;

		if(t.HeapSizes & 0x01)
		{
			wcscpy(lBuffer, pView->StringTableEntry(*((ULONG *)row)).c_str());
			row += 4;
		}
		else
		{
			wcscpy(lBuffer, pView->StringTableEntry(*((USHORT *)row)).c_str());
			row += 2;
		}
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

		if(t.HeapSizes & 0x02)
		{
			wcscpy(lBuffer, pView->GUIDTableEntry(*((ULONG *)row)).c_str());
			row += 4;
		}
		else
		{
			wcscpy(lBuffer, pView->GUIDTableEntry(*((USHORT *)row)).c_str());
			row += 2;
		}
		m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	}
}

void CMetadataTablesPage::HandleSingleTypeRef(int index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// ResolutionScope (indeks tablicy Module, ModuleRef, AssemblyRef lub TypeRef albo pusta wartość; jest to zakodowany indeks ResolutionScope)
	// Nazwa (indeks sterty String)
	// Przestrzeń nazw (indeks sterty String)
	HTREEITEM treeRowItem;
	HTREEITEM resolutionScopeItem;
	PBYTE row;
	WCHAR lBuffer[256];
	DWORD resolutionScope;
	BYTE resolutionTableSpec;
	DWORD nameIndex;
	DWORD nameSpaceIndex;
	DWORD resolutionIndex;

	row = t.Address + t.RowSize * index;

	// ResolutionScope
	if(t.IndexSizes & 0x01)
	{
		resolutionScope = *((DWORD *)row);
		row += 4;
	}
	else
	{
		resolutionScope = *((USHORT *)row);
		row += 2;
	}
	resolutionTableSpec = (byte)(resolutionScope & 0x3);
	resolutionScope = resolutionScope >> 2;
	resolutionIndex = resolutionScope;
	switch(resolutionTableSpec)
	{
	case 0:
		// Moduł to tablica 0
		break;
	case 1:
		// ModuleRef to tablica 0x1A
		resolutionScope |= 0x1A << 24;
		break;
	case 2:
		// AssemblyRef to tablica 0x23
		resolutionScope |= 0x23 << 24;
		break;
	case 3:
		// TypeRef to tablica 0x01
		resolutionScope |= 0x01 << 24;
		break;
	}

	// Nazwa
	if(t.HeapSizes & 0x01)
	{
		nameIndex = *((ULONG *)row);
		row += 4;
		nameSpaceIndex = *((ULONG *)row);
		row += 4;
		wsprintf(lBuffer, L"%ls.%ls",
			              pView->StringTableEntry(nameSpaceIndex).c_str(),
			              pView->StringTableEntry(nameIndex).c_str());
	}
	else
	{
		nameIndex = *((USHORT *)row);
		row += 2;
		nameSpaceIndex = *((USHORT *)row);
		row += 2;
		wsprintf(lBuffer, L"%ls.%ls",
			              pView->StringTableEntry(nameSpaceIndex).c_str(),
			              pView->StringTableEntry(nameIndex).c_str());
	}
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
	wsprintf(lBuffer, L"Zakres: 0x%08X -> %ls(%d)",
		              resolutionScope,
		              (resolutionTableSpec == 0) ? L"Moduł" : 
	                  (resolutionTableSpec == 1) ? L"ModuleRef" : 
	                  (resolutionTableSpec == 2) ? L"AssemblyRef" : 
	                  (resolutionTableSpec == 3) ? L"TypeRef" : L"nieznany",
		              resolutionIndex);
	resolutionScopeItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	MetaDataConstantIterator it = pView->MetaDataFind((TableType)(resolutionScope >> 24));
	if(it != pView->MetaDataEnd())
	{
		if(resolutionScope >> 24 == 0)
			HandleModule(it->second, pView, resolutionScopeItem);
		else if(resolutionScope >> 24 == 0x1A &&
			    resolutionIndex > 0)
			HandleSingleModuleRef(resolutionIndex - 1, it->second, pView, resolutionScopeItem);
		else if(resolutionScope >> 24 == 0x23 &&
			    resolutionIndex > 0)
			HandleSingleAssemblyRef(resolutionIndex - 1, it->second, pView, resolutionScopeItem);
		else if(resolutionScope >> 24 == 0x01 &&
			    resolutionIndex > 0)
			HandleSingleTypeRef(resolutionIndex - 1, it->second, pView, resolutionScopeItem);
	}
}

void CMetadataTablesPage::HandleTypeRef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleTypeRef(i, t, pView, tvitem);
	}
}

void CMetadataTablesPage::TypeAttributes(CorTypeAttr flags, HTREEITEM flagsItem)
{
	// 22.1.14	Znaczniki typów [TypeAttributes]
	if(flags & tdVisibilityMask)
	{
		CorTypeAttr accessFlags = (CorTypeAttr)(flags & tdVisibilityMask);
		if(accessFlags == tdNestedFamORAssem)
			m_ctrlTableTree.InsertItem(L"Klasa jest zagnieżdżona oraz widoczna w rodzinie lub podzespole.", flagsItem);
		if(accessFlags == tdNestedFamANDAssem)
			m_ctrlTableTree.InsertItem(L"Klasa jest zagnieżdżona oraz widoczna w rodzinie i podzespole.", flagsItem);
		if(accessFlags == tdNestedAssembly)
			m_ctrlTableTree.InsertItem(L"Klasa jest zagnieżdżona oraz widoczna w podzespole.", flagsItem);
		if(accessFlags == tdNestedFamily)
			m_ctrlTableTree.InsertItem(L"Klasa jest zagnieżdżona oraz widoczna w rodzinie.", flagsItem);
		if(accessFlags == tdNestedPrivate)
			m_ctrlTableTree.InsertItem(L"Klasa jest zagnieżdżona oraz widoczna prywatnie.", flagsItem);
		if(accessFlags == tdNestedPublic)
			m_ctrlTableTree.InsertItem(L"Klasa jest zagnieżdżona oraz widoczna publicznie.", flagsItem);
		if(accessFlags == tdPublic)
			m_ctrlTableTree.InsertItem(L"Klasa ma publiczny zakres.", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"Klasa nie ma publicznego zakresu.",  flagsItem);
	}

	if(flags & tdLayoutMask)
	{
		CorTypeAttr layoutFlags = (CorTypeAttr)(flags & tdLayoutMask);
		if(layoutFlags == tdSequentialLayout)
			m_ctrlTableTree.InsertItem(L"Pola klasy są ułożone kolejno.", flagsItem);
		if(layoutFlags == tdExplicitLayout)
			m_ctrlTableTree.InsertItem(L"Struktura jest podana", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"Pola klasy są ułożone automatycznie",  flagsItem);
	}

	if(flags & tdClassSemanticsMask)
	{
		CorTypeAttr semanticsFlags = (CorTypeAttr)(flags & tdClassSemanticsMask);
		if(semanticsFlags == tdInterface)
			m_ctrlTableTree.InsertItem(L"Typ jest interfejsem.", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"Typ jest klasą.",  flagsItem);
	}

	if(flags & tdAbstract)
		m_ctrlTableTree.InsertItem(L"Klasa abstrakcyjna.", flagsItem);
	if(flags & tdSealed)
		m_ctrlTableTree.InsertItem(L"Konkretna klasa, a jej rozszerzenie może nie być możliwe.", flagsItem);
	if(flags & tdSpecialName)
		m_ctrlTableTree.InsertItem(L"Specjalna nazwa klasy. Opis w nazwie.", flagsItem);

	if(flags & tdImport)
		m_ctrlTableTree.InsertItem(L"Importowana klasa lub interfejs.", flagsItem);
	if(flags & tdSerializable)
		m_ctrlTableTree.InsertItem(L"Klasa może być szeregowana.", flagsItem);

	if(flags & tdStringFormatMask)
	{
		CorTypeAttr stringFormatFlags = (CorTypeAttr)(flags & tdStringFormatMask);
		if(stringFormatFlags == tdUnicodeClass)
			m_ctrlTableTree.InsertItem(L"LPTSTR jest interpretowany jako UNICODE.", flagsItem);
		if(stringFormatFlags == tdAutoClass)
			m_ctrlTableTree.InsertItem(L"LPTSTR jest interpretowany automatycznie.", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"LPTSTR jest interpretowany w tej klasie jako ANSI.",  flagsItem);
	}

	if(flags & tdBeforeFieldInit)
		m_ctrlTableTree.InsertItem(L"Inicjalizacja klasy w dowolnym momencie przed pierwszym dostępem do pola statycznego.", flagsItem);

	if(flags & tdReservedMask)
	{
		CorTypeAttr reservedFlags = (CorTypeAttr)(flags & tdReservedMask);
		if(reservedFlags == tdRTSpecialName)
			m_ctrlTableTree.InsertItem(L"Moduł wykonawczy powinien sprawdzić kodowanie nazwy.", flagsItem);
		if(reservedFlags == tdHasSecurity)
			m_ctrlTableTree.InsertItem(L"Z klasą powiązane jest zabezpieczenie.", flagsItem);
	}
}

void CMetadataTablesPage::HandleSingleTypeDef(DWORD index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// Znaczniki (4-bajtowa maska bitowa typu TypeAttributes, sekcja 22.1.14)
	// Nazwa (indeks sterty String)
	// Przestrzeń nazw (indeks sterty String)
	// Extends (indeks tablicy TypeDef, TypeRef lub TypeSpec; jest to zakodowany indeks TypeDefOrRef)
	// FieldList (indeks tablicy Field; znakuje pierwsze ciągłe uruchomienie Fields należących do tego typu). Praca jest kontynuowana aż do osiągnięcia mniejszej wartości:
	//     ostatniego wiersza tablicy Field
	//     kolejnego uruchomienia Fields, które można odnaleźć poprzez zbadanie FieldList kolejnego wiersza w danej tablicy TypeDef
	// MethodList (indeks tablicy Method; znakuje pierwsze ciągłe uruchomienie Methods należących do tego typu). Praca jest kontynuowana aż do osiągnięcia mniejszej wartości:
	//     ostatniego wiersza tablicy Method
	//     kolejnego uruchomienia Methods, które można odnaleźć poprzez zbadanie MethodList kolejnego wiersza w danej tablicy TypeDef
	HTREEITEM treeRowItem;
	HTREEITEM extendsItem;
	HTREEITEM fieldsItem;
	HTREEITEM methodsItem;
	PBYTE row;
	WCHAR lBuffer[256];
	DWORD flags;
	DWORD nameIndex;
	DWORD nameSpaceIndex;
	DWORD extends;
	BYTE extendsTable;
	DWORD extendsIndex;
	DWORD fields;
	DWORD endFields;
	DWORD methods;
	DWORD endMethods;

	row = t.Address + t.RowSize * index;

	// Znaczniki
	flags = *((DWORD *)row);
	row += 4;

	// Nazwa
	if(t.HeapSizes & 0x01)
	{
		nameIndex = *((ULONG *)row);
		row += 4;
		nameSpaceIndex = *((ULONG *)row);
		row += 4;
		wsprintf(lBuffer, L"%ls.%ls",
			pView->StringTableEntry(nameSpaceIndex).c_str(),
			pView->StringTableEntry(nameIndex).c_str());
	}
	else
	{
		nameIndex = *((USHORT *)row);
		row += 2;
		nameSpaceIndex = *((USHORT *)row);
		row += 2;
		wsprintf(lBuffer, L"%ls.%ls",
			pView->StringTableEntry(nameSpaceIndex).c_str(),
			pView->StringTableEntry(nameIndex).c_str());
	}
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	TypeAttributes((CorTypeAttr)flags, treeRowItem);

	// Extends
	if(t.IndexSizes & 0x08)
	{
		extends = *((DWORD *)row);
		row += 4;
	}
	else
	{
		extends = *((USHORT *)row);
		row += 2;
	}
	extendsTable = (BYTE)(extends & 0x3);
	extendsIndex = extends = extends >> 2;
	switch(extendsTable)
	{
	case 0:
		// TypeDef
		extends |= 0x02 << 24;
		break;
	case 1:
		// TypeRef
		extends |= 0x01 << 24;
		break;
	case 2:
		// TypeSpec
		extends |= 0x1b << 24;
		break;
	}

	wsprintf(lBuffer, L"Extends: 0x%08X -> %ls(%d)",
		              extends,
		              (extendsTable == 0) ? L"TypeDef" : 
	                  (extendsTable == 1) ? L"TypeRef" : 
	                  (extendsTable == 2) ? L"TypeSpec" : L"nieznany",
		              extendsIndex);
	extendsItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	MetaDataConstantIterator it = pView->MetaDataFind((TableType)(extends >> 24));
	if(it != pView->MetaDataEnd())
	{
		if(extends >> 24 == 0x02 &&
		   extendsIndex > 0)
			HandleSingleTypeDef(extendsIndex - 1, it->second, pView, extendsItem);
		else if(extends >> 24 == 0x01 &&
			    extendsIndex > 0)
			HandleSingleTypeRef(extendsIndex - 1, it->second, pView, extendsItem);
	}

	// FieldList
	if(t.IndexSizes & 0x08)
	{
		fields = *((DWORD *)row);
		if(index < t.Rows - 1)
			endFields = *((DWORD *)(row + t.RowSize));
		else
			endFields = 0;
		row += 4;
	}
	else
	{
		fields = *((USHORT *)row);
		if(index < t.Rows - 1)
			endFields = *((USHORT *)(row + t.RowSize));
		else
			endFields = 0;
		row += 2;
	}
	wsprintf(lBuffer, L"FieldList: 0x%08X", fields);
	fieldsItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	// Uzyskanie wskaźnika tablicy pól jest wymagane tylko w przypadku osiągnięcia
	// końca listy, co pozwoli na sprawdzenie, czy dany typ posiada pola.
	if(index >= t.Rows - 1 ||
	   fields < endFields)
	{
		MetaDataConstantIterator it = pView->MetaDataFind((TableType)FieldDef);
		if(it != pView->MetaDataEnd())
		{
			// Wartość początkowa musi znaleźć się w zakresie 
			// dozwolonych wartości listy parametrów.
			if(fields < t.Rows)
			{
				// Jeśli osiągnięto koniec listy metod,
				// to należy upewnić się, czy ustawiono
				// koniec listy parametrów.
				if(index >= t.Rows - 1)
					endFields = it->second.Rows + 1;
				while(fields < endFields)
				{
					HandleSingleFieldDef(fields - 1, it->second, pView, fieldsItem);
					fields++;
				}
			}
		}
	}

	// MethodList
	if(t.IndexSizes & 0x08)
	{
		methods = *((DWORD *)row);
		if(index < t.Rows - 1)
			endMethods = *((DWORD *)(row + t.RowSize));
		else
			endMethods = 0;
		row += 4;
	}
	else
	{
		methods = *((USHORT *)row);
		if(index < t.Rows - 1)
			endMethods = *((USHORT *)(row + t.RowSize));
		else
			endMethods = 0;
		row += 2;
	}
	wsprintf(lBuffer, L"MethodList: 0x%08X", methods);
	methodsItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	// Uzyskanie wskaźnika tablicy pól jest wymagane tylko w przypadku osiągnięcia
	// końca listy, co pozwoli na sprawdzenie, czy dany typ posiada pola.
	if(index >= t.Rows - 1 ||
	   methods < endMethods)
	{
		MetaDataConstantIterator it = pView->MetaDataFind((TableType)MethodDef);
		if(it != pView->MetaDataEnd())
		{
			// Wartość początkowa musi znaleźć się w zakresie 
			// dozwolonych wartości listy parametrów.
			if(methods < t.Rows)
			{
				// Jeśli osiągnięto koniec listy metod,
				// to należy upewnić się, czy ustawiono
				// koniec listy parametrów.
				if(index >= t.Rows - 1)
					endMethods = it->second.Rows + 1;
				while(methods < endMethods)
				{
					HandleSingleMethodDef(methods - 1, it->second, pView, methodsItem);
					methods++;
				}
			}
		}
	}
}

void CMetadataTablesPage::HandleTypeDef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleTypeDef(i, t, pView, tvitem);
	}
}

void CMetadataTablesPage::HandleSingleFieldDef(DWORD index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// Znaczniki (dwubajtowa maska bitowa typu FieldAttributes, sekcja 22.1.5)
	// Nazwa (indeks sterty String)
	// Sygnatura (indeks sterty Blob)
	HTREEITEM treeRowItem;
	PBYTE row;
	WCHAR lBuffer[256];
	USHORT flags;
	DWORD name;
	DWORD signature;
	row = t.Address + t.RowSize * index;

	// Znaczniki
	flags = *((USHORT *)row);
	row += 2;

	// Nazwa
	if(t.HeapSizes & 0x01)
	{
		name = *((ULONG *)row);
		row += 4;
	}
	else
	{
		name = *((USHORT *)row);
		row += 2;
	}

	// Sygnatura
	if(t.HeapSizes & 0x04)
	{
		signature = *((ULONG *)row);
		row += 4;
	}
	else
	{
		signature = *((USHORT *)row);
		row += 2;
	}

	wcscpy(lBuffer, pView->StringTableEntry(name).c_str());
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	wsprintf(lBuffer, L"Znaczniki: 0x%04X", flags);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	wsprintf(lBuffer, L"Sygnatura: 0x%04X", signature);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
}

void CMetadataTablesPage::HandleFieldDef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleFieldDef(i, t, pView, tvitem);
	}
}

std::wstring TypeSig(PBYTE blob, DWORD& index);

std::wstring ArrayShape(PBYTE blob, DWORD& index)
{
	std::wostringstream ret;
	ret << TypeSig(blob, index);
	DWORD rank = 0;
	DWORD numSizes = 0;
	index += CorSigUncompressData(&(blob[index]), &rank);
	index += CorSigUncompressData(&(blob[index]), &numSizes);
	ULONG * sizeArray = new ULONG[numSizes];
	for(DWORD i = 0;i < numSizes;i++)
	{
		index += CorSigUncompressData(&(blob[index]), &sizeArray[i]);
	}
	DWORD numBounds = 0;
	index += CorSigUncompressData(&(blob[index]), &numBounds);
	ULONG * boundArray = new ULONG[numBounds];
	for(DWORD i = 0;i < numBounds;i++)
	{
		index += CorSigUncompressData(&(blob[index]), &boundArray[i]);
	}
	ret << L"[";
	for(DWORD i = 0;i < rank;i++)
	{
		if(i < numSizes)
		{
			if(i < numBounds)
			{
				ret << boundArray[i];
				ret << L"...";
				ret << boundArray[i] + sizeArray[i] - 1;
			}
			else
			{
				ret << L"0...";
				ret << sizeArray[i] - 1;
			}
		}
		if(i != rank - 1)
			ret << L",";
	}
	ret << L"]";
	delete [] boundArray;
	delete [] sizeArray;
	return ret.str();
}

// 22.2.12
std::wstring TypeSig(PBYTE blob, DWORD& index)
{
	std::wostringstream ret;
	mdToken typeDefOrRef;
	int sigByteLength;
	mdToken customModToken;
	switch((CorElementType)blob[index])
	{
		case ELEMENT_TYPE_BOOLEAN:
			index++;
			ret << L"bool";
			break;
		case ELEMENT_TYPE_CHAR:
			index++;
			ret << L"char";
			break;
		case ELEMENT_TYPE_I1:
			index++;
			ret << L"I1";
			break;
		case ELEMENT_TYPE_U1:
			index++;
			ret << L"byte";
			break;
		case ELEMENT_TYPE_U2:
			index++;
			ret << L"ushort";
			break;
		case ELEMENT_TYPE_I2:
			index++;
			ret << L"short";
			break;
		case ELEMENT_TYPE_I4:
			index++;
			ret << L"int";
			break;
		case ELEMENT_TYPE_U4:
			index++;
			ret << L"uint";
			break;
		case ELEMENT_TYPE_I8:
			index++;
			ret << L"long";
			break;
		case ELEMENT_TYPE_U8:
			index++;
			ret << L"ulong";
			break;
		case ELEMENT_TYPE_R4:
			index++;
			ret << L"float";
			break;
		case ELEMENT_TYPE_R8:
			index++;
			ret << L"double";
			break;
		case ELEMENT_TYPE_STRING:
			index++;
			ret << L"string";
			break;
		case ELEMENT_TYPE_I:
			index++;
			ret << L"IntPtr";
			break;
		case ELEMENT_TYPE_U:
			index++;
			ret << L"UIntPtr";
			break;
		case ELEMENT_TYPE_VALUETYPE:
			ret << L"VALUETYPE ";
			index++;
			sigByteLength = CorSigUncompressToken(&blob[index], &typeDefOrRef);
			index += sigByteLength;
			ret << L"0x";
			ret << std::hex << typeDefOrRef;
			break;
		case ELEMENT_TYPE_CLASS:
			ret << L"CLASS ";
			index++;
			sigByteLength = CorSigUncompressToken(&blob[index], &typeDefOrRef);
			index += sigByteLength;
			ret << L"0x";
			ret << std::hex << typeDefOrRef;
			break;
		case ELEMENT_TYPE_ARRAY:
			index++;
			ret << ArrayShape(blob, index);
			break;
		case ELEMENT_TYPE_SZARRAY:
			index++;
			ret << TypeSig(blob, index) << L"[]";
			break;
		case ELEMENT_TYPE_OBJECT:
			index++;
			ret << L"object";
			break;
		case ELEMENT_TYPE_CMOD_REQD:
		case ELEMENT_TYPE_CMOD_OPT:
			ret << ((blob[index] == ELEMENT_TYPE_CMOD_REQD) ? L"CMOD_REQD " : L"CMOD_OPT ");
			sigByteLength = CorSigUncompressToken(&blob[index], &customModToken);
			index += sigByteLength;
			sigByteLength = CorSigUncompressToken(&blob[index], &typeDefOrRef);
			index += sigByteLength;
			ret << L"0x" << std::hex << typeDefOrRef << L" ";
			ret << TypeSig(blob, index);
			break;
		case ELEMENT_TYPE_PTR:
			index++;
			ret << L"PTR ";
			ret << TypeSig(blob, index);
			break;
		case ELEMENT_TYPE_FNPTR:
			ret << L"FNPTR";
			break;
		default:
			ret << L"0x";
			ret << std::hex << blob[index];
			index++;
			break;
	}
	return ret.str();
}

void SkipArrayShape(PBYTE blob, DWORD& index)
{
	DWORD rank = 0;
	DWORD numSizes = 0;
	DWORD size;
	DWORD bound;
	index += CorSigUncompressData(&(blob[index]), &rank);
	index += CorSigUncompressData(&(blob[index]), &numSizes);
	for(DWORD i = 0;i < numSizes;i++)
	{
		index += CorSigUncompressData(&(blob[index]), &size);
	}
	DWORD numBounds = 0;
	index += CorSigUncompressData(&(blob[index]), &numBounds);
	for(DWORD i = 0;i < numBounds;i++)
	{
		index += CorSigUncompressData(&(blob[index]), &bound);
	}
}

// 22.2.12
bool IsTypeSig(CorElementType type, PBYTE blob, DWORD& index)
{
	bool ret = false;
	mdToken typeDefOrRef;
	int sigByteLength;
	mdToken customModToken;
	if(type == (CorElementType)blob[index])
		ret = true;
	else
		ret = false;
	switch((CorElementType)blob[index])
	{
		case ELEMENT_TYPE_BOOLEAN:
		case ELEMENT_TYPE_CHAR:
		case ELEMENT_TYPE_I1:
		case ELEMENT_TYPE_U1:
		case ELEMENT_TYPE_U2:
		case ELEMENT_TYPE_I2:
		case ELEMENT_TYPE_I4:
		case ELEMENT_TYPE_U4:
		case ELEMENT_TYPE_I8:
		case ELEMENT_TYPE_U8:
		case ELEMENT_TYPE_R4:
		case ELEMENT_TYPE_R8:
		case ELEMENT_TYPE_STRING:
		case ELEMENT_TYPE_I:
		case ELEMENT_TYPE_U:
		case ELEMENT_TYPE_OBJECT:
			index++;
			break;
		case ELEMENT_TYPE_PTR:
			index++;
			IsTypeSig(type, blob, index);
			break;
		case ELEMENT_TYPE_VALUETYPE:
			index++;
			sigByteLength = CorSigUncompressToken(&blob[index], &typeDefOrRef);
			index += sigByteLength;
			break;
		case ELEMENT_TYPE_CLASS:
			index++;
			sigByteLength = CorSigUncompressToken(&blob[index], &typeDefOrRef);
			index += sigByteLength;
			break;
		case ELEMENT_TYPE_ARRAY:
			index++;
			// Array type
			IsTypeSig(type, blob, index);
			SkipArrayShape(blob, index);
			break;
		case ELEMENT_TYPE_SZARRAY:
			index++;
			IsTypeSig(type, blob, index);
			break;
		case ELEMENT_TYPE_CMOD_REQD:
		case ELEMENT_TYPE_CMOD_OPT:
			sigByteLength = CorSigUncompressToken(&blob[index], &customModToken);
			index += sigByteLength;
			sigByteLength = CorSigUncompressToken(&blob[index], &typeDefOrRef);
			index += sigByteLength;
			IsTypeSig(type, blob, index);
			break;
		case ELEMENT_TYPE_FNPTR:
		default:
			index++;
			break;
	}
	return ret;
}

void CMetadataTablesPage::MethodImplAttributes(CorMethodImpl flags, HTREEITEM flagsItem)
{
	if(flags & miCodeTypeMask)
	{
		CorMethodImpl typeFlags = (CorMethodImpl)(flags & miCodeTypeMask);
		if(typeFlags == miNative)
			m_ctrlTableTree.InsertItem(L"Metoda impl jest typu macierzystego.", flagsItem);
		if(typeFlags == miOPTIL)
			m_ctrlTableTree.InsertItem(L"Metoda impl jest typu OPTIL.", flagsItem);
		if(typeFlags == miRuntime)
			m_ctrlTableTree.InsertItem(L"miRuntime", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"Metoda impl jest typu CIL.", flagsItem);
	}
	if(flags & miManagedMask)
	{
		m_ctrlTableTree.InsertItem(L"Metoda impl jest typu niezarządzanego.", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"Metoda impl jest typu zarządzanego.", flagsItem);
	}
	if(flags & miForwardRef)
		m_ctrlTableTree.InsertItem(L"Wskazuje, że metoda jest zdefiniowana; używane w przypadku łączenia.", flagsItem);
	if(flags & miPreserveSig)
		m_ctrlTableTree.InsertItem(L"Wskazuje, że sygnatura metody nie powinna być maglowana w celu konwersji do HRESULT.", flagsItem);
	if(flags & miInternalCall)
		m_ctrlTableTree.InsertItem(L"Zarezerwowane do wewnętrznego użytku.", flagsItem);
	if(flags & miSynchronized)
		m_ctrlTableTree.InsertItem(L"Metoda posiada pojedynczy wątek.", flagsItem);
	if(flags & miNoInlining)
		m_ctrlTableTree.InsertItem(L"Metoda nie może być wbudowana.", flagsItem);
}

void CMetadataTablesPage::MethodAttributes(CorMethodAttr flags, HTREEITEM flagsItem)
{
	// 22.1.9	Flags for Methods [MethodAttributes]
	if(flags & mdMemberAccessMask)
	{
		CorMethodAttr accessFlags = (CorMethodAttr)(flags & mdMemberAccessMask);
		if(accessFlags == mdPrivate)
			m_ctrlTableTree.InsertItem(L"Dostępna tylko dla typu macierzystego.", flagsItem);
		if(accessFlags == mdFamANDAssem)
			m_ctrlTableTree.InsertItem(L"Dostępna dla podtypów tylko w tym podzespole.", flagsItem);
		if(accessFlags == mdAssem)
			m_ctrlTableTree.InsertItem(L"Dostępna dla całego podzespołu.", flagsItem);
		if(accessFlags == mdFamily)
			m_ctrlTableTree.InsertItem(L"Dostępna tylko dla typu i podtypów.", flagsItem);
		if(accessFlags == mdFamORAssem)
			m_ctrlTableTree.InsertItem(L"Dostępna dla podtypów w dowolnym miejscu oraz dla całego podzespołu.", flagsItem);
		if(accessFlags == mdPublic)
			m_ctrlTableTree.InsertItem(L"Dostępna dla wszystkich elementów, dla których ten zakres jest widoczny.", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"PrivateScope",  flagsItem);
	}
	if(flags & mdStatic)
		m_ctrlTableTree.InsertItem(L"Statyczna", flagsItem);
	if(flags & mdFinal)
		m_ctrlTableTree.InsertItem(L"Metoda nie może być zastąpiona.", flagsItem);
	if(flags & mdVirtual)
		m_ctrlTableTree.InsertItem(L"Wirtualna", flagsItem);
	if(flags & mdHideBySig)
		m_ctrlTableTree.InsertItem(L"Metoda ukrywana według nazwy i sygnatury, a w innym wypadku według nazwy.", flagsItem);
	if(flags & mdVtableLayoutMask)
		m_ctrlTableTree.InsertItem(L"Metoda wykorzystuje istniejący wpis w vtable.", flagsItem);
	else
		m_ctrlTableTree.InsertItem(L"Metoda zawsze zajmuje nowy wpis w vtable.", flagsItem);
	if(flags & mdAbstract)
		m_ctrlTableTree.InsertItem(L"Metoda nie zapewnia implementacji.", flagsItem);
	if(flags & mdSpecialName)
		m_ctrlTableTree.InsertItem(L"SpecialName", flagsItem);
	if(flags & mdPinvokeImpl)
		m_ctrlTableTree.InsertItem(L"Implementacja jest przekazywana poprzez PInvoke.", flagsItem);
	if(flags & mdUnmanagedExport)
		m_ctrlTableTree.InsertItem(L"UnmanagedExport", flagsItem);
	if(flags & mdReservedMask)
	{
		CorMethodAttr reservedFlags = (CorMethodAttr)(flags & mdReservedMask);
		if(reservedFlags == mdRTSpecialName)
			m_ctrlTableTree.InsertItem(L"CLI zapewnia specjalne zachowanie w zależności od nazwy metody.", flagsItem);
		if(reservedFlags == mdHasSecurity)
			m_ctrlTableTree.InsertItem(L"Z metodą powiązane jest zabezpieczenie.", flagsItem);
		if(reservedFlags == mdRequireSecObject)
			m_ctrlTableTree.InsertItem(L"Metoda wywołuje inną metodę zawierającą kod zabezpieczeń.", flagsItem);
	}
}

void CMetadataTablesPage::MethodSignatureBlob(DWORD blobTableIndex, CAssemblyView* pView, HTREEITEM tvitem)
{
	DWORD size = 0;
	PBYTE blob = pView->BlobTableEntry(blobTableIndex, &size);

	std::wostringstream ret;

	DWORD index = 0;
	// Opening byte in a MethodDefSig (22.2.1)
	if(((CorCallingConvention)blob[index] & IMAGE_CEE_CS_CALLCONV_HASTHIS) > 0)
		ret << L"HASTHIS ";
	if(((CorCallingConvention)blob[index] & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) > 0)
		ret << L"EXPLICITTHIS ";
	if(((CorCallingConvention)blob[index] & IMAGE_CEE_CS_CALLCONV_VARARG) > 0)
		ret << L"VARARG ";
	else
		ret << L"DEFAULT ";
	index++;

	// Skompresowany licznik parametrów (także 22.2.1)
	DWORD parameterCount;
	index += CorSigUncompressData(&blob[index], &parameterCount);

	// Początek dekodowania typu zwrotnego (22.2.1)
	// Sprawdzenie obecności własnych modyfikatorów (22.2.11)
	while((CorElementType)blob[index] == ELEMENT_TYPE_CMOD_REQD ||
	      (CorElementType)blob[index] == ELEMENT_TYPE_CMOD_OPT)
	{
		ret << ((blob[index] == ELEMENT_TYPE_CMOD_REQD) ? L"CMOD_REQD " : L"CMOD_OPT ");
		// Najpierw dekompresja wartości
		// 22.2.8	TypeDefOrRefEncoded 
		int sigByteLength;
		mdToken customModToken;
		mdToken typeToken;
		sigByteLength = CorSigUncompressToken(&blob[index], &customModToken);
		index += sigByteLength;
		sigByteLength = CorSigUncompressToken(&blob[index], &typeToken);
		index += sigByteLength;
		ret << L"0x" << std::hex << typeToken << L" ";
	}
	// Sprawdzenie czy typ może być ograniczony 
	// przez TYPEDBYREF lub VOID (22.2.11)
	if((CorElementType)blob[index] == ELEMENT_TYPE_TYPEDBYREF)
	{
		index++;
		ret << L"typed by ref (";
	}
	else
	{
		if((CorElementType)blob[index] == ELEMENT_TYPE_VOID)
		{
			index++;
			ret << L"void (";
		}
		else
		{
			// Sprawdzenie bajtu BYREF
			if((CorElementType)blob[index] == ELEMENT_TYPE_BYREF)
			{
				index++;
				ret << L"ref ";
			}
			// Uzyskanie typu zwrotnego typu
			ret << TypeSig(blob, index);
			ret << L"(";
		}
	}
	// Dekodowanie parametrów
	for(DWORD i = 0; i < parameterCount; i++)
	{
    // Początek dekodowania typu zwrotnego (22.2.1)
	// Sprawdzenie obecności własnych modyfikatorów (22.2.11)
		while((CorElementType)blob[index] == ELEMENT_TYPE_CMOD_REQD ||
			  (CorElementType)blob[index] == ELEMENT_TYPE_CMOD_OPT)
		{
			ret << ((blob[index] == ELEMENT_TYPE_CMOD_REQD) ? L"CMOD_REQD " : L"CMOD_OPT ");
			// Najpierw dekompresja wartości
			// 22.2.8	TypeDefOrRefEncoded 
			int sigByteLength;
			mdToken customModToken;
			mdToken typeToken;
			sigByteLength = CorSigUncompressToken(&blob[index], &customModToken);
			index += sigByteLength;
			sigByteLength = CorSigUncompressToken(&blob[index], &typeToken);
			index += sigByteLength;
			
			ret << L"0x" << std::hex << typeToken << L" ";
		}
		// Sprawdzenie czy typ może być ograniczony 
	    // przez TYPEDBYREF lub VOID (22.2.11)
		if((CorElementType)blob[index] == ELEMENT_TYPE_TYPEDBYREF)
		{
			index++;
			ret << L"typed by ref ";
		}
		else
		{
			// Sprawdzenie bajtu BYREF
			if((CorElementType)blob[index] == ELEMENT_TYPE_BYREF)
			{
				index++;
				ret << L"ref ";
			}
			// Uzyskanie typu zwrotnego typu
			ret << TypeSig(blob, index);
			if(i != parameterCount - 1)
				ret << L",";
		}			
	}
	ret << L")";

	 m_ctrlTableTree.InsertItem(ret.str().c_str(), tvitem);
}

void CMetadataTablesPage::OutputExceptionHandlers(PBYTE exceptionPointer, CAssemblyView* pView, HTREEITEM tvitem)
{
	BYTE exceptionKind;
	WCHAR lBuffer[256];
	do
	{
		exceptionKind = (*exceptionPointer & CorILMethod_Sect_KindMask);
		if(exceptionKind & CorILMethod_Sect_FatFormat)
		{
			IMAGE_COR_ILMETHOD_SECT_FAT *pExceptionHeader = (IMAGE_COR_ILMETHOD_SECT_FAT *)exceptionPointer;
			wsprintf(lBuffer, L"Wyjątek FatFormat %d bajtów", pExceptionHeader->DataSize);
			m_ctrlTableTree.InsertItem(lBuffer, tvitem);
			exceptionPointer += sizeof(IMAGE_COR_ILMETHOD_SECT_FAT);
			int sekcjas = (pExceptionHeader->DataSize - sizeof(IMAGE_COR_ILMETHOD_SECT_FAT))/sizeof(IMAGE_COR_ILMETHOD_SECT_EH_sekcja_FAT);
			IMAGE_COR_ILMETHOD_SECT_EH_sekcja_FAT *pExceptionsekcja = (IMAGE_COR_ILMETHOD_SECT_EH_sekcja_FAT *)exceptionPointer;
			for(int i = 0; i < sekcjas; i++)
			{
				wcscpy(lBuffer,
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_NONE ? L"Typed" :
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_FILTER ? L"Filtr" :
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_FINALLY ? L"Finally" :
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_FAULT ? L"Błąd" : L"Nieznany");
				m_ctrlTableTree.InsertItem(lBuffer, tvitem);
				pExceptionsekcja++;
			}
			exceptionPointer += sizeof(IMAGE_COR_ILMETHOD_SECT_EH_sekcja_FAT) * sekcjas;
		}
		else
		{
			IMAGE_COR_ILMETHOD_SECT_SMALL *pExceptionHeader = (IMAGE_COR_ILMETHOD_SECT_SMALL *)exceptionPointer;
			wsprintf(lBuffer, L"Wyjątek SmallFormat %d bajtów", pExceptionHeader->DataSize);
			m_ctrlTableTree.InsertItem(lBuffer, tvitem);
			exceptionPointer += sizeof(IMAGE_COR_ILMETHOD_SECT_SMALL);
			// Zarezerwowane słowo
			exceptionPointer += 2;
			int sekcjas = (pExceptionHeader->DataSize - (sizeof(IMAGE_COR_ILMETHOD_SECT_SMALL) + 2))/sizeof(IMAGE_COR_ILMETHOD_SECT_EH_sekcja_SMALL);
			IMAGE_COR_ILMETHOD_SECT_EH_sekcja_SMALL *pExceptionsekcja = (IMAGE_COR_ILMETHOD_SECT_EH_sekcja_SMALL *)exceptionPointer;
			std::wstring tokenString;
			for(int i = 0; i < sekcjas; i++)
			{
				pView->MetaDataTokenToString(pExceptionsekcja->ClassToken, tokenString);
				wsprintf(lBuffer, L"%ls: 0x%04X 0x%02X 0x%04X 0x%02X -> %ls",
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_NONE ? L"Typed" :
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_FILTER ? L"Filtr" :
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_FINALLY ? L"Finally" :
					   pExceptionsekcja->Flags == COR_ILEXCEPTION_sekcja_FAULT ? L"Błąd" : L"Nieznany",
					   pExceptionsekcja->TryOffset,
					   pExceptionsekcja->TryLength,
					   pExceptionsekcja->HandlerOffset,
					   pExceptionsekcja->HandlerLength,
					   tokenString.c_str());
				m_ctrlTableTree.InsertItem(lBuffer, tvitem);
				pExceptionsekcja++;
			}
			exceptionPointer += sizeof(IMAGE_COR_ILMETHOD_SECT_EH_sekcja_SMALL) * sekcjas;
		}
	} while(exceptionKind & CorILMethod_Sect_MoreSects);
}

void CMetadataTablesPage::OutputOpcodes(CCILOpcode &op, CAssemblyView* pView, HTREEITEM tvitem)
{
	do
	{
	  if(!op.Next())
		  break;
	  if(op.IsSwitch())
	  {
			m_ctrlTableTree.InsertItem(op.Instruction().c_str(), tvitem);
	  }
	  else if(op.IsIndex() ||
	          op.IsValue() ||
		      op.IsBranch())
	  {
			m_ctrlTableTree.InsertItem(op.Instruction().c_str(), tvitem);
	  }
	  else if(op.IsToken())
	  {
			std::wstring instructionString = op.Instruction();
			ULONG token = *((ULONG *)op.Operand());
			std::wstring tokenString;
			pView->MetaDataTokenToString(token, tokenString);
			instructionString += std::wstring(L" ") + tokenString;
			m_ctrlTableTree.InsertItem(instructionString.c_str(), tvitem);
	  }
	  else
	  {
			m_ctrlTableTree.InsertItem(op.Instruction().c_str(), tvitem);
	  }
	} while(!op.IsEnd());
}

void CMetadataTablesPage::InstructionRVA(DWORD rva, CAssemblyView* pView, HTREEITEM tvitem)
{
	if(rva == 0) return;
	CAssemblyDoc* pDoc = pView->GetDocument();
	PBYTE pAssembly = pDoc->FileData();
	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pAssembly;
    if ( dosHeader->e_magic == IMAGE_DOS_SIGNATURE )
    {
		PIMAGE_NT_HEADERS pNTHeader;
		PIMAGE_NT_HEADERS64 pNTHeader64;

		// Utworzenie wskaźników do 32- i 64-bitowej wersji nagłówka.
		pNTHeader = MakePtr( PIMAGE_NT_HEADERS,
			                 dosHeader,
							 dosHeader->e_lfanew );

		pNTHeader64 = (PIMAGE_NT_HEADERS64)pNTHeader;
		// Najpierw weryfikacja, czy pole e_lfanew przekazało właściwy wskaźnik,
		// a następnie weryfikacja sygnatury PE.
		if ( IsBadReadPtr( pNTHeader, sizeof(pNTHeader->Signature) ) )
		{
			ATLTRACE(L"To nie jest plik typu Portable Executable (PE) EXE\n");
			return;
		}

		if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
		{
			ATLTRACE(L"To nie jest plik typu Portable Executable (PE) EXE\n");
			return;
		}

		bool bIs64Bit = ( pNTHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC );
		PBYTE instructions;
		if(bIs64Bit)
			instructions = (PBYTE)GetPtrFromRVA(rva, pNTHeader64, pAssembly);
		else
			instructions = (PBYTE)GetPtrFromRVA(rva, pNTHeader, pAssembly);
		bool exceptionHandler = false;
		if((instructions[0] & 3) == 0x2)
		{
			// 24.4.2
			// Nagłówki typu tiny wykorzystują kodowanie o długości 5 bitów. Poniższe warunki odnoszą się do wszystkich nagłówków tego typu:
			//  Zmienne lokalne nie są dozwolone
			//  Brak wyjątków
			//  Brak dodatkowych sekcji danych
			//  Stos operandów nie może przekraczać ośmiu pozycji
			WCHAR lBuffer[64];
			DWORD methodSize = instructions[0] >> 2;
			wsprintf(lBuffer, L"TinyFormat %d bytes", methodSize);
			m_ctrlTableTree.InsertItem(lBuffer, tvitem);
			m_ctrlTableTree.InsertItem(L".maxstack 8", tvitem);

			// Przekazanie kodów operacji
			CCILOpcode op(&instructions[1], methodSize);
			OutputOpcodes(op, pView, tvitem);
		}
		if((instructions[0] & 3) == 0x3)
		{
			WCHAR lBuffer[64];
			USHORT flagsSize = *((USHORT*)&instructions[0]);
			wsprintf(lBuffer, L"Nagłówek FatFormat %d bajtów", ((flagsSize >> 12) & 0xf) * 4);
			m_ctrlTableTree.InsertItem(lBuffer, tvitem);
			if((flagsSize & 0x8) != 0)
			{
				m_ctrlTableTree.InsertItem(L"MoreSects", tvitem);
				exceptionHandler = true;
			}
			else
			{
				exceptionHandler = false;
			}
			if((flagsSize & 0x10) != 0)
				m_ctrlTableTree.InsertItem(L"InitLocals", tvitem);
			wsprintf(lBuffer, L"Maksymalny stos: %d", *((USHORT*)&instructions[2]));
			m_ctrlTableTree.InsertItem(lBuffer, tvitem);
			DWORD codeSize = *((DWORD*)&instructions[4]);
			wsprintf(lBuffer, L"Wielkość kodu: %d", codeSize);
			m_ctrlTableTree.InsertItem(lBuffer, tvitem);
			DWORD localVariables = *((DWORD*)&instructions[8]);
			if(localVariables == 0)
				m_ctrlTableTree.InsertItem(L"LocalVarSigTok: (brak zmiennych lokalnych)", tvitem);
			else
			{
				BYTE localTable = (BYTE)(localVariables >> 24);
				wsprintf(lBuffer, L"LocalVarSigTok: 0x%08X Indeks %d %ls",
					     localVariables,
						 localVariables & 0x00FFFFFF,
						 (localTable == (BYTE)0x11) ? L"StandAloneSig" : L"Nieznany");
				HTREEITEM localVariablesNode;
				localVariablesNode = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
				MetaDataConstantIterator it = pView->MetaDataFind((TableType)localTable);
				if(it != pView->MetaDataEnd())
				{
					PBYTE standAloneSigRow;
					standAloneSigRow = it->second.Address + it->second.RowSize * ((localVariables & 0x00FFFFFF) - 1);
					DWORD signatureBlobIndex;
					if(it->second.HeapSizes & 0x04)
					{
						signatureBlobIndex = *((DWORD *)standAloneSigRow);
						standAloneSigRow += 4;
					}
					else
					{
						signatureBlobIndex = *((USHORT *)standAloneSigRow);
						standAloneSigRow += 2;
					}
					DWORD signatureBlobSize = 0;
					PBYTE signatureBlob = pView->BlobTableEntry(signatureBlobIndex, &signatureBlobSize);
					ASSERT(*signatureBlob == IMAGE_CEE_CS_CALLCONV_LOCAL_SIG);
					signatureBlob += 1;
					DWORD index = 0;
					DWORD localVariableCount;
					index += CorSigUncompressData(&signatureBlob[index], &localVariableCount);
					signatureBlob += index;
					WCHAR lVariableBuffer[256];
					for(USHORT variableIndex = 0; variableIndex < localVariableCount; variableIndex++)
					{
						wsprintf(lVariableBuffer, L"%d: ", variableIndex);
						// Constraint
						while(*signatureBlob == ELEMENT_TYPE_PINNED)
						{
							wcscat(lVariableBuffer, L"ELEMENT_TYPE_PINNED ");
							signatureBlob += 1;
						}
						if(*signatureBlob == ELEMENT_TYPE_BYREF)
						{
							wcscat(lVariableBuffer, L"ELEMENT_TYPE_BYREF ");
							signatureBlob += 1;
						}
						index = 0;
						wcscat(lVariableBuffer, TypeSig(signatureBlob, index).c_str());
						signatureBlob += index;
						m_ctrlTableTree.InsertItem(lVariableBuffer, localVariablesNode);
					}
				}
			}

			// Przekazanie kodów operacji
			CCILOpcode op(&instructions[12], codeSize);
			OutputOpcodes(op, pView, tvitem);
			if(exceptionHandler)
			{
				if((codeSize % 4) > 0)
				{
					OutputExceptionHandlers(&instructions[12 + codeSize + (4 - (int)(codeSize % 4))],
											pView, tvitem);
				}
				else
				{
					OutputExceptionHandlers(&instructions[12 + codeSize],
											pView, tvitem);
				}
			}
		}
	}
}


void CMetadataTablesPage::HandleSingleMethodDef(DWORD index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// RVA (4-bajtowa stała)
	// ImplFlags (dwubajtowa maska bitowa typu MethodImplAttributes, sekcja 22.1.9)
	// Znaczniki (dwubajtowa maska bitowa typu MethodAttribute, sekcja 22.1.9)
	// Nazwa (indeks sterty String)
	// Sygnatura (indeks sterty Blob)
	// ParamList (indeks tablicy Param). Znakuje pierwsze ciągłe uruchomienie Parameters należących do tej metody. Praca jest kontynuowana aż do osiągnięcia mniejszej wartości:
	//     ostatniego wiersza tablicy Param
	//     kolejnego uruchomienia Parameters, które można odnaleźć poprzez zbadanie ParamList kolejnego wiersza w tablicy Method.
	HTREEITEM treeRowItem;
	HTREEITEM signatureItem;
	HTREEITEM rvaItem;
	HTREEITEM implFlagsItem;
	HTREEITEM flagsItem;
	HTREEITEM parameterItem;
	PBYTE row;
	WCHAR lBuffer[256];
	DWORD rva;
	USHORT implFlags;
	USHORT flags;
	DWORD nameIndex;
	DWORD signature;
	DWORD params;
	DWORD endparams;

	row = t.Address + t.RowSize * index;

	// RVA
	rva = *((DWORD *)row);
	row += 4;

	// ImplFlags
	implFlags = *((USHORT *)row);
	row += 2;

	// Znaczniki
	flags = *((USHORT *)row);
	row += 2;

	// Nazwa
	if(t.HeapSizes & 0x01)
	{
		nameIndex = *((ULONG *)row);
		row += 4;
		wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
	}
	else
	{
		nameIndex = *((USHORT *)row);
		row += 2;
		wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
	}
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	// Sygnatura
	if(t.HeapSizes & 0x04)
	{
		signature = *((DWORD *)row);
		row += 4;
	}
	else
	{
		signature = *((USHORT *)row);
		row += 2;
	}
	wsprintf(lBuffer, L"Signature: 0x%08X", signature);
	signatureItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	MethodSignatureBlob(signature, pView, signatureItem);

	wsprintf(lBuffer, L"RVA: 0x%08X", rva);
	rvaItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	InstructionRVA(rva, pView, rvaItem);

	wsprintf(lBuffer, L"ImplFlags: 0x%08X", implFlags);
	implFlagsItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	MethodImplAttributes((CorMethodImpl)implFlags, implFlagsItem);

	wsprintf(lBuffer, L"Flags: 0x%08X", flags);
	flagsItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	MethodAttributes((CorMethodAttr)flags, flagsItem);

	// ParamList
	if(t.IndexSizes & 0x10)
	{
		params = *((DWORD *)row);
		// Sprawdzenie czy osiągnięto koniec listy.
		// Jeśli nie jest to koniec, to należy sprawdzić, od którego
		// parametru zaczyna się kolejna metoda. Jeśli indeksy parametrów
		// są identyczne, to dana metoda ma teraz parametry.
		// W przeciwnym wypadku uzyskano początek i koniec 
		// listy parametrów.
		if(index < t.Rows - 1)
			endparams = *((DWORD *)(row + t.RowSize));
		else
			endparams = 0;
		row += 4;
	}
	else
	{
		params = *((USHORT *)row);
		// Sprawdzenie czy osiągnięto koniec listy.
		// Jeśli nie jest to koniec, to należy sprawdzić, od którego
		// parametru zaczyna się kolejna metoda. Jeśli indeksy parametrów
		// są identyczne, to dana metoda ma teraz parametry.
		// W przeciwnym wypadku uzyskano początek i koniec 
		// listy parametrów.
		if(index < t.Rows - 1)
			endparams = *((USHORT *)(row + t.RowSize));
		else
			endparams = 0;
		row += 2;
	}

	wsprintf(lBuffer, L"Parametry: 0x%08X", params);
	parameterItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	// Uzyskanie wskaźnika tablicy parametrów jest wymagane tylko w przypadku osiągnięcia
	// końca listy, co pozwoli na sprawdzenie, czy dana metoda posiada parametry.
	if(index >= t.Rows - 1 ||
	   params < endparams)
	{
		MetaDataConstantIterator it = pView->MetaDataFind((TableType)ParamDef);
		if(it != pView->MetaDataEnd())
		{
			// Wartość początkowa musi znaleźć się w zakresie 
			// dozwolonych wartości listy parametrów.
			if(params < t.Rows)
			{
				// Jeśli osiągnięto koniec listy metod,
				// to należy upewnić się, czy ustawiono
				// koniec listy parametrów.
				if(index >= t.Rows - 1)
					endparams = it->second.Rows + 1;
				while(params < endparams)
				{
					HandleSingleParamDef(params - 1, it->second, pView, parameterItem);
					params++;
				}
			}
		}
	}

}

void CMetadataTablesPage::HandleMethodDef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleMethodDef(i, t, pView, tvitem);
	}
}

void CMetadataTablesPage::HandleSingleParamDef(int index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// Znaczniki (dwubajtowa maska bitowa typu ParamAttributes, sekcja 22.1.12)
	// Sekwencja (dwubajtowa stała)
	// Nazwa (indeks sterty String)
	HTREEITEM treeRowItem;
	PBYTE row;
	WCHAR lBuffer[256];
	USHORT flags;
	USHORT sequence;
	DWORD nameIndex;
	row = t.Address + t.RowSize * index;

	// Znaczniki
	flags = *((USHORT *)row);
	row += 2;

	// Sekwencja
	sequence = *((USHORT *)row);
	row += 2;

	// Nazwa
	if(t.HeapSizes & 0x01)
	{
		nameIndex = *((ULONG *)row);
		row += 4;
		wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
	}
	else
	{
		nameIndex = *((USHORT *)row);
		row += 2;
		wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
	}
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
	wsprintf(lBuffer, L"Flags: 0x%04X", flags);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	wsprintf(lBuffer, L"Sequence: 0x%04X", sequence);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
}

void CMetadataTablesPage::HandleParamDef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleParamDef(i, t, pView, tvitem);
	}
}

void CMetadataTablesPage::HandleSingleMemberRef(DWORD index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// Klasa (indeks tablicy TypeRef, ModuleRef, Method, TypeSpec lub TypeDef tables; jest to zakodowany indeks MemberRefParent)
	// Nazwa (indeks sterty String)
	// Sygnatura (indeks sterty Blob)
	HTREEITEM treeRowItem;
	HTREEITEM signatureItem;
	HTREEITEM classItem;
	PBYTE row;
	WCHAR lBuffer[256];
	DWORD classIndex;
	BYTE classTable;
	DWORD nameIndex;
	DWORD signature;

	row = t.Address + t.RowSize * index;

	// Klasa
	if(t.IndexSizes & 0x01)
	{
		classIndex = *((DWORD *)row);
		row += 4;
	}
	else
	{
		classIndex = *((USHORT *)row);
		row += 2;
	}
	classTable = (BYTE)(classIndex & 0x07);
	classIndex = classIndex >> 3;
	switch(classTable)
	{
	case 0:
		// Nieużywane
		break;
	case 1:
		// TypeRef
		classTable = 0x01;
		classIndex |= classTable << 24;
		break;
	case 2:
		// ModuleRef
		classTable = 0x1a;
		classIndex |= classTable << 24;
		break;
	case 3:
		// MethodDef
		classTable = 0x06;
		classIndex |= classTable << 24;
		break;
	case 4:
		// TypeSpec
		classTable = 0x1b;
		classIndex |= classTable << 24;
		break;
	}
	// Name
	if(t.HeapSizes & 0x01)
	{
		nameIndex = *((ULONG *)row);
		row += 4;
		wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
	}
	else
	{
		nameIndex = *((USHORT *)row);
		row += 2;
		wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
	}
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	// Sygnatura
	if(t.HeapSizes & 0x04)
	{
		signature = *((ULONG *)row);
		row += 4;
	}
	else
	{
		signature = *((USHORT *)row);
		row += 2;
	}

	wsprintf(lBuffer, L"Klasa: 0x%08X", classIndex);
	classItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	MetaDataConstantIterator it = pView->MetaDataFind((TableType)(classTable));
	if(it != pView->MetaDataEnd())
	{
		switch((TableType)classTable)
		{
		case TypeRef:
			// TypeRef
			HandleSingleTypeRef((classIndex & 0x00ffffff) - 1, it->second, pView, classItem);
			break;
		case ModuleRef:
			// ModuleRef
			HandleModule(it->second, pView, classItem);
			break;
		case MethodDef:
			// MethodDef
			HandleSingleMethodDef((classIndex & 0x00ffffff) - 1, it->second, pView, classItem);
			break;
		case TypeSpec:
			// TypeSpec
			break;
		}
	}
	wsprintf(lBuffer, L"Sygnatura: 0x%08X", signature);
	signatureItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	MethodSignatureBlob(signature, pView, signatureItem);
}

void CMetadataTablesPage::HandleMemberRef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleMemberRef(i, t, pView, tvitem);
	}
}
void CMetadataTablesPage::CustomAttributeBlob(DWORD blobTableIndex, DWORD signatureBlobIndex, CAssemblyView* pView, HTREEITEM tvitem)
{
	DWORD attributeSize = 0;
	DWORD signatureSize = 0;
	PBYTE attributeBlob = pView->BlobTableEntry(blobTableIndex, &attributeSize);
	PBYTE signatureBlob = pView->BlobTableEntry(signatureBlobIndex, &signatureSize);
	WCHAR lBuffer[256];
	wsprintf(lBuffer, L"PROLOG: 0x%04X", *((USHORT *)attributeBlob));
	m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	DWORD parameterCount;
	// Załóżmy, że pierwszy bajt jest poprawny.
	DWORD index = 1;
	index += CorSigUncompressData(&signatureBlob[index], &parameterCount);
	wsprintf(lBuffer, L"Sygnatura: 0x%08X %d", signatureBlobIndex, parameterCount);
	m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	while((CorElementType)signatureBlob[index] == ELEMENT_TYPE_CMOD_REQD ||
	      (CorElementType)signatureBlob[index] == ELEMENT_TYPE_CMOD_OPT)
	{
		// Najpierw dekompresja wartości
		// 22.2.8	TypeDefOrRefEncoded 
		int sigByteLength;
		mdToken customModToken;
		mdToken typeToken;
		sigByteLength = CorSigUncompressToken(&signatureBlob[index], &customModToken);
		index += sigByteLength;
		sigByteLength = CorSigUncompressToken(&signatureBlob[index], &typeToken);
		index += sigByteLength;
	}
	// Sprawdzenie czy typ może być ograniczony 
	// przez TYPEDBYREF lub VOID (22.2.11)
	if((CorElementType)signatureBlob[index] == ELEMENT_TYPE_TYPEDBYREF)
	{
		index++;
	}
	else
	{
		if((CorElementType)signatureBlob[index] == ELEMENT_TYPE_VOID)
		{
			index++;
		}
		else
		{
			// Sprawdzenie bajtu BYREF
			if((CorElementType)signatureBlob[index] == ELEMENT_TYPE_BYREF)
			{
				index++;
			}
			// Uzyskanie typu zwrotnego typu
			IsTypeSig(ELEMENT_TYPE_BOOLEAN, signatureBlob, index);
		}
	}
	// Dekodowanie parametrów
	DWORD parameterIndex = 2;
	for(DWORD i = 0; i < parameterCount; i++)
	{
		while((CorElementType)signatureBlob[index] == ELEMENT_TYPE_CMOD_REQD ||
			  (CorElementType)signatureBlob[index] == ELEMENT_TYPE_CMOD_OPT)
		{
			// Najpierw dekompresja wartości
			// 22.2.8	TypeDefOrRefEncoded 
			int sigByteLength;
			mdToken customModToken;
			mdToken typeToken;
			sigByteLength = CorSigUncompressToken(&signatureBlob[index], &customModToken);
			index += sigByteLength;
			sigByteLength = CorSigUncompressToken(&signatureBlob[index], &typeToken);
			index += sigByteLength;
		}
		// Sprawdzenie czy typ może być ograniczony 
	    // przez TYPEDBYREF lub VOID (22.2.11)
		if((CorElementType)signatureBlob[index] == ELEMENT_TYPE_TYPEDBYREF)
		{
			index++;
		}
		else
		{
			// Sprawdzenie bajtu BYREF
			if((CorElementType)signatureBlob[index] == ELEMENT_TYPE_BYREF)
			{
				index++;
			}
			DWORD savedIndex = index;
			// Odnalezienie typu parametru
			if(IsTypeSig(ELEMENT_TYPE_BOOLEAN, signatureBlob, savedIndex))
			{
				wsprintf(lBuffer, L"%d: %ls", i, attributeBlob[parameterIndex] ? L"true" : L"false");
				m_ctrlTableTree.InsertItem(lBuffer, tvitem);
				parameterIndex ++;

				index = savedIndex;
				continue;
			}
			savedIndex = index;
			if(IsTypeSig(ELEMENT_TYPE_STRING, signatureBlob, savedIndex))
			{
				DWORD stringLength;
				// Załóżmy, że pierwszy bajt jest poprawny
				parameterIndex += CorSigUncompressData(&attributeBlob[parameterIndex], &stringLength);
				if(stringLength != 0)
				{
					int nchars = MultiByteToWideChar(CP_UTF8, 0,		// Konwersja UTF8
													 (const char *)&attributeBlob[parameterIndex],	// Ciąg źródłowy
													 stringLength,		// Zakończony pustą wartością
													 NULL,				// Sprawdzenie liczby wymaganych bajtów
													 0);
					LPWSTR buffer = new wchar_t[nchars + 1];
					memset(buffer, 0, (nchars + 1)*sizeof(wchar_t));
					int err = MultiByteToWideChar(CP_UTF8, 0,
												  (const char *)&attributeBlob[parameterIndex],
												  stringLength,
												  buffer,
												  nchars);
					if(err == 0)
						ATLTRACE(L"Błąd konwersji\n");
					else
					{
						wsprintf(lBuffer, L"%d: %ls", i, buffer);
						m_ctrlTableTree.InsertItem(lBuffer, tvitem);
					}
					delete [] buffer;
				}
				else
				{
					wsprintf(lBuffer, L"%d: (zerowa długość)", i);
					m_ctrlTableTree.InsertItem(lBuffer, tvitem);
				}
				parameterIndex += stringLength;

				index = savedIndex;
				continue;
			}
			index = savedIndex;
		}			
	}
}

void CMetadataTablesPage::HandleSingleCustomAttribute(DWORD index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// Rodzic (ndeks tablicy metadanych z wyjątkiem samej tablicy CustomAttribute; jest to zakodowany indeks HasCustomAttribute)
	// Typ (indeks tablicy Method lub MethodRef; jest to zakodowany indeks CustomAttributeType)
	// Wartość (indeks sterty Blob)
	HTREEITEM treeRowItem;
	HTREEITEM typeItem;
	HTREEITEM valueItem;
	PBYTE row;
	WCHAR lBuffer[256];
	DWORD parent;
	BYTE parentTable;
	DWORD type;
	BYTE typeTable;
	DWORD value;

	row = t.Address + t.RowSize * index;

	// Rodzic
	if(t.IndexSizes & 0x01)
	{
		parent = *((DWORD *)row);
		row += 4;
	}
	else
	{
		parent = *((USHORT *)row);
		row += 2;
	}
	parentTable = (BYTE)(parent & 0x01F);
	parent = parent >> 5;
	MetaDataConstantIterator it;
	switch(parentTable)
	{
	case 0:
		// MethodDef
		parent |= MethodDef << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X MethodDef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		it = pView->MetaDataFind((TableType)(MethodDef));
		if(it != pView->MetaDataEnd())
		{
			HandleSingleMethodDef((parent & 0x00ffffff) - 1, it->second, pView, treeRowItem);
		}
		break;
	case 1:
		// FieldDef
		parent |= FieldDef << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X FieldDef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 2:
		// TypeRef
		parent |= TypeRef << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X TypeRef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 3:
		// TypeDef
		parent |= 0x02 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X TypeDef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 4:
		// ParamDef
		parent |= 0x08 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X ParamDef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 5:
		// InterfaceImpl
		parent |= 0x09 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X InterfaceImpl", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 6:
		// MemberRef
		parent |= 0x0a << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X MemberRef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 7:
		// Moduł
		wsprintf(lBuffer, L"Rodzic: 0x%08X Moduł", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 8:
		// DeclSecurity
		parent |= 0x0e << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X DeclSecurity", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 9:
		// Właściwość
		parent |= 0x17 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X Właściwość", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 10:
		// Zdarzenie
		parent |= 0x14 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X Zdarzenie", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 11:
		// Sygnatura
		parent |= 0x11 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X Sygnatura", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 12:
		// ModuleRef
		parent |= 0x1a << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X ModuleRef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 13:
		// TypeSpec
		parent |= 0x1b << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X TypeSpec", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 14:
		// Podzespół
		parent |= Assembly << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X Podzespół", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		it = pView->MetaDataFind((TableType)(Assembly));
		if(it != pView->MetaDataEnd())
		{
			HandleAssembly(it->second, pView, treeRowItem);
		}
		break;
	case 15:
		// AssemblyRef
		parent |= 0x23 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X AssemblyRef", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 16:
		// Plik
		parent |= 0x26 << 24;
		wsprintf(lBuffer, L"Parent: 0x%08X Plik", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 17:
		// ExportedType
		parent |= 0x27 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X ExportedType", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	case 18:
		// ManifestResource
		parent |= 0x28 << 24;
		wsprintf(lBuffer, L"Rodzic: 0x%08X ManifestResource", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	default:
		wsprintf(lBuffer, L"Rodzic: 0x%08X", parent);
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);
		break;
	}

	// Typ
	if(t.IndexSizes & 0x02)
	{
		type = *((ULONG *)row);
		row += 4;
	}
	else
	{
		type = *((USHORT *)row);
		row += 2;
	}
	typeTable = (BYTE)(type & 0x07);
	type = type >> 3;
	ULONG signature = 0;
	switch(typeTable)
	{
	case 2:
		// MethodDef
		type |= MethodDef << 24;
		wsprintf(lBuffer, L"Typ: 0x%08X MethodDef", type);
		typeItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		it = pView->MetaDataFind((TableType)(MethodDef));
		if(it != pView->MetaDataEnd())
		{
			PBYTE row = it->second.Address + it->second.RowSize * ((type & 0x00ffffff) - 1);
			// RVA
			row += 4;
			// ImplFlags
			row += 2;
			// Znaczniki
			row += 2;
			// Nazwa
			if(it->second.HeapSizes & 0x01)
				row += 4;
			else
				row += 2;
			// Sygnatura
			if(it->second.HeapSizes & 0x04)
			{
				signature = *((ULONG *)row);
				row += 4;
			}
			else
			{
				signature = *((USHORT *)row);
				row += 2;
			}
			HandleSingleMethodDef((type & 0x00ffffff) - 1, it->second, pView, typeItem);
		}
		break;
	case 3:
		// MemberRef
		type |= MemberRef << 24;
		wsprintf(lBuffer, L"Typ: 0x%08X MemberRef", type);
		typeItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		it = pView->MetaDataFind((TableType)(MemberRef));
		if(it != pView->MetaDataEnd())
		{
			PBYTE row = it->second.Address + it->second.RowSize * ((type & 0x00ffffff) - 1);
			// Klasa
			if(it->second.IndexSizes & 0x01)
				row += 4;
			else
				row += 2;
			// Nazwa
			if(it->second.HeapSizes & 0x01)
				row += 4;
			else
				row += 2;
			// Sygnatura
			if(it->second.HeapSizes & 0x04)
			{
				signature = *((ULONG *)row);
				row += 4;
			}
			else
			{
				signature = *((USHORT *)row);
				row += 2;
			}
			HandleSingleMemberRef((type & 0x00ffffff) - 1, it->second, pView, typeItem);
		}
		break;
	default:
		wsprintf(lBuffer, L"Typ: 0x%08X", type);
		typeItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		break;
	}

	// Wartość
	if(t.HeapSizes & 0x04)
	{
		value = *((ULONG *)row);
		row += 4;
	}
	else
	{
		value = *((USHORT *)row);
		row += 2;
	}
	wsprintf(lBuffer, L"Wartość: 0x%08X", value);
	valueItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	CustomAttributeBlob(value, signature, pView, valueItem);
}

void CMetadataTablesPage::HandleCustomAttribute(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleCustomAttribute(i, t, pView, tvitem);
	}
}

void CMetadataTablesPage::HandleSingleModuleRef(int index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// Nazwa (indeks sterty String)
	PBYTE row;
	WCHAR lBuffer[256];
	DWORD name;
	row = t.Address + t.RowSize * index;

	// Nazwa
	if(t.HeapSizes & 0x01)
	{
		name = *((DWORD *)row);
		row += 4;
	}
	else
	{
		name = *((USHORT *)row);
		row += 2;
	}
	wcscpy(lBuffer, pView->StringTableEntry(name).c_str());
	m_ctrlTableTree.InsertItem(lBuffer, tvitem);
}

void CMetadataTablesPage::HandleModuleRef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleModuleRef(i, t, pView, tvitem);
	}
}
void CMetadataTablesPage::ImplMappingAttributes(CorPinvokeMap flags, HTREEITEM flagsItem)
{
	if(flags == pmNoMangle)
		m_ctrlTableTree.InsertItem(L"Pinvoke wykorzysta nazwę elementu w podany sposób.", flagsItem);

	// 22.1.9	Znaczniki metod [MethodAttributes]
	if(flags & pmCharSetMask)
	{
		CorPinvokeMap charSetFlags = (CorPinvokeMap)(flags & pmCharSetMask);
		if(charSetFlags == pmCharSetAnsi)
			m_ctrlTableTree.InsertItem(L"Ansi", flagsItem);
		if(charSetFlags == pmCharSetUnicode)
			m_ctrlTableTree.InsertItem(L"Unicode", flagsItem);
		if(charSetFlags == pmCharSetAuto)
			m_ctrlTableTree.InsertItem(L"Auto", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"Nie zdefiniowano zestawu znaków",  flagsItem);
	}

	if(flags & pmSupportsLastError)
		m_ctrlTableTree.InsertItem(L"Powiązane z ostatnim błędem. Informacje o funkcji docelowej. Nie odnosi się do pól.", flagsItem);
	if(flags & pmCallConvMask)
	{
		CorPinvokeMap callConvFlags = (CorPinvokeMap)(flags & pmCallConvMask);
		if(callConvFlags == pmCallConvWinapi)
			m_ctrlTableTree.InsertItem(L"Pinvoke wykorzysta macierzystą konwencję wywoływania dla docelowej platformy Windows.", flagsItem);
		if(callConvFlags == pmCallConvCdecl)
			m_ctrlTableTree.InsertItem(L"CallConvCdecl", flagsItem);
		if(callConvFlags == pmCallConvStdcall)
			m_ctrlTableTree.InsertItem(L"StdCall", flagsItem);
		if(callConvFlags == pmCallConvThiscall)
			m_ctrlTableTree.InsertItem(L"ThisCall. W M9 pinvoke zgłosi wyjątek.", flagsItem);
		if(callConvFlags == pmCallConvFastcall)
			m_ctrlTableTree.InsertItem(L"FastCall", flagsItem);
	}
	else
	{
		m_ctrlTableTree.InsertItem(L"Nie zdefiniowano konwencji wywoływania",  flagsItem);
	}
}

void CMetadataTablesPage::HandleImplMap(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// MappingFlags (dwubajtowa maska bitowa typu PInvokeAttributes, sekcja 22.1.7)
	// MemberForwarded (indeks tablicy Field lub Method table; jest to zakodowany indeks MemberForwarded. Indeksuje jednak tylko tablicę Method, ponieważ eksport tablicy Field nie jest obsługiwany.
	// ImportName (indeks sterty String)
	// ImportScope (indeks tablicy ModuleRef)
	PBYTE row;
	WCHAR lBuffer[256];
	USHORT mappingFlags;
	DWORD memberForwarded;
	DWORD importName;
	DWORD importScope;
	HTREEITEM treeRowItem;
	HTREEITEM mappingFlagsItem;
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		row = t.Address + t.RowSize * i;

		mappingFlags = *((USHORT *)row);
		row += 2;

		// MemberForwarded
		if(t.IndexSizes & 0x02)
		{
			memberForwarded = *((DWORD *)row);
			row += 4;
		}
		else
		{
			memberForwarded = *((USHORT *)row);
			row += 2;
		}

		// ImportName
		if(t.HeapSizes & 0x01)
		{
			importName = *((DWORD *)row);
			row += 4;
		}
		else
		{
			importName = *((USHORT *)row);
			row += 2;
		}

		wcscpy(lBuffer, pView->StringTableEntry(importName).c_str());
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

		if(t.IndexSizes & 0x08)
		{
			importScope = *((DWORD *)row);
			row += 4;
		}
		else
		{
			importScope = *((USHORT *)row);
			row += 2;
		}

		wsprintf(lBuffer, L"Znaczniki mapowania: 0x%04X", mappingFlags);
		mappingFlagsItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		ImplMappingAttributes((CorPinvokeMap)mappingFlags, mappingFlagsItem);

		wsprintf(lBuffer, L"Przekazywanie elementu składowego: 0x%04X", memberForwarded);
		m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);

		wsprintf(lBuffer, L"Zakres importu: 0x%04X", importScope);
		m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	}
}

void CMetadataTablesPage::HandleAssembly(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// HashAlgId (czterobajtowa stała typu AssemblyHashAlgorithm, sekcja 22.1.1)
	// MajorVersion, MinorVersion, BuildNumber, RevisionNumber (dwubajtowe stałe)
	// Znaczniki (4-bajtowa maska bitowa typu AssemblyFlags, sekcja 22.1.2)
	// PublicKey (indeks sterty Blob)
	// Nazwa (indeks sterty String)
	// Kultura (indeks sterty String) 
	HTREEITEM treeRowItem;
	HTREEITEM publicKeyItem;
	PBYTE row;
	WCHAR lBuffer[256];
	DWORD hashAlgId;
	USHORT major, minor, build, revision;
	DWORD flags;
	DWORD publicKey;
	DWORD cultureIndex;
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		row = t.Address + t.RowSize * i;

		// Skrót
		hashAlgId = *((DWORD *)row);
		row += 4;

		// Wersja
		major = *((USHORT *)row);
		row += 2;
		minor = *((USHORT *)row);
		row += 2;
		build = *((USHORT *)row);
		row += 2;
		revision = *((USHORT *)row);
		row += 2;

		// Znaczniki
		flags = *((DWORD *)row);
		row += 4;

		// Klucz publiczny
		if(t.HeapSizes & 0x04)
		{
			publicKey = *((ULONG *)row);
			row += 4;
		}
		else
		{
			publicKey = *((USHORT *)row);
			row += 2;
		}

		// Nazwa
		if(t.HeapSizes & 0x01)
		{
			wcscpy(lBuffer, pView->StringTableEntry(*((ULONG *)row)).c_str());
			row += 4;
		}
		else
		{
			wcscpy(lBuffer, pView->StringTableEntry(*((USHORT *)row)).c_str());
			row += 2;
		}
		treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

		// Kultura
		if(t.HeapSizes & 0x01)
		{
			cultureIndex = *((ULONG *)row);
			row += 4;
		}
		else
		{
			cultureIndex = *((USHORT *)row);
			row += 2;
		}

		// Umieszczenie wersji w drzewie
		wsprintf(lBuffer, L"Wersja: %u.%u.%u.%u", major, minor, build, revision);
		m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		wsprintf(lBuffer, L"Kultura: %ls",pView->StringTableEntry(cultureIndex).c_str());
		m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		wsprintf(lBuffer, L"Skrót: 0x%08X %ls", hashAlgId, hashAlgId == 0x8004 ? L"SHA1":L"");
		m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		wsprintf(lBuffer, L"Znaczniki: 0x%08X", flags);
		m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		wsprintf(lBuffer, L"Klucz publiczny: 0x%08X", publicKey);
		publicKeyItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
		if(publicKey != 0)
		{
			DWORD size;
			DWORD index;
			std::wstring line;
			const PBYTE blob = pView->BlobTableEntry(publicKey, &size);
			index = 0;
			while(index < size)
			{
				if(index > 0 &&
				   index % 16 == 0)
				{
					 m_ctrlTableTree.InsertItem(line.c_str(), publicKeyItem);
					 line.clear();
				}
				wsprintf(lBuffer, L"%02X ", blob[index]);
				line += lBuffer;
				index++;
			}
			if(index % 16)
				m_ctrlTableTree.InsertItem(line.c_str(), publicKeyItem);
		}
	}
}

void CMetadataTablesPage::HandleSingleAssemblyRef(int index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	// MajorVersion, MinorVersion, BuildNumber, RevisionNumber (dwubajtowe stałe)
	// Znaczniki (4-bajtowa maska bitowa typu AssemblyFlags, sekcja 22.1.2)
	// PublicKeyOrToken (indeks sterty Blob; klucz publiczny lub żeton identyfikujący autora podzespołu)
	// Nazwa (indeks sterty String)
	// Kultura (indeks sterty String)
	// HashValue (indeks sterty Blob) 
	HTREEITEM treeRowItem;
	HTREEITEM publicKeyItem;
	PBYTE row;
	WCHAR lBuffer[256];
	USHORT major, minor, build, revision;
	DWORD flags;
	DWORD publicKey;
	DWORD cultureIndex;
	DWORD hashAlgId;

	row = t.Address + t.RowSize * index;

	// Wersja
	major = *((USHORT *)row);
	row += 2;
	minor = *((USHORT *)row);
	row += 2;
	build = *((USHORT *)row);
	row += 2;
	revision = *((USHORT *)row);
	row += 2;

	// Znaczniki
	flags = *((DWORD *)row);
	row += 4;

	// Klucz publiczny
	if(t.HeapSizes & 0x04)
	{
		publicKey = *((ULONG *)row);
		row += 4;
	}
	else
	{
		publicKey = *((USHORT *)row);
		row += 2;
	}

	// Nazwa
	if(t.HeapSizes & 0x01)
	{
		wcscpy(lBuffer, pView->StringTableEntry(*((ULONG *)row)).c_str());
		row += 4;
	}
	else
	{
		wcscpy(lBuffer, pView->StringTableEntry(*((USHORT *)row)).c_str());
		row += 2;
	}
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	// Kultura
	if(t.HeapSizes & 0x01)
	{
		cultureIndex = *((ULONG *)row);
		row += 4;
	}
	else
	{
		cultureIndex = *((USHORT *)row);
		row += 2;
	}

	// Skrót
	if(t.HeapSizes & 0x04)
	{
		hashAlgId = *((ULONG *)row);
		row += 4;
	}
	else
	{
		hashAlgId = *((USHORT *)row);
		row += 2;
	}

	// Umieszczenie wersji w drzewie
	wsprintf(lBuffer, L"Wersja: %u.%u.%u.%u", major, minor, build, revision);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	wsprintf(lBuffer, L"Kultura: %ls",pView->StringTableEntry(cultureIndex).c_str());
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	wsprintf(lBuffer, L"Skrót: 0x%08X %ls", hashAlgId, hashAlgId == 0x8004 ? L"SHA1":L"");
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	wsprintf(lBuffer, L"Znaczniki: 0x%08X", flags);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	wsprintf(lBuffer, L"Klucz publiczny: 0x%08X", publicKey);
	publicKeyItem = m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
	if(publicKey != 0)
	{
		DWORD size;
		DWORD index;
		std::wstring line;
		const PBYTE blob = pView->BlobTableEntry(publicKey, &size);
		index = 0;
		while(index < size)
		{
			if(index > 0 &&
				index % 16 == 0)
			{
				m_ctrlTableTree.InsertItem(line.c_str(), publicKeyItem);
				line.clear();
			}
			wsprintf(lBuffer, L"%02X ", blob[index]);
			line += lBuffer;
			index++;
		}
		if(index % 16)
			m_ctrlTableTree.InsertItem(line.c_str(), publicKeyItem);
	}
}

void CMetadataTablesPage::HandleAssemblyRef(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleAssemblyRef(i, t, pView, tvitem);
	}
}

void CMetadataTablesPage::HandleSingleInterfaceImpl(DWORD index, const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	HTREEITEM treeRowItem;
	PBYTE row;
	DWORD classIndex;
	DWORD interfaceIndex;

	WCHAR lBuffer[128];

	// Klasa (indeks tablicy TypeDef)
	// Interfejs (indeks tablicy TypeDef, TypeRef lub TypeSpec; jest to zakodowany indeks TypeDefOrRef)

	row = t.Address + t.RowSize * index;

	// Klasa
	if(t.IndexSizes & 0x01)
	{
		classIndex = *((DWORD *)row);
		row += 4;
	}
	else
	{
		classIndex = *((USHORT *)row);
		row += 2;
	}
	
	wsprintf(lBuffer, L"TypeDef: 0x%08X", (0x02 << 24) | (classIndex));
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	MetaDataConstantIterator it = pView->MetaDataFind(TypeDef);
	if(it != pView->MetaDataEnd())
	{
		HandleSingleTypeDef(classIndex, it->second, pView, treeRowItem);
	}

	// Interfejs
	if(t.IndexSizes & 0x02)
	{
		interfaceIndex = *((DWORD *)row);
		row += 4;
	}
	else
	{
		interfaceIndex = *((USHORT *)row);
		row += 2;
	}
	
	TableType tt = (TableType)(interfaceIndex & 0x03);
	it = pView->MetaDataFind(tt);
	if(it != pView->MetaDataEnd())
	{
		switch(tt)
		{
		case TypeRef:
			HandleSingleTypeRef((interfaceIndex >> 2) - 1, it->second, pView, treeRowItem);
			break;
		case TypeDef:
			HandleSingleTypeDef((interfaceIndex >> 2) - 1, it->second, pView, treeRowItem);
			break;
		case TypeSpec:
			wsprintf(lBuffer, L"TypeSpec: 0x%08X", (0x1B << 24) | (interfaceIndex >> 2));
			m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
			break;
		}
	}
}

void CMetadataTablesPage::HandleInterfaceImpl(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleInterfaceImpl(i, t, pView, tvitem);
	}
}

static std::wstring ConstantTypeName(BYTE type)
{
	std::wstring ret;
	switch(type)
	{
	case ELEMENT_TYPE_BOOLEAN:
		ret = L"bool";
		break;
	case ELEMENT_TYPE_CHAR:
		ret = L"char";
		break;
	case ELEMENT_TYPE_I1:
		ret = L"I1";
		break;
	case ELEMENT_TYPE_U1:
		ret = L"U1";
		break;
	case ELEMENT_TYPE_I2:
		ret = L"short";
		break;
	case ELEMENT_TYPE_U2:
		ret = L"ushort";
		break;
	case ELEMENT_TYPE_I4:
		ret = L"int";
		break;
	case ELEMENT_TYPE_U4:
		ret = L"uint";
		break;
	case ELEMENT_TYPE_I8:
		ret = L"long";
		break;
	case ELEMENT_TYPE_U8:
		ret = L"ulong";
		break;
	case ELEMENT_TYPE_R4:
		ret = L"float";
		break;
	case ELEMENT_TYPE_R8:
		ret = L"double";
		break;
	case ELEMENT_TYPE_STRING:
		ret = L"string";
		break;
	case ELEMENT_TYPE_CLASS:
		ret = L"class";
		break;
	}
	return ret;
}

void CMetadataTablesPage::HandleSingleConstant(DWORD index, const META_TABLE_ENTRY& t, CAssemblyView* , HTREEITEM tvitem)
{
	HTREEITEM treeRowItem;
	PBYTE row;
	BYTE type;
	DWORD parentIndex;
	DWORD valueIndex;

	WCHAR lBuffer[128];

	// Typ (jednobajtowa stała, po której następuje uzupełniający pusty bajt): sekcja 22.1.15. Kodowanie typu dla wartości nullref w <fieldInit> w ilasm (sekcja 15.2) to ELEMENT_TYPE_CLASS o wartości równej zero. W przeciwieństwie do  ELEMENT_TYPE_CLASS w sygnaturach w tym przypadku nie występuje żeton typu.
	// Rodzic (indeks tablicy Param, Field lub Property; jest to zakodowany indeks HasConst)
	// Value (indeks sterty Blob)

	row = t.Address + t.RowSize * index;

	type = *row;

	wsprintf(lBuffer, L"Typ: 0x%02X %ls", type, ConstantTypeName(type).c_str());
	treeRowItem = m_ctrlTableTree.InsertItem(lBuffer, tvitem);

	// Pominięcie uzupełniających zer.
	row += 2;

	// Indeks rodzica
	if(t.IndexSizes & 0x02)
	{
		parentIndex = *((DWORD *)row);
		row += 4;
	}
	else
	{
		parentIndex = *((USHORT *)row);
		row += 2;
	}

	wsprintf(lBuffer, L"Rodzic: 0x%08X", parentIndex);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);

	// Wartość
	if(t.HeapSizes & 0x04)
	{
		valueIndex = *((DWORD *)row);
		row += 4;
	}
	else
	{
		valueIndex = *((USHORT *)row);
		row += 2;
	}
	wsprintf(lBuffer, L"Wartość: 0x%08X", valueIndex);
	m_ctrlTableTree.InsertItem(lBuffer, treeRowItem);
}

void CMetadataTablesPage::HandleConstant(const META_TABLE_ENTRY& t, CAssemblyView* pView, HTREEITEM tvitem)
{
	for(DWORD i = 0; i < t.Rows; ++i)
	{
		HandleSingleConstant(i, t, pView, tvitem);
	}
}
