Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

825 lines
22 KiB

//-------------------------------------------------------------------------
// TmUtils.cpp - theme manager shared utilities
//-------------------------------------------------------------------------
#include "stdafx.h"
#include "TmUtils.h"
#include "ThemeFile.h"
#include "loader.h"
//-------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
CMemoryDC::CMemoryDC()
{
_hBitmap = NULL;
_hdc = NULL;
_hOldBitmap = NULL;
}
//-------------------------------------------------------------------------
CMemoryDC::~CMemoryDC()
{
CloseDC();
}
//-------------------------------------------------------------------------
HRESULT CMemoryDC::OpenDC(HDC hdcSource, int iWidth, int iHeight)
{
HRESULT hr;
BOOL fDeskDC = FALSE;
if (! hdcSource)
{
hdcSource = GetWindowDC(NULL);
if (! hdcSource)
{
hr = MakeErrorLast();
goto exit;
}
fDeskDC = TRUE;
}
_hBitmap = CreateCompatibleBitmap(hdcSource, iWidth, iHeight);
if (! _hBitmap)
{
hr = MakeErrorLast();
goto exit;
}
_hdc = CreateCompatibleDC(hdcSource);
if (! _hdc)
{
hr = MakeErrorLast();
goto exit;
}
_hOldBitmap = (HBITMAP) SelectObject(_hdc, _hBitmap);
if (! _hOldBitmap)
{
hr = MakeErrorLast();
goto exit;
}
hr = S_OK;
exit:
if (fDeskDC)
ReleaseDC(NULL, hdcSource);
if (FAILED(hr))
CloseDC();
return hr;
}
//-------------------------------------------------------------------------
void CMemoryDC::CloseDC()
{
if (_hOldBitmap)
{
SelectObject(_hdc, _hOldBitmap);
_hOldBitmap = NULL;
}
if (_hdc)
{
DeleteDC(_hdc);
_hdc = NULL;
}
if (_hBitmap)
{
DeleteObject(_hBitmap);
_hBitmap = NULL;
}
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
CBitmapPixels::CBitmapPixels()
{
_hdrBitmap = NULL;
_buffer = NULL;
_iWidth = 0;
_iHeight = 0;
}
//-------------------------------------------------------------------------
CBitmapPixels::~CBitmapPixels()
{
if (_buffer)
delete [] (BYTE *)_buffer;
}
//-------------------------------------------------------------------------
BYTE* CBitmapPixels::Buffer()
{
return _buffer;
}
//-------------------------------------------------------------------------
HRESULT CBitmapPixels::OpenBitmap(HDC hdc, HBITMAP bitmap, BOOL fForceRGB32,
DWORD OUT **pPixels, OPTIONAL OUT int *piWidth, OPTIONAL OUT int *piHeight,
OPTIONAL OUT int *piBytesPerPixel, OPTIONAL OUT int *piBytesPerRow,
OPTIONAL OUT int *piPreviousBytesPerPixel, OPTIONAL UINT cbBytesBefore)
{
if (! pPixels)
return MakeError32(E_INVALIDARG);
bool fNeedRelease = false;
if (! hdc)
{
hdc = GetWindowDC(NULL);
if (! hdc)
{
return MakeErrorLast();
}
fNeedRelease = true;
}
BITMAP bminfo;
GetObject(bitmap, sizeof(bminfo), &bminfo);
_iWidth = bminfo.bmWidth;
_iHeight = bminfo.bmHeight;
int iBytesPerPixel;
if (piPreviousBytesPerPixel != NULL)
{
*piPreviousBytesPerPixel = bminfo.bmBitsPixel / 8;
}
if ((fForceRGB32) || (bminfo.bmBitsPixel == 32))
iBytesPerPixel = 4;
else
iBytesPerPixel = 3;
int iRawBytes = _iWidth * iBytesPerPixel;
int iBytesPerRow = 4*((iRawBytes+3)/4);
int size = sizeof(BITMAPINFOHEADER) + _iHeight*iBytesPerRow;
_buffer = new BYTE[size + cbBytesBefore + 100]; // avoid random GetDIBits() failures with 100 bytes padding (?)
if (! _buffer)
return MakeError32(E_OUTOFMEMORY);
_hdrBitmap = (BITMAPINFOHEADER *)(_buffer + cbBytesBefore);
memset(_hdrBitmap, 0, sizeof(BITMAPINFOHEADER));
_hdrBitmap->biSize = sizeof(BITMAPINFOHEADER);
_hdrBitmap->biWidth = _iWidth;
_hdrBitmap->biHeight = _iHeight;
_hdrBitmap->biPlanes = 1;
_hdrBitmap->biBitCount = static_cast<WORD>(8*iBytesPerPixel);
_hdrBitmap->biCompression = BI_RGB;
#ifdef DEBUG
int linecnt =
#endif
GetDIBits(hdc, bitmap, 0, _iHeight, DIBDATA(_hdrBitmap), (BITMAPINFO *)_hdrBitmap,
DIB_RGB_COLORS);
ATLASSERT(linecnt == _iHeight);
if (fNeedRelease)
ReleaseDC(NULL, hdc);
*pPixels = (DWORD *)DIBDATA(_hdrBitmap);
if (piWidth)
*piWidth = _iWidth;
if (piHeight)
*piHeight = _iHeight;
if (piBytesPerPixel)
*piBytesPerPixel = iBytesPerPixel;
if (piBytesPerRow)
*piBytesPerRow = iBytesPerRow;
return S_OK;
}
//-------------------------------------------------------------------------
void CBitmapPixels::CloseBitmap(HDC hdc, HBITMAP hBitmap)
{
if (_hdrBitmap && _buffer)
{
if (hBitmap) // rewrite bitmap
{
bool fNeedRelease = false;
if (! hdc)
{
hdc = GetWindowDC(NULL);
fNeedRelease = true;
}
SetDIBits(hdc, hBitmap, 0, _iHeight, DIBDATA(_hdrBitmap), (BITMAPINFO *)_hdrBitmap,
DIB_RGB_COLORS);
if ((fNeedRelease) && (hdc))
ReleaseDC(NULL, hdc);
}
delete [] (BYTE *)_buffer;
_hdrBitmap = NULL;
_buffer = NULL;
}
}
//-------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
HRESULT LoadThemeLibrary(LPCWSTR pszThemeName, HINSTANCE *phInst)
{
HRESULT hr = S_OK;
HINSTANCE hInst = NULL;
hInst = LoadLibraryEx(pszThemeName, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (! hInst)
{
hr = MakeErrorLast();
goto exit;
}
//---- is this version supported? ----
void *pvVersion;
DWORD dwVersionLength;
hr = GetPtrToResource(hInst, L"PACKTHEM_VERSION", MAKEINTRESOURCE(1), &pvVersion, &dwVersionLength);
if (SUCCEEDED(hr))
{
if (dwVersionLength != sizeof(SHORT))
hr = E_FAIL;
else
{
SHORT sVersionNum = *(SHORT *)pvVersion;
if (sVersionNum != PACKTHEM_VERSION)
hr = E_FAIL;
}
}
if (FAILED(hr))
{
hr = MakeError32(ERROR_BAD_FORMAT);
goto exit;
}
*phInst = hInst;
exit:
if (FAILED(hr))
{
if (hInst)
FreeLibrary(hInst);
}
return hr;
}
//---------------------------------------------------------------------------
LPCWSTR ThemeString(CUxThemeFile *pThemeFile, int iOffset)
{
LPCWSTR p = L"";
if ((pThemeFile) && (pThemeFile->_pbThemeData) && (iOffset > 0))
{
p = (LPCWSTR) (pThemeFile->_pbThemeData + iOffset);
}
return p;
}
//---------------------------------------------------------------------------
int GetLoadIdFromTheme(CUxThemeFile *pThemeFile)
{
int iLoadId = 0;
if (pThemeFile)
{
THEMEHDR *hdr = (THEMEHDR *)pThemeFile->_pbThemeData;
iLoadId = hdr->iLoadId;
}
return iLoadId;
}
//---------------------------------------------------------------------------
HRESULT GetThemeNameId(CUxThemeFile *pThemeFile, LPWSTR pszFileNameBuff, UINT cchFileNameBuff,
LPWSTR pszColorParam, UINT cchColorParam, LPWSTR pszSizeParam, UINT cchSizeParam, int *piSysMetricsOffset, LANGID *pwLangID)
{
HRESULT hr = S_OK;
THEMEHDR *hdr = (THEMEHDR *)pThemeFile->_pbThemeData;
if (piSysMetricsOffset)
*piSysMetricsOffset = hdr->iSysMetricsOffset;
if (pszFileNameBuff)
{
hr = hr_lstrcpy(pszFileNameBuff, ThemeString(pThemeFile, hdr->iDllNameOffset), cchFileNameBuff);
}
if (SUCCEEDED(hr) && pszColorParam)
{
hr = hr_lstrcpy(pszColorParam, ThemeString(pThemeFile, hdr->iColorParamOffset), cchColorParam);
}
if (SUCCEEDED(hr) && pszSizeParam)
{
hr = hr_lstrcpy(pszSizeParam, ThemeString(pThemeFile, hdr->iSizeParamOffset), cchSizeParam);
}
if (SUCCEEDED(hr) && pwLangID)
*pwLangID = (LANGID) hdr->dwLangID;
return hr;
}
//---------------------------------------------------------------------------
BOOL ThemeMatch (CUxThemeFile *pThemeFile, LPCWSTR pszThemeName, LPCWSTR pszColorName, LPCWSTR pszSizeName, LANGID wLangID)
{
WCHAR szThemeFileName[MAX_PATH];
WCHAR szColorParam[MAX_PATH];
WCHAR szSizeParam[MAX_PATH];
LANGID wThemeLangID = 0;
bool bLangMatch = true;
HRESULT hr = GetThemeNameId(pThemeFile,
szThemeFileName, ARRAYSIZE(szThemeFileName),
szColorParam, ARRAYSIZE(szColorParam),
szSizeParam, ARRAYSIZE(szSizeParam), NULL, &wThemeLangID);
if (wLangID != 0 && ((wThemeLangID != wLangID) || (wLangID != GetUserDefaultUILanguage())))
{
Log(LOG_TMLOAD, L"UxTheme: Reloading theme because of language change.");
Log(LOG_TMLOAD, L"UxTheme: User LangID=0x%x, current theme=0x%x, LastUserLangID=0x%x", GetUserDefaultUILanguage(), wThemeLangID, wLangID);
bLangMatch = false;
}
return(SUCCEEDED(hr) &&
(lstrcmpiW(pszThemeName, szThemeFileName) == 0) &&
(lstrcmpiW(pszColorName, szColorParam) == 0) &&
(lstrcmpiW(pszSizeName, szSizeParam) == 0) &&
bLangMatch);
}
//---------------------------------------------------------------------------
HRESULT GetColorSchemeIndex(HINSTANCE hInst, LPCWSTR pszColor, int *piIndex)
{
HRESULT hr;
WCHAR szColor[_MAX_PATH+1];
for (int i=0; i < 1000; i++)
{
hr = GetResString(hInst, L"COLORNAMES", i, szColor, ARRAYSIZE(szColor));
if (FAILED(hr))
break;
if (lstrcmpi(pszColor, szColor)==0)
{
*piIndex = i;
return S_OK;
}
}
return MakeError32(ERROR_NOT_FOUND); // not found
}
//---------------------------------------------------------------------------
HRESULT GetSizeIndex(HINSTANCE hInst, LPCWSTR pszSize, int *piIndex)
{
HRESULT hr;
WCHAR szSize[_MAX_PATH+1];
for (int i=0; i < 1000; i++)
{
hr = GetResString(hInst, L"SIZENAMES", i, szSize, ARRAYSIZE(szSize));
if (FAILED(hr))
break;
if (lstrcmpi(pszSize, szSize)==0)
{
*piIndex = i;
return S_OK;
}
}
return MakeError32(ERROR_NOT_FOUND); // not found
}
//---------------------------------------------------------------------------
HRESULT FindComboData(HINSTANCE hDll, COLORSIZECOMBOS **ppCombos)
{
HRSRC hRsc = FindResource(hDll, L"COMBO", L"COMBODATA");
if (! hRsc)
return MakeErrorLast();
HGLOBAL hGlobal = LoadResource(hDll, hRsc);
if (! hGlobal)
return MakeErrorLast();
*ppCombos = (COLORSIZECOMBOS *)LockResource(hGlobal);
if (! *ppCombos)
return MakeErrorLast();
return S_OK;
}
//---------------------------------------------------------------------------
BOOL FormatLocalMsg(HINSTANCE hInst, int iStringNum,
LPWSTR pszMessageBuff, DWORD dwMaxMessageChars, DWORD *pdwParams, TMERRINFO *pErrInfo)
{
BOOL fGotMsg = FALSE;
WCHAR szBuff[_MAX_PATH+1];
WCHAR *p;
//----- get string from string table ----
if (LoadString(hInst, iStringNum, szBuff, ARRAYSIZE(szBuff)))
{
//---- repl %1 or %2 with %s ----
p = szBuff;
while (*p)
{
if (*p == '%')
{
p++;
if ((*p == '1') || (*p == '2'))
*p = 's';
p++;
}
else
p++;
}
int len = lstrlen(szBuff);
if (len)
{
wsprintf(pszMessageBuff, szBuff, pErrInfo->szMsgParam1, pErrInfo->szMsgParam2);
fGotMsg = TRUE;
}
}
return fGotMsg;
}
//---------------------------------------------------------------------------
HRESULT _FormatParseMessage(TMERRINFO *pErrInfo,
OUT LPWSTR pszMessageBuff, DWORD dwMaxMessageChars)
{
LogEntry(L"_FormatParseMessage");
HRESULT hr = S_OK;
DWORD dwParams[] = {PtrToInt(pErrInfo->szMsgParam1), PtrToInt(pErrInfo->szMsgParam2)};
DWORD dwCode = pErrInfo->dwParseErrCode;
BOOL fGotMsg = FALSE;
int iStringNum = SCODE_CODE(dwCode);
//---- get process name (see if we are "packthem.exe" ----
WCHAR szPath[MAX_PATH];
if (! GetModuleFileNameW( NULL, szPath, ARRAYSIZE(szPath) ))
goto exit;
WCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szBase[_MAX_FNAME], szExt[_MAX_EXT];
_wsplitpath(szPath, szDrive, szDir, szBase, szExt);
if (lstrcmpi(szBase, L"packthem")==0) // give packthem priority
{
fGotMsg = FormatLocalMsg(GetModuleHandle(NULL), iStringNum,
pszMessageBuff, dwMaxMessageChars, dwParams, pErrInfo);
}
if (! fGotMsg) // try normal route: uxtheme.dll
{
HINSTANCE hInst = LoadLibrary(L"uxtheme.dll");
if (! hInst)
{
Log(LOG_ALWAYS, L"_FormatParseMessage: Could not load uxtheme.dll");
hr = E_FAIL;
goto exit;
}
fGotMsg = FormatLocalMsg(hInst, iStringNum,
pszMessageBuff, dwMaxMessageChars, dwParams, pErrInfo);
FreeLibrary(hInst);
}
if (! fGotMsg)
hr = MakeErrorLast();
exit:
LogExit(L"_FormatParseMessage");
return hr;
}
//---------------------------------------------------------------------------
HRESULT _GetThemeParseErrorInfo(OUT PARSE_ERROR_INFO *pInfo)
{
LogEntry(L"_GetThemeParseErrorInfo");
HRESULT hr = S_OK;
if (pInfo->dwSize != sizeof(PARSE_ERROR_INFO)) // unsupported size
{
hr = MakeError32(E_INVALIDARG);
goto exit;
}
TMERRINFO *pErrInfo = GetParseErrorInfo(TRUE);
if (! pErrInfo)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
//---- convert code into msg using param strings ----
hr = _FormatParseMessage(pErrInfo, pInfo->szMsg, ARRAYSIZE(pInfo->szMsg));
if (FAILED(hr))
goto exit;
//---- transfer the other info ----
pInfo->dwParseErrCode = pErrInfo->dwParseErrCode;
pInfo->iLineNum = pErrInfo->iLineNum;
lstrcpy_truncate(pInfo->szFileName, pErrInfo->szFileName, ARRAYSIZE(pInfo->szFileName));
lstrcpy_truncate(pInfo->szSourceLine, pErrInfo->szSourceLine, ARRAYSIZE(pInfo->szSourceLine));
exit:
LogExit(L"_GetThemeParseErrorInfo");
return hr;
}
//---------------------------------------------------------------------------
HRESULT _ParseThemeIniFile(LPCWSTR pszFileName,
DWORD dwParseFlags, OPTIONAL THEMEENUMPROC pfnCallBack, OPTIONAL LPARAM lparam)
{
LogEntry(L"ParseThemeIniFile");
HRESULT hr;
CThemeParser *pParser = new CThemeParser;
if (! pParser)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
hr = pParser->ParseThemeFile(pszFileName, NULL, NULL, pfnCallBack, lparam,
dwParseFlags);
delete pParser;
exit:
LogExit(L"ParseThemeIniFile");
return hr;
}
//---------------------------------------------------------------------------
BOOL ThemeLibStartUp(BOOL fThreadAttach)
{
BOOL fInit = FALSE;
if (fThreadAttach)
{
//---- nothing to do here ----
}
else // process
{
_tls_ErrorInfoIndex = TlsAlloc();
if (_tls_ErrorInfoIndex == (DWORD)-1)
goto exit;
if (! LogStartUp())
goto exit;
if (! UtilsStartUp())
goto exit;
}
fInit = TRUE;
exit:
return fInit;
}
//---------------------------------------------------------------------------
BOOL ThemeLibShutDown(BOOL fThreadDetach)
{
if (fThreadDetach)
{
//---- destroy the thread-local Error Info ----
TMERRINFO * ei = GetParseErrorInfo(FALSE);
if (ei)
{
TlsSetValue(_tls_ErrorInfoIndex, NULL);
delete ei;
}
}
else // process
{
UtilsShutDown();
LogShutDown();
if (_tls_ErrorInfoIndex != (DWORD)-1)
{
TlsFree(_tls_ErrorInfoIndex);
_tls_ErrorInfoIndex = (DWORD)-1;
}
}
return TRUE;
}
//---------------------------------------------------------------------------
HRESULT GetThemeSizeId(int iSysSizeId, int *piThemeSizeId)
{
HRESULT hr = S_OK;
*piThemeSizeId = 0;
switch (iSysSizeId)
{
case SM_CXSIZEFRAME:
*piThemeSizeId = TMT_SIZINGBORDERWIDTH;
break;
case SM_CYSIZEFRAME:
*piThemeSizeId = TMT_SIZINGBORDERWIDTH;
break;
case SM_CXVSCROLL:
*piThemeSizeId = TMT_SCROLLBARWIDTH;
break;
case SM_CYHSCROLL:
*piThemeSizeId = TMT_SCROLLBARHEIGHT;
break;
case SM_CXSIZE:
*piThemeSizeId = TMT_CAPTIONBARWIDTH;
break;
case SM_CYSIZE:
*piThemeSizeId = TMT_CAPTIONBARHEIGHT;
break;
case SM_CXSMSIZE:
*piThemeSizeId = TMT_SMCAPTIONBARWIDTH;
break;
case SM_CYSMSIZE:
*piThemeSizeId = TMT_SMCAPTIONBARHEIGHT;
break;
case SM_CXMENUSIZE:
*piThemeSizeId = TMT_MENUBARWIDTH;
break;
case SM_CYMENUSIZE:
*piThemeSizeId = TMT_MENUBARHEIGHT;
break;
default:
hr = MakeError32(E_INVALIDARG);
break;
}
return hr;
}
//---------------------------------------------------------------------------
HRESULT _EnumThemeSizes(HINSTANCE hInst, LPCWSTR pszThemeName,
OPTIONAL LPCWSTR pszColorScheme, DWORD dwSizeIndex, OUT THEMENAMEINFO *ptn, BOOL fCheckColorDepth)
{
HRESULT hr;
COLORSIZECOMBOS *combos;
hr = FindComboData(hInst, &combos);
if (FAILED(hr))
goto exit;
int iMinColor, iMaxColor;
iMinColor = 0;
iMaxColor = combos->cColorSchemes-1;
if (pszColorScheme) // translate "pszColorScheme" into a color number
{
int index;
hr = GetColorSchemeIndex(hInst, pszColorScheme, &index);
if (FAILED(hr))
goto exit;
//---- restrict our search to just this color ----
iMinColor = index;
iMaxColor = index;
}
int s, c;
DWORD dwSizeNum;
dwSizeNum = 0;
BOOL gotall;
gotall = FALSE;
DWORD dwCurMinDepth = 0;
if (fCheckColorDepth)
{
dwCurMinDepth = MinimumDisplayColorDepth();
}
for (s=0; s < combos->cSizes; s++)
{
BOOL fFoundOne = FALSE;
for (c=iMinColor; c <= iMaxColor; c++)
{
if (COMBOENTRY(combos, c, s) != -1)
{
fFoundOne = TRUE;
break;
}
}
if (fFoundOne && (!fCheckColorDepth || CheckMinColorDepth(hInst, dwCurMinDepth, COMBOENTRY(combos, c, s))))
{
if (dwSizeNum++ == dwSizeIndex)
{
hr = GetResString(hInst, L"SIZENAMES", s, ptn->szName, ARRAYSIZE(ptn->szName));
if (FAILED(hr))
*ptn->szName = 0;
if (! LoadString(hInst, s+RES_BASENUM_SIZEDISPLAYS, ptn->szDisplayName, ARRAYSIZE(ptn->szDisplayName)))
*ptn->szDisplayName = 0;
if (! LoadString(hInst, s+RES_BASENUM_SIZETOOLTIPS, ptn->szToolTip, ARRAYSIZE(ptn->szToolTip)))
*ptn->szToolTip = 0;
gotall = TRUE;
break;
}
}
}
if ((SUCCEEDED(hr)) && (! gotall))
hr = MakeError32(ERROR_NOT_FOUND);
exit:
return hr;
}
//---------------------------------------------------------------------------
HRESULT _EnumThemeColors(HINSTANCE hInst, LPCWSTR pszThemeName,
OPTIONAL LPCWSTR pszSizeName, DWORD dwColorIndex, OUT THEMENAMEINFO *ptn, BOOL fCheckColorDepth)
{
HRESULT hr;
COLORSIZECOMBOS *combos;
hr = FindComboData(hInst, &combos);
if (FAILED(hr))
goto exit;
int iMinSize, iMaxSize;
iMinSize = 0;
iMaxSize = combos->cSizes-1;
if (pszSizeName) // translate "pszSizeName" into a size number
{
int index;
hr = GetSizeIndex(hInst, pszSizeName, &index);
if (FAILED(hr))
goto exit;
//---- restrict our search to just this size ----
iMinSize = index;
iMaxSize = index;
}
int s, c;
DWORD dwColorNum;
dwColorNum = 0;
BOOL gotall;
gotall = FALSE;
DWORD dwCurMinDepth = 0;
if (fCheckColorDepth)
{
dwCurMinDepth = MinimumDisplayColorDepth();
}
for (c=0; c < combos->cColorSchemes; c++)
{
BOOL fFoundOne = FALSE;
for (s=iMinSize; s <= iMaxSize; s++)
{
if (COMBOENTRY(combos, c, s) != -1)
{
fFoundOne = TRUE;
break;
}
}
if (fFoundOne && (!fCheckColorDepth || CheckMinColorDepth(hInst, dwCurMinDepth, COMBOENTRY(combos, c, s))))
{
if (dwColorNum++ == dwColorIndex)
{
hr = GetResString(hInst, L"COLORNAMES", c, ptn->szName, ARRAYSIZE(ptn->szName));
if (FAILED(hr))
*ptn->szName = 0;
if (! LoadString(hInst, c+RES_BASENUM_COLORDISPLAYS, ptn->szDisplayName, ARRAYSIZE(ptn->szDisplayName)))
*ptn->szDisplayName = 0;
if (! LoadString(hInst, c+RES_BASENUM_COLORTOOLTIPS, ptn->szToolTip, ARRAYSIZE(ptn->szToolTip)))
*ptn->szToolTip = 0;
gotall = true;
break;
}
}
}
if ((SUCCEEDED(hr)) && (! gotall))
hr = MakeError32(ERROR_NOT_FOUND);
exit:
return hr;
}