/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

	StdAfx.cpp

Abstract:

	This module contains the implementation for the base
	ATL methods.

Author:

	Don Dumitru	(dondu@microsoft.com)

Revision History:

	dondu	12/04/96	created

--*/


// stdafx.cpp : source file that includes just the standard includes
//  stdafx.pch will be the pre-compiled header
//  stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

#ifdef _ATL_STATIC_REGISTRY
#include <statreg.h>
#include <statreg.cpp>
#endif

#include <atlimpl.cpp>


HRESULT AtlAllocRegMapEx(_ATL_REGMAP_ENTRY **pparmeResult,
						 const CLSID *pclsid,
						 CSEOComModule *pmodule,
						 LPCOLESTR pszIndex,
						 ...) {
	LPBYTE pbAdd = NULL;		// Working pointer to the next available "scratch space" in the map.
	DWORD dwCnt = 0;			// The count of entries in the map.
	LPOLESTR pszCLSID = NULL;	// The CLSID as a string.
	LPOLESTR pszTLID = NULL;	// The TLID as a string.
	LPOLESTR pszAPPID = NULL;	// The APPID as a string.

	if (!pparmeResult) {
		// The caller did not give us a place to return the result.
		return (E_POINTER);
	}
	*pparmeResult = NULL;	// For the first time through the loop, the result is NULL.
	// We are going to loop through twice.  The first time through, we haven't allocate the map yet, so
	// we will count all the strings, and add up their lenghts - this will give us the size of the buffer
	// we need to allocate for the map.  Then the second time through the loop, we will store all the
	// strings in the map.
	while (1) {
		if (pclsid) {
			// If we were passed a CLSID, then we want to include that in the map.
			if (!*pparmeResult) {
				// If this is the first time through, then we need to convert the CLSID to a string.
				HRESULT hrRes;

				hrRes = StringFromCLSID(*pclsid,&pszCLSID);
				if (!SUCCEEDED(hrRes)) {
					// We failed to convert the CLSID to a string.
					return (hrRes);
				}
			} else {
				// If this isn't the first time through, then we already have the CLSID as a string, so
				// we just need to put it in the map.
				(*pparmeResult)[dwCnt].szKey = L"CLSID";
				(*pparmeResult)[dwCnt].szData = (LPCOLESTR) pbAdd;
				wcscpy((LPOLESTR) (*pparmeResult)[dwCnt].szData,pszCLSID);
			}
			// Whether or not this is the first time through, we increment some stuff based on the size
			// of the CLSID string and the fact that we have a CLSID in the map.
			pbAdd += (wcslen(pszCLSID)+1) * sizeof(OLECHAR);
			dwCnt++;
			if (*pparmeResult) {
				// If this is not the first time through, make sure we clean up after ourselves.
				CoTaskMemFree(pszCLSID);
				pszCLSID = NULL;
			}
		}
		if (pmodule) {
			// If we were passed a module, then we want to include the TLID and APPID in the map.
			if (!*pparmeResult) {
				// If this is the first time through, then we need to load the type library, get its
				// TLID, and convert it to a string.
				USES_CONVERSION;
				HRESULT hrRes;
				TCHAR szModule[MAX_PATH];
				LPOLESTR pszModule;
				CComPtr<ITypeLib> pTypeLib;
				TLIBATTR *ptlaAttr;
				if (!GetModuleFileName(pmodule->GetTypeLibInstance(),
									   szModule,
									   sizeof(szModule)/sizeof(TCHAR))) {
					hrRes = HRESULT_FROM_WIN32(GetLastError());
					if (SUCCEEDED(hrRes)) {
						// GetModuleFileName() failed, but GetLastError() didn't report an error - so
						// fake it.
						hrRes = E_OUTOFMEMORY;
					}
					CoTaskMemFree(pszCLSID);
					return (hrRes);
				}
				if (pszIndex) {
					// If we were passed an index, that means that the type library desired is not the
					// first type library in the resources - so append the index to the module name.
					lstrcat(szModule,OLE2T(pszIndex));
				}
				pszModule = T2OLE(szModule);
				hrRes = LoadTypeLib(pszModule,&pTypeLib);
				if (!SUCCEEDED(hrRes)) {
					// If we couldn't load the type library from the module, let's try changing the
					// module name to a type library name (change the extension to .TLB) and try to load
					// *that*.
					LPTSTR pszExt = NULL;
					LPTSTR psz;

					for (psz=szModule;*psz;psz=CharNext(psz)) {
						if (*psz == _T('.')) {
							pszExt = psz;
						}
					}
					if (!pszExt) {
						pszExt = psz;
					}
					lstrcpy(pszExt,_T(".tlb"));
					pszModule = T2OLE(szModule);
					hrRes = LoadTypeLib(pszModule,&pTypeLib);
				}
				if (!SUCCEEDED(hrRes)) {
					// We failed to load the type library.
					CoTaskMemFree(pszCLSID);
					return (hrRes);
				}
				hrRes = pTypeLib->GetLibAttr(&ptlaAttr);
				if (!SUCCEEDED(hrRes)) {
					// We failed to get the type library attributes.
					CoTaskMemFree(pszCLSID);
					return (hrRes);
				}
				hrRes = StringFromCLSID(ptlaAttr->guid,&pszTLID);
				if (!SUCCEEDED(hrRes)) {
					// We failed to convert the TLID to a string.
					CoTaskMemFree(pszCLSID);
					return (hrRes);
				}
			} else {
				// If this isn't the first time through, then we already have the TLID as a string, so
				// we just need to put it in the map.
				(*pparmeResult)[dwCnt].szKey = L"LIBID";
				(*pparmeResult)[dwCnt].szData = (LPCOLESTR) pbAdd;
				wcscpy((LPOLESTR) (*pparmeResult)[dwCnt].szData,pszTLID);
			}
			// Whether or not this is the first time through, we increment some stuff based on the size
			// of the TLID string and the fact that we have a TLID in the map.
			pbAdd += (wcslen(pszTLID)+1) * sizeof(OLECHAR);
			dwCnt++;
			if (*pparmeResult) {
				// If this is not the first time through, make sure we clean up after ourselves.
				CoTaskMemFree(pszTLID);
				pszTLID = NULL;
			}
			if (!*pparmeResult) {
				// If this is the first time through, see if an APPID is provided by the
				// module.
				const GUID *pappid = pmodule->GetAPPID();

				if (pappid) {
					// If there is an APPID, convert it to a string.
					HRESULT hrRes;

					hrRes = StringFromCLSID(*pappid,&pszAPPID);
					if (!SUCCEEDED(hrRes)) {
						// We failed to convert the APPID to a string.
						CoTaskMemFree(pszCLSID);
						return (hrRes);
					}
				}
			}
			if (pszAPPID) {
				// If the module provides an APPID, we need to add it to the map.
				if (*pparmeResult) {
					// If this isn't the first time through, then we already have the APPID as a string,
					// so we just need to put it in the map.
					(*pparmeResult)[dwCnt].szKey = L"APPID";
					(*pparmeResult)[dwCnt].szData = (LPCOLESTR) pbAdd;
					wcscpy((LPOLESTR) (*pparmeResult)[dwCnt].szData,pszAPPID);
				}
				// Whether or not this is the first time through, we increment some stuff based on the
				// size of the APPID string and the fact that we have a APPID in the map.
				pbAdd += (wcslen(pszAPPID)+1) * sizeof(OLECHAR);
				dwCnt++;
				if (*pparmeResult) {
					// If this is not the first time through, make sure we clean up after ourselves.
					CoTaskMemFree(pszAPPID);
					pszAPPID = NULL;
				}
			}
		}
		{	// Now we need to go through the varargs.  All of the varargs must be LPOLESTR (i.e. they
			// must be UNICODE), and they will consist of pairs - the key name followed by the data.  If
			// either member of the pair is NULL, that signals the end of the varargs.
			va_list valArgs;

			// Set the va_list to the start of the varargs.
			va_start(valArgs,pszIndex);
			while (1) {
				LPCOLESTR pszKey;
				LPCOLESTR pszData;

				// Get the first of the pair - this is the key name.
				pszKey = va_arg(valArgs,LPCOLESTR);
				if (!pszKey) {
					break;
				}
				// Get the second of the pair - this is the data.
				pszData = va_arg(valArgs,LPCOLESTR);
				if (!pszData) {
					break;
				}
				if (*pparmeResult) {
					// If this isn't the first time through, then we need to store the key name to the
					// map.
					(*pparmeResult)[dwCnt].szKey = (LPCOLESTR) pbAdd;
					wcscpy((LPOLESTR) (*pparmeResult)[dwCnt].szKey,pszKey);
				}
				// Whether or not this is the first time through, we increment some stuff based on the
				// size of the string.
				pbAdd += (wcslen(pszKey)+1) * sizeof(OLECHAR);
				if (*pparmeResult) {
					// If this isn't the first time through, then we need to store the data to the map.
					(*pparmeResult)[dwCnt].szData = (LPCOLESTR) pbAdd;
					wcscpy((LPOLESTR) (*pparmeResult)[dwCnt].szData,pszData);
				}
				// Whether or not this is the first time through, we increment some stuff based on the
				// size of the string and the fact that we have a string in the map.
				pbAdd += (wcslen(pszData)+1) * sizeof(OLECHAR);
				dwCnt++;
			}
			// Reset the va_list, for the sake of cleanliness.
			va_end(valArgs);
		}
		if (*pparmeResult) {
			// If we have allocated the map, that means that we are finishing the second time through
			// the loop - so we are done!
			break;
		}
		if (!*pparmeResult) {
			// If we havemn't allocate the map, that means that we are finishing the first time through
			// the loop - so we need to allocate the map in preparation for the second time through.
			// First we calculate the number of bytes needed for the map - this is one ATL_REGMAP_ENTRY
			// for each entry, plus one _ATL_REGMAP_ENTRY which signals the end of the map, plus enough
			// space for all of the strings to follow.
			DWORD dwBytes = (dwCnt + 1) * sizeof(_ATL_REGMAP_ENTRY) + (DWORD)(pbAdd-(LPBYTE) NULL);

			*pparmeResult = (_ATL_REGMAP_ENTRY *) CoTaskMemAlloc(dwBytes);
			if (!*pparmeResult) {
				// The memory allocation failed.
				CoTaskMemFree(pszCLSID);
				CoTaskMemFree(pszTLID);
				CoTaskMemFree(pszAPPID);
				return (E_OUTOFMEMORY);
			}
			// The memory allocation was successful - fill the memory with zeroes in preparation for
			// loading with the values.
			memset(*pparmeResult,0,dwBytes);
			// Reset the counters to the "beginning" - so that on the second time through, they are used
			// to keep track of where each successive value gets stored in the memory block.
			pbAdd = ((LPBYTE) *pparmeResult) + (dwCnt + 1) * sizeof(_ATL_REGMAP_ENTRY);
			dwCnt = 0;
		}
	}
	return (S_OK);
}