738 lines
15 KiB
C++
738 lines
15 KiB
C++
#include "stdinc.h"
|
|
#include "debmacro.h"
|
|
#include "ntdef.h"
|
|
#include "fusionparser.h"
|
|
#include "shlwapi.h"
|
|
|
|
#if !defined(NUMBER_OF)
|
|
#define NUMBER_OF(x) (sizeof(x) / sizeof((x)[0]))
|
|
#endif
|
|
|
|
BOOL
|
|
CFusionParser::ParseVersion(
|
|
ASSEMBLY_VERSION &rav,
|
|
PCWSTR sz,
|
|
SIZE_T cch,
|
|
bool &rfSyntaxValid
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
ULONG cDots = 0;
|
|
PCWSTR pszTemp;
|
|
SIZE_T cchLeft;
|
|
ULONG ulTemp;
|
|
ASSEMBLY_VERSION avTemp;
|
|
PCWSTR pszLast;
|
|
bool fSyntaxValid = true;
|
|
|
|
rfSyntaxValid = false;
|
|
|
|
PARAMETER_CHECK((sz != NULL) || (cch == 0));
|
|
|
|
avTemp.Major = 0;
|
|
avTemp.Minor = 0;
|
|
avTemp.Revision = 0;
|
|
avTemp.Build = 0;
|
|
|
|
while ((cch != 0) && (sz[cch - 1] == L'\0'))
|
|
cch--;
|
|
|
|
// Unfortunately there isn't a StrChrN(), so we'll look for the dots ourselves...
|
|
pszTemp = sz;
|
|
cchLeft = cch;
|
|
|
|
while (cchLeft-- != 0)
|
|
{
|
|
WCHAR wch = *pszTemp++;
|
|
|
|
if (wch == L'.')
|
|
{
|
|
cDots++;
|
|
|
|
if (cDots >= 4)
|
|
{
|
|
fSyntaxValid = false;
|
|
break;
|
|
}
|
|
}
|
|
else if ((wch < L'0') || (wch > L'9'))
|
|
{
|
|
fSyntaxValid = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fSyntaxValid && (cDots < 3))
|
|
fSyntaxValid = false;
|
|
|
|
if (fSyntaxValid)
|
|
{
|
|
pszTemp = sz;
|
|
pszLast = sz + cch;
|
|
|
|
ulTemp = 0;
|
|
for (;;)
|
|
{
|
|
WCHAR wch = *pszTemp++;
|
|
|
|
if (wch == L'.')
|
|
break;
|
|
|
|
ulTemp = (ulTemp * 10) + (wch - L'0');
|
|
|
|
if (ulTemp > 65535)
|
|
{
|
|
fSuccess = true;
|
|
// rfSyntaxValid implicitly false
|
|
ASSERT(!rfSyntaxValid);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
avTemp.Major = (USHORT) ulTemp;
|
|
|
|
ulTemp = 0;
|
|
for (;;)
|
|
{
|
|
WCHAR wch = *pszTemp++;
|
|
|
|
if (wch == L'.')
|
|
break;
|
|
|
|
ulTemp = (ulTemp * 10) + (wch - L'0');
|
|
|
|
if (ulTemp > 65535)
|
|
{
|
|
fSuccess = true;
|
|
// rfSyntaxValid implicitly false
|
|
ASSERT(!rfSyntaxValid);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
avTemp.Minor = (USHORT) ulTemp;
|
|
|
|
ulTemp = 0;
|
|
for (;;)
|
|
{
|
|
WCHAR wch = *pszTemp++;
|
|
|
|
if (wch == L'.')
|
|
break;
|
|
|
|
ulTemp = (ulTemp * 10) + (wch - L'0');
|
|
|
|
if (ulTemp > 65535)
|
|
{
|
|
fSuccess = true;
|
|
// rfSyntaxValid implicitly false
|
|
ASSERT(!rfSyntaxValid);
|
|
goto Exit;
|
|
}
|
|
}
|
|
avTemp.Revision = (USHORT) ulTemp;
|
|
|
|
// Now the tricky bit. We aren't necessarily null-terminated, so we
|
|
// have to just look for hitting the end.
|
|
ulTemp = 0;
|
|
while (pszTemp < pszLast)
|
|
{
|
|
WCHAR wch = *pszTemp++;
|
|
ulTemp = (ulTemp * 10) + (wch - L'0');
|
|
|
|
if (ulTemp > 65535)
|
|
{
|
|
fSuccess = true;
|
|
// rfSyntaxValid implicitly false
|
|
ASSERT(!rfSyntaxValid);
|
|
goto Exit;
|
|
}
|
|
}
|
|
avTemp.Build = (USHORT) ulTemp;
|
|
|
|
rav = avTemp;
|
|
}
|
|
|
|
rfSyntaxValid = fSyntaxValid;
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
#if FUSION_DISABLED_CODE
|
|
HRESULT CFusionParser::ParseThreadingModel(ULONG &threadingModel, PCWSTR sz, SIZE_T cch)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
#define FUSIONPARSER_PARSETHREADINGMODELENTRY(x, y) \
|
|
{ x, NUMBER_OF(x) - 1, y },
|
|
|
|
|
|
static const struct
|
|
{
|
|
PCWSTR m_psz;
|
|
SIZE_T m_cch;
|
|
ULONG m_threadingModel;
|
|
} s_rgmap[] =
|
|
{
|
|
FUSIONPARSER_PARSETHREADINGMODELENTRY(L"Free", COMCLASS_THREADINGMODEL_FREE)
|
|
FUSIONPARSER_PARSETHREADINGMODELENTRY(L"Apartment", COMCLASS_THREADINGMODEL_APARTMENT)
|
|
FUSIONPARSER_PARSETHREADINGMODELENTRY(L"Both", COMCLASS_THREADINGMODEL_BOTH)
|
|
};
|
|
|
|
ULONG i;
|
|
|
|
// If the caller included the terminating null character, back up one.
|
|
while ((cch != 0) && (sz[cch - 1] == L'\0'))
|
|
cch--;
|
|
|
|
for (i=0; i<NUMBER_OF(s_rgmap); i++)
|
|
{
|
|
if ((s_rgmap[i].m_cch == cch) &&
|
|
(StrCmpNI(s_rgmap[i].m_psz, sz, cch) == 0))
|
|
break;
|
|
}
|
|
|
|
if (i == NUMBER_OF(s_rgmap))
|
|
{
|
|
// ParseError
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
threadingModel = s_rgmap[i].m_threadingModel;
|
|
|
|
hr = NOERROR;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFusionParser::ParseBoolean(BOOLEAN &rfValue, PCWSTR sz, SIZE_T cch)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
static const struct
|
|
{
|
|
PCWSTR m_psz;
|
|
SIZE_T m_cch;
|
|
BOOLEAN m_fValue;
|
|
} s_rgmap[] =
|
|
{
|
|
{ L"yes", 3, TRUE },
|
|
{ L"no", 2, FALSE },
|
|
};
|
|
|
|
ULONG i;
|
|
|
|
if (cch < 0)
|
|
cch = ::wcslen(sz);
|
|
|
|
// Some callers may erroneously include the null character in the length...
|
|
while ((cch != 0) && (sz[cch - 1] == L'\0'))
|
|
cch--;
|
|
|
|
for (i=0; i<NUMBER_OF(s_rgmap); i++)
|
|
{
|
|
if ((cch == s_rgmap[i].m_cch) &&
|
|
(StrCmpNIW(s_rgmap[i].m_psz, sz, cch) == 0))
|
|
break;
|
|
}
|
|
|
|
if (i == NUMBER_OF(s_rgmap))
|
|
{
|
|
// ParseError
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
rfValue = s_rgmap[i].m_fValue;
|
|
|
|
hr = NOERROR;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFusionParser::ParseBLOB(BLOB &rblob, PCWSTR szIn, SSIZE_T cch)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
PCWSTR psz;
|
|
WCHAR wch;
|
|
SIZE_T cchLeft;
|
|
|
|
BYTE *pBlobData = NULL;
|
|
ULONG cbSize = 0;
|
|
|
|
psz = szIn;
|
|
|
|
if (cch < 0)
|
|
cch = ::wcslen(szIn);
|
|
|
|
if ((cch != 0) && (szIn[cch - 1] == L'\0'))
|
|
cch--;
|
|
|
|
cchLeft = cch;
|
|
|
|
// Run through the string twice; once to count bytes and the second time to
|
|
// parse them.
|
|
while (cchLeft-- != 0)
|
|
{
|
|
wch = *psz++;
|
|
|
|
// Space and dash are nice separators; just ignore them.
|
|
if ((wch == L' ') ||
|
|
(wch == L'-'))
|
|
continue;
|
|
|
|
if (!SxspIsHexDigit(wch))
|
|
{
|
|
// Bad character; punt.
|
|
// ParseError
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
// First character was good; look for the 2nd nibble
|
|
wch = *psz++;
|
|
|
|
if ((wch == L'\0') ||
|
|
(wch == L'-') ||
|
|
(wch == L' '))
|
|
{
|
|
cbSize++;
|
|
|
|
if (wch == L'\0')
|
|
break;
|
|
}
|
|
else if (!SxspIsHexDigit(wch))
|
|
{
|
|
// ParseError
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
cbSize++;
|
|
}
|
|
}
|
|
|
|
if (cbSize != 0)
|
|
{
|
|
BYTE *pb = NULL;
|
|
|
|
// Allocate the buffer and parse the string for real this time.
|
|
pBlobData = NEW(BYTE[cbSize]);
|
|
if (pBlobData == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// We do not attempt to clean up pBlobData in the exit path, so
|
|
// if you add code here that can fail, be sure to clean up pBlobData
|
|
// if you do fail.
|
|
|
|
psz = szIn;
|
|
cchLeft = cch;
|
|
|
|
pb = pBlobData;
|
|
|
|
while (cchLeft-- != 0)
|
|
{
|
|
wch = *psz++;
|
|
|
|
// Space and dash are nice separators; just ignore them.
|
|
if ((wch == L' ') ||
|
|
(wch == L'-'))
|
|
continue;
|
|
|
|
bTemp = SxspHexDigitToValue(wch);
|
|
|
|
wch = *psz++;
|
|
|
|
if ((wch == L'\0') ||
|
|
(wch == L'-') ||
|
|
(wch == L' '))
|
|
{
|
|
*pb++ = bTemp;
|
|
|
|
if (wch == L'\0')
|
|
break;
|
|
}
|
|
*pb++ = (bTemp << 4) | SxspHexDigitToValue(wch);
|
|
}
|
|
}
|
|
|
|
rblob.pBlobData = pBlobData;
|
|
rblob.cbSize = cbSize;
|
|
|
|
hr = NOERROR;
|
|
|
|
Exit:
|
|
// No need to clean up pBlobData; we don't fail after its attempted allocation.
|
|
return hr;
|
|
}
|
|
|
|
#endif // FUSION_DISABLED_CODE
|
|
|
|
BOOL
|
|
CFusionParser::ParseULONG(
|
|
ULONG &rul,
|
|
PCWSTR sz,
|
|
SIZE_T cch,
|
|
ULONG Radix
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
ULONG ulTemp = 0;
|
|
|
|
PARAMETER_CHECK((Radix >= 2) && (Radix <= 36));
|
|
|
|
while (cch != 0)
|
|
{
|
|
const WCHAR wch = *sz++;
|
|
ULONG Digit = 0;
|
|
cch--;
|
|
|
|
if ((wch >= L'0') && (wch <= L'9'))
|
|
Digit = (wch - L'0');
|
|
else if ((wch >= L'a') && (wch <= L'z'))
|
|
Digit = (10 + wch - L'a');
|
|
else if ((wch >= L'A') && (wch <= L'Z'))
|
|
Digit = (10 + wch - L'A');
|
|
else
|
|
ORIGINATE_WIN32_FAILURE_AND_EXIT(InvalidDigit, ERROR_SXS_MANIFEST_PARSE_ERROR);
|
|
|
|
if (Digit >= Radix)
|
|
ORIGINATE_WIN32_FAILURE_AND_EXIT(InvalidDigitForRadix, ERROR_SXS_MANIFEST_PARSE_ERROR);
|
|
|
|
ulTemp = (ulTemp * Radix) + Digit;
|
|
}
|
|
|
|
rul = ulTemp;
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CFusionParser::ParseIETFDate(
|
|
FILETIME &rft,
|
|
PCWSTR sz,
|
|
SIZE_T cch
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
SYSTEMTIME st;
|
|
ULONG ulTemp;
|
|
|
|
//
|
|
// Our format is:
|
|
//
|
|
// DD/MM/YYYY
|
|
// 0123456789
|
|
|
|
//
|
|
// Strip off extra \0's from the end of sz, adjusting the cch
|
|
//
|
|
while ( ( cch != 0 ) && ( sz[cch - 1] == L'\0' ) )
|
|
cch--;
|
|
|
|
PARAMETER_CHECK(cch == 10);
|
|
PARAMETER_CHECK(sz[2] == L'/');
|
|
PARAMETER_CHECK(sz[5] == L'/');
|
|
|
|
ZeroMemory( &st, sizeof( st ) );
|
|
|
|
IFW32FALSE_EXIT(CFusionParser::ParseULONG(ulTemp, sz, 2, 10));
|
|
st.wDay = (WORD)ulTemp;
|
|
|
|
IFW32FALSE_EXIT(CFusionParser::ParseULONG(ulTemp, sz + 3, 2, 10));
|
|
st.wMonth = (WORD)ulTemp;
|
|
|
|
IFW32FALSE_EXIT(CFusionParser::ParseULONG(ulTemp, sz + 6, 4, 10));
|
|
st.wYear = (WORD)ulTemp;
|
|
|
|
IFW32FALSE_ORIGINATE_AND_EXIT(::SystemTimeToFileTime(&st, &rft));
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CFusionParser::ParseFILETIME(
|
|
FILETIME &rft,
|
|
PCWSTR sz,
|
|
SIZE_T cch
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
SYSTEMTIME st;
|
|
ULONG ulTemp;
|
|
|
|
while ((cch != 0) && (sz[cch - 1] == L'\0'))
|
|
cch--;
|
|
|
|
// MM/DD/YYYY HH:MM:SS.CCC
|
|
// 01234567890123456789012
|
|
|
|
if (cch != 23) // 23 is the exact length of a filetime that we require
|
|
ORIGINATE_WIN32_FAILURE_AND_EXIT(BadFiletimeLength, ERROR_SXS_MANIFEST_PARSE_ERROR);
|
|
|
|
st.wMonth = 0;
|
|
st.wDay = 0;
|
|
st.wYear = 0;
|
|
st.wHour = 0;
|
|
st.wMinute = 0;
|
|
st.wSecond = 0;
|
|
st.wMilliseconds = 0;
|
|
|
|
if ((sz[2] != L'/') ||
|
|
(sz[5] != L'/') ||
|
|
(sz[10] != L' ') ||
|
|
(sz[13] != L':') ||
|
|
(sz[16] != L':') ||
|
|
(sz[19] != L'.'))
|
|
{
|
|
::SetLastError(ERROR_SXS_MANIFEST_PARSE_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
#define PARSE_FIELD(_field, _index, _length) \
|
|
do \
|
|
{ \
|
|
if (!CFusionParser::ParseULONG(ulTemp, &sz[(_index)], (_length))) \
|
|
goto Exit; \
|
|
st._field = (WORD) ulTemp; \
|
|
} while (0)
|
|
|
|
PARSE_FIELD(wMonth, 0, 2);
|
|
PARSE_FIELD(wDay, 3, 2);
|
|
PARSE_FIELD(wYear, 6, 4);
|
|
PARSE_FIELD(wHour, 11, 2);
|
|
PARSE_FIELD(wMinute, 14, 2);
|
|
PARSE_FIELD(wSecond, 17, 2);
|
|
PARSE_FIELD(wMilliseconds, 20, 3);
|
|
|
|
IFW32FALSE_ORIGINATE_AND_EXIT(::SystemTimeToFileTime(&st, &rft));
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
BOOL
|
|
FusionDupString(
|
|
LPWSTR *ppszOut,
|
|
PCWSTR szIn,
|
|
SIZE_T cchIn
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
if (ppszOut != NULL)
|
|
*ppszOut = NULL;
|
|
|
|
PARAMETER_CHECK((cchIn == 0) || (szIn != NULL));
|
|
PARAMETER_CHECK(ppszOut != NULL);
|
|
|
|
IFALLOCFAILED_EXIT(*ppszOut = FUSION_NEW_ARRAY(WCHAR, cchIn + 1));
|
|
|
|
if (cchIn != 0)
|
|
memcpy(*ppszOut, szIn, cchIn * sizeof(WCHAR));
|
|
|
|
(*ppszOut)[cchIn] = L'\0';
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
|
|
int SxspHexDigitToValue(WCHAR wch)
|
|
{
|
|
if ((wch >= L'a') && (wch <= L'f'))
|
|
return 10 + (wch - L'a');
|
|
else if ((wch >= L'A') && (wch <= 'F'))
|
|
return 10 + (wch - L'A');
|
|
else if (wch >= '0' && wch <= '9')
|
|
return (wch - L'0');
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
bool SxspIsHexDigit(WCHAR wch)
|
|
{
|
|
return (((wch >= L'0') && (wch <= L'9')) ||
|
|
((wch >= L'a') && (wch <= L'f')) ||
|
|
((wch >= L'A') && (wch <= L'F')));
|
|
}
|
|
|
|
#if FUSION_DISABLED_CODE
|
|
|
|
HRESULT CFusionParser::ParseULARGE_INTEGER(ULARGE_INTEGER &ruli, PCWSTR sz, SIZE_T cch)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
ULONGLONG ullTemp = 0;
|
|
|
|
if (cch < 0)
|
|
cch = ::wcslen(sz);
|
|
|
|
while ((cch != 0) && (sz[cch - 1] == L'\0'))
|
|
cch--;
|
|
|
|
while (cch-- != 0)
|
|
{
|
|
WCHAR wch = *sz++;
|
|
|
|
// If we see anything other than a digit, we fail. We're not
|
|
// atoi(); we're hard core.
|
|
if ((wch < L'0') ||
|
|
(wch > L'9'))
|
|
{
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
// I don't know if I really need all these casts, but I don't know what the
|
|
// compiler's documented behavior for expressions of mixed unsigned __int64, int and
|
|
// unsigned short types will be. Instead we'll explicitly cast everything to
|
|
// unsigned __int64 (e.g. ULONGLONG) and hopefully the right stuff will happen.
|
|
ullTemp = (ullTemp * static_cast<ULONGLONG>(10)) + static_cast<ULONGLONG>(wch - L'\0');
|
|
}
|
|
|
|
ruli.QuadPart = ullTemp;
|
|
hr = NOERROR;
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CFusionParser::ParseHexString(PCWSTR sz, SIZE_T cch, DWORD &rdwOut, PCWSTR &rszOut)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
FN_TRACE_HR(hr);
|
|
DWORD dw = 0;
|
|
|
|
if (cch < 0)
|
|
cch = ::wcslen(sz);
|
|
|
|
while ((cch != 0) && (sz[cch - 1] == L'\0'))
|
|
cch--;
|
|
|
|
while (cch-- != 0)
|
|
{
|
|
int i = ::SxspHexDigitToValue(*sz++);
|
|
INTERNAL_ERROR_CHECK(i >= 0);
|
|
dw = (dw << 4) | i;
|
|
}
|
|
|
|
rdwOut = dw;
|
|
rszOut = sz;
|
|
|
|
hr = NOERROR;
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
FusionCopyString(
|
|
WCHAR *prgchBuffer,
|
|
SIZE_T *pcchBuffer,
|
|
PCWSTR szIn,
|
|
SIZE_T cchIn)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
FN_TRACE_HR(hr);
|
|
SIZE_T cch;
|
|
|
|
PARAMETER_CHECK(pcchBuffer != NULL);
|
|
PARAMETER_CHECK((pcchBuffer == NULL) || ((*pcchBuffer == 0) || (prgchBuffer != NULL)));
|
|
PARAMETER_CHECK((szIn != NULL) || (cchIn == 0));
|
|
|
|
if (cchIn < 0)
|
|
cchIn = ::wcslen(szIn);
|
|
|
|
if ((*pcchBuffer) < ((SIZE_T) (cchIn + 1)))
|
|
{
|
|
*pcchBuffer = cchIn + 1;
|
|
ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
memcpy(prgchBuffer, szIn, cchIn * sizeof(WCHAR));
|
|
prgchBuffer[cchIn] = L'\0';
|
|
|
|
*pcchBuffer = (cchIn + 1);
|
|
|
|
hr = NOERROR;
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT FusionCopyBlob(BLOB *pblobOut, const BLOB &rblobIn)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
if (pblobOut != NULL)
|
|
{
|
|
pblobOut->cbSize = 0;
|
|
pblobOut->pBlobData = NULL;
|
|
}
|
|
|
|
if (pblobOut == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
goto Exit;
|
|
}
|
|
|
|
if (rblobIn.cbSize != 0)
|
|
{
|
|
pblobOut->pBlobData = NEW(BYTE[rblobIn.cbSize]);
|
|
if (pblobOut->pBlobData == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
memcpy(pblobOut->pBlobData, rblobIn.pBlobData, rblobIn.cbSize);
|
|
pblobOut->cbSize = rblobIn.cbSize;
|
|
}
|
|
|
|
hr = NOERROR;
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
VOID FusionFreeBlob(BLOB *pblob)
|
|
{
|
|
if (pblob != NULL)
|
|
{
|
|
if (pblob->pBlobData != NULL)
|
|
{
|
|
CSxsPreserveLastError ple;
|
|
delete []pblob->pBlobData;
|
|
pblob->pBlobData = NULL;
|
|
ple.Restore();
|
|
}
|
|
|
|
pblob->cbSize = 0;
|
|
}
|
|
}
|
|
|
|
#endif // FUSION_DISABLED_CODE
|