#pragma comment(lib, "comctl32")

#include <windows.h>
#include <atlbase.h>
#include <commctrl.h>
#include <sstream>
#include <vector>

struct EVeryBadThing { };
struct CComTypeAttr {
	TYPEATTR* _typeAttr;
	CComPtr<ITypeInfo> _pTypeInfo;
	operator TYPEATTR*() { return _typeAttr; }
	TYPEATTR* operator->() { return _typeAttr; }
	explicit CComTypeAttr(ITypeInfo* ti) throw(EVeryBadThing) : _pTypeInfo(ti) {
		HRESULT hr(_pTypeInfo->GetTypeAttr(&_typeAttr));
		if(hr) throw EVeryBadThing();
	}
	~CComTypeAttr() { _pTypeInfo->ReleaseTypeAttr(_typeAttr); }
};
struct CComFuncDesc {
	FUNCDESC* _funcDesc;
	operator FUNCDESC*() { return _funcDesc; }
	FUNCDESC* operator->() { return _funcDesc; }
	CComPtr<ITypeInfo> _pTypeInfo;
	CComFuncDesc(ITypeInfo* ti, int index) throw(EVeryBadThing) : _pTypeInfo(ti) {
		HRESULT hr(_pTypeInfo->GetFuncDesc(index, &_funcDesc));
		if(hr) throw EVeryBadThing();
	}
	~CComFuncDesc() { _pTypeInfo->ReleaseFuncDesc(_funcDesc); }
};
struct CComVarDesc {
	VARDESC* _varDesc;
	operator VARDESC*() { return _varDesc; }
	VARDESC* operator->() { return _varDesc; }
	CComPtr<ITypeInfo> _pTypeInfo;
	CComVarDesc(ITypeInfo* ti, int index) throw(EVeryBadThing) : _pTypeInfo(ti) {
		HRESULT hr(_pTypeInfo->GetVarDesc(index, &_varDesc));
		if(hr) throw EVeryBadThing();
	}
	~CComVarDesc() { _pTypeInfo->ReleaseVarDesc(_varDesc); }
};
struct CComLibAttr {
	TLIBATTR* _libAttr;
	operator TLIBATTR*() { return _libAttr; }
	TLIBATTR* operator->() { return _libAttr; }
	CComPtr<ITypeLib> _pTypeLib;
	explicit CComLibAttr(ITypeLib* tlb) throw(EVeryBadThing) : _pTypeLib(tlb) {
		HRESULT hr(_pTypeLib->GetLibAttr(&_libAttr));
		if(hr) throw EVeryBadThing();
	}
	~CComLibAttr() { _pTypeLib->ReleaseTLibAttr(_libAttr); }
};

std::string stringifyParameterAttributes(PARAMDESC* paramDesc) {
	USHORT paramFlags = paramDesc->wParamFlags;
	int numFlags(0);
	for(DWORD bit(1); bit <= PARAMFLAG_FHASDEFAULT; bit<<=1) 
		numFlags += (paramFlags & bit) ? 1 : 0;
	if(!numFlags) return "";
	std::ostringstream oss;
	oss<< '[';
	if(paramFlags & PARAMFLAG_FIN) 
	{ oss<< "in"; if(--numFlags) oss<< ", "; }
	if(paramFlags & PARAMFLAG_FOUT) 
	{ oss<< "out"; if(--numFlags) oss<< ", "; }
	if(paramFlags & PARAMFLAG_FLCID) 
	{ oss<< "lcid"; if(--numFlags) oss<< ", "; }
	if(paramFlags & PARAMFLAG_FRETVAL) 
	{ oss<< "retval"; if(--numFlags) oss<< ", "; }
	if(paramFlags & PARAMFLAG_FOPT) 
	{ oss<< "optional"; if(--numFlags) oss<< ", "; }
	if(paramFlags & PARAMFLAG_FHASDEFAULT) {
		oss<< "defaultvalue";
		if(paramDesc->pparamdescex) {
			oss<< '(';
			PARAMDESCEX& paramDescEx = *(paramDesc->pparamdescex);
			VARIANT defVal();
			CComBSTR bstrDefValue;
			CComVariant variant;
			HRESULT hr(VariantChangeType(&variant, 
				&paramDescEx.varDefaultValue, 0, VT_BSTR));
			if(hr) oss<< "???)";
			else {
				char ansiDefValue[MAX_PATH];
				WideCharToMultiByte(CP_ACP, 0, variant.bstrVal, 
					SysStringLen(variant.bstrVal) + 1, ansiDefValue, 
					MAX_PATH, 0, 0);
				if(paramDescEx.varDefaultValue.vt == VT_BSTR)
					oss<< '\"'<< ansiDefValue<< '\"'<< ')';
				else oss<< ansiDefValue<< ')';
			}
		}
	}
	oss<< ']';
	return oss.str();
}


std::string stringifyCustomType(HREFTYPE refType, ITypeInfo* pti) {
	CComPtr<ITypeInfo> pTypeInfo(pti);
	CComPtr<ITypeInfo> pCustTypeInfo;
	HRESULT hr(pTypeInfo->GetRefTypeInfo(refType, &pCustTypeInfo));
	if(hr) return "UnknownCustomType";
	CComBSTR bstrType;
	hr = pCustTypeInfo->GetDocumentation(-1, &bstrType, 0, 0, 0);
	if(hr) return "UnknownCustomType";
	char ansiType[MAX_PATH];
	WideCharToMultiByte(CP_ACP, 0, bstrType, bstrType.Length() + 1, 
		ansiType, MAX_PATH, 0, 0);
	return ansiType;
}

