// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"

#ifdef AFXCTL_FACT_SEG
#pragma code_seg(AFXCTL_FACT_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

#define GUID_CCH    39  // Characters in string form of guid, including '\0'

inline BOOL _AfxRegDeleteKeySucceeded(LONG error)
{
	return (error == ERROR_SUCCESS) || (error == ERROR_BADKEY) ||
		(error == ERROR_FILE_NOT_FOUND);
}

// Under Win32, a reg key may not be deleted unless it is empty.
// Thus, to delete a tree,  one must recursively enumerate and
// delete all of the sub-keys.

LONG AFXAPI _AfxRecursiveRegDeleteKey(HKEY hParentKey, LPTSTR szKeyName)
{
	// one implementation for everybody
	return AfxDelRegTreeHelper(hParentKey, szKeyName);
}

void _AfxUnregisterInterfaces(ITypeLib* pTypeLib)
{
	TCHAR szKey[128];
	lstrcpy(szKey, _T("Interface\\"));
	LPTSTR pszGuid = szKey + lstrlen(szKey);

	int cTypeInfo = pTypeLib->GetTypeInfoCount();

	for (int i = 0; i < cTypeInfo; i++)
	{
		TYPEKIND tk;
		if (SUCCEEDED(pTypeLib->GetTypeInfoType(i, &tk)) &&
			(tk == TKIND_DISPATCH || tk == TKIND_INTERFACE))
		{
			ITypeInfo* pTypeInfo = NULL;
			if (SUCCEEDED(pTypeLib->GetTypeInfo(i, &pTypeInfo)))
			{
				TYPEATTR* pTypeAttr;
				if (SUCCEEDED(pTypeInfo->GetTypeAttr(&pTypeAttr)))
				{
#if defined(_UNICODE) || defined(OLE2ANSI)
					StringFromGUID2(pTypeAttr->guid, pszGuid, GUID_CCH);
#else
					WCHAR wszGuid[39];
					StringFromGUID2(pTypeAttr->guid, wszGuid, GUID_CCH);
					_wcstombsz(pszGuid, wszGuid, GUID_CCH);
#endif
					_AfxRecursiveRegDeleteKey(HKEY_CLASSES_ROOT, szKey);
					pTypeInfo->ReleaseTypeAttr(pTypeAttr);
				}

				pTypeInfo->Release();
			}
		}
	}
}

BOOL AFXAPI AfxOleRegisterTypeLib(HINSTANCE hInstance, REFGUID tlid,
	LPCTSTR pszFileName, LPCTSTR pszHelpDir)
{
	USES_CONVERSION;

	BOOL bSuccess = FALSE;
	CString strPathName;
	TCHAR *szPathName = strPathName.GetBuffer(_MAX_PATH);
	::GetModuleFileName(hInstance, szPathName, _MAX_PATH);
	strPathName.ReleaseBuffer();
	LPTYPELIB ptlib = NULL;

	// If a filename was specified, replace final component of path with it.
	if (pszFileName != NULL)
	{
		int iBackslash = strPathName.ReverseFind('\\');
		if (iBackslash != -1)
			strPathName = strPathName.Left(iBackslash+1);
		strPathName += pszFileName;
	}

	if (SUCCEEDED(LoadTypeLib(T2COLE(strPathName), &ptlib)))
	{
		ASSERT_POINTER(ptlib, ITypeLib);

		LPTLIBATTR pAttr;
		GUID tlidActual = GUID_NULL;

		if (SUCCEEDED(ptlib->GetLibAttr(&pAttr)))
		{
			ASSERT_POINTER(pAttr, TLIBATTR);
			tlidActual = pAttr->guid;
			ptlib->ReleaseTLibAttr(pAttr);
		}

		// Check that the guid of the loaded type library matches
		// the tlid parameter.
		ASSERT(IsEqualGUID(tlid, tlidActual));

		if (IsEqualGUID(tlid, tlidActual))
		{
			// Register the type library.
			if (SUCCEEDED(RegisterTypeLib(ptlib,
					T2OLE((LPTSTR)(LPCTSTR)strPathName), T2OLE((LPTSTR)pszHelpDir))))
				bSuccess = TRUE;
		}

		RELEASE(ptlib);
	}
	else
	{
		TRACE1("Warning: Could not load type library from %s\n", (LPCTSTR)strPathName);
	}

	return bSuccess;
}

