STATREG.CPP

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

///////////////////////////////////////
#define RET_ON_ERROR(x) \
if (FAILED(hr = x))\
return hr;
///////////////////////////////////////
#define BREAK_ON_ERROR(x) \
if (FAILED(hr = x))\
break;
///////////////////////////////////////
#ifdef _DEBUG
#define REPORT_ERROR(name, func) \
if (func != ERROR_SUCCESS)\
ATLTRACE(_T("NON CRITICAL ERROR : %s failed\n"), name);
#define REG_TRACE_RECOVER() \
if (!bRecover) \
ATLTRACE(_T("Opened Key %s\n"), szToken); \
else \
ATLTRACE(_T("Ignoring Open key on %s : In Recovery mode\n"), szToken);
#else //!_DEBUG
#define REG_TRACE_RECOVER()
#define REPORT_ERROR(name, func) \
func;
#endif //_DEBUG

///////////////////////////////////////
#define MAX_TYPE MAX_VALUE
#define MAX_VALUE 4096

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

class CParseBuffer
{
public:
int nPos;
int nSize;
LPTSTR p;
CParseBuffer(int nInitial);
~CParseBuffer() {CoTaskMemFree(p);}
BOOL AddChar(TCHAR ch);
BOOL AddString(LPCOLESTR lpsz);
LPTSTR Detach();

};

LPCTSTR rgszNeverDelete[] = //Component Catagories
{
_T("CLSID"), _T("TYPELIB")
};

const int cbNeverDelete = sizeof(rgszNeverDelete) / sizeof(LPCTSTR*);

static LPTSTR StrChr(LPTSTR lpsz, TCHAR ch)
{
LPTSTR p = NULL;
while (*lpsz)
{
if (*lpsz == ch)
{
p = lpsz;
break;
}
lpsz = CharNext(lpsz);
}
return p;
}