std::string stringifyTypeDesc(TYPEDESC* typeDesc, ITypeInfo* pti) {
	std::ostringstream oss;
	if(typeDesc->vt == VT_PTR) {
		oss<< stringifyTypeDesc(typeDesc->lptdesc, pti)<< '*';
		return oss.str();
	}
	if(typeDesc->vt == VT_SAFEARRAY) {
		oss<< "SAFEARRAY("
			<< stringifyTypeDesc(typeDesc->lptdesc, pti)<< ')';
		return oss.str();
	}
	if(typeDesc->vt == VT_CARRAY) {
		oss<< stringifyTypeDesc(&typeDesc->lpadesc->tdescElem, pti);
		for(int dim(0); typeDesc->lpadesc->cDims; ++dim) 
			oss<< '['<< typeDesc->lpadesc->rgbounds[dim].cElements<< ']';
		return oss.str();
	}
	if(typeDesc->vt == VT_USERDEFINED) {
		oss<< stringifyCustomType(typeDesc->hreftype, pti);
		return oss.str();
	}
	
	switch(typeDesc->vt) {
		// VARIANT compatible types
	case VT_I2: return "short";
	case VT_I4: return "long";
	case VT_R4: return "float";
	case VT_R8: return "double";
	case VT_CY: return "CY";
	case VT_DATE: return "DATE";
	case VT_BSTR: return "BSTR";
	case VT_DISPATCH: return "IDispatch*";
	case VT_ERROR: return "SCODE";
	case VT_BOOL: return "VARIANT_BOOL";
	case VT_VARIANT: return "VARIANT";
	case VT_UNKNOWN: return "IUnknown*";
	case VT_UI1: return "BYTE";
		// VARIANTARG compatible types
	case VT_DECIMAL: return "DECIMAL";
	case VT_I1: return "char";
	case VT_UI2: return "USHORT";
	case VT_UI4: return "ULONG";
	case VT_I8: return "__int64";
	case VT_UI8: return "unsigned __int64";
	case VT_INT: return "int";
	case VT_UINT: return "UINT";
	case VT_HRESULT: return "HRESULT";
	case VT_VOID: return "void";
	case VT_LPSTR: return "char*";
	case VT_LPWSTR: return "wchar_t*";
	}
	return "BIG ERROR!";
}

std::string stringifyVarDesc(VARDESC* varDesc, ITypeInfo* pti) {
	CComPtr<ITypeInfo> pTypeInfo(pti);
	std::ostringstream oss;
	if(varDesc->varkind == VAR_CONST) oss<< "const ";
	oss<< stringifyTypeDesc(&varDesc->elemdescVar.tdesc, pTypeInfo);
	CComBSTR bstrName;
	HRESULT hr(pTypeInfo->GetDocumentation(varDesc->memid, &bstrName, 0, 0, 0));
	if(hr) return "UnknownName";
	char ansiName[MAX_PATH];
	WideCharToMultiByte(CP_ACP, 0, bstrName, bstrName.Length() + 1, ansiName,
		MAX_PATH, 0, 0);
	oss<< ' '<< ansiName;
	if(varDesc->varkind != VAR_CONST) return oss.str();
	oss<< " = ";
	CComVariant variant;
	hr = VariantChangeType(&variant, varDesc->lpvarValue, 0, VT_BSTR);
	if(hr) oss<< "???";
	else {
		WideCharToMultiByte(CP_ACP, 0, variant.bstrVal, 
			SysStringLen(variant.bstrVal) + 1, ansiName, MAX_PATH, 0, 0);
		oss<< ansiName;
	}
	return oss.str();
}


std::string stringifyFunctionArgument(ELEMDESC* elemDesc, ITypeInfo* pti) {
	CComPtr<ITypeInfo> pTypeInfo(pti);
	std::ostringstream oss;
	oss<< stringifyParameterAttributes(&elemDesc->paramdesc);
	if(oss.str().size()) oss<< ' ';
	oss<< stringifyTypeDesc(&elemDesc->tdesc, pti);
	return oss.str();
}

std::string stringifyCOMMethod(FUNCDESC* funcDesc, ITypeInfo* pti) {
	CComPtr<ITypeInfo> pTypeInfo(pti);
	std::ostringstream oss;
	if(funcDesc->funckind == FUNC_DISPATCH)	
		oss<< "[id("<< (int)funcDesc->memid<< ')';
	else oss<< "[VOffset("<< funcDesc->oVft<< ')';
	switch(funcDesc->invkind) {
	case INVOKE_PROPERTYGET: oss<< ", propget] "; break;
	case INVOKE_PROPERTYPUT: oss<< ", propput] "; break;
	case INVOKE_PROPERTYPUTREF: oss<< ", propputref] "; break;
	case INVOKE_FUNC: oss<< "] "; break;
	}
	oss<< stringifyTypeDesc(&funcDesc->elemdescFunc.tdesc, pTypeInfo);
	CComBSTR bstrName;
	pTypeInfo->GetDocumentation(funcDesc->memid, &bstrName, 0, 0, 0);
	char ansiName[MAX_PATH];
	WideCharToMultiByte(CP_ACP, 0, bstrName, bstrName.Length() + 1, 
		ansiName, MAX_PATH, 0, 0);
	oss<< ' '<< ansiName<< '(';
	for(int curParam(0); curParam < funcDesc->cParams; ++curParam) {
		oss<< stringifyFunctionArgument(&funcDesc->lprgelemdescParam[curParam], 
			pTypeInfo);
		if(curParam < funcDesc->cParams - 1) oss<< ", ";
	}
	oss<< ')';
	return oss.str();
}

template<typename T> class CExpansionNode {
	mutable HTREEITEM _expNode;
	public:
		void BuildExpansionNode() {
			if(_expNode) return;
			TVINSERTSTRUCT tvInsertStruct;
			tvInsertStruct.hParent = static_cast<T*>(this)->treeItem();
			tvInsertStruct.hInsertAfter = TVI_FIRST;
			tvInsertStruct.item.mask = 0;
			_expNode = TreeView_InsertItem(static_cast<T*>(this)->treeView(), 
				&tvInsertStruct);
		}
		
		void KillExpansionNode() {
			if(!_expNode) return;
			TreeView_DeleteItem(static_cast<T*>(this)->treeView(), _expNode);
			_expNode = 0;
		}
		
		CExpansionNode() : _expNode(0) { }
		CExpansionNode(const CExpansionNode& c) : _expNode(c._expNode) {
			c._expNode = 0;
		}
		CExpansionNode& operator=(const CExpansionNode& c) {
			KillExpansionNode();
			_expNode = c._expNode;
			c._expNode = 0;
			return *this;
		}
		~CExpansionNode() {
			KillExpansionNode();
		}
};

