1

我最近遇到了 COM inproc 服务器的问题。

我有一个实现接口的MCanvasFontCOM 对象:ICanvasFont

ICanvasFont : public IUnknown
{
    virtual HRESULT STDMETHODCALLTYPE Create(
        /* [in] */ BSTR bstrFamily,
        /* [in] */ double fSize) = 0;

    virtual HRESULT STDMETHODCALLTYPE SetSize(
        /* [retval][out] */ double fSize) = 0;
};

这些MCanvasFont对象持有一个不透明的指针,指向一个FontDelegate实现实际字体操作的对象:

class MCanvasFont : public ICanvasFont
{
public:
    MCanvasFont();
    virtual ~MCanvasFont();

    virtual HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppv);
    virtual ULONG   STDMETHODCALLTYPE AddRef();
    virtual ULONG   STDMETHODCALLTYPE Release();

    virtual HRESULT STDMETHODCALLTYPE Create(
        /* [in] */ BSTR bstrFamily,
        /* [in] */ double fSize);

    virtual HRESULT STDMETHODCALLTYPE SetSize(
        /* [in] */ double fSize);

protected:
    class FontDelegate;
    const std::unique_ptr<FontDelegate> m_font; // opaque pointer

    // Reference count
    long m_cRef;
};

还有一个MCanvas实现ICanvas接口的类。该ICanvas接口具有SetFont(ICanvasFont* pFont)设置当前字体的抽象方法和DrawText在画布上绘制文本的方法:

class MCanvas : public ICanvas
{
public:
    MCanvas();
    virtual ~MCanvas();

    virtual HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppv);
    virtual ULONG   STDMETHODCALLTYPE AddRef();
    virtual ULONG   STDMETHODCALLTYPE Release();

    STDMETHODDECL GetFont(
        /* [retval][out] */ ICanvasFont** ppFont);
    STDMETHODDECL SetFont(
        /* [in] */ ICanvasFont* pFont);

    // ...

    STDMETHODDECL DrawText(
        /* [in] */ double x,
        /* [in] */ double y,
        /* [in] */ BSTR text);

protected:
    COMAutoPtr<ICanvasPen>     m_pen;
    COMAutoPtr<ICanvasBrush>   m_brush;
    COMAutoPtr<ICanvasFont>    m_font;

    // Reference count
    long m_cRef;
};

我需要一种从方法访问FontDelegate对象的MCanvas::DrawText方法。最好的方法是什么?

4

2 回答 2

2

另一种方法似乎是从 ICanvasFont 转换为 MCanvasFont。

一种对 COM 友好的方法是定义一个私有 GUID 来“按原样”表示您的类:让 MCanvasFont 在其 QI 中通过返回一个未强制转换的“this”来响应它。然后,MCanvas 可以使用带有这个私有 GUID 的 QI 来检查 ICanvasFont 是否真的是 MCanvasFont;如果 QI 成功,它现在可以使用正确投射的 MCanvasFont。

这允许第 3 方代码以不透明 COM 对象的形式获取和传递他们从您那里获得的对象,但是当您取回您自己的对象之一时,您可以确定它实际上是“您自己的”,然后适当地访问其内部.

这种技术的好处是它在不受支持的场景中优雅地失败 - 您可以将其视为一种安全的 COM 样式转换,将您从 COM 的不可知世界带回 C++ 的世界:

如果第 3 方尝试自己实现 ICanvasFont,那么它当然会通过 QI 测试,因为他们不会实现私有 GUID,所以你会知道拒绝它,这是合适的,因为听起来你的代码是使用此接口作为库功能的抽象,而不是作为扩展点。

如果您收到一个远程对象的 ICanvasFont,QI 将失败,因为 COM 远程处理基础设施将无法为您的私有 GUID 找到代理/存根信息,如果您不考虑提供远程处理,这可能也是合适的或在这种情况下支持跨单元/多线程。


...或者至少如果您有一个期望在单个线程(公寓)中使用的简单 GUI/STA 风格对象,它可以工作。如果你允许你的接口在一个进程中被编组到不同的单元,你需要在上面的一般主题上做更多的工作,因为你实际上可能想要支持一个源自另一个单元的 ICanvasFont。您仍然可以使用带有私有 GUID 的 QI 来询问该对象是否是“您的一个”,但现在您需要 QI 到一个也具有编组支持的中间私有接口,然后有一个方法,您可以隧道原始指针通过(并且,为了偏执,检查对象是否在同一个进程中!);一旦你这样做了,你需要知道你从那里开始承担所有编组/线程的责任。但考虑到您问题的 GUI 风格性质,我猜这里可能不是这种情况。

于 2012-06-01T07:43:15.970 回答
1

也许我错过了一些东西。您不能简单地将 MCanvas 声明为 MCanvasFont 中的友元类,然后将 ICanvasFont 指针向下转换为 MCanvas::DrawText 中的 MCanvasFont 吗?

应该注意的是,您正在使用的 ICanvasFont 指针可能不是 MCanvasFont 类型。如果您使用dynamic_cast向下转换,这应该只在您获得的 ICanvasFont 指针来自与您的 MCanvas 实例相同的进程内服务器的情况下才能成功。这是我能看到的唯一一种情况,你正在尝试做的事情甚至是可能的。但是在尝试访问 FontDelegate 之前,您应该检测并验证您是否在此上下文中操作。

于 2012-05-29T00:41:02.540 回答