static HKEY WINAPI HKeyFromString(LPTSTR szToken)
{
struct keymap
{
LPCTSTR lpsz;
HKEY hkey;
};
static const keymap map[] = {
{_T("HKCR"), HKEY_CLASSES_ROOT},
{_T("HKCU"), HKEY_CURRENT_USER},
{_T("HKLM"), HKEY_LOCAL_MACHINE},
{_T("HKU"), HKEY_USERS},
{_T("HKPD"), HKEY_PERFORMANCE_DATA},
{_T("HKDD"), HKEY_DYN_DATA},
{_T("HKCC"), HKEY_CURRENT_CONFIG},
{_T("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT},
{_T("HKEY_CURRENT_USER"), HKEY_CURRENT_USER},
{_T("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE},
{_T("HKEY_USERS"), HKEY_USERS},
{_T("HKEY_PERFORMANCE_DATA"), HKEY_PERFORMANCE_DATA},
{_T("HKEY_DYN_DATA"), HKEY_DYN_DATA},
{_T("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG}
};

for (int i=0;i<sizeof(map)/sizeof(keymap);i++)
{
if (!lstrcmpi(szToken, map[i].lpsz))
return map[i].hkey;
}
return NULL;
}

static HKEY HKeyFromCompoundString(LPTSTR szToken, LPTSTR& szTail)
{
if (NULL == szToken)
return NULL;

LPTSTR lpsz = StrChr(szToken, chDirSep);

if (NULL == lpsz)
return NULL;

szTail = CharNext(lpsz);
*lpsz = chEOS;
HKEY hKey = HKeyFromString(szToken);
*lpsz = chDirSep;
return hKey;
}

static LPVOID QueryValue(HKEY hKey, LPCTSTR szValName, DWORD& dwType)
{
DWORD dwCount = 0;

if (RegQueryValueEx(hKey, szValName, NULL, &dwType, NULL, &dwCount) != ERROR_SUCCESS)
{
ATLTRACE(_T("RegQueryValueEx failed for Value %s\n"), szValName);
return NULL;
}

if (!dwCount)
{
ATLTRACE(_T("RegQueryValueEx returned 0 bytes\n"));
return NULL;
}

// Not going to Check for fail on CoTaskMemAlloc & RegQueryValueEx as NULL
// will be returned regardless if anything failed

LPVOID pData = CoTaskMemAlloc(dwCount);
RegQueryValueEx(hKey, szValName, NULL, &dwType, (LPBYTE) pData, &dwCount);
return pData;
}

/////////////////////////////////////////////////////////////////////////////
//

HRESULT CRegParser::GenerateError(UINT nID)
{
// m_re.m_nID = nID;
// m_re.m_cLines = m_cLines;
return DISP_E_EXCEPTION;
}


CRegParser::CRegParser(CRegObject* pRegObj)
{
m_pRegObj = pRegObj;
m_pchCur = NULL;
m_cLines = 1;
}

BOOL CRegParser::IsSpace(TCHAR ch)
{
switch (ch)
{
case chSpace:
case chTab:
case chCR:
case chLF:
return TRUE;
}

return FALSE;
}

void CRegParser::IncrementLinePos()
{
m_pchCur = CharNext(m_pchCur);
if (chLF == *m_pchCur)
IncrementLineCount();
}

void CRegParser::SkipWhiteSpace()
{
while(IsSpace(*m_pchCur))
IncrementLinePos();
}

HRESULT CRegParser::NextToken(LPTSTR szToken)
{
USES_CONVERSION;

UINT ichToken = 0;

SkipWhiteSpace();

// NextToken cannot be called at EOS
if (chEOS == *m_pchCur)
return GenerateError(E_ATL_UNEXPECTED_EOS);

// handle quoted value / key
if (chQuote == *m_pchCur)
{
LPCTSTR szOrig = szToken;

IncrementLinePos(); // Skip Quote

while (chEOS != *m_pchCur && !EndOfVar())
{
if (chQuote == *m_pchCur) // If it is a quote that means we must skip it
IncrementLinePos(); // as it has been escaped

LPTSTR pchPrev = m_pchCur;
IncrementLinePos();

if (szToken + sizeof(WORD) >= MAX_VALUE + szOrig)
return GenerateError(E_ATL_VALUE_TOO_LARGE);
for (int i = 0; pchPrev+i < m_pchCur; i++, szToken++)
*szToken = *(pchPrev+i);
}

if (chEOS == *m_pchCur)
{
ATLTRACE(_T("NextToken : Unexpected End of File\n"));
return GenerateError(E_ATL_UNEXPECTED_EOS);
}

*szToken = chEOS;
IncrementLinePos(); // Skip end quote
}

else
{ // Handle non-quoted ie parse up till first "White Space"
while (chEOS != *m_pchCur && !IsSpace(*m_pchCur))
{
LPTSTR pchPrev = m_pchCur;
IncrementLinePos();
for (int i = 0; pchPrev+i < m_pchCur; i++, szToken++)
*szToken = *(pchPrev+i);
}

*szToken = chEOS;
}
return S_OK;
}

static BOOL VTFromRegType(LPCTSTR szValueType, VARTYPE& vt)
{
struct typemap
{
LPCTSTR lpsz;
VARTYPE vt;
};
static const typemap map[] = {
{szStringVal, VT_BSTR},
{szDwordVal, VT_I4}
};

for (int i=0;i<sizeof(map)/sizeof(typemap);i++)
{
if (!lstrcmpi(szValueType, map[i].lpsz))
{
vt = map[i].vt;
return TRUE;
}
}

return FALSE;

}

HRESULT CRegParser::AddValue(CRegKey& rkParent,LPCTSTR szValueName, LPTSTR szToken)
{
USES_CONVERSION;
HRESULT hr;

TCHAR szTypeToken[MAX_TYPE];
VARTYPE vt;
LONG lRes = ERROR_SUCCESS;
UINT nIDRes = 0;

RET_ON_ERROR(NextToken(szTypeToken))
if (!VTFromRegType(szTypeToken, vt))
{
ATLTRACE(_T("%s Type not supported\n"), szTypeToken);
return GenerateError(E_ATL_TYPE_NOT_SUPPORTED);
}

TCHAR szValue[MAX_VALUE];
SkipWhiteSpace();
RET_ON_ERROR(NextToken(szValue));
long lVal;

switch (vt)
{
case VT_BSTR:
lRes = rkParent.SetValue(szValue, szValueName);
ATLTRACE(_T("Setting Value %s at %s\n"), szValue, !szValueName ? _T("default") : szValueName);
break;
case VT_I4:
VarI4FromStr(T2OLE(szValue), 0, 0, &lVal);
lRes = rkParent.SetValue(lVal, szValueName);
ATLTRACE(_T("Setting Value %d at %s\n"), lVal, !szValueName ? _T("default") : szValueName);
break;
}

if (ERROR_SUCCESS != lRes)
{
nIDRes = E_ATL_VALUE_SET_FAILED;
hr = HRESULT_FROM_WIN32(lRes);
}

RET_ON_ERROR(NextToken(szToken))

return S_OK;
}

BOOL CRegParser::CanForceRemoveKey(LPCTSTR szKey)
{
for (int iNoDel = 0; iNoDel < cbNeverDelete; iNoDel++)
if (!lstrcmpi(szKey, rgszNeverDelete[iNoDel]))
return FALSE; // We cannot delete it

return TRUE;
}

BOOL CRegParser::HasSubKeys(HKEY hkey)
{
DWORD cbSubKeys = 0;

if (FAILED(RegQueryInfoKey(hkey, NULL, NULL, NULL,
&cbSubKeys, NULL, NULL,
NULL, NULL, NULL, NULL, NULL)))
{
ATLTRACE(_T("Should not be here!!\n"));
_ASSERTE(FALSE);
return FALSE;
}

return cbSubKeys > 0;
}

BOOL CRegParser::HasValues(HKEY hkey)
{
DWORD cbValues = 0;

LONG lResult = RegQueryInfoKey(hkey, NULL, NULL, NULL,
NULL, NULL, NULL,
&cbValues, NULL, NULL, NULL, NULL);
if (ERROR_SUCCESS != lResult)
{
ATLTRACE(_T("RegQueryInfoKey Failed "));
_ASSERTE(FALSE);
return FALSE;
}

if (1 == cbValues)
{
DWORD cbData = 0;
lResult = RegQueryValueEx(hkey, NULL, NULL, NULL, NULL, &cbData);

if (ERROR_SUCCESS == lResult)
return !cbData;
else
return TRUE;
}

return cbValues > 0;
}

HRESULT CRegParser::SkipAssignment(LPTSTR szToken)
{
HRESULT hr;
TCHAR szValue[MAX_VALUE];

if (*szToken == chEquals)
{
RET_ON_ERROR(NextToken(szToken))
// Skip assignment
SkipWhiteSpace();
RET_ON_ERROR(NextToken(szValue));
RET_ON_ERROR(NextToken(szToken))
}

return S_OK;
}


HRESULT CRegParser::RegisterSubkeys(HKEY hkParent, BOOL bRegister, BOOL bRecover)
{
CRegKey keyCur;
TCHAR szToken[MAX_VALUE];
LONG lRes;
TCHAR szKey[MAX_VALUE];
BOOL bDelete = TRUE;
BOOL bInRecovery = bRecover;
HRESULT hr = S_OK;

ATLTRACE(_T("Num Els = %d\n"), cbNeverDelete);
RET_ON_ERROR(NextToken(szToken)) // Should be key name


while (*szToken != chRightBracket) // Continue till we see a }
{
BOOL bTokenDelete = !lstrcmpi(szToken, szDelete);

if (!lstrcmpi(szToken, szForceRemove) || bTokenDelete)
{
BREAK_ON_ERROR(NextToken(szToken))

if (bRegister)
{
CRegKey rkForceRemove;

if (StrChr(szToken, chDirSep) != NULL)
return GenerateError(E_ATL_COMPOUND_KEY);

if (CanForceRemoveKey(szToken))
{
rkForceRemove.Attach(hkParent);
rkForceRemove.RecurseDeleteKey(szToken);
rkForceRemove.Detach();
}
if (bTokenDelete)
{
BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))
goto EndCheck;
}
}

}

if (!lstrcmpi(szToken, szNoRemove))
{
bDelete = FALSE; // set even for register
BREAK_ON_ERROR(NextToken(szToken))
}

if (!lstrcmpi(szToken, szValToken)) // need to add a value to hkParent
{
TCHAR szValueName[_MAX_PATH];

BREAK_ON_ERROR(NextToken(szValueName))
BREAK_ON_ERROR(NextToken(szToken))

if (*szToken != chEquals)
return GenerateError(E_ATL_EXPECTING_EQUAL);

if (bRegister)
{
CRegKey rk;

rk.Attach(hkParent);
hr = AddValue(rk, szValueName, szToken);
rk.Detach();

if (FAILED(hr))
return hr;

goto EndCheck;
}
else
{
if (!bRecover)
{
ATLTRACE(_T("Deleting %s\n"), szValueName);
REPORT_ERROR(_T("RegDeleteValue"), RegDeleteValue(hkParent, szValueName))
}

BREAK_ON_ERROR(SkipAssignment(szToken)) // Strip off type
continue; // can never have a subkey
}
}

if (StrChr(szToken, chDirSep) != NULL)
return GenerateError(E_ATL_COMPOUND_KEY);

if (bRegister)
{
lRes = keyCur.Open(hkParent, szToken, KEY_ALL_ACCESS);
if (ERROR_SUCCESS != lRes)
{
// Failed all access try read only
lRes = keyCur.Open(hkParent, szToken, KEY_READ);
if (ERROR_SUCCESS != lRes)
{
// Finally try creating it
ATLTRACE(_T("Creating key %s\n"), szToken);
lRes = keyCur.Create(hkParent, szToken);
if (ERROR_SUCCESS != lRes)
return GenerateError(E_ATL_CREATE_KEY_FAILED);
}
}

BREAK_ON_ERROR(NextToken(szToken))

if (*szToken == chEquals)
BREAK_ON_ERROR(AddValue(keyCur, NULL, szToken)) // NULL == default
}
else
{
if (!bRecover && keyCur.Open(hkParent, szToken) != ERROR_SUCCESS)
bRecover = TRUE;

// TRACE out Key open status and if in recovery mode
REG_TRACE_RECOVER()

// Remember Subkey
lstrcpyn(szKey, szToken, _MAX_PATH);

// If in recovery mode

if (bRecover || HasSubKeys(keyCur) || HasValues(keyCur))
{
BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))

if (*szToken == chLeftBracket)
{
BREAK_ON_ERROR(RegisterSubkeys(keyCur.m_hKey, bRegister, bRecover))
if (bRecover) // Turn off recovery if we are done
{
bRecover = bInRecovery;
ATLTRACE(_T("Ending Recovery Mode\n"));
BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))
continue;
}
}

if (!bRecover && HasSubKeys(keyCur))
{
// See if the KEY is in the NeverDelete list and if so, don't
if (CanForceRemoveKey(szKey))
{
ATLTRACE(_T("Deleting non-empty subkey %s by force\n"), szKey);
REPORT_ERROR(_T("RecurseDeleteKey"), keyCur.RecurseDeleteKey(szKey))
}
BREAK_ON_ERROR(NextToken(szToken))
continue;
}

if (bRecover)
continue;
}

if (!bRecover && keyCur.Close() != ERROR_SUCCESS)
return GenerateError(E_ATL_CLOSE_KEY_FAILED);

if (!bRecover && bDelete)
{
ATLTRACE(_T("Deleting Key %s\n"), szKey);
REPORT_ERROR(_T("RegDeleteKey"), RegDeleteKey(hkParent, szKey))
}

BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))
}

