C++ 표준 라이브러리 기반 컬렉션 구현

ATL은 개체에서 ICollectionOnSTLImpl C++ 표준 라이브러리 기반 컬렉션 인터페이스를 신속하게 구현할 수 있도록 하는 인터페이스를 제공합니다. 이 클래스의 작동 방식을 이해하려면 이 클래스를 사용하여 Automation 클라이언트를 대상으로 하는 읽기 전용 컬렉션을 구현하는 간단한 예제(아래)를 통해 작업합니다.

샘플 코드는 ATLCollections 샘플에서 가져옵니다.

이 절차를 완료하려면 다음을 수행합니다.

새 단순 개체 생성

애플리케이션 설정 아래의 특성 상자가 지워지도록 새 프로젝트를 만듭니다. ATL 클래스 추가 대화 상자와 단순 개체 추가 마법사를 사용하여 호출 Words된 단순 개체를 생성합니다. 호출 IWords 된 이중 인터페이스가 생성되었는지 확인합니다. 생성된 클래스의 개체는 단어 컬렉션(즉, 문자열)을 나타내는 데 사용됩니다.

IDL 파일 편집

이제 아래와 같이 IDL 파일을 열고 읽기 전용 컬렉션 인터페이스로 전환하는 IWords 데 필요한 세 가지 속성을 추가합니다.

[
   object,
   uuid(7B3AC376-509F-4068-87BA-03B73ADC359B),
   dual,                                                    // (1)
   nonextensible,                                           // (2)
   pointer_default(unique)
]
interface IWords : IDispatch
{
   [id(DISPID_NEWENUM), propget]                            // (3)
   HRESULT _NewEnum([out, retval] IUnknown** ppUnk);

   [id(DISPID_VALUE), propget]                              // (4)
   HRESULT Item([in] long Index, [out, retval] BSTR* pVal); // (5)

   [id(0x00000001), propget]                                // (6)
   HRESULT Count([out, retval] long* pVal);

};

Automation 클라이언트를 염두에 두고 디자인된 읽기 전용 컬렉션 인터페이스의 표준 양식입니다. 이 인터페이스 정의의 번호가 매겨진 주석은 아래 주석에 해당합니다.

  1. Automation 클라이언트는 을 통해 IDispatch::Invoke속성에 _NewEnum 액세스하기 때문에 컬렉션 인터페이스는 일반적으로 이중입니다. 그러나 Automation 클라이언트는 vtable을 통해 다시 기본 메서드에 액세스할 수 있으므로 이중 인터페이스가 dispinterfaces보다 좋습니다.

  2. 이중 인터페이스 또는 dispinterface가 런타임에 확장되지 않는 경우(즉, 추가 메서드 또는 속성을 통해 IDispatch::Invoke제공하지 않음) 정의에 사용할 수 없는 특성을 적용해야 합니다. 이 특성을 사용하면 Automation 클라이언트가 컴파일 시간에 전체 코드 확인을 수행할 수 있습니다. 이 경우 인터페이스를 확장하면 안 됩니다.

  3. Automation 클라이언트가 이 속성을 사용할 수 있도록 하려면 올바른 DISPID가 중요합니다. (DISPID_NEWENUM 밑줄이 하나만 있습니다.)

  4. 모든 값을 속성의 Item DISPID로 제공할 수 있습니다. 그러나 Item 일반적으로 DISPID_VALUE 사용하여 컬렉션의 기본 속성으로 만듭니다. 이렇게 하면 Automation 클라이언트가 명시적으로 이름을 지정하지 않고 속성을 참조할 수 있습니다.

  5. 속성의 Item 반환 값에 사용되는 데이터 형식은 COM 클라이언트와 관련된 한 컬렉션에 저장된 항목의 형식입니다. 인터페이스는 문자열을 반환하므로 표준 COM 문자열 형식인 BSTR을 사용해야 합니다. 곧 표시되는 것처럼 내부적으로 다른 형식으로 데이터를 저장할 수 있습니다.

  6. 속성의 DISPID에 Count 사용되는 값은 완전히 임의입니다. 이 속성에 대한 표준 DISPID는 없습니다.

스토리지 및 노출에 대한 Typedef 만들기

컬렉션 인터페이스가 정의되면 데이터를 저장하는 방법과 열거자를 통해 데이터가 노출되는 방법을 결정해야 합니다.

이러한 질문에 대한 답변은 새로 만든 클래스의 헤더 파일 맨 위에 추가할 수 있는 여러 typedef 형식으로 제공될 수 있습니다.

