65.9K
CodeProject 正在变化。 阅读更多。
Home

让(ATL)ActiveX 控件从 Office 应用程序打印

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (8投票s)

2002年11月27日

CPOL

5分钟阅读

viewsIcon

71734

让 ActiveX 控件在 Office 应用程序中可打印可能很困难。本文将向您展示原因以及如何解决此问题。

引言

既然这是我第一次在 CodeProject 上发帖,请允许我简单介绍一下我是谁以及我做什么。我一直都在从事 C 和 C++ 的相关工作(以及我接触过的其他各种语言)。如今,我大部分的开发工作都集中在 Microsoft Windows 平台,并使用 VC6、VC2002.NET 进行开发。我深度参与 BI(商业智能)开发,并在业余时间开发一些小型 ActiveX 控件和游戏等。

起因

在学习 COM 一段时间后,我自然而然地转向了 ATL,以简化样板代码的开发,并利用微软的模板库。随着经验的增长,我开始使用 ATL 框架创建 ActiveX 控件……生活很美好。我能在短时间内快速开发出相当实用的(尽管不太复杂)控件。最近,我被要求创建一个 KPI(关键绩效指标)控件,该控件可以嵌入到网页以及 Excel 文档中。显而易见,基于我的经验(显然经验不足),我认为这不会有问题,于是就开始着手编写满足功能规范的代码(我们不都这样做吗?)。

几天后,控件开发完毕,正在进行最终测试时,有人要求我打印一个包含嵌入式控件的示例电子表格的硬拷贝。这就是我噩梦的开始。我的控件不仅无法打印,而且也无法清楚地知道为什么无法打印。于是,我开始探索这个显而易见的谜团。

您是否尝试过将第三方 ActiveX 控件集成到 Office 文档中?它们似乎工作正常,但大多数(除微软控件外)在您请求打印预览或简单打印工作表或文档时,似乎都无法自行渲染。所以,如果您曾经遇到过这个问题,或者从未接触过,但认为自己可能将要面临这个问题,请注意这一点,因为它可能会为您节省数小时的挫败感和在 MSDNGoogle 上疯狂搜索的时间。

那么现在怎么办?

首先需要认识到的是,尽管我们拥有 Office 2000 和 Office XP,但打印架构仍然使用旧的Windows 格式图元文件进行打印操作。这种图元文件格式曾用于 16 位 Windows 应用程序(回想一下 Win3.1)。现在,这对于希望其控件能在 Office 应用程序中打印的 ActiveX 开发者来说是一个大问题,因为这种旧的图元文件格式仅支持有限的 GDI 功能。支持的 GDI 函数列表可以在 这里找到。

现在您已经了解了有限的函数集,您可能会痛苦地意识到,您无法再创建内存 DC,无法再使用您喜欢的 DrawText() 函数,当然也无法再调用 GetTextExtentPoint32() 函数。但是,这些限制仅适用于将您的控件渲染到旧格式图元文件的情况。那么,我们如何让我们的控件知道它正在被渲染到旧格式图元文件呢?很简单,我们使用 GetObjectType() 函数并检查结果是否等于 OBJ_METADC(旧图元文件格式)。

HRESULT Cxxxxx::OnDraw(ATL_DRAWINFO& di)
{
    HDC hdc = di.hdcDraw;
    bool bMetaFile = false;

    //
    // lets check if we're drawing to an old
    // metafile format.. (like Office printing)
    //

    if ( GetObjectType(hdc) == OBJ_METADC )
        bOldMetaFile = true;

    //
    // the rest of your code...
    //
}

有趣的是,OBJ_METADC 的对立面是 OBJ_ENHMETADC(请参阅 此 MSDN 文档)。

现在我们知道了是否正在绘制到旧图元文件格式,我们可以编写自适应代码来处理每种情况,或者我们也可以只使用旧图元文件 DC 所支持的有限功能集来编写所有的绘图逻辑。

关于字体和文本宽度?

任何 ATL ActiveX 开发者都知道,在 AX 控件中使用字体带来的乐趣有限。典型的代码可能看起来像这样

    //
    // ... some code
    //
    CComQIPtr<IFont, &IID_IFont> pFont(m_pFont);
    TEXTMETRICOLE tm;
    if ( pFont != NULL )
    {
        pFont->get_hFont(&newFont);
        pFont->AddRefHfont(newFont);
        pFont->QueryTextMetrics(&tm);
        oldFont = (HFONT) SelectObject(dc, newFont);
    }

加粗的代码行是我不经常使用的,因为我实际上不需要了解我字体细节的细分,因为我可以使用 GetTextExtentPoint32() 函数。不幸的是,在这种情况下,我们无法使用该函数来确定文本的宽度(以像素为单位)。但是,还有另一种方法可以相对准确地计算出来,如下面的代码所示

//
// assume that we have called QueryTextMetrics() and
// have a filled TEXTMETRICOLE structure called tm
//
CComBSTR strText(_T("Hello, world"));
SIZE sz;

sz.cx = strText.Length() * tm.tmAveCharWidth;
sz.cy = tm.tmHeight;

话虽如此,如果我想让我的 ActiveX 控件能够被 Office 打印,还有许多其他我经常使用的函数我不能用。但就像 GetTextExtentPoint32() 及其对应的替代函数一样,总有一种方法可以使用“旧图元文件安全绘图代码”(Old-Metafile-Safe-Drawing-Code,OMSDC)来替换这些函数。*也许这个缩写会流行起来*

结论

在创建您知道将在 Office 应用程序中使用并且很可能被打印的 ActiveX 控件时,请记住在开发绘图逻辑时遵循这些准则。在我搜索如何在 Office 应用程序中启用我的 ActiveX 控件打印的信息时,MSDN 和网络上可用的信息量非常少,这让我感到相当震惊。有数百篇关于 ActiveX 控件在 Internet Explorer 中打印的文档,但没有一篇解决了这个特定问题。也许我找错了地方。希望这篇文章有一天能帮助到您中的一两人;)

致谢

非常感谢 Igor Tandetnik 在这方面给我指明了方向。

© . All rights reserved.