class CTypeBrowserBase {
	mutable HTREEITEM _treeItem;
	HWND _treeView;
	std::string _displayName;
	HTREEITEM _parentTreeItem;
protected:
	CTypeBrowserBase* GetParentNode() {
		if(_parentTreeItem == TVI_ROOT || !_parentTreeItem) return 0;
		TVITEM tvItem;
		tvItem.lParam = 0;
		tvItem.hItem = _parentTreeItem;
		tvItem.mask = TVIF_PARAM;
		TreeView_GetItem(_treeView, &tvItem);
		return reinterpret_cast<CTypeBrowserBase*>(tvItem.lParam);
	}
public:
	CTypeBrowserBase() : _treeView(0) { }
	CTypeBrowserBase& operator=(const CTypeBrowserBase& c) {
		if(_treeItem) TreeView_DeleteItem(treeView(), treeItem());
		_treeView = c._treeView;
		_displayName = c._displayName;
		_treeItem = c._treeItem;
		TVITEM tvItem;
		tvItem.mask = TVIF_HANDLE | TVIF_PARAM;
		tvItem.hItem = _treeItem;	
		TreeView_GetItem(treeView(), &tvItem);
		tvItem.lParam = reinterpret_cast<LPARAM>(this);
		TreeView_SetItem(treeView(), &tvItem);
		c._treeItem = 0;
		return *this;
	}
	
	std::string displayName() { return _displayName; }
	HWND treeView() { return _treeView; }
	HTREEITEM treeItem() { return _treeItem; }		
	
	CTypeBrowserBase(BSTR name, HWND tv, HTREEITEM parentTreeItem) :
		_treeView(tv), _parentTreeItem(parentTreeItem) {
		char ansiName[MAX_PATH];
		WideCharToMultiByte(CP_ACP, 0, name, SysStringLen(name) + 1, 
			ansiName, MAX_PATH, 0, 0);
		_displayName = ansiName;
		
		TVINSERTSTRUCT tvInsertStruct;
		tvInsertStruct.hParent = parentTreeItem;
		tvInsertStruct.hInsertAfter = TVI_LAST;
		tvInsertStruct.item.mask = TVIF_TEXT | TVIF_PARAM;
		tvInsertStruct.item.pszText = LPSTR_TEXTCALLBACK;
		tvInsertStruct.item.cchTextMax = 0;
		tvInsertStruct.item.lParam = reinterpret_cast<LPARAM>(this);
		
		_treeItem = TreeView_InsertItem(treeView(), &tvInsertStruct);
	}
	
	CTypeBrowserBase(const CTypeBrowserBase& c) : _treeView(c._treeView),
		_displayName(c._displayName), _treeItem(c._treeItem) {
		TVITEM tvItem;
		tvItem.mask = TVIF_HANDLE | TVIF_PARAM;
		tvItem.hItem = _treeItem;	
		TreeView_GetItem(treeView(), &tvItem);
		tvItem.lParam = reinterpret_cast<LPARAM>(this);
		TreeView_SetItem(treeView(), &tvItem);
		c._treeItem = 0;
	}
	
	~CTypeBrowserBase() {
		if(treeItem()) {
			TreeView_DeleteItem(treeView(), treeItem());
			_treeItem = 0;
		}
	}
	virtual void collapse() = 0;
	virtual void expand() = 0;
	
	virtual std::string editBoxText() {
		return std::string("    COM Owns You");
	}
};

class CComEndNode : public CTypeBrowserBase {
public:
	CComEndNode(BSTR name, HWND tv, HTREEITEM parentTreeItem) :
		CTypeBrowserBase(name, tv, parentTreeItem) { }
	virtual void collapse() { }
	virtual void expand() { }
};

class CComMessageEndNode : public CComEndNode {
	std::string _message;
public:
	CComMessageEndNode(BSTR name, HWND tv, HTREEITEM parentTreeItem, 
		std::string message) :
		CComEndNode(name, tv, parentTreeItem), _message(message) { }
	virtual std::string editBoxText() { return _message; }
};

class CComVariableGroup : public CTypeBrowserBase, 
	public CExpansionNode<CComVariableGroup> {
	CComPtr<ITypeInfo> _pTypeInfo;
	std::vector<CComEndNode> _items;
public:
	CComVariableGroup(BSTR name, HWND tv, HTREEITEM parentTreeItem, 
		ITypeInfo* ti) : CTypeBrowserBase(name, tv, parentTreeItem), 
		_pTypeInfo(ti) {
		BuildExpansionNode();
	}
	virtual void collapse() {
		if(_items.size()) {
			_items.clear();
			BuildExpansionNode();
		}
	}
	virtual void expand() {
		try {
			KillExpansionNode();
			CComTypeAttr typeAttr(_pTypeInfo);
			int totalVars(typeAttr->cVars);
			for(int var(0); var < totalVars; ++var) {
				try {
					CComVarDesc varDesc(_pTypeInfo, var);
					CComBSTR name(stringifyVarDesc(varDesc, _pTypeInfo).c_str());
					_items.push_back(CComEndNode(name, treeView(), treeItem()));
				} catch (EVeryBadThing) { }
			}
		} catch (EVeryBadThing) { }
	}
};

