我最近遇到了COM inproc服务器的问题。
我有一个实现MCanvasFont
接口的ICanvasFont
COM对象:
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
对象的方法。最好的方法是什么?
答案 0 :(得分:2)
另一种方式似乎是从ICanvasFont转换为MCanvasFont。
一种COM友好的方法是定义一个私有GUID,它“按原样”表示你的类:让MCanvasFont在其QI中通过返回一个非转换的'this'来响应它。然后,MCanvas可以使用此私有GUID的QI来检查ICanvasFont是否真的是MCanvasFont;如果QI成功,它现在有一个正确的MCanvasFont可以玩。
这允许第三方代码获取并传递他们从您那里获得的对象作为不透明的COM对象,但是当您获得自己的一个对象时,您可以确定它实际上是“您自己的一个”,然后访问适当的内部。
这种技术的好处是它在不受支持的场景中优雅地失败 - 您可以将其视为一种安全的COM样式转换,使您从COM的不可知世界回到C ++世界:
如果第三方试图自己实现ICanvasFont,那么当然它将无法通过QI测试,因为它们不会实现私有GUID,所以你会知道拒绝它,这是合适的,因为它听起来像您的代码使用此接口作为库的功能的抽象,而不是作为可扩展点。
如果您收到一个远程对象的ICanvasFont,QI将失败,因为COM远程处理基础结构将无法找到您的私有GUID的代理/存根信息,如果您不查看,这可能也是合适的在这种情况下提供远程处理或跨公寓/多线程支持。
...或者至少就是如果你有一个简单的GUI / STA-flavor对象可以在单个线程(单元)中使用它的方式。 如果你允许你的接口被封送到一个进程中的不同公寓,你需要在上面的一般主题上做更多的工作,因为你可能真的想要支持被传递一个ICanvasFont来源在花药公寓。你仍然可以使用带有私有GUID的QI来询问对象是否是“你的一个”,但现在你需要QI到一个也有编组支持的中间私有接口,然后有一个方法你可以隧道原始指针通过(并且,为了偏执,请检查对象是否在同一个进程中!);一旦你这样做,你需要知道你从那里开始承担所有的编组/线程责任。但考虑到你的问题的GUI风格,我猜这可能不是这里的情况。
答案 1 :(得分:1)
也许我错过了什么。难道你不能简单地将MCanvas声明为MCanvasFont中的友元类,然后在MCanvas :: DrawText中将ICanvasFont指针向下转换为MCanvasFont吗?
应该注意,您正在使用的ICanvasFont指针可能不是MCanvasFont类型。如果你使用dynamic_cast
进行向下转换,只有在你给出的ICanvasFont指针来自与MCanvas实例相同的进程内服务器的情况下,这才会成功。这是我能看到的唯一情况,即你想做的事情甚至是可能的。但是在尝试访问FontDelegate之前,您应该检测并验证您是否在此上下文中运行。