#define TYPELIBWIN   _T("win32")
#define TYPELIBWIN_2 _T("win16")

BOOL AFXAPI AfxOleUnregisterTypeLib(REFGUID tlid, WORD wVerMajor,
	WORD wVerMinor, LCID lcid)
{
	USES_CONVERSION;

	// Load type library before unregistering it.
	ITypeLib* pTypeLib = NULL;
	if (wVerMajor != 0)
	{
		if (FAILED(LoadRegTypeLib(tlid, wVerMajor, wVerMinor, lcid, &pTypeLib)))
			pTypeLib = NULL;
	}

	// Format typelib guid as a string
	OLECHAR szTypeLibID[GUID_CCH];
	int cchGuid = ::StringFromGUID2(tlid, szTypeLibID, GUID_CCH);

	ASSERT(cchGuid == GUID_CCH);    // Did StringFromGUID2 work?
	if (cchGuid != GUID_CCH)
		return FALSE;

	TCHAR szKeyTypeLib[_MAX_PATH];
	BOOL bSurgical = FALSE;
	LONG error = ERROR_SUCCESS;

	wsprintf(szKeyTypeLib, _T("TYPELIB\\%s"), OLE2CT(szTypeLibID));

	HKEY hKeyTypeLib;
	if (RegOpenKey(HKEY_CLASSES_ROOT, szKeyTypeLib, &hKeyTypeLib) ==
		ERROR_SUCCESS)
	{
		int iKeyVersion = 0;
		HKEY hKeyVersion;
		TCHAR szVersion[_MAX_PATH];

		// Iterate through all installed versions of the control

		while (RegEnumKey(hKeyTypeLib, iKeyVersion, szVersion, _MAX_PATH) ==
			ERROR_SUCCESS)
		{
			hKeyVersion = NULL;
			BOOL bSurgicalVersion = FALSE;

			if (RegOpenKey(hKeyTypeLib, szVersion, &hKeyVersion) !=
				ERROR_SUCCESS)
			{
				++iKeyVersion;
				continue;
			}

			int iKeyLocale = 0;
			HKEY hKeyLocale;
			TCHAR szLocale[_MAX_PATH];

			// Iterate through all registered locales for this version

			while (RegEnumKey(hKeyVersion, iKeyLocale, szLocale, _MAX_PATH) ==
				ERROR_SUCCESS)
			{
				// Don't remove HELPDIR or FLAGS keys.
				if ((lstrcmpi(szLocale, _T("HELPDIR")) == 0) ||
					(lstrcmpi(szLocale, _T("FLAGS")) == 0))
				{
					++iKeyLocale;
					continue;
				}

				hKeyLocale = NULL;

				if (RegOpenKey(hKeyVersion, szLocale, &hKeyLocale) !=
					ERROR_SUCCESS)
				{
					++iKeyLocale;
					continue;
				}

				// Check if a 16-bit key is found when unregistering 32-bit
				HKEY hkey;
				if (RegOpenKey(hKeyLocale, TYPELIBWIN_2, &hkey) ==
					ERROR_SUCCESS)
				{
					RegCloseKey(hkey);

					// Only remove the keys specific to the 32-bit version
					// of control, leaving things intact for 16-bit version.
					error = _AfxRecursiveRegDeleteKey(hKeyLocale, TYPELIBWIN);
					bSurgicalVersion = TRUE;
					RegCloseKey(hKeyLocale);
				}
				else
				{
					// Delete everything for this locale.
					RegCloseKey(hKeyLocale);
					if (_AfxRecursiveRegDeleteKey(hKeyVersion, szLocale) ==
						ERROR_SUCCESS)
					{
						// Start over again, so we don't skip anything.
						iKeyLocale = 0;
						continue;
					}
				}
				++iKeyLocale;
			}
			RegCloseKey(hKeyVersion);

			if (bSurgicalVersion)
			{
				bSurgical = TRUE;
			}
			else
			{
				if (_AfxRecursiveRegDeleteKey(hKeyTypeLib, szVersion) ==
					ERROR_SUCCESS)
				{
					// Start over again, to make sure we don't skip anything.
					iKeyVersion = 0;
					continue;
				}
			}

			++iKeyVersion;
		}
		RegCloseKey(hKeyTypeLib);
	}

	if (!bSurgical)
		error = _AfxRecursiveRegDeleteKey(HKEY_CLASSES_ROOT, szKeyTypeLib);

	if (_AfxRegDeleteKeySucceeded(error))
	{
		// If type library was unregistered successfully, then also unregister
		// interfaces.
		if (pTypeLib != NULL)
		{
			ITypeLib* pDummy = NULL;
			if (FAILED(LoadRegTypeLib(tlid, wVerMajor, wVerMinor, lcid, &pDummy)))
				_AfxUnregisterInterfaces(pTypeLib);
			else
				pDummy->Release();

			pTypeLib->Release();
		}
	}

	return _AfxRegDeleteKeySucceeded(error);
}