class CComInterfaceJoint : public CTypeBrowserBase, 
	public CExpansionNode<CComInterfaceJoint> {
	CComPtr<ITypeInfo> _pTypeInfo;
	std::vector<CComMessageEndNode> _items;
public:
	CComInterfaceJoint(BSTR name, HWND tv, HTREEITEM parentTreeItem, 
		ITypeInfo* ti) : CTypeBrowserBase(name, tv, parentTreeItem), 
		_pTypeInfo(ti) {
		BuildExpansionNode();
	}
	virtual void collapse() {
		if(_items.size()) {
			_items.clear();
			BuildExpansionNode();
		}
	}
	virtual void expand() {
		try {
			KillExpansionNode();
			CComTypeAttr typeAttr(_pTypeInfo);
			int totalFuncs(typeAttr->cFuncs);
			for(int curFunc(0); curFunc < totalFuncs; ++curFunc) {
				try {
					CComFuncDesc funcDesc(_pTypeInfo, curFunc);
					CComBSTR name[1];
					UINT cNames;
					HRESULT hr(_pTypeInfo->GetNames(funcDesc->memid, 
						reinterpret_cast<BSTR*>(&name),	1, &cNames));
					if(hr) continue;
					char ansiName[MAX_PATH];
					WideCharToMultiByte(CP_ACP, 0, *name, name->Length() + 1, 
						ansiName, MAX_PATH, 0, 0);
					std::ostringstream oss;
					oss<< ansiName;
					switch(funcDesc->invkind) {
					case INVOKE_FUNC: oss<< "  (method)"; break;
					case INVOKE_PROPERTYGET: oss<< "  (prop get)"; break;
					case INVOKE_PROPERTYPUT: oss<< "  (prop put)"; break;
					case INVOKE_PROPERTYPUTREF: oss<< "  (prop put ref)"; break;
					}
					std::string methodMessage(stringifyCOMMethod(funcDesc, 
						_pTypeInfo));
					_items.push_back(CComMessageEndNode(CComBSTR(oss.str().c_str()),
						treeView(), treeItem(),	methodMessage));
				} catch(EVeryBadThing) { }
			}
		} catch(EVeryBadThing) { }
	}
	
	virtual std::string editBoxText() { 
		try {
			CComTypeAttr typeAttr(_pTypeInfo);
			wchar_t stringClsid[39];
			StringFromGUID2(typeAttr->guid, stringClsid, 39);
			char ansiClsid[39];
			WideCharToMultiByte(CP_ACP, 0, stringClsid, 39, ansiClsid, 39, 0, 0);
			std::ostringstream oss;
			oss<< "    IID: "<< ansiClsid<< ' '<<displayName();
			return oss.str();
		} catch(EVeryBadThing) { return std::string(""); }			
	}
};

template<TYPEKIND tk> class CComVariableGroupContainer : 
	public CTypeBrowserBase, public CExpansionNode<CComVariableGroupContainer> {
	CComPtr<ITypeLib> _pTypeLib;
	std::vector<CComVariableGroup> _variables;
public:	
	CComVariableGroupContainer(BSTR name, HWND tv, HTREEITEM parentTreeItem,
		ITypeLib* pTlb) : CTypeBrowserBase(name, tv, parentTreeItem), 
		_pTypeLib(pTlb) {
		BuildExpansionNode();	
	}
	  
	virtual void expand() {
		KillExpansionNode();
		if(!_pTypeLib) return;
		UINT infoCount(_pTypeLib->GetTypeInfoCount());
		if(!infoCount) return;
		  
		for(int info(0); info < infoCount; ++info) {
			TYPEKIND typeKind;
			HRESULT hr(_pTypeLib->GetTypeInfoType(info, &typeKind));
			if(hr || typeKind != tk) continue;
			 
			CComBSTR name;
			hr = _pTypeLib->GetDocumentation(info, &name, 0, 0, 0);
			if(hr) continue;
			CComPtr<ITypeInfo> pTypeInfo;
			hr = _pTypeLib->GetTypeInfo(info, &pTypeInfo);
			if(hr) continue;
			_variables.push_back(CComVariableGroup(name, treeView(), 
				treeItem(), pTypeInfo));
		}
	}
	  
	virtual void collapse() {
		if(_variables.size()) {
			_variables.clear();
			BuildExpansionNode();
		}
	}
	virtual std::string editBoxText() { };
};

template<> std::string CComVariableGroupContainer<TKIND_ENUM>::editBoxText() {
	std::ostringstream oss;
	oss<< "    Enumerations in "<< GetParentNode()->displayName();
	return oss.str();
}

template<> std::string CComVariableGroupContainer<TKIND_UNION>::editBoxText() {
	std::ostringstream oss;
	oss<< "    Teamsters in "<< GetParentNode()->displayName();
	return oss.str();
}

template<> std::string CComVariableGroupContainer<TKIND_RECORD>::editBoxText() {
	std::ostringstream oss;
	oss<< "    Structures in "<< GetParentNode()->displayName();
	return oss.str();
}

template<TYPEKIND tk> class CComInterfaceGroupContainer :
	public CTypeBrowserBase, public CExpansionNode<CComInterfaceGroupContainer> {
	CComPtr<ITypeLib> _pTypeLib;
	std::vector<CComInterfaceJoint> _interfaces;
public:
	CComInterfaceGroupContainer(BSTR name, HWND tv, HTREEITEM parentTreeItem,
		ITypeLib* pTlb) : CTypeBrowserBase(name, tv, parentTreeItem), 
		_pTypeLib(pTlb) {
		BuildExpansionNode();
	}
	virtual void expand() {
		KillExpansionNode();
		if(!_pTypeLib) return;
		UINT infoCount(_pTypeLib->GetTypeInfoCount());
		if(!infoCount) return;
		for(int info(0); info < infoCount; ++info) {
			TYPEKIND typeKind;
			HRESULT hr(_pTypeLib->GetTypeInfoType(info, &typeKind));
			if(hr || typeKind != tk) continue;
			  
			CComBSTR name;
			hr = _pTypeLib->GetDocumentation(info, &name, 0, 0, 0);
			if(hr) continue;
			CComPtr<ITypeInfo> pTypeInfo;
			hr = _pTypeLib->GetTypeInfo(info, &pTypeInfo);
			if(hr) continue;
			_interfaces.push_back(CComInterfaceJoint(name, treeView(), 
				treeItem(), pTypeInfo));
		}
	}
	virtual void collapse() {
		if(_interfaces.size()) {
			_interfaces.clear();
			BuildExpansionNode();
		}
	}
	virtual std::string editBoxText() { }
};

template<> std::string CComInterfaceGroupContainer<TKIND_DISPATCH>::editBoxText() {
	std::ostringstream oss;
	oss<< "    Dispatch Interfaces in "<< GetParentNode()->displayName();
	return oss.str();
}

template<> std::string CComInterfaceGroupContainer<TKIND_INTERFACE>::editBoxText() {
	std::ostringstream oss;
	oss<< "    VTable Interfaces in "<< GetParentNode()->displayName();
	return oss.str();
}

template<> void CComInterfaceGroupContainer<TKIND_INTERFACE>::expand() { 
	KillExpansionNode();
	if(!_pTypeLib) return;
	UINT infoCount(_pTypeLib->GetTypeInfoCount());
	if(!infoCount) return;
	for(int info(0); info < infoCount; ++info) {
		try {
			TYPEKIND typeKind;
			HRESULT hr(_pTypeLib->GetTypeInfoType(info, &typeKind));
			if(hr) continue;
			if(typeKind == TKIND_DISPATCH) {
				CComPtr<ITypeInfo> pDispatchInfo;
				hr = _pTypeLib->GetTypeInfo(info, &pDispatchInfo);
				if(hr) continue;
				HREFTYPE interfaceRefType;
				hr = pDispatchInfo->GetRefTypeOfImplType(-1, &interfaceRefType);
				if(hr) continue;
				CComPtr<ITypeInfo> pInterfaceInfo;
				hr = pDispatchInfo->GetRefTypeInfo(interfaceRefType, 
					&pInterfaceInfo);
				if(hr) continue;
				CComTypeAttr typeAttr(pInterfaceInfo);
				if(typeAttr->typekind != TKIND_INTERFACE) continue;
				CComBSTR name;
				hr = _pTypeLib->GetDocumentation(info, &name, 0, 0, 0);
				if(hr) continue;
				_interfaces.push_back(CComInterfaceJoint(name, treeView(), 
					treeItem(), pInterfaceInfo));
				continue;
			}
			if(typeKind != TKIND_INTERFACE) continue;
			CComBSTR name;
			hr = _pTypeLib->GetDocumentation(info, &name, 0, 0, 0);
			if(hr) continue;
			CComPtr<ITypeInfo> pTypeInfo;
			hr = _pTypeLib->GetTypeInfo(info, &pTypeInfo);
			if(hr) continue;
			_interfaces.push_back(CComInterfaceJoint(name, treeView(), 
				treeItem(), pTypeInfo));
		} catch(EVeryBadThing) { }
	}
}

class CComTypedefContainer : public CTypeBrowserBase, 
	CExpansionNode<CComTypedefContainer> {
	CComPtr<ITypeLib> _pTypeLib;
	std::vector<CComEndNode> _typedefs;
public:
	CComTypedefContainer(HWND tv, HTREEITEM parentTreeItem, ITypeLib* pTlb) :
		CTypeBrowserBase(CComBSTR("Typedefs"), tv, parentTreeItem),
		_pTypeLib(pTlb) {
		BuildExpansionNode();
	}
	virtual void expand() {
		KillExpansionNode();
		if(!_pTypeLib) return;
		UINT count(_pTypeLib->GetTypeInfoCount());
		for(int curAlias(0); curAlias < count; ++curAlias) {
			try {
				TYPEKIND typeKind;
				HRESULT hr(_pTypeLib->GetTypeInfoType(curAlias, &typeKind));
				if(hr || typeKind != TKIND_ALIAS) continue;
				CComBSTR name;
				hr = _pTypeLib->GetDocumentation(curAlias, &name, 0, 0, 0);
				if(hr) continue;
				CComPtr<ITypeInfo> pTypeInfo;
				hr = _pTypeLib->GetTypeInfo(curAlias, &pTypeInfo);
				if(hr) continue;
				CComTypeAttr typeAttr(pTypeInfo);
								  
				std::ostringstream typeDefString;
				typeDefString<< "typedef "<< 
					stringifyTypeDesc(&typeAttr->tdescAlias, pTypeInfo);
				char ansiName[MAX_PATH];
				WideCharToMultiByte(CP_ACP, 0, name, name.Length() + 1, 
					ansiName, MAX_PATH, 0, 0);
				typeDefString<< ' '<< ansiName;
				_typedefs.push_back(CComEndNode(CComBSTR(
					typeDefString.str().c_str()), treeView(), treeItem()));
			} catch(EVeryBadThing) { }
		}
	}
	virtual void collapse() {
		if(_typedefs.size()) {
			_typedefs.clear();
			BuildExpansionNode();
		}
	}
	virtual std::string editBoxText() {
		std::ostringstream oss;
		oss<< "    Type Definitions in "<< GetParentNode()->displayName();
		return oss.str();
	}		
};

