Leaked source code of windows server 2003
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.
 
 
 
 
 
 

938 lines
30 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
ichannel.hxx
Abstract:
Declaration of CVssIOCTLChannel
Adi Oltean [aoltean] 10/18/1999
Revision History:
Name Date Comments
aoltean 10/18/1999 Created
--*/
#ifndef __VSS_ICHANNEL_HXX__
#define __VSS_ICHANNEL_HXX__
#if _MSC_VER > 1000
#pragma once
#endif
////////////////////////////////////////////////////////////////////////
// Standard foo for file name aliasing. This code block must be after
// all includes of VSS header files.
//
#ifdef VSS_FILE_ALIAS
#undef VSS_FILE_ALIAS
#endif
#define VSS_FILE_ALIAS "INCICHLH"
//
////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Constants
const x_nMemInitialSize = 4096; // Buffers allocated with 4096 bytes initially.
const x_nMemGrowthFactor = 2; // If allocation fault occurs, the memory is shrinked twice
/////////////////////////////////////////////////////////////////////////////
// CVssIOCTLChannel declarations
/*
This class is used to send IOCTLs and correctly deal with its parameters.
*/
typedef enum _VSS_ICHANNEL_LOGGING {
VSS_ICHANNEL_LOG_NONE=0,
VSS_ICHANNEL_LOG_COORD=1,
VSS_ICHANNEL_LOG_PROV=2,
VSS_ICHANNEL_LOG_PROV_TIMEOUT=3,
VSS_ICHANNEL_LOG_PROV_NOTFOUND=4,
VSS_ICHANNEL_LOG_LOVELACE_HOLD=5,
VSS_ICHANNEL_LOG_LOVELACE_RELEASE=6
} VSS_ICHANNEL_LOGGING;
class CVssIOCTLChannel
{
// Constructors& destructors
private:
CVssIOCTLChannel(const CVssIOCTLChannel&); // disable copy constructor
public:
CVssIOCTLChannel(
IN WORD wAlignmentBytes = 0 // Bytes alignment for pushed parameters.
):
m_hDevice(NULL),
m_dwInputCurrentOffset(0),
m_dwInputAllocatedSize(0),
m_pbInputBuffer(NULL),
m_dwOutputCurrentOffset(0),
m_dwOutputAllocatedSize(0),
m_dwOutputCurrentSize(0),
m_pbOutputBuffer(NULL),
m_wAlignmentBytes(wAlignmentBytes)
{};
~CVssIOCTLChannel()
{
// Close and reset some offsets
Close();
// Delete the allocated buffers
if (m_pbInputBuffer)
::free(m_pbInputBuffer);
m_pbInputBuffer = NULL;
m_dwInputAllocatedSize = 0;
if (m_pbOutputBuffer)
::free(m_pbOutputBuffer);
m_pbOutputBuffer = NULL;
m_dwOutputAllocatedSize = 0;
}
// Main operations
public:
bool IsOpen() const { return (m_hDevice != NULL); };
HRESULT Open(
IN CVssFunctionTracer& ft,
IN const WCHAR* pwszObjectName,
IN bool bEliminateLastBackslash,
IN bool bThrowIfErrorOnOpen,
IN VSS_ICHANNEL_LOGGING eLogging = VSS_ICHANNEL_LOG_NONE,
IN DWORD dwAccessType = GENERIC_READ | GENERIC_WRITE,
IN DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
) throw(HRESULT)
/*
Connect to that device in order to send the IOCTL.
if (bThrowIfErrorOnOpen) throw on E_UNEXPECTED on error.
else return VSS_E_OBJECT_NOT_FOUND if device name not found
otherwise E_UNEXPECTED.
*/
{
// The length of the interanl buffer. Includes the trailing zero character.
const nInternalBufferLen = MAX_PATH;
BS_ASSERT(pwszObjectName);
BS_ASSERT(bThrowIfErrorOnOpen || (eLogging == VSS_ICHANNEL_LOG_NONE));
// Reset the error code
ft.hr = S_OK;
// Close
Close();
// Eliminate the internal backslash, if needed
LPCWSTR pwszObjectNameInternal = pwszObjectName;
WCHAR wszBuffer[nInternalBufferLen];
if (bEliminateLastBackslash) {
// Check to see if the buffer is large enough
size_t nObjectNameLen = ::wcslen(pwszObjectName);
if (nInternalBufferLen < nObjectNameLen) {
BS_ASSERT(false);
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
L"Error: the buffer is too small to fit %s (%d < %d)",
pwszObjectName, nInternalBufferLen, nObjectNameLen);
}
// Check if the string is terminated with a backslash
if (pwszObjectName[nObjectNameLen - 1] != L'\\') {
BS_ASSERT(false);
ft.Throw(VSSDBG_GEN, E_UNEXPECTED,
L"Error: the object name %s does terminate with a backslash",
pwszObjectName);
}
// Copy the string and append the trailing zero.
::wcsncpy(wszBuffer, pwszObjectName, nObjectNameLen - 1);
wszBuffer[nObjectNameLen - 1] = L'\0';
BS_ASSERT(::wcslen(wszBuffer) == nObjectNameLen - 1);
pwszObjectNameInternal = wszBuffer;
}
// Open the handle to the new object.
m_hDevice = ::CreateFile(pwszObjectNameInternal,
dwAccessType,
dwShareMode,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
INVALID_HANDLE_VALUE);
if (m_hDevice == INVALID_HANDLE_VALUE)
{
m_hDevice = NULL;
DWORD dwLastErr = GetLastError();
if (bThrowIfErrorOnOpen) {
switch(eLogging) {
default:
BS_ASSERT(false);
case VSS_ICHANNEL_LOG_NONE:
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
L"Could not open the object \'%s\'. [0x%08lx]\n",
pwszObjectNameInternal, dwLastErr);
break;
#ifdef IN_VSS
case VSS_ICHANNEL_LOG_COORD:
ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
pwszObjectNameInternal, dwAccessType, dwShareMode);
break;
case VSS_ICHANNEL_LOG_PROV:
ft.TranslateInternalProviderError(VSSDBG_SWPRV,
HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
L"CreateFileW(%s,0x%08lx,0x%08lx,...)",
pwszObjectNameInternal, dwAccessType, dwShareMode);
break;
#endif
}
}
else
{
ft.Trace( VSSDBG_IOCTL,
L"Could not open the object \'%s\'. [0x%08lx]\n",
pwszObjectNameInternal, dwLastErr);
//ft.hr = ((dwLastErr == ERROR_FILE_NOT_FOUND) ||
// (dwLastErr == ERROR_NOT_READY) ||
// (dwLastErr == ERROR_DEVICE_NOT_CONNECTED))?
// (VSS_E_OBJECT_NOT_FOUND): E_UNEXPECTED;
ft.hr = HRESULT_FROM_WIN32(dwLastErr);
}
}
else
{
ft.Trace( VSSDBG_IOCTL,
L"The object \'%s\'. was opened under the device handle 0x%08lx",
pwszObjectNameInternal, m_hDevice);
}
return ft.hr;
}
void Close()
{
// Close previous handle, if needed.
if (m_hDevice)
::CloseHandle(m_hDevice);
m_hDevice= NULL;
// Reset internal offsets
ResetOffsets();
m_dwOutputCurrentSize = 0;
}
HRESULT Call(
IN CVssFunctionTracer& ft,
IN DWORD dwIoControlCode,
IN bool bThrowIfErrorOnCall = true,
IN VSS_ICHANNEL_LOGGING eLogging = VSS_ICHANNEL_LOG_NONE
) throw(HRESULT)
/*
Send the IOCTL to that device.
Before that reserve dwProposedOutputBufferSize bytes for the output buffer.
*/
{
DWORD dwProposedOutputBufferSize = 0;
// Reset the error code
ft.hr = S_OK;
BS_ASSERT(bThrowIfErrorOnCall || (eLogging == VSS_ICHANNEL_LOG_NONE));
// Check if connected
if (m_hDevice == NULL)
{
BS_ASSERT(false);
// Reset internal offsets
ResetOffsets();
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
L"Channel not open");
}
// Reset output buffer
m_dwOutputCurrentOffset = 0;
m_dwOutputCurrentSize = 0;
// Repeat while the output buffer can hold the data
BOOL bResult = false;
while(true)
{
// If no buffer and no sizes, try with the default size
if ((dwProposedOutputBufferSize == 0) && (m_dwOutputAllocatedSize == 0))
{
BS_ASSERT(m_pbOutputBuffer == NULL);
dwProposedOutputBufferSize = x_nMemInitialSize;
}
// Realloc the existing buffer if needed
if (dwProposedOutputBufferSize > m_dwOutputAllocatedSize)
{
PBYTE pbOutputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbOutputBuffer, dwProposedOutputBufferSize));
if (pbOutputBuffer == NULL)
{
// Reset internal offsets
ResetOffsets();
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Could not extend the output buffer");
}
// Change the buffer
m_pbOutputBuffer = pbOutputBuffer;
m_dwOutputAllocatedSize = dwProposedOutputBufferSize;
}
ft.Trace( VSSDBG_IOCTL, L"IOCTL sent: %lx\n Input buffer size: %ld, Output buffer size: %ld",
dwIoControlCode, m_dwInputCurrentOffset, m_dwOutputAllocatedSize );
ft.TraceBuffer( VSSDBG_IOCTL, m_dwInputCurrentOffset, m_pbInputBuffer );
// Send the IOCTL.
bResult = ::DeviceIoControl(m_hDevice,
dwIoControlCode,
m_pbInputBuffer,
m_dwInputCurrentOffset,
m_pbOutputBuffer,
m_dwOutputAllocatedSize,
&m_dwOutputCurrentSize,
NULL
);
// If the error was "insufficient buffer" then try to reallocate a bigger one.
// Otherwise end the iteration
if (!bResult && ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) || (GetLastError() == ERROR_MORE_DATA)))
dwProposedOutputBufferSize = x_nMemGrowthFactor*m_dwOutputAllocatedSize;
else
break;
}
DWORD dwLastErr = GetLastError();
// Reset internal offsets
ResetOffsets();
// Treat the error case
if (!bResult) {
if (bThrowIfErrorOnCall) {
switch(eLogging) {
default:
BS_ASSERT(false);
case VSS_ICHANNEL_LOG_NONE:
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
L"Could not send the IOCTL 0x%08lx on device 0x%08lx. [0x%08lx]\n",
dwIoControlCode, m_hDevice, dwLastErr);
break;
case VSS_ICHANNEL_LOG_COORD:
ft.TranslateGenericError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
m_hDevice, dwIoControlCode,
m_pbInputBuffer, m_dwInputCurrentOffset,
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
break;
#ifdef IN_VSS
case VSS_ICHANNEL_LOG_LOVELACE_HOLD:
case VSS_ICHANNEL_LOG_LOVELACE_RELEASE:
ft.TranslateInternalLovelaceError(VSSDBG_COORD, HRESULT_FROM_WIN32(dwLastErr),
((eLogging == VSS_ICHANNEL_LOG_LOVELACE_HOLD) ? TRUE : FALSE),
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
m_hDevice, dwIoControlCode,
m_pbInputBuffer, m_dwInputCurrentOffset,
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
break;
case VSS_ICHANNEL_LOG_PROV_TIMEOUT:
if (dwLastErr == ERROR_INVALID_PARAMETER)
// This logging flag indicates that this actually means timeout
dwLastErr = ERROR_TIMEOUT;
// fall through...
case VSS_ICHANNEL_LOG_PROV:
ft.TranslateInternalProviderError(VSSDBG_SWPRV,
HRESULT_FROM_WIN32(dwLastErr), VSS_E_PROVIDER_VETO,
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
m_hDevice, dwIoControlCode,
m_pbInputBuffer, m_dwInputCurrentOffset,
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
break;
case VSS_ICHANNEL_LOG_PROV_NOTFOUND:
{
HRESULT hrThrow = VSS_E_PROVIDER_VETO;
if (dwLastErr == ERROR_INVALID_PARAMETER)
hrThrow = VSS_E_OBJECT_NOT_FOUND;
ft.TranslateInternalProviderError(VSSDBG_SWPRV,
HRESULT_FROM_WIN32(dwLastErr), hrThrow,
L"DeviceIoControl(%p,0x%08lx,%p,%d,%p,%d,[%d])",
m_hDevice, dwIoControlCode,
m_pbInputBuffer, m_dwInputCurrentOffset,
m_pbOutputBuffer, m_dwOutputAllocatedSize, m_dwOutputCurrentSize );
break;
}
#endif
}
} else {
ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx failed on device 0x%08lx. Error code = 0x%08lx",
dwIoControlCode, m_hDevice, dwLastErr);
ft.hr = HRESULT_FROM_WIN32(dwLastErr);
}
} else {
ft.Trace( VSSDBG_IOCTL, L"IOCTL %lx succeeded on device 0x%08lx. \n Output buffer: size received = %ld",
dwIoControlCode, m_hDevice, m_dwOutputCurrentSize );
ft.TraceBuffer( VSSDBG_IOCTL, m_dwOutputCurrentSize, m_pbOutputBuffer );
ft.hr = S_OK; // Clear the previous HRESULT value
}
return ft.hr;
}
template <class T>
PVOID PackArray(
IN CVssFunctionTracer& ft,
IN T* pSourceObject,
IN DWORD dwNumObjects
) throw(HRESULT)
/*
Copy the contents of the given object(s) into the input buffer...
If out of memory, the old input buffer remains unchanged and an exception is thrown.
*/
{
BS_ASSERT(pSourceObject);
BS_ASSERT(dwNumObjects);
// Reset the error code
ft.hr = S_OK;
DWORD dwSize = sizeof(T) * dwNumObjects;
if ( dwSize / dwNumObjects != sizeof(T) )
{
// Reset internal offsets
ResetOffsets();
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Arithmetic overflow. dwNumObjects = %lu too large",
dwNumObjects);
}
// Make room for the GUID. Here an E_OUTOFMEMORY exception can occur.
ExpandInputBuffer( ft, dwSize );
BS_ASSERT(m_pbInputBuffer);
BS_ASSERT(m_dwInputCurrentOffset + dwSize <= m_dwInputAllocatedSize);
// Copy the object contents... Check again for arithmetic overflow
if (m_pbInputBuffer + m_dwInputCurrentOffset + dwSize < m_pbInputBuffer)
{
// Reset internal offsets
ResetOffsets();
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Arithmetic overflow. m_dwInputCurrentOffset = %lu or dwSize = %lu too large.",
m_dwInputCurrentOffset, dwSize);
}
BS_ASSERT(reinterpret_cast<T*>(reinterpret_cast<PBYTE>(pSourceObject)) == pSourceObject);
::CopyMemory( m_pbInputBuffer + m_dwInputCurrentOffset,
reinterpret_cast<PBYTE>(pSourceObject),
dwSize
);
// Remember the copied object
T* pCopiedObject = reinterpret_cast<T*>(m_pbInputBuffer + m_dwInputCurrentOffset);
// Increase the current offset
m_dwInputCurrentOffset += dwSize;
if (m_wAlignmentBytes)
{
// Round up the value in relation with the existing alignment
WORD wAlignmentOffset = (WORD)( m_dwInputCurrentOffset % m_wAlignmentBytes );
BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
if (wAlignmentOffset != 0)
m_dwInputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
BS_ASSERT( m_dwInputCurrentOffset % m_wAlignmentBytes == 0 );
}
// Return the copied object
return pCopiedObject;
}
template <class T>
PVOID Pack(
IN CVssFunctionTracer& ft,
IN const T& SourceObject
) throw(HRESULT)
/*
Copy the contents of the given object(s) into the input buffer...
If out of memory, the old input buffer remains unchanged and an exception is thrown.
*/
{
return PackArray( ft, const_cast<T*>(&SourceObject), 1 );
};
PVOID PackSmallString(
IN CVssFunctionTracer& ft,
IN LPCWSTR wszText
) throw(HRESULT)
/*
Copy the contents of the given string into the input buffer.
The storing format is
+--------+
| Len | USHORT Length, in bytes
+--------+
| String | WCHAR[] String characters, without terminating zero
| ... |
+--------+
If the string pointer is NULL or the string is empty then a NULL string will be put.
+--------+
| 0 | USHORT
+--------+
If out of memory, the old input buffer remains unchanged and an exception is thrown.
The pointer to the string is returned.
*/
{
PVOID pwszReturned = NULL;
// Reset the error code
ft.hr = S_OK;
if ( wszText != NULL)
{
size_t nLen = ::wcslen(wszText);
USHORT usLen = (USHORT)(nLen);
// Check buffer overflow
if ((size_t)usLen != nLen)
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY, L"Arithmetic overflow. %ld", nLen);
// Pack the length and the string
Pack( ft, (USHORT)(usLen * sizeof(WCHAR)) );
if ( usLen != 0)
pwszReturned = PackArray( ft, const_cast<LPWSTR>(wszText), usLen );
}
else
Pack( ft, (USHORT)0 );
return pwszReturned;
};
template <class T>
void Unpack(
IN CVssFunctionTracer& ft,
IN T* pDestinationObject,
IN DWORD dwNumObjects = 1,
IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
) throw(HRESULT)
/*
Copy the contents of the given object(s) from the output buffer...
*/
{
BS_ASSERT((pDestinationObject != NULL) || !bTrueUnpack);
BS_ASSERT(dwNumObjects);
// Compute size, in bytes for the allocated objects. Check for Arithmetic overflow.
DWORD dwSize = sizeof(T) * dwNumObjects;
if ( dwSize / dwNumObjects != sizeof(T) )
{
// Reset internal offsets
ResetOffsets();
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Arithmetic overflow. dwNumObjects = %lu too large",
dwNumObjects);
}
// Check for buffer overflow.
if ((m_pbOutputBuffer == NULL) ||
(m_dwOutputCurrentOffset + dwSize > m_dwOutputCurrentSize))
{
// Reset internal offsets
ResetOffsets();
BS_ASSERT(false);
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED,
L"Output buffer overflow. Reading bad arguments. dwSize = %lu",
dwSize);
}
// Check again for arithmetic overflow and Copy the object contents...
if (m_pbOutputBuffer + m_dwOutputCurrentOffset + dwSize < m_pbOutputBuffer)
{
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Arithmetic overflow. m_dwOutputCurrentOffset = %lu or dwSize = %lu too large.",
m_dwOutputCurrentOffset, dwSize);
// Reset internal offsets
ResetOffsets();
}
PBYTE pbBuffer = reinterpret_cast<PBYTE>((PVOID)(pDestinationObject));
BS_ASSERT(reinterpret_cast<T*>(pbBuffer) == pDestinationObject);
if (bTrueUnpack)
::CopyMemory( pbBuffer, m_pbOutputBuffer + m_dwOutputCurrentOffset, dwSize );
// Increase the current offset
m_dwOutputCurrentOffset += dwSize;
if (m_wAlignmentBytes)
{
// Round up the value in relation with the existing alignment
WORD wAlignmentOffset = (WORD)( m_dwOutputCurrentOffset % m_wAlignmentBytes );
BS_ASSERT(m_wAlignmentBytes > wAlignmentOffset);
if (wAlignmentOffset != 0)
m_dwOutputCurrentOffset += m_wAlignmentBytes - wAlignmentOffset;
BS_ASSERT( m_dwOutputCurrentOffset % m_wAlignmentBytes == 0 );
}
}
LPCWSTR UnpackSmallString(
IN CVssFunctionTracer& ft,
IN OUT LPCWSTR& pwszText,
IN bool bTrueUnpack = true // If false then just fake the unpacking (increment the offsets but do not unpach nothing
) throw(HRESULT)
/*
Allocate a string and copy the contents of the
given string from the output buffer.
Deallocate the previous string, if any.
The storing format is
+--------+
| Len | USHORT Length, in bytes
+--------+
| String | WCHAR[] String characters, without terminating zero
| ... |
+--------+
If the string is empty then a NULL string will be put.
+--------+
| 0 | USHORT
+--------+
If out of memory, the old output buffer is freed and an exception is thrown.
The pointer to the allocated string is returned.
*/
{
// Reset the error code
ft.hr = S_OK;
// Free the previous string, if any
if (bTrueUnpack)
::VssFreeString(pwszText);
// Catch Unpack failures
ft.hr = S_OK;
try
{
// Get the string length
USHORT usSize = 0;
Unpack( ft, & usSize );
// Convert from number of bytes into number of WCHARs
USHORT usLen = (USHORT)(usSize/2);
BS_ASSERT( usLen*2 == usSize );
// Get the string
if (usLen > 0)
{
// Get the string (length = usLen+1, including the zero)
if (bTrueUnpack)
pwszText = ::VssAllocString(ft, usLen);
// Unpack ulLen characters.
Unpack( ft, pwszText, usLen, bTrueUnpack );
// Setup the zero character
if (bTrueUnpack)
const_cast<LPWSTR>(pwszText)[usLen] = L'\0';
}
}
VSS_STANDARD_CATCH(ft)
if (ft.HrFailed()) {
if (bTrueUnpack)
::VssFreeString(pwszText);
ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
}
return bTrueUnpack? pwszText: NULL;
};
LPCWSTR UnpackZeroString(
IN CVssFunctionTracer& ft,
IN OUT LPCWSTR& pwszText
) throw(HRESULT)
/*
Allocate a string and copy the contents of the
given string from the output buffer.
Deallocate the previous string, if any.
The storing format is
+--------+
| String | WCHAR[] String characters, with terminating zero
| ... |
+--------+
| 0 |
+--------+
If the string is empty then a NULL string will be put.
+--------+
| 0 | WCHAR
+--------+
If out of memory, the old output buffer is freed anyway and an exception is thrown.
The pointer to the allocated string is returned.
*/
{
// Free the previous string, if any
::VssFreeString(pwszText);
// Reset the error code
ft.hr = S_OK;
// Catch Unpack failures
try
{
// Check for buffer validity.
if (m_pbOutputBuffer == NULL)
{
// Reset internal offsets
ResetOffsets();
BS_ASSERT(false);
ft.Throw( VSSDBG_IOCTL, E_UNEXPECTED, L"Reading from NULL buffer");
}
// Get the string in the buffer
LPWSTR pwszTextInBuffer = reinterpret_cast<LPWSTR>(m_pbOutputBuffer + m_dwOutputCurrentOffset);
BS_ASSERT( reinterpret_cast<PBYTE>(pwszTextInBuffer) == m_pbOutputBuffer + m_dwOutputCurrentOffset);
DWORD dwStrLen = (DWORD)::wcslen(pwszTextInBuffer);
if (dwStrLen > 0)
{
// Allocate the new string
pwszText = ::VssAllocString(ft, dwStrLen);
BS_ASSERT(pwszText);
// Unpack ulLen+1 characters (including the zero).
Unpack( ft, pwszText, dwStrLen+1 );
BS_ASSERT(pwszText[dwStrLen] == L'\0');
}
else
{
// Unpack the zero character
WCHAR wchTest;
Unpack( ft, &wchTest);
BS_ASSERT(wchTest == L'\0');
}
}
VSS_STANDARD_CATCH(ft)
if (ft.HrFailed()) {
::VssFreeString(pwszText);
ft.Throw(VSSDBG_GEN, ft.hr, L"Exception re-thrown 0x%08lx",ft.hr);
}
return pwszText;
};
void PadWithZero(
IN CVssFunctionTracer& ft,
IN DWORD dwAlignToBytes = sizeof(INT64)
)
{
INT nBytesRemaining = m_dwInputCurrentOffset % dwAlignToBytes;
// Reset the error code
ft.hr = S_OK;
if (nBytesRemaining != 0)
{
INT nBytesToBePadded = dwAlignToBytes - nBytesRemaining;
for (int i = 0; i < nBytesToBePadded; i++)
Pack( ft, (BYTE)0 );
}
}
void ResetOffsets()
{
m_dwInputCurrentOffset = 0;
m_dwOutputCurrentOffset = 0;
}
DWORD GetCurrentInputOffset() { return m_dwInputCurrentOffset; };
DWORD GetCurrentOutputOffset() { return m_dwOutputCurrentOffset; };
// Internal operations
private:
void ExpandInputBuffer(
IN CVssFunctionTracer& ft,
IN DWORD dwBytes
) throw(HRESULT)
/*
Expand the input buffer to accomodate adding another dwBytes.
The m_dwInputCurrentOffset remains unchanged.
If out of memory, the old input buffer remains unchanged and an exception is thrown.
*/
{
// Reset the error code
ft.hr = S_OK;
// Check for arithmetic overflow
if (dwBytes + m_dwInputCurrentOffset < m_dwInputCurrentOffset)
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Arithmetic overflow. dwBytes = %lu too large", dwBytes);
// Check if the buffer needs to be extended
if (dwBytes + m_dwInputCurrentOffset > m_dwInputAllocatedSize)
{
// Compute the new size of the buffer...
DWORD dwNewSize = m_dwInputAllocatedSize;
while(dwBytes + m_dwInputCurrentOffset > dwNewSize)
{
if (dwNewSize != 0)
{
// Check again for arithmetic overflow
if (dwNewSize * x_nMemGrowthFactor <= dwNewSize)
{
// Reset internal offsets
ResetOffsets();
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Arithmetic overflow. dwNewSize = %lu too large", dwNewSize);
}
// Increase the allocated size.
dwNewSize = dwNewSize * x_nMemGrowthFactor;
}
else
dwNewSize = x_nMemInitialSize;
}
// Reallocate the buffer. If realloc fails, the old buffer is still kept.
PBYTE pbNewInputBuffer = reinterpret_cast<PBYTE>(::realloc(m_pbInputBuffer, dwNewSize));
if (pbNewInputBuffer == NULL)
{
// Reset internal offsets
ResetOffsets();
ft.Throw( VSSDBG_IOCTL, E_OUTOFMEMORY,
L"Could not extend the input buffer");
}
// Change the buffer
m_pbInputBuffer = pbNewInputBuffer;
m_dwInputAllocatedSize = dwNewSize;
}
}
// Internal data members.
private:
// I/O Device-related members
HANDLE m_hDevice;
// Input buffer
DWORD m_dwInputCurrentOffset;
DWORD m_dwInputAllocatedSize;
PBYTE m_pbInputBuffer;
// Output buffer
DWORD m_dwOutputCurrentOffset;
DWORD m_dwOutputAllocatedSize;
DWORD m_dwOutputCurrentSize;
PBYTE m_pbOutputBuffer;
WORD m_wAlignmentBytes;
};
#endif // __VSS_ICHANNEL_HXX__