为什么使用自定义笔时 GetLogPen 失败?

Why does GetLogPen fail with a custom pen?

GetLogPen()GetExtLogPen()的调用都失败了。他们 return 零...

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;
}

失败似乎是因为 PS_USERSTYLE 被用于钢笔样式。如果我用 PS_SOLID 笔执行此操作,我会按预期填充 LogPen 结构。

有什么想法吗?

EXTLOGPEN 结构只为一个样式参数定义了 space,但最多允许 16 个。不清楚 GetExtLogPen 如何确定有多少 space 可用。试试这个,看看是否有帮助。

struct EXTLOGPEN16 : public EXTLOGPEN
{
    DWORD elpStyleMore[15];

public:
    EXTLOGPEN16()
    {
        elpNumEntries = 16;
    }
};

EXTLOGPEN16 ExtLogPen;
if (CustomPen->GetExtLogPen(&ExtLogPen) == 0)
    // ...

这是 CPen::GetExtLogPen 实现中的错误:

int CPen::GetExtLogPen(EXTLOGPEN* pLogPen) {
    return ::GetObject(m_hObject, sizeof(EXTLOGPEN), pLogPen);
}

该实现忽略了 EXTLOGPEN 结构中 DWORD 的尾部可变大小数组。此结构定义为:

typedef struct tagEXTLOGPEN {
    DWORD     elpPenStyle;
    DWORD     elpWidth;
    UINT      elpBrushStyle;
    COLORREF  elpColor;
    ULONG_PTR elpHatch;
    DWORD     elpNumEntries;
    DWORD     elpStyleEntry[1];
} EXTLOGPEN, *PEXTLOGPEN;

如果 elpStyleEntry 数组的长度最多为一个元素,则对 CPen::GetExtLogPen 的调用会成功。除了那些具有 PS_USERSTYLE 钢笔样式的钢笔外,所有钢笔都是如此。使用 PS_USERSTYLE 笔样式时,elpStyleEntry 条目将至少有 2 个条目。

使用 PS_USERSTYLE 钢笔样式的解决方法是使用 Windows API GetObject 调用,并绕过 MFC 实现:

// 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());

不幸的是, 没有解决问题,因为 CPen::GetExtLogPensizeof(EXTLOGPEN) 传递给 GetObject 调用,而不是其参数的真实大小。


注意:bug report 已提交至 Microsoft Connect。