EndCheck:

if (bRegister)
{
if (*szToken == chLeftBracket)
{
BREAK_ON_ERROR(RegisterSubkeys(keyCur.m_hKey, bRegister, FALSE))
BREAK_ON_ERROR(NextToken(szToken))
}
}
}

return hr;
}

LPTSTR CParseBuffer::Detach()
{
LPTSTR lp = p;
p = NULL;
return lp;
}

CParseBuffer::CParseBuffer(int nInitial)
{
nPos = 0;
nSize = nInitial;
p = (LPTSTR) CoTaskMemAlloc(nSize*sizeof(TCHAR));
}

BOOL CParseBuffer::AddString(LPCOLESTR lpsz)
{
USES_CONVERSION;
LPCTSTR lpszT = OLE2CT(lpsz);
while (*lpszT)
{
AddChar(*lpszT);
lpszT++;
}
return TRUE;
}

BOOL CParseBuffer::AddChar(TCHAR ch)
{
if (nPos == nSize) // realloc
{
nSize *= 2;
p = (LPTSTR) CoTaskMemRealloc(p, nSize*sizeof(TCHAR));
}
p[nPos++] = ch;
return TRUE;
}

HRESULT CRegParser::PreProcessBuffer(LPTSTR lpszReg, LPTSTR* ppszReg)
{
USES_CONVERSION;
_ASSERTE(lpszReg != NULL);
_ASSERTE(ppszReg != NULL);
*ppszReg = NULL;
int nSize = lstrlen(lpszReg)*2;
CParseBuffer pb(nSize);
if (pb.p == NULL)
return E_OUTOFMEMORY;
m_pchCur = lpszReg;
HRESULT hr = S_OK;

while (*m_pchCur != NULL) // look for end
{
if (*m_pchCur == _T('%'))
{
IncrementLinePos();
if (*m_pchCur == _T('%'))
pb.AddChar(*m_pchCur);
else
{
LPTSTR lpszNext = StrChr(m_pchCur, _T('%'));
if (lpszNext == NULL)
{
ATLTRACE(_T("Error no closing % found\n"));
hr = GenerateError(E_ATL_UNEXPECTED_EOS);
break;
}
int nLength = lpszNext - m_pchCur;
if (nLength > 31)
{
hr = E_FAIL;
break;
}
TCHAR buf[32];
lstrcpyn(buf, m_pchCur, nLength+1);
LPCOLESTR lpszVar = m_pRegObj->StrFromMap(buf);
if (lpszVar == NULL)
{
hr = GenerateError(E_ATL_NOT_IN_MAP);
break;
}
pb.AddString(lpszVar);
while (m_pchCur != lpszNext)
IncrementLinePos();
}
}
else
pb.AddChar(*m_pchCur);
IncrementLinePos();
}
pb.AddChar(NULL);
if (SUCCEEDED(hr))
*ppszReg = pb.Detach();
return hr;
}