AFX_STATIC_DATA const LPCTSTR _afxCtrlProgID[] =
{
	_T("\0") _T("%1"),
	_T("CLSID\0") _T("%2"),
	NULL
};

#define INPROCSERVER   _T("InprocServer32")
#define INPROCSERVER_2 _T("InprocServer")
#define TOOLBOXBITMAP  _T("ToolboxBitmap32")

AFX_STATIC_DATA const LPCTSTR _afxCtrlClassID[] =
{
	_T("\0") _T("%1"),
	_T("ProgID\0") _T("%2"),
	INPROCSERVER _T("\0%3"),
	TOOLBOXBITMAP _T("\0%3, %4"),
	_T("MiscStatus\0") _T("0"),
	_T("MiscStatus\\1\0") _T("%5"),
	_T("Control\0") _T(""),
	_T("TypeLib\0") _T("%6"),
	_T("Version\0") _T("%7"),
	NULL
};

BOOL AFXAPI AfxOleRegisterControlClass(HINSTANCE hInstance,
	REFCLSID clsid, LPCTSTR pszProgID, UINT idTypeName, UINT idBitmap,
	int nRegFlags, DWORD dwMiscStatus, REFGUID tlid, WORD wVerMajor,
	WORD wVerMinor)
{
	USES_CONVERSION;

	BOOL bSuccess = FALSE;

	// Format class ID as a string
	OLECHAR szClassID[GUID_CCH];
	int cchGuid = ::StringFromGUID2(clsid, szClassID, GUID_CCH);
	LPCTSTR lpszClassID = OLE2CT(szClassID);

	ASSERT(cchGuid == GUID_CCH);    // Did StringFromGUID2 work?
	if (cchGuid != GUID_CCH)
		return FALSE;

	// Format typelib guid as a string
	OLECHAR szTypeLibID[GUID_CCH];
	cchGuid = ::StringFromGUID2(tlid, szTypeLibID, GUID_CCH);

	ASSERT(cchGuid == GUID_CCH);    // Did StringFromGUID2 work?
	if (cchGuid != GUID_CCH)
		return FALSE;

	CString strPathName;
	AfxGetModuleShortFileName(hInstance, strPathName);

	CString strTypeName;
	if (!strTypeName.LoadString(idTypeName))
	{
		ASSERT(FALSE);  // Name string not present in resources
		strTypeName = lpszClassID; // Use Class ID instead
	}

	TCHAR szBitmapID[_MAX_PATH];
	_itot(idBitmap, szBitmapID, 10);

	TCHAR szMiscStatus[_MAX_PATH];
	_ltot(dwMiscStatus, szMiscStatus, 10);

	// Format version string as "major.minor"
	TCHAR szVersion[_MAX_PATH];
	wsprintf(szVersion, _T("%d.%d"), wVerMajor, wVerMinor);

	// Attempt to open registry keys.
	HKEY hkeyClassID = NULL;
	HKEY hkeyProgID = NULL;

	TCHAR szScratch[_MAX_PATH];
	wsprintf(szScratch, _T("CLSID\\%s"), lpszClassID);
	if (::RegCreateKey(HKEY_CLASSES_ROOT, szScratch, &hkeyClassID) !=
		ERROR_SUCCESS)
		goto Error;
	if (::RegCreateKey(HKEY_CLASSES_ROOT, pszProgID, &hkeyProgID) !=
		ERROR_SUCCESS)
		goto Error;

	ASSERT(hkeyClassID != NULL);
	ASSERT(hkeyProgID != NULL);

	LPCTSTR rglpszSymbols[7];
	rglpszSymbols[0] = strTypeName;
	rglpszSymbols[1] = lpszClassID;
	bSuccess = AfxOleRegisterHelper(_afxCtrlProgID, rglpszSymbols, 2,
		TRUE, hkeyProgID);

	if (!bSuccess)
		goto Error;

	rglpszSymbols[1] = pszProgID;
	rglpszSymbols[2] = strPathName;
	rglpszSymbols[3] = szBitmapID;
	rglpszSymbols[4] = szMiscStatus;
	rglpszSymbols[5] = OLE2CT(szTypeLibID);
	rglpszSymbols[6] = szVersion;
	bSuccess = AfxOleRegisterHelper(_afxCtrlClassID, rglpszSymbols, 7,
		TRUE, hkeyClassID);

	if (!bSuccess)
		goto Error;

	if (nRegFlags & afxRegInsertable)
	{
		bSuccess =
			(::RegSetValue(hkeyProgID, _T("Insertable"), REG_SZ, _T(""), 0) ==
				ERROR_SUCCESS) &&
			(::RegSetValue(hkeyClassID, _T("Insertable"), REG_SZ, _T(""), 0) ==
				ERROR_SUCCESS);
	}

	if (nRegFlags & afxRegApartmentThreading)
	{
		HKEY hkeyInprocServer32;
		bSuccess = (::RegOpenKey(hkeyClassID, INPROCSERVER,
			&hkeyInprocServer32) == ERROR_SUCCESS);
		if (!bSuccess)
			goto Error;
		ASSERT(hkeyInprocServer32 != NULL);
		static TCHAR szApartment[] = _T("Apartment");
		bSuccess = (::RegSetValueEx(hkeyInprocServer32, _T("ThreadingModel"), 0,
			REG_SZ, (const BYTE*)szApartment, (lstrlen(szApartment)+1) * sizeof(TCHAR)) ==
			ERROR_SUCCESS);
		::RegCloseKey(hkeyInprocServer32);
	}

Error:
	if (hkeyProgID != NULL)
		::RegCloseKey(hkeyProgID);

	if (hkeyClassID != NULL)
		::RegCloseKey(hkeyClassID);

	return bSuccess;
}

