【问题标题】:Why does GetLogPen fail with a custom pen?为什么 GetLogPen 使用自定义笔失败?
【发布时间】:2016-09-19 19:08:08
【问题描述】:

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

有什么想法吗?

【问题讨论】:

    标签: windows mfc gdi


    【解决方案1】:

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

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

    该实现忽略DWORDs 结构中DWORDs 的尾随可变大小数组。这个结构定义为:

    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::GetExtLogPen 将sizeof(EXTLOGPEN) 传递给GetObject 调用,而不是其参数的真实大小。


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

    【讨论】:

      【解决方案2】:

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

      struct EXTLOGPEN16 : public EXTLOGPEN
      {
          DWORD elpStyleMore[15];
      
      public:
          EXTLOGPEN16()
          {
              elpNumEntries = 16;
          }
      };
      
      EXTLOGPEN16 ExtLogPen;
      if (CustomPen->GetExtLogPen(&ExtLogPen) == 0)
          // ...
      

      【讨论】:

      • 这个建议让我想起了将大小值作为第一个条目的数据结构。我试过这个,但没有运气。我还尝试使用样式数组的单个值并且它有效!显然,为此编写代码的 MS 程序员决定,如果他们的线条有更复杂的 dash/dot/gap 样式,则不需要该函数工作。
      • GetExtLogPen 大概调用了GetObject。 GetObject 返回复制到返回缓冲区的字节数。该值隐含地告诉您使用的值的数量。 @DavidRector:这是可变大小结构中使用的一种非常常见的模式。尾随的一号数组是编译器产生正确对齐所必需的。这解决了与您所说的问题不同的问题;带有显式大小字段作为其第一个条目的结构对于实现版本控制很有用。
      • @IInspectable,我熟悉我编写的旧 Windows 代码中的可变大小结构模式。 GetExtLogPen 不接受结构大小值,因此只能在样式数组中有零个或一个条目时返回数据。我现在意识到我可以自己打电话给GetObject,我怀疑它会按需要工作。如果您将GetObject 建议作为答案发布,我会尝试然后将其标记为答案,因为很明显GetExtLogPen 无法处理结构中的多个样式元素。
      • 为什么说它最多允许16个?查看文档,除了条目数必须适合DWORD(因此它可以适合elpNumEntries)之外,我看不到长度的任何上限。我要求不要学究,但因为我只是在包装类中编写了一个检索函数,并且不得不使用动态分配和智能指针,如果我可以确定永远不会有更多,我宁愿不这样做16 个条目,可以改用您的方法。
      • @CodyGray 如果您查看ExtCreatePen 的dwStyleCount 参数,您会发现它被限制为16 个。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多