HRESULT CRegParser::RegisterBuffer(LPTSTR szBuffer, BOOL bRegister)
{
TCHAR szToken[_MAX_PATH];
HRESULT hr = S_OK;

LPTSTR szReg;
hr = PreProcessBuffer(szBuffer, &szReg);
if (FAILED(hr))
return hr;

m_pchCur = szReg;

// Preprocess szReg

while (chEOS != *m_pchCur)
{
BREAK_ON_ERROR(NextToken(szToken))
HKEY hkBase;
if ((hkBase = HKeyFromString(szToken)) == NULL)
{
ATLTRACE(_T("HKeyFromString failed on %s\n"), szToken);
hr = GenerateError(E_ATL_BAD_HKEY);
break;
}

BREAK_ON_ERROR(NextToken(szToken))

if (chLeftBracket != *szToken)
{
ATLTRACE(_T("Syntax error, expecting a {, found a %s\n"), szToken);
hr = GenerateError(E_ATL_MISSING_OPENKEY_TOKEN);
break;
}
if (bRegister)
{
LPTSTR szRegAtRegister = m_pchCur;
hr = RegisterSubkeys(hkBase, bRegister);
if (FAILED(hr))
{
ATLTRACE(_T("Failed to register, cleaning up!\n"));
m_pchCur = szRegAtRegister;
RegisterSubkeys(hkBase, FALSE);
break;
}
}
else
{
BREAK_ON_ERROR(RegisterSubkeys(hkBase, bRegister))
}

SkipWhiteSpace();
}
CoTaskMemFree(szReg);
return hr;
}

