特性编程常见问题

什么是 HRESULT?

HRESULT 是一种简单的数据类型,通常被特性和 ATL 作为返回值使用。 下表对各种值进行了说明。 更多的值包含在头文件 winerror.h 中。

名称 描述
S_OK 操作成功 0x00000000
E_UNEXPECTED 意外失败 0x8000FFFF
E_NOTIMPL 未实现 0x80004001
E_OUTOFMEMORY 无法分配必要的内存 0x8007000E
E_INVALIDARG 一个或多个参数无效 0x80070057
E_NOINTERFACE 不支持此类接口 0x80004002
E_POINTER 无效指针 0x80004003
E_HANDLE 无效句柄 0x80070006
E_ABORT 操作已中止 0x80004004
E_FAIL 未知故障 0x80004005
E_ACCESSDENIED 一般性的“访问被拒”错误 0x80070005

何时必须为特性指定参数名称?

在大多数情况下,如果特性具有单个参数,则会为该参数命名。 在代码中插入特性时不需要此名称。 例如,可聚合特性的以下用法:

[coclass, aggregatable(value=allowed)]
class CMyClass
{
// The class declaration
};

与以下用法完全相同:

[coclass, aggregatable(allowed)]
class CMyClass
{
// The class declaration
};

但是,以下特性具有单个未命名的参数:

是否可以在特性块中使用注释?

可以在特性块内使用单行和多行注释。 但是,在括住特性参数的括号内,不能使用任何一种注释样式。

允许以下操作:

[ coclass, progid("MyClass.CMyClass.1"), /* Multiple-line
                                       comment */
   threading("both") // Single-line comment
]

不允许以下操作:

[ coclass, progid("MyClass.CMyClass.1" /* Multiple-line comment */ ), threading("both" // Single-line comment)
]

特性如何与继承交互?

可以从其他类继承已特性化的类和未特性化的类,这些类本身可能已特性化,也可能未特性化。 从特性化类派生的结果与在特性提供程序转换代码后从该类派生的结果相同。 特性不会通过 C++ 继承传输到派生类。 特性提供程序只对其特性附近的代码进行转换。

如何在非特性化 ATL 项目中使用特性?

你可能有一个非特性化的 ATL 项目,其中包含一个 .idl 文件,并且你可能想要开始添加特性化对象。 在这种情况下,请使用“添加类向导”提供代码。

如何在特性化项目中使用 .idl 文件?

你可能有一个要在 ATL 特性化项目中使用的 .idl 文件。 在这种情况下,你将使用 importidl 特性,将 .idl 文件编译为 .h 文件(参阅项目“属性页”对话框中的“MIDL 属性页”),然后将 .h 文件包含在项目中。

是否可以修改由特性注入的代码?

一些特性将代码注入项目。 你可以使用 /Fx 编译器选项查看已注入的代码。 还可以从已注入的文件复制代码,并将其粘贴到源代码中。 这样,就可以修改特性的行为了。 但是,你可能还需要修改代码的其他部分。

以下示例是将注入的代码复制到源代码文件中的结果:

// attr_injected.cpp
// compile with: comsupp.lib
#define _ATL_ATTRIBUTES 1
#include <atlbase.h>
#include <atlcom.h>

[ module(name="MyLibrary") ];

// ITestTest
[
   object, uuid("DADECE00-0FD2-46F1-BFD3-6A0579CA1BC4"), dual, helpstring("ITestTest Interface"), pointer_default(unique)
]

__interface ITestTest : IDispatch {
   [id(1), helpstring("method DoTest")]
   HRESULT DoTest([in] BSTR str);
};

// _ITestTestEvents
[
   uuid("12753B9F-DEF4-49b0-9D52-A79C371F2909"), dispinterface, helpstring("_ITestTestEvents Interface")
]

__interface _ITestTestEvents {
   [id(1), helpstring("method BeforeChange")] HRESULT BeforeChange([in] BSTR str, [in,out] VARIANT_BOOL* bCancel);
};

// CTestTest
[
   coclass, threading(apartment), vi_progid("TestATL1.TestTest"), progid("TestATL1.TestTest.1"), version(1.0), uuid("D9632007-14FA-4679-9E1C-28C9A949E784"), // this line would be commented out from original file
   // event_source("com"), // this line would be added to support injected code
   source(_ITestTestEvents), helpstring("TestTest Class")
]

class ATL_NO_VTABLE CTestTest : public ITestTest,
// the following base classes support added injected code
public IConnectionPointContainerImpl<CTestTest>,
public IConnectionPointImpl<CTestTest, &__uuidof(::_ITestTestEvents), CComDynamicUnkArray>
{
public:
   CTestTest() {
   }
   // this line would be commented out from original file
   // __event __interface _ITestTestEvents;
   DECLARE_PROTECT_FINAL_CONSTRUCT()
   HRESULT FinalConstruct() {
      return S_OK;
   }

void FinalRelease() {}

public:
   CComBSTR m_value;
   STDMETHOD(DoTest)(BSTR str) {
      VARIANT_BOOL bCancel = FALSE;
      BeforeChange(str,&bCancel);
      if (bCancel) {
          return Error("Error : Someone don't want us to change the value");
      }

   m_value =str;
   return S_OK;
    }
// the following was copied in from the injected code.
HRESULT BeforeChange(::BSTR i1,::VARIANT_BOOL* i2) {
   HRESULT hr = S_OK;
   IConnectionPointImpl<CTestTest, &__uuidof(_ITestTestEvents), CComDynamicUnkArray>* p = this;
   VARIANT rgvars[2];
   Lock();
   IUnknown** pp = p->m_vec.begin();
   Unlock();
   while (pp < p->m_vec.end()) {
      if (*pp != NULL) {
         IDispatch* pDispatch = (IDispatch*) *pp;
         ::VariantInit(&rgvars[1]);
         rgvars[1].vt = VT_BSTR;
         V_BSTR(&rgvars[1])= (BSTR) i1;
         ::VariantInit(&rgvars[0]);
         rgvars[0].vt = (VT_BOOL | VT_BYREF);
         V_BOOLREF(&rgvars[0])= (VARIANT_BOOL*) i2;
         DISPPARAMS disp = { rgvars, NULL, 2, 0 };
         VARIANT ret_val;
         hr = __ComInvokeEventHandler(pDispatch, 1, 1, &disp, &ret_val);
         if (FAILED(hr))
            break;
      }
      pp++;
   }
   return hr;
}

BEGIN_CONNECTION_POINT_MAP(CTestTest)
CONNECTION_POINT_ENTRY(__uuidof(::_ITestTestEvents))
END_CONNECTION_POINT_MAP()
// end added code section

// _ITestCtrlEvents Methods
public:
};

int main() {}

如何对特性化接口进行前向声明?

如果要对特性化接口进行前向声明,必须将应用于实际接口声明的相同特性应用于前向声明。 此外,还必须将导出特性应用于前向声明。

是否可以对从同样使用特性的类派生的类使用特性?

不可以,不支持对从同样使用特性的类派生的类使用特性。