为什么GetLogPen失败了自定义笔?

时间:2016-05-23 02:42:08

标签: windows mfc gdi

GetLogPen()GetExtLogPen()的调用都失败了。它们返回零......

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结构。

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

这是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());

不幸的是,solution posted by Mark Ransom无法解决问题,因为CPen::GetExtLogPensizeof(EXTLOGPEN)传递给GetObject调用,而不是其参数的真实大小。

<小时/> 注意:bug report已提交给Microsoft Connect。

答案 1 :(得分:0)

EXTLOGPEN结构仅为一个样式参数定义空间,但最多允许16个。目前还不清楚GetExtLogPen如何确定可用空间的大小。试试这个,看看它是否有帮助。

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

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

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