BOOL AFXAPI AfxOleUnregisterClass(REFCLSID clsid, LPCTSTR pszProgID)
{
	USES_CONVERSION;

	// Format class ID as a string
	OLECHAR szClassID[GUID_CCH];
	int cchGuid = ::StringFromGUID2(clsid, szClassID, GUID_CCH);
	LPCTSTR lpszClassID = OLE2CT(szClassID);

	ASSERT(cchGuid == GUID_CCH);    // Did StringFromGUID2 work?
	if (cchGuid != GUID_CCH)
		return FALSE;

	TCHAR szKey[_MAX_PATH];
	long error;
	BOOL bRetCode = TRUE;

	// check to see if a 16-bit InprocServer key is found when unregistering
	// 32-bit (or vice versa).
	wsprintf(szKey, _T("CLSID\\%s\\%s"), lpszClassID, INPROCSERVER_2);
	HKEY hkey;
	BOOL bSurgical = RegOpenKey(HKEY_CLASSES_ROOT, szKey, &hkey) ==
		ERROR_SUCCESS;

	if (bSurgical)
	{
		// Only remove the keys specific to this version of the control,
		// leaving things in tact for the other version.
		wsprintf(szKey, _T("CLSID\\%s\\%s"), lpszClassID, INPROCSERVER);
		error = RegDeleteKey(HKEY_CLASSES_ROOT, szKey);
		bRetCode = bRetCode && _AfxRegDeleteKeySucceeded(error);

		wsprintf(szKey, _T("CLSID\\%s\\%s"), lpszClassID, TOOLBOXBITMAP);
		error = RegDeleteKey(HKEY_CLASSES_ROOT, szKey);
		bRetCode = bRetCode && _AfxRegDeleteKeySucceeded(error);
	}
	else
	{
		// No other versions of this control were detected,
		// so go ahead and remove the control completely.
		wsprintf(szKey, _T("CLSID\\%s"), lpszClassID);
		error = _AfxRecursiveRegDeleteKey(HKEY_CLASSES_ROOT, szKey);
		bRetCode = bRetCode && _AfxRegDeleteKeySucceeded(error);

		if (pszProgID != NULL)
		{
			error = _AfxRecursiveRegDeleteKey(HKEY_CLASSES_ROOT,
				(LPTSTR)pszProgID);
			bRetCode = bRetCode && _AfxRegDeleteKeySucceeded(error);
		}
	}

	return bRetCode;
}

AFX_STATIC_DATA const LPCTSTR _afxPropPageClass[] =
{
	_T("\0") _T("%1"),
	INPROCSERVER _T("\0%2"),
	NULL
};

BOOL AFXAPI AfxOleRegisterPropertyPageClass(HINSTANCE hInstance,
	REFCLSID clsid, UINT idTypeName)
{
	return AfxOleRegisterPropertyPageClass(hInstance, clsid, idTypeName, 0);
}

BOOL AFXAPI AfxOleRegisterPropertyPageClass(HINSTANCE hInstance,
	REFCLSID clsid, UINT idTypeName, int nRegFlags)
{
	ASSERT(!(nRegFlags & afxRegInsertable));    // can't be insertable

	USES_CONVERSION;

	BOOL bSuccess = FALSE;

	// Format class ID as a string
	OLECHAR szClassID[GUID_CCH];
	int cchGuid = ::StringFromGUID2(clsid, szClassID, GUID_CCH);
	LPCTSTR lpszClassID = OLE2CT(szClassID);

	ASSERT(cchGuid == GUID_CCH);    // Did StringFromGUID2 work?
	if (cchGuid != GUID_CCH)
		return FALSE;

	CString strPathName;
	AfxGetModuleShortFileName(hInstance, strPathName);

	CString strTypeName;
	if (!strTypeName.LoadString(idTypeName))
	{
		ASSERT(FALSE);  // Name string not present in resources
		strTypeName = lpszClassID; // Use Class ID instead
	}

	HKEY hkeyClassID = NULL;

	TCHAR szKey[_MAX_PATH];
	wsprintf(szKey, _T("CLSID\\%s"), lpszClassID);
	if (::RegCreateKey(HKEY_CLASSES_ROOT, szKey, &hkeyClassID) !=
		ERROR_SUCCESS)
		goto Error;

	LPCTSTR rglpszSymbols[2];
	rglpszSymbols[0] = strTypeName;
	rglpszSymbols[1] = strPathName;
	bSuccess = AfxOleRegisterHelper(_afxPropPageClass, rglpszSymbols,
		2, TRUE, hkeyClassID);

	if (!bSuccess)
		goto Error;

	if (nRegFlags & afxRegApartmentThreading)
	{
		HKEY hkeyInprocServer32;
		bSuccess = (::RegOpenKey(hkeyClassID, INPROCSERVER,
			&hkeyInprocServer32) == ERROR_SUCCESS);
		if (!bSuccess)
			goto Error;
		ASSERT(hkeyInprocServer32 != NULL);
		static TCHAR szApartment[] = _T("Apartment");
		bSuccess = (::RegSetValueEx(hkeyInprocServer32, _T("ThreadingModel"), 0,
			REG_SZ, (const BYTE*)szApartment, (lstrlen(szApartment)+1) * sizeof(TCHAR)) ==
			ERROR_SUCCESS);
		::RegCloseKey(hkeyInprocServer32);
	}

Error:
	if (hkeyClassID != NULL)
		::RegCloseKey(hkeyClassID);

	return bSuccess;
}

/////////////////////////////////////////////////////////////////////////////
// Force any extra compiler-generated code into AFX_INIT_SEG

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif
