The calls to GetLogPen()
and GetExtLogPen()
both fail. They return zero...
CBrush Brush;
Brush.CreateSolidBrush( COLOR_MINORSELECTION );
Brush.GetLogBrush( &lBrush );
DWORD Style[] = { 3, 1 };
CPen CustomPen;
CustomPen.CreatePen( PS_USERSTYLE, 1, &lBrush, 2, Style );
CPen *pOldPen = pDC->SelectObject( &CustomPen );
LOGPEN LogPen;
if( CustomPen->GetLogPen( &LogPen ) == 0 )
{
EXTLOGPEN ExtLogPen;
if( CustomPen->GetExtLogPen( &ExtLogPen ) == 0 )
return;
}
The failure appears to be because of the PS_USERSTYLE
being used for the pen style. If I do this with a PS_SOLID
pen, I get the LogPen
structure filled in as I expect.
Any thoughts?
This is a bug in the CPen::GetExtLogPen implementation:
int CPen::GetExtLogPen(EXTLOGPEN* pLogPen) { return ::GetObject(m_hObject, sizeof(EXTLOGPEN), pLogPen); }
The implementation ignores the trailing variable-sized array of DWORD
s in the EXTLOGPEN structure. This structure is defined as:
typedef struct tagEXTLOGPEN { DWORD elpPenStyle; DWORD elpWidth; UINT elpBrushStyle; COLORREF elpColor; ULONG_PTR elpHatch; DWORD elpNumEntries; DWORD elpStyleEntry[1]; } EXTLOGPEN, *PEXTLOGPEN;
The call to CPen::GetExtLogPen
succeeds, if the elpStyleEntry array is at most one element long. This is true for all pens except for those pens that have the PS_USERSTYLE
pen style. When using the PS_USERSTYLE
pen style, the elpStyleEntry entry will have at least 2 entries.
The workaround in the case of pens with the PS_USERSTYLE
pen style is to use the Windows API GetObject call, and circumvent the MFC implementation:
// Query for required buffer size
int sizeRequired = ::GetObject(CustomPen.m_hObject, 0, nullptr);
// Allocate buffer (may not be properly aligned)
std::vector<byte> buffer(sizeRequired);
// Retrieve the entire EXTLOGPEN structure
int ret = ::GetObject(CustomPen.m_hObject, static_cast<int>(buffer.size()), buffer.data());
assert(ret == static_cast<int>(buffer.size());
// Cast to const ref for convenient access
const EXTLOGPEN& elp = *reinterpret_cast<const EXTLOGPEN*>(buffer.data());
Unfortunately, the solution posted by Mark Ransom doesn't solve the issue, because the CPen::GetExtLogPen
passes sizeof(EXTLOGPEN)
to the GetObject
call, not the true size of its argument.