HRESULT CExpansionVector::Add(LPCOLESTR lpszKey, LPCOLESTR lpszValue)
{
USES_CONVERSION;
HRESULT hr = S_OK;

EXPANDER* pExpand = NULL;
ATLTRY(pExpand = new EXPANDER);
if (pExpand == NULL)
return E_OUTOFMEMORY;

DWORD cbKey = (ocslen(lpszKey)+1)*sizeof(OLECHAR);
DWORD cbValue = (ocslen(lpszValue)+1)*sizeof(OLECHAR);
pExpand->szKey = (LPOLESTR)CoTaskMemAlloc(cbKey);
pExpand->szValue = (LPOLESTR)CoTaskMemAlloc(cbValue);
if (pExpand->szKey == NULL || pExpand->szValue == NULL)
{
CoTaskMemFree(pExpand->szKey);
CoTaskMemFree(pExpand->szValue);
delete pExpand;
return E_OUTOFMEMORY;
}
memcpy(pExpand->szKey, lpszKey, cbKey);
memcpy(pExpand->szValue, lpszValue, cbValue);

if (m_cEls == m_nSize)
{
m_nSize*=2;
m_p = (EXPANDER**)realloc(m_p, m_nSize*sizeof(EXPANDER*));
}

if (NULL != m_p)
{
m_p[m_cEls] = pExpand;
m_cEls++;
}
else
hr = E_OUTOFMEMORY;

return hr;

}