class CComCoClassNode : public CTypeBrowserBase, 
	public CExpansionNode<CComCoClassNode> {
	CComPtr<ITypeInfo> _pTypeInfo;
	std::vector<CComEndNode> _interfaces;
public:
	CComCoClassNode(BSTR name, HWND tv, HTREEITEM parentTreeItem, 
		ITypeInfo* ti) : CTypeBrowserBase(name, tv, parentTreeItem), 
		_pTypeInfo(ti) {
		BuildExpansionNode();
	}
	virtual void expand() {
		try{ 
			KillExpansionNode();
			if(!_pTypeInfo) return;
			CComTypeAttr typeAttr(_pTypeInfo);
			int implInterfaces(typeAttr->cImplTypes);
			for(int curInterface(0); curInterface < implInterfaces; 
				++curInterface) {
				HREFTYPE hRefType;
				HRESULT hr(_pTypeInfo->GetRefTypeOfImplType(curInterface,
					&hRefType));
				CComPtr<ITypeInfo> interfaceTypeInfo;
				hr = _pTypeInfo->GetRefTypeInfo(hRefType, &interfaceTypeInfo);
				if(hr) continue;
				CComBSTR interfaceName;
				hr = interfaceTypeInfo->GetDocumentation(-1, &interfaceName,
					0, 0, 0);
				if(hr) continue;
				_interfaces.push_back(CComEndNode(interfaceName, treeView(), 
					treeItem()));
			}
		} catch(EVeryBadThing) { }
	}
	virtual void collapse() {
		BuildExpansionNode();
	}
	virtual std::string editBoxText() { 
		try {
			CComTypeAttr typeAttr(_pTypeInfo);
			wchar_t stringClsid[39];
			StringFromGUID2(typeAttr->guid, stringClsid, 39);
			char ansiClsid[39];
			WideCharToMultiByte(CP_ACP, 0, stringClsid, 39, ansiClsid, 39, 0, 0);
			std::ostringstream oss;
			oss<< "    CLSID: "<< ansiClsid<< "     "<<displayName();
			return oss.str();
		} catch(EVeryBadThing) { return std::string(""); }			
	}
};

class CComCoClassContainer : public CTypeBrowserBase,
public CExpansionNode<CComCoClassContainer> {
	CComPtr<ITypeLib> _pTypeLib;
	std::vector<CComCoClassNode> _coClasses;
public:
	CComCoClassContainer(HWND tv, HTREEITEM parentTreeItem, ITypeLib* pTlb) : 
		CTypeBrowserBase(CComBSTR("CoClasses"), tv, parentTreeItem), 
		_pTypeLib(pTlb) {
		BuildExpansionNode();
	}
	virtual void expand() {
		KillExpansionNode();
		if(!_pTypeLib) return;
		UINT count(_pTypeLib->GetTypeInfoCount());
		for(int curCoClass(0); curCoClass < count; ++curCoClass) {
			try {
				TYPEKIND typeKind;
				HRESULT hr(_pTypeLib->GetTypeInfoType(curCoClass, &typeKind));
				if(hr || typeKind != TKIND_COCLASS) continue;
				CComBSTR name;
				hr = _pTypeLib->GetDocumentation(curCoClass, &name, 0, 0, 0);
				if(hr) continue;
				char ansiName[MAX_PATH];
				WideCharToMultiByte(CP_ACP, 0, name, name.Length() + 1, 
					ansiName, MAX_PATH, 0, 0);
				CComPtr<ITypeInfo> pTypeInfo;
				hr = _pTypeLib->GetTypeInfo(curCoClass, &pTypeInfo);
				if(hr) continue;
				_coClasses.push_back(CComCoClassNode(name, treeView(),
					treeItem(), pTypeInfo));
			} catch(EVeryBadThing) { }
		} 
	}
	virtual void collapse() {
		 if(_coClasses.size()) {
			_coClasses.clear();
			BuildExpansionNode();
		}
	}
	virtual std::string editBoxText() {
		std::ostringstream oss;
		oss<< "    CoClasses in "<< GetParentNode()->displayName();
		return oss.str();
	}
};

class CComTypeLibrary : public CTypeBrowserBase, 
	public CExpansionNode<CComTypeLibrary> {
	std::auto_ptr<CComVariableGroupContainer<TKIND_ENUM> > _enumContainer;
	std::auto_ptr<CComVariableGroupContainer<TKIND_RECORD> > _structContainer;
	std::auto_ptr<CComVariableGroupContainer<TKIND_UNION> > _unionContainer;
	std::auto_ptr<CComTypedefContainer> _typedefContainer;
	std::auto_ptr<CComInterfaceGroupContainer<TKIND_INTERFACE> > 
		_interfaceContainer;
	std::auto_ptr<CComInterfaceGroupContainer<TKIND_DISPATCH> > 
		_dispatchContainer;
	std::auto_ptr<CComCoClassContainer> _coClassContainer;
	std::string _editBoxText;
	CComBSTR _filename;
public:
	CComTypeLibrary(BSTR name, HWND tv, HTREEITEM parentTreeItem, 
		CComBSTR filename, std::string editBoxInfo) : CTypeBrowserBase(name,
		tv, parentTreeItem), _editBoxText(editBoxInfo), _filename(filename) {
		BuildExpansionNode();
	}
	
	virtual void expand() {
		KillExpansionNode();
		CComPtr<ITypeLib> pTypeLib;
		HRESULT hr(LoadTypeLib(_filename, &pTypeLib));
		if(hr) return;
		_enumContainer = std::auto_ptr<CComVariableGroupContainer<TKIND_ENUM> >(
			new	CComVariableGroupContainer<TKIND_ENUM>(CComBSTR(L"Enums"), 
			treeView(), treeItem(), pTypeLib));
		_structContainer = std::auto_ptr<CComVariableGroupContainer<TKIND_RECORD> >(
			new CComVariableGroupContainer<TKIND_RECORD>(CComBSTR(L"Structs"), 
			treeView(), treeItem(), pTypeLib));
		_unionContainer = std::auto_ptr<CComVariableGroupContainer<TKIND_UNION> >(
			new CComVariableGroupContainer<TKIND_UNION>(CComBSTR(L"Unions"), 
			treeView(), treeItem(), pTypeLib));
		_typedefContainer = std::auto_ptr<CComTypedefContainer>(new 
			CComTypedefContainer(treeView(), treeItem(), pTypeLib));
		_interfaceContainer = 
			std::auto_ptr<CComInterfaceGroupContainer<TKIND_INTERFACE> >(
			new	CComInterfaceGroupContainer<TKIND_INTERFACE>(
			CComBSTR(L"Interfaces"), treeView(), treeItem(), pTypeLib));
		_dispatchContainer = 
			std::auto_ptr<CComInterfaceGroupContainer<TKIND_DISPATCH> >(
			new	CComInterfaceGroupContainer<TKIND_DISPATCH>(
			CComBSTR(L"Dispinterfaces"), treeView(), treeItem(), pTypeLib));
		_coClassContainer = std::auto_ptr<CComCoClassContainer>(
			new CComCoClassContainer(treeView(), treeItem(), pTypeLib));
	}
	
	virtual void collapse() {
		_enumContainer = 
			std::auto_ptr<CComVariableGroupContainer<TKIND_ENUM> >(0);
		_structContainer = 
			std::auto_ptr<CComVariableGroupContainer<TKIND_RECORD> >(0);
		_unionContainer = 
			std::auto_ptr<CComVariableGroupContainer<TKIND_UNION> >(0);
		_typedefContainer = 
			std::auto_ptr<CComTypedefContainer>(0);
		_interfaceContainer = 
			std::auto_ptr<CComInterfaceGroupContainer<TKIND_INTERFACE> >(0);
		_dispatchContainer = 
			std::auto_ptr<CComInterfaceGroupContainer<TKIND_DISPATCH> >(0);
		_coClassContainer = std::auto_ptr<CComCoClassContainer>(0);
		BuildExpansionNode();
	}
	
	~CComTypeLibrary() {
		KillExpansionNode();
	}

	virtual std::string editBoxText() { return _editBoxText; }
};