// Store the data in a vector of std::strings
typedef std::vector< std::string >         ContainerType;

// The collection interface exposes the data as BSTRs
typedef BSTR                               CollectionExposedType;
typedef IWords                             CollectionInterface;

// Use IEnumVARIANT as the enumerator for VB compatibility
typedef VARIANT                            EnumeratorExposedType;
typedef IEnumVARIANT                       EnumeratorInterface;

이 경우 std::strings의 std::vector데이터를 저장합니다. std::vector 는 관리되는 배열처럼 동작하는 C++ 표준 라이브러리 컨테이너 클래스입니다. std::string 은 C++ 표준 라이브러리의 문자열 클래스입니다. 이러한 클래스를 사용하면 문자열 컬렉션을 쉽게 작업할 수 있습니다.

Visual Basic 지원은 이 인터페이스의 성공에 매우 중요하므로 속성에서 반환된 _NewEnum 열거자는 인터페이스를 IEnumVARIANT 지원해야 합니다. Visual Basic에서 이해하는 유일한 열거자 인터페이스입니다.

정책 클래스 복사에 대한 Typedefs 만들기

지금까지 만든 typedefs는 열거자 및 컬렉션에서 사용할 복사 클래스에 대한 추가 typedef를 만드는 데 필요한 모든 정보를 제공합니다.

// Typedef the copy classes using existing typedefs
typedef VCUE::GenericCopy<EnumeratorExposedType, ContainerType::value_type> EnumeratorCopyType;
typedef VCUE::GenericCopy<CollectionExposedType, ContainerType::value_type> CollectionCopyType;

이 예제에서는 ATLCollections 샘플에서 VCUE_Copy.h 및 VCUE_CopyString.h에 정의된 사용자 지정 GenericCopy 클래스를 사용할 수 있습니다 . 다른 코드에서 이 클래스를 사용할 수 있지만 사용자 고유의 GenericCopy 컬렉션에 사용되는 데이터 형식을 지원하기 위한 추가 특수화를 정의해야 할 수도 있습니다. 자세한 내용은 ATL 복사 정책 클래스를 참조 하세요.

열거형 및 컬렉션에 대한 Typedef 만들기

이제 이 상황에 대한 클래스 및 ICollectionOnSTLImpl 특수화 CComEnumOnSTL 에 필요한 모든 템플릿 매개 변수가 typedef 형식으로 제공되었습니다. 특수화 사용을 간소화하려면 아래와 같이 두 개의 typedef를 더 만듭니다.

typedef CComEnumOnSTL< EnumeratorInterface, &__uuidof(EnumeratorInterface), EnumeratorExposedType, EnumeratorCopyType, ContainerType > EnumeratorType;
typedef ICollectionOnSTLImpl< CollectionInterface, ContainerType, CollectionExposedType, CollectionCopyType, EnumeratorType > CollectionType;

이제 CollectionType 앞에서 정의한 인터페이스를 구현 IWords 하고 지원하는 열거자를 제공하는 특수화 ICollectionOnSTLImpl 의 동의어입니다IEnumVARIANT.

마법사에서 생성된 코드 편집

이제 아래와 같이 typedef가 IWords아닌 typedef로 표현되는 CollectionType 인터페이스 구현에서 파생 CWords 해야 합니다.

class ATL_NO_VTABLE CWords :
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass<CWords, &CLSID_Words>,
   // 'CollectionType' replaces 'IWords' in next line
   public IDispatchImpl<CollectionType, &IID_IWords, &LIBID_NVC_ATL_COMLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
DECLARE_REGISTRY_RESOURCEID(IDR_WORDS)


BEGIN_COM_MAP(CWords)
   COM_INTERFACE_ENTRY(IWords)
   COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

// Remainder of class declaration omitted.

컬렉션을 채우는 코드 추가

다시 기본 유일한 방법은 벡터를 데이터로 채우는 것입니다. 이 간단한 예제에서는 클래스에 대한 생성자의 컬렉션에 몇 단어를 추가할 수 있습니다.

CWords()
{
    m_coll.push_back("this");
    m_coll.push_back("is");
    m_coll.push_back("a");
    m_coll.push_back("test");
}

이제 선택한 클라이언트를 사용하여 코드를 테스트할 수 있습니다.

참고 항목

컬렉션 및 열거자
ATLCollections 샘플
ATL 복사 정책 클래스