LPCOLESTR CExpansionVector::Find(LPTSTR lpszKey)
{
USES_CONVERSION;
for (int iExpand = 0; iExpand < m_cEls; iExpand++)
{
if (!lstrcmpi(OLE2T(m_p[iExpand]->szKey), lpszKey)) //are equal
return m_p[iExpand]->szValue;
}
return NULL;
}

HRESULT CExpansionVector::ClearReplacements()
{
for (int iExpand = 0; iExpand < m_cEls; iExpand++)
{
EXPANDER* pExp = m_p[iExpand];
CoTaskMemFree(pExp->szValue);
CoTaskMemFree(pExp->szKey);
delete pExp;
}
m_cEls = 0;
return S_OK;
}

HRESULT CRegObject::GenerateError(UINT nID)
{
// re.m_nID = nID;
// re.m_cLines = -1;

return DISP_E_EXCEPTION;
}

HRESULT STDMETHODCALLTYPE CRegObject::AddReplacement(LPCOLESTR lpszKey, LPCOLESTR lpszItem)
{
m_csMap.Lock();
HRESULT hr = m_RepMap.Add(lpszKey, lpszItem);
m_csMap.Unlock();
return hr;
}

HRESULT CRegObject::RegisterFromResource(LPCOLESTR bstrFileName, LPCTSTR szID,
LPCTSTR szType, BOOL bRegister)
{
USES_CONVERSION;

HRESULT hr;
CRegParser parser(this);
HINSTANCE hInstResDll;
HRSRC hrscReg;
HGLOBAL hReg;
DWORD dwSize;
LPSTR szRegA;
LPTSTR szReg;

hInstResDll = LoadLibraryEx(OLE2CT(bstrFileName), NULL, LOAD_LIBRARY_AS_DATAFILE);

if (NULL == hInstResDll)
{
ATLTRACE(_T("Failed to LoadLibrary on %s\n"), OLE2CT(bstrFileName));
hr = HRESULT_FROM_WIN32(GetLastError());
goto ReturnHR;
}

hrscReg = FindResource((HMODULE)hInstResDll, szID, szType);

if (NULL == hrscReg)
{
ATLTRACE(_T("Failed to FindResource on ID:%s TYPE:%s\n"), szID, szType);
hr = HRESULT_FROM_WIN32(GetLastError());
goto ReturnHR;
}

hReg = LoadResource((HMODULE)hInstResDll, hrscReg);

if (NULL == hReg)
{
ATLTRACE(_T("Failed to LoadResource \n"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto ReturnHR;
}

dwSize = SizeofResource((HMODULE)hInstResDll, hrscReg);
szRegA = (LPSTR)hReg;
if (szRegA[dwSize] != NULL)
{
szRegA = (LPSTR)_alloca(dwSize+1);
memcpy(szRegA, (void*)hReg, dwSize+1);
szRegA[dwSize] = NULL;
}

szReg = A2T(szRegA);

#ifdef _DEBUG
OutputDebugString(szReg); //would call ATLTRACE but szReg is > 512 bytes
OutputDebugString(_T("\n"));
#endif //_DEBUG

hr = parser.RegisterBuffer(szReg, bRegister);

ReturnHR:

if (NULL != hInstResDll)
FreeLibrary((HMODULE)hInstResDll);
return hr;
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceRegister(LPCOLESTR szFileName, UINT nID, LPCOLESTR szType)
{
USES_CONVERSION;
return RegisterFromResource(szFileName, MAKEINTRESOURCE(nID), OLE2CT(szType), TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceRegisterSz(LPCOLESTR szFileName, LPCOLESTR szID, LPCOLESTR szType)
{
USES_CONVERSION;

if (szID == NULL || szType == NULL)
return E_INVALIDARG;

return RegisterFromResource(szFileName, OLE2CT(szID), OLE2CT(szType), TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceUnregister(LPCOLESTR szFileName, UINT nID, LPCOLESTR szType)
{
USES_CONVERSION;
return RegisterFromResource(szFileName, MAKEINTRESOURCE(nID), OLE2CT(szType), FALSE);
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceUnregisterSz(LPCOLESTR szFileName, LPCOLESTR szID, LPCOLESTR szType)
{
USES_CONVERSION;
if (szID == NULL || szType == NULL)
return E_INVALIDARG;

return RegisterFromResource(szFileName, OLE2CT(szID), OLE2CT(szType), FALSE);
}

HRESULT CRegObject::RegisterWithString(LPCOLESTR bstrData, BOOL bRegister)
{
USES_CONVERSION;
CRegParser parser(this);


LPCTSTR szReg = OLE2CT(bstrData);

#ifdef _DEBUG
OutputDebugString(szReg); //would call ATLTRACE but szReg is > 512 bytes
OutputDebugString(_T("\n"));
#endif //_DEBUG

HRESULT hr = parser.RegisterBuffer((LPTSTR)szReg, bRegister);

return hr;
}

HRESULT CRegObject::ClearReplacements()
{
m_csMap.Lock();
HRESULT hr = m_RepMap.ClearReplacements();
m_csMap.Unlock();
return hr;
}


LPCOLESTR CRegObject::StrFromMap(LPTSTR lpszKey)
{
m_csMap.Lock();
LPCOLESTR lpsz = m_RepMap.Find(lpszKey);
if (lpsz == NULL) // not found!!
ATLTRACE(_T("Map Entry not found\n"));
m_csMap.Unlock();
return lpsz;
}

HRESULT CRegObject::MemMapAndRegister(LPCOLESTR bstrFileName, BOOL bRegister)
{
USES_CONVERSION;

CRegParser parser(this);

HANDLE hFile = CreateFile(OLE2CT(bstrFileName), GENERIC_READ, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);

if (NULL == hFile)
{
ATLTRACE(_T("Failed to CreateFile on %s\n"), OLE2CT(bstrFileName));
return HRESULT_FROM_WIN32(GetLastError());
}

DWORD cbFile = GetFileSize(hFile, NULL); // No HiOrder DWORD required

HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);

if (NULL == hMapping)
{
ATLTRACE(_T("Failed to CreateFileMapping\n"));
return HRESULT_FROM_WIN32(GetLastError());
}

LPVOID pMap = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);

if (NULL == pMap)
{
ATLTRACE(_T("Failed to MapViewOfFile\n"));
return HRESULT_FROM_WIN32(GetLastError());
}

LPTSTR szReg = A2T((char*)pMap);

if (chEOS != szReg[cbFile]) //ensure buffer is NULL terminated
{
ATLTRACE(_T("ERROR : Bad or missing End of File\n"));
return E_FAIL; // make a real error
}

#ifdef _DEBUG
OutputDebugString(szReg); //would call ATLTRACE but szReg is > 512 bytes
OutputDebugString(_T("\n"));
#endif //_DEBUG

HRESULT hRes = parser.RegisterBuffer(szReg, bRegister);

// if (FAILED(hRes))
// re = parser.GetRegException();

UnmapViewOfFile(pMap);
CloseHandle(hMapping);
CloseHandle(hFile);

return hRes;
}

HRESULT STDMETHODCALLTYPE CRegObject::FileRegister(LPCOLESTR bstrFileName)
{
return MemMapAndRegister(bstrFileName, TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::FileUnregister(LPCOLESTR bstrFileName)
{
return MemMapAndRegister(bstrFileName, FALSE);
}

HRESULT STDMETHODCALLTYPE CRegObject::StringRegister(LPCOLESTR bstrData)
{
return RegisterWithString(bstrData, TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::StringUnregister(LPCOLESTR bstrData)
{
return RegisterWithString(bstrData, FALSE);
}

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif