From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- files/zh-cn/implementing_queryinterface/index.html | 172 +++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 files/zh-cn/implementing_queryinterface/index.html (limited to 'files/zh-cn/implementing_queryinterface/index.html') diff --git a/files/zh-cn/implementing_queryinterface/index.html b/files/zh-cn/implementing_queryinterface/index.html new file mode 100644 index 0000000000..6fe9761e22 --- /dev/null +++ b/files/zh-cn/implementing_queryinterface/index.html @@ -0,0 +1,172 @@ +--- +title: 实现QueryInterface接口 +slug: Implementing_QueryInterface +translation_of: Mozilla/Implementing_QueryInterface +--- +

本文档介绍正确地实现QueryInterface()的方式 

+ +

QueryInterface的参考实现

+ +
NS_IMETHODIMP
+nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
+  {
+    NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
+      // It's a logic error, not a runtime error, to call me without any place to put my answer!
+
+      // ...but that won't matter when someone calls me wrongly in a non-debug build.
+    if ( !aInstancePtr )
+      return NS_ERROR_NULL_POINTER;
+
+    nsISupports* foundInterface;
+
+    if ( aIID.Equals(nsCOMTypeInfo<nsIX>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIX*, this);
+    else if ( aIID.Equals(nsCOMTypeInfo<nsIY>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIY*, this);
+
+    // ...as many cases as needed...
+
+    else if ( aIID.Equals(nsCOMTypeInfo<nsISupports>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsISupports*, NS_STATIC_CAST(nsIX*, this));
+        // I (may) have multiple |nsISupports| in me,
+        //  so first I cast to a specific base to avoid ambiguity
+    else
+      foundInterface = 0;
+
+
+    nsresult status;
+    if ( !foundInterface )
+      status = NS_NOINTERFACE;
+    else
+      {
+        NS_ADDREF(foundInterface);
+        status = NS_OK;
+      }
+
+    *aInstancePtr = foundInterface;
+    return status;
+  }
+
+
+ +

有什么好处呢?

+ +

代码优点:

+ + + +

一些替代方案

+ +

NS_IMPL_QUERY_INTERFACE[012] 宏

+ +

除了nsISupports外,上面的示例还实现了两个{{ mediawiki.external('XP') }} COM接口。NS_IMPL_QUERY_INTERFACE2宏可以为您编写此函数(尽管我很难推荐宏),例如,

+ +
NS_IMPL_QUERY_INTERFACE2(nsMyImplementation, nsIX, nsIY)
+                                          // implements |nsMyImplementation::QueryInterface| as above
+
+NS_IMPL_QUERY_INTERFACE1(nsFoo, nsIFoo)   // |nsFoo::QueryInterface| provides |nsIFoo| and |nsISupports|
+NS_IMPL_QUERY_INTERFACE0(nsBar)           // |nsBar::QueryInterface| can only provide an |nsISupports|
+
+ +

同样,当仅实现一个附加接口时,可以使用宏NS_IMPL_QUERY_INTERFACE1;当仅实现nsISupports时,可以使用宏NS_IMPL_QUERY_INTERFACE0 。如果使用NS_IMPL_ISUPPORTS*宏,将为您调用这些宏,这些宏提供相应的QueryInterface 实现,以及AddRefRelease

+ +

调用继承的 QueryInterface

+ +

有时,您只是将一个或两个新接口添加到已经支持许多其他接口的实现中。在这种情况下,在测试了所关心的特定IID之后,您可能希望调用底层实现。这节省了代码空间并降低了复杂性。下面的代码突出显示了这些差异。

+ +
class nsMyImplmentation : public nsBaseImplementation, public nsIX, public nsIY { ... };
+
+NS_IMETHODIMP
+nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
+    /*
+      I just add the interfaces |nsIX| and |nsIY|.
+      My base class |nsBaseImplementation| provides all the rest.
+    */
+  {
+    NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
+
+    if ( !aInstancePtr )
+      return NS_ERROR_NULL_POINTER;
+
+    nsISupports* foundInterface;
+
+    if ( aIID.Equals(nsCOMTypeInfo<nsIX>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIX*, this);
+    else if ( aIID.Equals(nsCOMTypeInfo<nsIY>::GetIID()) )
+      foundInterface = NS_STATIC_CAST(nsIY*, this);
+    // Note: Don't check for |nsISupports|; |nsBaseImplementation| will do that for me.
+    else
+      foundInterface = 0;
+
+
+    nsresult status;
+    if ( !foundInterface )
+        // OK, _I_ didn't find an interface.  Maybe my base class can.
+      status = nsBaseImplementation::QueryInterface(aIID, &foundInterface);
+    else
+      {
+        NS_ADDREF(foundInterface);
+        status = NS_OK;
+      }
+
+    *aInstancePtr = foundInterface;
+    return status;
+  }
+
+ +

请注意,如果 base实现 的 QueryInterface找到了适当的接口,则你的QueryInterface 不能 AddRef 它。这反映在上面的代码中。
+
+ 这种技术之所以有效,是因为 nsBaseImplementation 已经是一个可以单独使用的完整类。当你从几个完整的类派生时,这种技术不太合适;但如果您对顺序敏感,则仍然可以使用它,例如,

+ +
// ...
+    nsresult status;
+    if ( !foundInterface )
+      {
+        // OK, ask |nsBase1Imp| first, because I want _it_ to be the one true |nsISupports|.
+        status = nsBase1Imp::QueryInterface(aIID, &foundInterface);
+
+        if ( !foundInterface )
+          status = nsBase2Imp::QueryInterface(aIID, &foundInterface);
+
+        if ( !foundInterface )
+          status = nsBase3Imp::QueryInterface(aIID, &foundInterface);
+      }
+    else
+      {
+        NS_ADDREF(foundInterface);
+        status = NS_OK;
+      }
+    // ...
+
+ +

如果您的任何基类参与真正的聚合,那么要把正确的事情做好 即使不是不可能,也是很困难的。您将不能在聚合对象上获得对 QueryInterface 的调用,这可能会返回错误的接口。这也是一个需要特别避免 聚合和复杂的层次结构 的原因。

+ +

NS_GET_IID 宏

+ +

您可以使用·NS_GET_IID 宏,而不是键入完整的GetIID 表达式。一般来说,我不赞成宏,除非在不同的情况下宏必须扩展为不同的文本,例如,不同的平台,调试与非调试,等等。在这种情况下,宏是必不可少的。在其他情况下,宏可能会帮助一些人,但通常会给其他人带来模糊的问题。它们总是使程序源更加脆弱。在这种情况下,宏只是为了方便起见,所以我不推荐它,但我确实提供了它作为替代。

+ +
// ...
+    if ( aIID.Equals(NS_GET_IID(nsIX)) )
+      foundInterface = NS_STATIC_CAST(nsIX*, this);
+    else if ( aIID.Equals(NS_GET_IID(nsIY)) )
+      foundInterface = NS_STATIC_CAST(nsIY*, this);
+
+    // ...as many cases as needed...
+
+    else if ( aIID.Equals(NS_GET_IID(nsISupports)) )
+    // ...
+ +

Thanks

+ +

特别感谢 Heikki ToivonenChris WatersonJohn Bandhauer 提供的宝贵反馈,这些反馈极大地改进了这里提供的实现

-- cgit v1.2.3-54-g00ecf