class CComTypeLibraryContainer : public CTypeBrowserBase, 
	public CExpansionNode<CComTypeLibraryContainer> {
	std::vector<CComTypeLibrary> _libraries;
public:
	CComTypeLibraryContainer(BSTR name, HWND tv, HTREEITEM parentTreeItem) :
		CTypeBrowserBase(name, tv, parentTreeItem) {
		BuildExpansionNode();		
	}
	virtual void expand() {
		KillExpansionNode();
		 
		CRegKey typeLibKey;
		typeLibKey.Open(HKEY_CLASSES_ROOT, "TypeLib", KEY_ENUMERATE_SUB_KEYS 
			| KEY_QUERY_VALUE);
		DWORD totalLibs;
		RegQueryInfoKey(typeLibKey, 0, 0, 0, &totalLibs, 0, 0, 0, 0, 0, 0, 0);
		_libraries.reserve(totalLibs * 1.5);
		for(int curLib(0); curLib < totalLibs; ++curLib) {
			char ansiKeyName[39];
			DWORD cchAnsiKeyName(39);
			RegEnumKeyEx(typeLibKey, curLib, ansiKeyName, &cchAnsiKeyName,
				0, 0, 0, 0);
			if(cchAnsiKeyName != 38) continue;
			GUID libGUID;
			wchar_t wideKeyName[39];
			MultiByteToWideChar(CP_ACP, 0, ansiKeyName, 39, wideKeyName, 39);
			HRESULT hr(CLSIDFromString(wideKeyName, &libGUID));
			if(hr) continue;
			  
			CRegKey libKey;
			if(libKey.Open(typeLibKey, ansiKeyName, KEY_ENUMERATE_SUB_KEYS 
				| KEY_QUERY_VALUE)) continue;
			DWORD totalVersions;
			RegQueryInfoKey(libKey, 0, 0, 0, &totalVersions, 0, 0, 0, 
				0, 0, 0, 0);
			for(int curVersion(0); curVersion < totalVersions; ++curVersion) {
				char ansiVersion[16];
				DWORD cchAnsiVersion(16);
				RegEnumKeyEx(libKey, curVersion, ansiVersion, &cchAnsiVersion,
					0, 0, 0, 0);
				DWORD verMajor, verMinor;
				if(sscanf(ansiVersion, "%u.%u", &verMajor, &verMinor) != 2) 
					continue;
				  
				CRegKey versionKey;
				if(versionKey.Open(libKey, ansiVersion, KEY_ENUMERATE_SUB_KEYS 
					| KEY_QUERY_VALUE)) 
					continue;
				char typeLibName[MAX_PATH];
				DWORD cchTypeLibName(MAX_PATH);
				std::ostringstream ossTypeLibName;
				if(!versionKey.QueryValue(typeLibName, 0, &cchTypeLibName) 
					&& lstrlen(typeLibName)) ossTypeLibName<< typeLibName;
				else ossTypeLibName<< ansiKeyName;
				ossTypeLibName<< " <"<< ansiVersion<< '>';
				  
				DWORD totalLcids;
				RegQueryInfoKey(versionKey, 0, 0, 0, &totalLcids, 0, 0, 
					0, 0, 0, 0, 0);
				for(int curLcid(0); curLcid < totalLcids; ++curLcid) {
					char stringLcid[16];
					DWORD cchStringLcid(16);
					RegEnumKeyEx(versionKey, curLcid, stringLcid, 
						&cchStringLcid, 0, 0, 0, 0);
					if(!strcmpi("helpdir", stringLcid)) continue;
					if(!strcmpi("flags", stringLcid)) continue;
					  
					LCID lcidVal;
					if(!sscanf(stringLcid, "%x", &lcidVal)) continue;
					strncat(stringLcid, "\\win32", 6);
					CRegKey locationKey;
					if(locationKey.Open(versionKey, stringLcid, KEY_QUERY_VALUE)) 
						continue;
					  
					char ansiPath[MAX_PATH];
					DWORD cchAnsiPath(MAX_PATH);
					if(locationKey.QueryValue(ansiPath, 0, &cchAnsiPath)) 
						continue;
					  
					std::ostringstream editText;
					editText<< "    "<< ansiKeyName<< "     ";
					editText.flags(std::ios_base::hex);
					editText<< "LCID: "<< lcidVal;
					editText<< "     "<< ansiPath;
					_libraries.push_back(CComTypeLibrary(
						CComBSTR(ossTypeLibName.str().c_str()), treeView(),
						treeItem(), CComBSTR(ansiPath), editText.str()));
				}
			}
		}
		_libraries.reserve(_libraries.size());
	}
	virtual void collapse() {
		_libraries.clear();
		BuildExpansionNode();
	}
	~CComTypeLibraryContainer() {
		KillExpansionNode();
	}
};

