windows-nt/Source/XPSP1/NT/shell/comctl32/v6/dlgcvt.cpp
2020-09-26 16:20:57 +08:00

840 lines
20 KiB
C++

//
// This module provides the following functions:
//
// CvtDlgToDlgEx - Converts a DLGTEMPLATE to a DLGTEMPLATEEX
//
//
#include "ctlspriv.h"
#include "dlgcvt.h"
//
// Define the amount (bytes) the stream buffer grows when required.
// It will grow enough to satisfy the required write PLUS this
// amount.
//
#ifdef DEBUG
# define STREAM_GROW_BYTES 32 // Exercise stream growth.
#else
# define STREAM_GROW_BYTES 512
#endif
//
// Simple MIN/MAX inline helpers.
//
#if (defined UNIX && defined ux10)
//IEUNIX: hp's version of "/usr/local/include/sys/param.h defines MAX and MIN
//macro's and breaks hp's build
#undef MAX(a,b)
#undef MIN(a,b)
#endif //UNIX && ux10
template <class T>
inline const T& MIN(const T& a, const T& b)
{
return a < b ? a : b;
}
template <class T>
inline const T& MAX(const T& a, const T& b)
{
return a > b ? a : b;
}
//
// This class implements a simple dynamic stream that grows as you
// add data to it. It's modeled after the strstream class provided
// by the C++ std lib. Unlike the std lib implementation, this one
// doesn't require C++ EH to be enabled. If comctl32 compiled with
// C++ EH enabled, I would have used strstream instead.
// [brianau - 10/5/98]
//
class CByteStream
{
public:
explicit CByteStream(int cbDefGrow = 512);
~CByteStream(void);
//
// Used as argument to AlignXXXX member functions.
//
enum AlignType { eAlignWrite, eAlignRead };
//
// Basic read/write functions.
//
int Read(LPVOID pb, int cb);
int Write(const VOID *pb, int cb);
//
// Determine if there was an error when reading or
// writing to the stream.
//
bool ReadError(void) const
{ return m_bReadErr; }
bool WriteError(void) const
{ return m_bWriteErr; }
//
// Reset the stream read or write pointer.
//
void ResetRead(void)
{ m_pbRead = m_pbBuf; m_bReadErr = false; }
void ResetWrite(void)
{ m_pbWrite = m_pbBuf; m_bWriteErr = false; }
//
// Reset the stream.
//
void Reset(void);
//
// These functions align the read and write stream pointers.
//
void AlignReadWord(void)
{ Align(eAlignRead, sizeof(WORD)); }
void AlignReadDword(void)
{ Align(eAlignRead, sizeof(DWORD)); }
void AlignReadQword(void)
{ Align(eAlignRead, sizeof(ULONGLONG)); }
void AlignWriteWord(void)
{ Align(eAlignWrite, sizeof(WORD)); }
void AlignWriteDword(void)
{ Align(eAlignWrite, sizeof(DWORD)); }
void AlignWriteQword(void)
{ Align(eAlignWrite, sizeof(ULONGLONG)); }
//
// GetBuffer returns the address of the stream buffer in memory.
// The buffer is "frozen" so it will not be released if the stream
// object is destroyed. At this point, you own the buffer.
// If bPermanent is false, you can call ReleaseBuffer to return
// control of the buffer to the stream object.
//
LPBYTE GetBuffer(bool bPermanent = false);
//
// ReleaseBuffer returns control of the buffer obtained with GetBuffer
// to the stream object.
//
bool ReleaseBuffer(LPBYTE pbBuf);
//
// Overload the insertion and extraction operators so we can
// work like a normal std lib stream class.
//
template <class T>
CByteStream& operator >> (T& x)
{ Read(&x, sizeof(x)); return *this; }
template <class T>
CByteStream& operator << (const T& x)
{ Write(&x, sizeof(x)); return *this; }
private:
int m_cbDefGrow; // Default amount (bytes) to grow when expanding buffer.
LPBYTE m_pbBuf; // Addr of allocated buffer.
LPBYTE m_pbRead; // Addr for next read.
LPBYTE m_pbWrite; // Addr for next write.
LPBYTE m_pbEnd; // Addr of byte following last byte in buffer.
bool m_bWriteErr; // Any read errors?
bool m_bReadErr; // Any write errors?
bool m_bOwnsBuf; // true == delete buffer in dtor.
//
// Expand the buffer as needed.
//
bool GrowBuffer(int cb = 0);
//
// Align the read or write buffer pointer.
// Used internally by the AlignXXXXX member functions.
//
void Align(AlignType a, size_t n);
//
// Internal consistency checks for debug builds.
//
void Validate(void) const;
//
// Prevent copy.
//
CByteStream(const CByteStream& rhs);
CByteStream& operator = (const CByteStream& rhs);
};
//
// Class for converting in-memory dialog templates between the two
// structures DLGTEMPLATE <-> DLGTEMPLATEEX.
//
// Currently, the object only converts from DLGTEMPLATE -> DLGTEMPLATEEX.
// It would be simple to create the code for the inverse conversion. However,
// it's currently not needed so I didn't create it.
//
class CDlgTemplateConverter
{
public:
explicit CDlgTemplateConverter(int iCharSet = DEFAULT_CHARSET)
: m_iCharset(iCharSet),
m_stm(STREAM_GROW_BYTES) { }
~CDlgTemplateConverter(void) { }
HRESULT DlgToDlgEx(LPDLGTEMPLATE pTemplateIn, LPDLGTEMPLATEEX *ppTemplateOut);
HRESULT DlgExToDlg(LPDLGTEMPLATEEX pTemplateIn, LPDLGTEMPLATE *ppTemplateOut)
{ return E_NOTIMPL; }
private:
int m_iCharset;
CByteStream m_stm; // For converted template.
#ifndef UNIX
HRESULT DlgHdrToDlgEx(CByteStream& s, LPWORD *ppw);
HRESULT DlgItemToDlgEx(CByteStream& s, LPWORD *ppw);
#else
HRESULT DlgHdrToDlgEx(CByteStream& s, LPDWORD *ppw);
HRESULT DlgItemToDlgEx(CByteStream& s, LPDWORD *ppw);
#endif
HRESULT DlgExHdrToDlg(CByteStream& s, LPWORD *ppw)
{ return E_NOTIMPL; }
HRESULT DlgExItemToDlg(CByteStream& s, LPWORD *ppw)
{ return E_NOTIMPL; }
//
// Copy a string from pszW into a CByteStream object.
// Copies at most cch chars. If cch is -1, assumes the string is
// nul-terminated and will copy all chars in string including
// terminating NULL.
//
int CopyStringW(CByteStream& stm, LPWSTR pszW, int cch = -1);
//
// Prevent copy.
//
CDlgTemplateConverter(const CDlgTemplateConverter& rhs);
CDlgTemplateConverter& operator = (const CDlgTemplateConverter& rhs);
};
//
// Generic alignment function.
// Give it an address and an alignment size and it returns
// the address adjusted for the requested alignment.
//
// n : 2 = 16-bit
// 4 = 32-bit
// 8 = 64-bit
//
LPVOID Align(LPVOID pv, size_t n)
{
const ULONG_PTR x = static_cast<ULONG_PTR>(n) - 1;
return reinterpret_cast<LPVOID>((reinterpret_cast<ULONG_PTR>(pv) + x) & ~x);
}
inline LPVOID AlignWord(LPVOID pv)
{
return ::Align(pv, sizeof(WORD));
}
inline LPVOID AlignDWord(LPVOID pv)
{
return ::Align(pv, sizeof(DWORD));
}
inline LPVOID AlignQWord(LPVOID pv)
{
return ::Align(pv, sizeof(ULONGLONG));
}
CByteStream::CByteStream(
int cbDefGrow
) : m_cbDefGrow(MAX(cbDefGrow, 1)),
m_pbBuf(NULL),
m_pbRead(NULL),
m_pbWrite(NULL),
m_pbEnd(NULL),
m_bWriteErr(false),
m_bReadErr(false),
m_bOwnsBuf(true)
{
}
CByteStream::~CByteStream(
void
)
{
if (m_bOwnsBuf && NULL != m_pbBuf)
{
LocalFree(m_pbBuf);
}
}
//
// Simple checks to validate stream state.
// In non-debug builds, this will be a no-op.
// Use ASSERT_VALIDSTREAM macro.
//
void
CByteStream::Validate(
void
) const
{
ASSERT(m_pbEnd >= m_pbBuf);
ASSERT(m_pbWrite >= m_pbBuf);
ASSERT(m_pbRead >= m_pbBuf);
ASSERT(m_pbWrite <= m_pbEnd);
ASSERT(m_pbRead <= m_pbEnd);
}
#ifdef DEBUG
# define ASSERT_VALIDSTREAM(ps) ps->Validate()
#else
# define ASSERT_VALIDSTREAM(ps)
#endif
//
// Read "cb" bytes from the stream and write them to
// the location specified in "pb". Return number
// of bytes read. Note that if we don't "own" the
// buffer (i.e. the client has called GetBuffer but
// not ReleaseBuffer), no read will occur.
//
int
CByteStream::Read(
LPVOID pb,
int cb
)
{
ASSERT_VALIDSTREAM(this);
int cbRead = 0;
if (m_bOwnsBuf)
{
cbRead = MIN(static_cast<int>(m_pbEnd - m_pbRead), cb);
CopyMemory(pb, m_pbRead, cbRead);
m_pbRead += cbRead;
if (cb != cbRead)
m_bReadErr = true;
}
ASSERT_VALIDSTREAM(this);
return cbRead;
}
//
// Write "cb" bytes from location "pb" into the stream.
// Return number of bytes written. Note that if we don't "own" the
// buffer (i.e. the client has called GetBuffer but
// not ReleaseBuffer), no write will occur.
//
int
CByteStream::Write(
const VOID *pb,
int cb
)
{
ASSERT_VALIDSTREAM(this);
int cbWritten = 0;
if (m_bOwnsBuf)
{
if (m_pbWrite + cb < m_pbEnd ||
GrowBuffer(static_cast<int>(m_pbEnd - m_pbBuf) + cb + m_cbDefGrow))
{
CopyMemory(m_pbWrite, pb, cb);
m_pbWrite += cb;
cbWritten = cb;
}
else
m_bWriteErr = true;
}
ASSERT_VALIDSTREAM(this);
return cbWritten;
}
//
// Reallocate the buffer by cb or m_cbDefGrow.
// Copy existing contents to new buffer. All internal
// pointers are updated.
//
bool
CByteStream::GrowBuffer(
int cb // optional. Default is 0 causing us to use m_cbDefGrow.
)
{
bool bResult = false;
int cbGrow = 0 < cb ? cb : m_cbDefGrow;
ULONG_PTR ulReadOfs = m_pbRead - m_pbBuf;
ULONG_PTR ulWriteOfs = m_pbWrite - m_pbBuf;
ULONG_PTR cbAlloc = m_pbEnd - m_pbBuf;
LPBYTE pNew = static_cast<LPBYTE>(LocalAlloc(LPTR, cbAlloc + cbGrow));
if (NULL != pNew)
{
if (NULL != m_pbBuf)
{
CopyMemory(pNew, m_pbBuf, cbAlloc);
LocalFree(m_pbBuf);
}
m_pbBuf = pNew;
m_pbRead = m_pbBuf + ulReadOfs;
m_pbWrite = m_pbBuf + ulWriteOfs;
m_pbEnd = m_pbBuf + cbAlloc + cbGrow;
bResult = true;
}
ASSERT_VALIDSTREAM(this);
return bResult;
}
//
// Align the read or write pointer on the stream.
// The write pointer is aligned by padding skipped bytes with 0.
//
void
CByteStream::Align(
CByteStream::AlignType a,
size_t n
)
{
static const BYTE fill[8] = {0};
if (m_bOwnsBuf)
{
switch(a)
{
case eAlignWrite:
Write(fill, static_cast<int>(reinterpret_cast<LPBYTE>(::Align(m_pbWrite, n)) - m_pbWrite));
break;
case eAlignRead:
m_pbRead = reinterpret_cast<LPBYTE>(::Align(m_pbRead, n));
if (m_pbRead >= m_pbEnd)
m_bReadErr = true;
break;
default:
break;
}
}
ASSERT_VALIDSTREAM(this);
}
//
// Caller takes ownership of the buffer.
//
LPBYTE
CByteStream::GetBuffer(
bool bPermanent // optional. Default is false.
)
{
LPBYTE pbRet = m_pbBuf;
if (bPermanent)
{
//
// Caller now permanently owns the buffer.
// Can't return it through ReleaseBuffer().
// Reset the internal stream control values.
//
m_pbBuf = m_pbWrite = m_pbRead = m_pbEnd = NULL;
m_bWriteErr = m_bReadErr = false;
m_bOwnsBuf = true;
}
else
{
//
// Caller now owns the buffer but it can be returned
// through ReleaseBuffer().
//
m_bOwnsBuf = false;
}
return pbRet;
}
//
// Take back ownership of the buffer.
// Returns:
//
// true = CByteStream object took back ownership.
// false = CByteStream object couldn't take ownership.
//
bool
CByteStream::ReleaseBuffer(
LPBYTE pbBuf
)
{
if (pbBuf == m_pbBuf)
{
m_bOwnsBuf = true;
return true;
}
return false;
}
//
// Reset the stream.
//
void
CByteStream::Reset(
void
)
{
if (NULL != m_pbBuf)
{
LocalFree(m_pbBuf);
}
m_pbBuf = m_pbWrite = m_pbRead = m_pbEnd = NULL;
m_bWriteErr = m_bReadErr = false;
m_bOwnsBuf = true;
}
//
// Copy one or more WORDs from the location provided in "pszW" into
// the stream. If cch is -1, it's assumed that the string is nul-terminated.
// Returns the number of WCHARs written.
//
int
CDlgTemplateConverter::CopyStringW(
CByteStream& stm,
LPWSTR pszW,
int cch
)
{
if (-1 == cch)
cch = lstrlenW(pszW) + 1;
return stm.Write(pszW, cch * sizeof(WCHAR)) / sizeof(WCHAR);
}
//
// Convert a DLGTEMPLATE structure to a DLGTEMPLATEEX structure.
// pti is the address of the DLGTEMPLATE to be converted.
// ppto points to a LPDLGTEMPLATEEX ptr to receive the address of the
// converted template structure. Caller is responsible for freeing
// this buffer with LocalFree.
//
// Returns: E_OUTOFMEMORY, NOERROR
//
HRESULT
CDlgTemplateConverter::DlgToDlgEx(
LPDLGTEMPLATE pti,
LPDLGTEMPLATEEX *ppto
)
{
HRESULT hr = NOERROR;
#ifndef UNIX
LPWORD pw = reinterpret_cast<LPWORD>(pti);
#else
LPDWORD pw = reinterpret_cast<LPDWORD>(pti);
#endif
*ppto = NULL;
//
// Reset the stream.
//
m_stm.Reset();
//
// Convert DLGTEMPLATE -> DLGTEMPLATEEX
//
hr = DlgHdrToDlgEx(m_stm, &pw);
//
// Convert each DLGITEMTEMPLATE -> DLGITEMTEMPLATEEX
//
for (int i = 0; i < pti->cdit && SUCCEEDED(hr); i++)
{
#ifndef UNIX
pw = reinterpret_cast<LPWORD>(::AlignDWord(pw));
#else
pw = reinterpret_cast<LPDWORD>(::AlignDWord(pw));
#endif
m_stm.AlignWriteDword();
hr = DlgItemToDlgEx(m_stm, &pw);
}
if (SUCCEEDED(hr))
{
//
// Return the buffer to the caller. Buffer is permanently
// detached from the stream object so the stream's dtor
// won't free it.
//
*ppto = reinterpret_cast<LPDLGTEMPLATEEX>(m_stm.GetBuffer(true));
}
return hr;
};
//
// Convert DLGTEMPLATE -> DLGTEMPLATEEX
//
// s = Stream to hold converted template.
// ppw = Address of current read pointer into the template being converted.
// On exit, the referenced pointer is updated with the current read location.
//
// Returns: E_OUTOFMEMORY, NOERROR
//
HRESULT
CDlgTemplateConverter::DlgHdrToDlgEx(
CByteStream& s,
#ifndef UNIX
LPWORD *ppw
#else
LPDWORD *ppw
#endif
)
{
#ifndef UNIX
LPWORD pw = *ppw;
#else
LPDWORD pw = *ppw;
#endif
LPDLGTEMPLATE pt = reinterpret_cast<LPDLGTEMPLATE>(pw);
//
// Convert the fixed-length stuff.
//
s << static_cast<WORD>(1) // wDlgVer
<< static_cast<WORD>(0xFFFF) // wSignature
<< static_cast<DWORD>(0) // dwHelpID
<< static_cast<DWORD>(pt->dwExtendedStyle)
<< static_cast<DWORD>(pt->style)
<< static_cast<WORD>(pt->cdit)
<< static_cast<short>(pt->x)
<< static_cast<short>(pt->y)
<< static_cast<short>(pt->cx)
<< static_cast<short>(pt->cy);
//
// Arrays are always WORD aligned.
//
#ifndef UNIX
pw = reinterpret_cast<LPWORD>(::AlignWord(reinterpret_cast<LPBYTE>(pw) + sizeof(DLGTEMPLATE)));
s.AlignWriteWord();
#else
pw = reinterpret_cast<LPDWORD>(::AlignDWord(reinterpret_cast<LPBYTE>(pw) + sizeof(DLGTEMPLATE)));
s.AlignWriteDword();
#endif
//
// Copy the menu array.
//
switch(*pw)
{
case 0xFFFF:
s << *pw++;
//
// Fall through...
//
case 0x0000:
s << *pw++;
break;
default:
#ifndef UNIX
pw += CopyStringW(s, (LPWSTR)pw);
#else
pw += CopyStringW (s, reinterpret_cast<LPWSTR>(pw));
#endif
break;
};
//
// Copy the class array.
//
switch(*pw)
{
case 0xFFFF:
s << *pw++;
//
// Fall through...
//
case 0x0000:
s << *pw++;
break;
default:
#ifndef UNIX
pw += CopyStringW(s, (LPWSTR)pw);
#else
pw += CopyStringW (s, reinterpret_cast<LPWSTR>(pw));
#endif
break;
};
//
// Copy the title array.
//
switch(*pw)
{
case 0x0000:
s << *pw++;
break;
default:
#ifndef UNIX
pw += CopyStringW(s, (LPWSTR)pw);
#else
pw += CopyStringW (s, reinterpret_cast<LPWSTR>(pw));
#endif
break;
};
//
// Copy font information if it's present.
//
if (DS_SETFONT & pt->style)
{
s << *pw++; // pt size
s << static_cast<WORD>(FW_NORMAL); // weight (default, not in DLGTEMPLATE)
s << static_cast<BYTE>(FALSE); // italic (default, not in DLGTEMPLATE)
s << static_cast<BYTE>(m_iCharset); // charset (default if not given,
// not in DLGTEMPLATE)
#ifndef UNIX
pw += CopyStringW(s, (LPWSTR)pw);
#else
pw += CopyStringW (s, reinterpret_cast<LPWSTR>(pw));
#endif
}
*ppw = pw;
return s.WriteError() ? E_OUTOFMEMORY : NOERROR;
}
//
// Convert DLGITEMTEMPLATE -> DLGITEMTEMPLATEEX
//
// s = Stream to hold converted template.
// ppw = Address of current read pointer into the template being converted.
// On exit, the referenced pointer is updated with the current read location.
//
// Returns: E_OUTOFMEMORY, NOERROR
//
HRESULT
CDlgTemplateConverter::DlgItemToDlgEx(
CByteStream& s,
#ifndef UNIX
LPWORD *ppw
#else
LPDWORD *ppw
#endif
)
{
#ifndef UNIX
LPWORD pw = *ppw;
#else
LPDWORD pw = *ppw;
#endif
LPDLGITEMTEMPLATE pit = reinterpret_cast<LPDLGITEMTEMPLATE>(pw);
//
// Convert the fixed-length stuff.
//
s << static_cast<DWORD>(0) // dwHelpID
<< static_cast<DWORD>(pit->dwExtendedStyle)
<< static_cast<DWORD>(pit->style)
<< static_cast<short>(pit->x)
<< static_cast<short>(pit->y)
<< static_cast<short>(pit->cx)
<< static_cast<short>(pit->cy)
<< static_cast<DWORD>(pit->id);
//
// Arrays are always word aligned.
//
#ifndef UNIX
pw = reinterpret_cast<LPWORD>(::AlignWord(reinterpret_cast<LPBYTE>(pw) + sizeof(DLGITEMTEMPLATE)));
s.AlignWriteWord();
#else
pw = reinterpret_cast<LPDWORD>(::AlignWord(reinterpret_cast<LPBYTE>(pw) + sizeof(DLGITEMTEMPLATE)));
s.AlignWriteDword();
#endif
//
// Copy the class array.
//
switch(*pw)
{
case 0xFFFF:
s << *pw++;
s << *pw++; // Class code.
break;
default:
#ifndef UNIX
pw += CopyStringW(s, (LPWSTR)pw);
#else
pw += CopyStringW (s, reinterpret_cast<LPWSTR>(pw));
#endif
break;
};
//
// Copy the title array.
//
switch(*pw)
{
case 0xFFFF:
s << *pw++;
s << *pw++; // Resource ordinal value.
break;
default:
#ifndef UNIX
pw += CopyStringW(s, (LPWSTR)pw);
#else
pw += CopyStringW (s, reinterpret_cast<LPWSTR>(pw));
#endif
break;
};
//
// Copy the creation data.
// *pw is either 0 or the number of bytes of creation data,
// including *pw.
//
switch(*pw)
{
case 0x0000:
s << *pw++;
break;
default:
#ifndef UNIX
pw += s.Write(pw, *pw) / sizeof(WORD);
#else
pw += s.Write(pw, *pw) / sizeof(DWORD);
#endif
break;
};
*ppw = pw;
return s.WriteError() ? E_OUTOFMEMORY : NOERROR;
}
//
// This is the public function for converting a DLGTEMPLATE to
// a DLGTEMPLATEEX.
//
// Returns: E_OUTOFMEMORY, NOERROR
//
HRESULT
CvtDlgToDlgEx(
LPDLGTEMPLATE pTemplate,
LPDLGTEMPLATEEX *ppTemplateExOut,
int iCharset
)
{
CDlgTemplateConverter dtc(iCharset);
return dtc.DlgToDlgEx(pTemplate, ppTemplateExOut);
}