struct SAppWindows {
	HWND mainWindow, treeView, editBox;
	int editBoxHeight;
};

long _stdcall mainProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
	SAppWindows& windows = *reinterpret_cast<SAppWindows*>(
		GetWindowLong(hwnd, GWL_USERDATA));
	NMHDR* nmhdr;
	switch(message) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_SIZE:
		SetWindowPos(windows.treeView, 0, 0, 0, LOWORD(lparam), HIWORD(lparam) -
			windows.editBoxHeight, SWP_NOZORDER);
		SetWindowPos(windows.editBox, 0, 0, HIWORD(lparam) - 
			windows.editBoxHeight, LOWORD(lparam), windows.editBoxHeight, 
			SWP_NOZORDER);
		return 0;
	case WM_NOTIFY:
		nmhdr = reinterpret_cast<NMHDR*>(lparam);
		switch(nmhdr->code) {
		case TVN_GETDISPINFO:
			if(nmhdr->hwndFrom == windows.treeView) {
				NMTVDISPINFO* tvDispInfo = 
					reinterpret_cast<NMTVDISPINFO*>(lparam);
				CTypeBrowserBase* browserBase = 
					reinterpret_cast<CTypeBrowserBase*>(tvDispInfo->item.lParam);
				tvDispInfo->item.pszText = 
					const_cast<char*>(browserBase->displayName().c_str());
				tvDispInfo->item.cchTextMax = browserBase->displayName().size();
				return 0;
			}
			break;
		case TVN_SELCHANGED:
			if(nmhdr->hwndFrom == windows.treeView) {
				NMTREEVIEW* changedStruct = reinterpret_cast<NMTREEVIEW*>(lparam);
				CTypeBrowserBase& browseBase = 
					*reinterpret_cast<CTypeBrowserBase*>(
					changedStruct->itemNew.lParam);
				static std::string editBoxText;
				editBoxText = browseBase.editBoxText();
				SetWindowText(windows.editBox, editBoxText.c_str());				
				return 0;
			}
			break;
		case TVN_ITEMEXPANDING:
			if(nmhdr->hwndFrom == windows.treeView) {
				NMTREEVIEW* nmTreeView = reinterpret_cast<NMTREEVIEW*>(lparam);
				TreeView_SelectItem(nmhdr->hwndFrom, nmTreeView->itemNew.hItem);
				CTypeBrowserBase* browserBase = 
					reinterpret_cast<CTypeBrowserBase*>(
					nmTreeView->itemNew.lParam);
				if(nmTreeView->action & TVE_EXPAND)	browserBase->expand();
				return 0;
			}
			break;
		case TVN_ITEMEXPANDED:
			if(nmhdr->hwndFrom == windows.treeView) {
				NMTREEVIEW* nmTreeView = reinterpret_cast<NMTREEVIEW*>(lparam);
				CTypeBrowserBase* browserBase = 
					reinterpret_cast<CTypeBrowserBase*>(
					nmTreeView->itemNew.lParam);
				if(nmTreeView->action & TVE_COLLAPSE) browserBase->collapse();
				return 0;
			}
			break;
		}
	}
	return DefWindowProc(hwnd, message, wparam, lparam);
}


int _stdcall WinMain(HINSTANCE h, HINSTANCE, char*, int s) {
	CoInitialize(0);
	WNDCLASSEX wc;
	ZeroMemory(&wc, sizeof(wc));
	wc.cbSize = sizeof(wc);
	wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_WINLOGO);
	wc.hCursor = LoadCursor(0, IDC_ARROW);
	wc.lpszClassName = "Cornholio";
	wc.lpfnWndProc = mainProc;
	wc.hInstance = h;
	RegisterClassEx(&wc);
	
	SAppWindows appWindows;
	appWindows.mainWindow = CreateWindow(wc.lpszClassName, 
		"COM Type Info Browser", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, h, 0);
	
	InitCommonControls();
	appWindows.treeView = CreateWindow(WC_TREEVIEW, 0, TVS_HASBUTTONS 
		| TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | WS_CHILD 
		| WS_VISIBLE, 0, 0, 0, 0, appWindows.mainWindow, 0, h, 0);
	
	appWindows.editBox = CreateWindow("EDIT", 0, WS_VISIBLE | WS_CHILD | ES_LEFT 
		| ES_READONLY | ES_AUTOHSCROLL,	0, 0, 0, 0, 
		appWindows.mainWindow, 0, h, 0); 
	
	HFONT font(CreateFont(16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial"));
	SendMessage(appWindows.editBox, WM_SETFONT, 
		reinterpret_cast<WPARAM>(font), 0);
	TEXTMETRIC textMetric;
	HDC hdc(GetDC(0));
	HFONT standardFont(reinterpret_cast<HFONT>(SelectObject(hdc, font)));
	GetTextMetrics(hdc, &textMetric);
	SelectObject(hdc, standardFont);
	ReleaseDC(0, hdc);
	appWindows.editBoxHeight = textMetric.tmHeight;
	SetWindowText(appWindows.editBox, "    COM Owns You");
	
	SetWindowLong(appWindows.mainWindow, GWL_USERDATA, 
		reinterpret_cast<long>(&appWindows));
	
	CComTypeLibraryContainer typeLibraryContainer(
		CComBSTR("HKEY_CLASSES_ROOT\\TypeLib"), appWindows.treeView, TVI_ROOT);
	ShowWindow(appWindows.mainWindow, s);
	MSG msg;
	while(GetMessage(&msg, 0, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 0;
}
