공급자의 책갈피 지원

이 항목의 예제에서는 클래스에 IRowsetLocate 인터페이스를 추가합니다 CCustomRowset . 거의 모든 경우에 기존 COM 개체에 인터페이스를 추가하여 시작합니다. 그런 다음 소비자 템플릿에서 더 많은 호출을 추가하여 테스트할 수 있습니다. 이 예제에서는 다음 방법을 보여 줍니다.

  • 공급자에 인터페이스를 추가합니다.

  • 소비자에게 반환할 열을 동적으로 결정합니다.

  • 책갈피 지원을 추가합니다.

IRowsetLocate 인터페이스는 IRowset 인터페이스에서 상속됩니다. 인터페이스를 IRowsetLocate 추가하려면 IRowsetLocateImpl에서 상속합니다CCustomRowset.

인터페이스를 IRowsetLocate 추가하는 것은 대부분의 인터페이스와 약간 다릅니다. VTABLE를 정렬하기 위해 OLE DB 공급자 템플릿에는 파생된 인터페이스를 처리하는 템플릿 매개 변수가 있습니다. 다음 코드는 새 상속 목록을 보여 줍니다.

////////////////////////////////////////////////////////////////////////
// CustomRS.h

// CCustomRowset
class CCustomRowset : public CRowsetImpl< CCustomRowset,
      CTextData, CCustomCommand, CAtlArray<CTextData>,
      CSimpleRow,
          IRowsetLocateImpl<CCustomRowset, IRowsetLocate>>

네 번째, 다섯 번째 및 여섯 번째 매개 변수가 모두 추가됩니다. 이 예제에서는 네 번째 및 다섯 번째 매개 변수의 기본값을 사용하지만 여섯 번째 매개 변수로 지정 IRowsetLocateImpl 합니다. IRowsetLocateImpl 는 두 개의 템플릿 매개 변수를 사용하는 OLE DB 템플릿 클래스입니다. 이러한 매개 변수는 인터페이스를 IRowsetLocate 클래스에 CCustomRowset 연결합니다. 대부분의 인터페이스를 추가하려면 이 단계를 건너뛰고 다음 단계로 이동할 수 있습니다. IRowsetLocate 이러한 방식으로 인터페이스와 IRowsetScroll 인터페이스만 처리해야 합니다.

그런 다음 인터페이스를 CCustomRowset 호출 QueryInterface IRowsetLocate 하도록 지시해야 합니다. 지도에 줄을 COM_INTERFACE_ENTRY(IRowsetLocate) 추가합니다. 인터페이스 CCustomRowset 맵은 다음 코드와 같이 표시되어야 합니다.

////////////////////////////////////////////////////////////////////////
// CustomRS.h

typedef CRowsetImpl< CCustomRowset, CTextData, CCustomCommand, CAtlArray<CTextData>, CSimpleRow, IRowsetLocateImpl<CCustomRowset, IRowsetLocate>> _RowsetBaseClass;

BEGIN_COM_MAP(CCustomRowset)
   COM_INTERFACE_ENTRY(IRowsetLocate)
   COM_INTERFACE_ENTRY_CHAIN(_RowsetBaseClass)
END_COM_MAP()

또한 맵을 클래스에 CRowsetImpl 연결해야 합니다. COM_INTERFACE_ENTRY_CHAIN 매크로를 추가하여 맵에 후크합니다 CRowsetImpl . 또한 상속 정보로 구성된 typedef를 RowsetBaseClass 만듭니다. 이 typedef는 임의이며 무시될 수 있습니다.

마지막으로 호출을 처리합니다 IColumnsInfo::GetColumnsInfo . 일반적으로 PROVIDER_COLUMN_ENTRY 매크로를 사용하여 이 작업을 수행합니다. 그러나 소비자는 책갈피를 사용할 수 있습니다. 소비자가 책갈피를 요청하는지 여부에 따라 공급자가 반환하는 열을 변경할 수 있어야 합니다.

호출을 IColumnsInfo::GetColumnsInfo 처리하려면 클래스에서 PROVIDER_COLUMN 맵을 삭제합니다 CTextData . PROVIDER_COLUMN_MAP 매크로는 함수 GetColumnInfo를 정의합니다. 사용자 고유의 함수를 GetColumnInfo 정의합니다. 함수 선언은 다음과 같습니다.

////////////////////////////////////////////////////////////////////////
// CustomRS.H

class CTextData
{
   ...
     // NOTE: Be sure you removed the PROVIDER_COLUMN_MAP!
   static ATLCOLUMNINFO* GetColumnInfo(CCustomRowset* pThis,
        ULONG* pcCols);
   static ATLCOLUMNINFO* GetColumnInfo(CCustomCommand* pThis,
        ULONG* pcCols);
...
};

그런 다음, 다음과 같이 사용자 지정RS.cpp 파일에서 함수를 구현 GetColumnInfo 합니다.

////////////////////////////////////////////////////////////////////
// CustomRS.cpp

template <class TInterface>
ATLCOLUMNINFO* CommonGetColInfo(IUnknown* pPropsUnk, ULONG* pcCols)
{
   static ATLCOLUMNINFO _rgColumns[5];
   ULONG ulCols = 0;

   CComQIPtr<TInterface> spProps = pPropsUnk;

   CDBPropIDSet set(DBPROPSET_ROWSET);
   set.AddPropertyID(DBPROP_BOOKMARKS);
   DBPROPSET* pPropSet = NULL;
   ULONG ulPropSet = 0;
   HRESULT hr;

   if (spProps)
      hr = spProps->GetProperties(1, &set, &ulPropSet, &pPropSet);

   // Check the property flag for bookmarks, if it is set, set the
// zero ordinal entry in the column map with the bookmark
// information.

   if (pPropSet)
   {
      CComVariant var = pPropSet->rgProperties[0].vValue;
      CoTaskMemFree(pPropSet->rgProperties);
      CoTaskMemFree(pPropSet);

      if ((SUCCEEDED(hr) && (var.boolVal == VARIANT_TRUE)))
      {
         ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Bookmark"), 0,
                     sizeof(DWORD), DBTYPE_BYTES,
            0, 0, GUID_NULL, CAgentMan, dwBookmark,
                        DBCOLUMNFLAGS_ISBOOKMARK)
         ulCols++;
      }
   }

   // Next set the other columns up.
   ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Field1"), 1, 16, DBTYPE_STR,
          0xFF, 0xFF, GUID_NULL, CTextData, szField1)
   ulCols++;
   ADD_COLUMN_ENTRY_EX(ulCols, OLESTR("Field2"), 2, 16, DBTYPE_STR,
       0xFF, 0xFF, GUID_NULL, CTextData, szField2)
   ulCols++;

   if (pcCols != NULL)
      *pcCols = ulCols;

   return _rgColumns;
}

ATLCOLUMNINFO* CTextData::GetColumnInfo(CCustomCommand* pThis,
     ULONG* pcCols)
{
   return CommonGetColInfo<ICommandProperties>(pThis->GetUnknown(),
        pcCols);
}

ATLCOLUMNINFO* CAgentMan::GetColumnInfo(RUpdateRowset* pThis, ULONG* pcCols)
{
   return CommonGetColInfo<IRowsetInfo>(pThis->GetUnknown(), pcCols);
}

GetColumnInfo 먼저 호출 DBPROP_IRowsetLocate 된 속성이 설정되어 있는지 확인합니다. OLE DB에는 행 집합 개체의 각 선택적 인터페이스에 대한 속성이 있습니다. 소비자가 이러한 선택적 인터페이스 중 하나를 사용하려는 경우 속성을 true로 설정합니다. 그런 다음 공급자는 이 속성을 확인하고 이에 따라 특별한 조치를 취할 수 있습니다.

구현에서 명령 개체에 대한 포인터를 사용하여 속성을 가져옵니다. 포인터는 pThis 행 집합 또는 명령 클래스를 나타냅니다. 여기서 템플릿을 사용하므로 이를 포인터로 void 전달해야 합니다. 그렇지 않으면 코드가 컴파일되지 않습니다.

열 정보를 저장할 정적 배열을 지정합니다. 소비자가 책갈피 열을 원하지 않는 경우 배열의 항목이 낭비됩니다. 이 배열을 동적으로 할당할 수 있지만 제대로 삭제해야 합니다. 이 예제에서는 매크로 ADD_COLUMN_ENTRY 및 ADD_COLUMN_ENTRY_EX 정의하고 사용하여 배열에 정보를 삽입합니다. 사용자 지정RS에 매크로를 추가할 수 있습니다. 다음 코드와 같이 H 파일:

////////////////////////////////////////////////////////////////////////
// CustomRS.h

#define ADD_COLUMN_ENTRY(ulCols, name, ordinal, colSize, type, precision, scale, guid, dataClass, member) \
   _rgColumns[ulCols].pwszName = (LPOLESTR)name; \
   _rgColumns[ulCols].pTypeInfo = (ITypeInfo*)NULL; \
   _rgColumns[ulCols].iOrdinal = (ULONG)ordinal; \
   _rgColumns[ulCols].dwFlags = 0; \
   _rgColumns[ulCols].ulColumnSize = (ULONG)colSize; \
   _rgColumns[ulCols].wType = (DBTYPE)type; \
   _rgColumns[ulCols].bPrecision = (BYTE)precision; \
   _rgColumns[ulCols].bScale = (BYTE)scale; \
   _rgColumns[ulCols].cbOffset = offsetof(dataClass, member);

#define ADD_COLUMN_ENTRY_EX(ulCols, name, ordinal, colSize, type, precision, scale, guid, dataClass, member, flags) \
   _rgColumns[ulCols].pwszName = (LPOLESTR)name; \
   _rgColumns[ulCols].pTypeInfo = (ITypeInfo*)NULL; \
   _rgColumns[ulCols].iOrdinal = (ULONG)ordinal; \
   _rgColumns[ulCols].dwFlags = flags; \
   _rgColumns[ulCols].ulColumnSize = (ULONG)colSize; \
   _rgColumns[ulCols].wType = (DBTYPE)type; \
   _rgColumns[ulCols].bPrecision = (BYTE)precision; \
   _rgColumns[ulCols].bScale = (BYTE)scale; \
   _rgColumns[ulCols].cbOffset = offsetof(dataClass, member); \
   memset(&(_rgColumns[ulCols].columnid), 0, sizeof(DBID)); \
   _rgColumns[ulCols].columnid.uName.pwszName = (LPOLESTR)name;

소비자의 코드를 테스트하려면 처리기를 몇 가지 변경 OnRun 해야 합니다. 함수의 첫 번째 변경 내용은 속성 집합에 속성을 추가하는 코드를 추가하는 것입니다. 이 코드는 속성을 true로 설정 DBPROP_IRowsetLocate 하므로 공급자에게 책갈피 열을 사용할 것을 알려 줄 수 있습니다. 처리기 코드는 OnRun 다음과 같이 표시됩니다.

//////////////////////////////////////////////////////////////////////
// TestProv Consumer Application in TestProvDlg.cpp

void CTestProvDlg::OnRun()
{
   CCommand<CAccessor<CProvider>> table;
   CDataSource source;
   CSession   session;

   if (source.Open("Custom.Custom.1", NULL, NULL, NULL,
          NULL) != S_OK)
      return;

   if (session.Open(source) != S_OK)
      return;

   CDBPropSet propset(DBPROPSET_ROWSET);
   propset.AddProperty(DBPROP_IRowsetLocate, true);
   if (table.Open(session, _T("c:\\public\\testprf2\\myData.txt"),
          &propset) != S_OK)
      return;

   CBookmark<4> tempBookmark;
   ULONG ulCount=0;
   while (table.MoveNext() == S_OK)
   {

      DBCOMPARE compare;
      if (ulCount == 2)
         tempBookmark = table.bookmark;

HRESULT hr = table.Compare(table.dwBookmark, table.dwBookmark,
                 &compare);
      if (FAILED(hr))
         ATLTRACE(_T("Compare failed: 0x%X\n"), hr);
      else
         _ASSERTE(compare == DBCOMPARE_EQ);

      m_ctlString1.AddString(table.szField1);
      m_ctlString2.AddString(table.szField2);
      ulCount++;
   }

   table.MoveToBookmark(tempBookmark);
   m_ctlString1.AddString(table.szField1);
   m_ctlString2.AddString(table.szField2);
}

루프에는 while 인터페이스에서 메서드를 Compare 호출하는 코드가 IRowsetLocate 포함되어 있습니다. 정확히 동일한 책갈피를 비교하기 때문에 사용자가 가진 코드는 항상 전달되어야 합니다. 또한 루프가 완료된 후 while 소비자 템플릿에서 함수를 호출 MoveToBookmark 하는 데 사용할 수 있도록 하나의 책갈피를 임시 변수에 저장합니다. 함수는 MoveToBookmark .에서 메서드를 GetRowsAt 호출합니다 IRowsetLocate.

또한 소비자에서 사용자 레코드를 업데이트해야 합니다. COLUMN_MAP 책갈피 및 항목을 처리할 항목을 클래스에 추가합니다.

///////////////////////////////////////////////////////////////////////
// TestProvDlg.cpp

class CProvider
{
// Attributes
public:
   CBookmark<4>    bookmark;  // Add this line
   char   szCommand[16];
   char   szText[256];

   // Binding Maps
BEGIN_ACCESSOR_MAP(CProvider, 1)
   BEGIN_ACCESSOR(0, true)   // auto accessor
      BOOKMARK_ENTRY(bookmark)  // Add this line
      COLUMN_ENTRY(1, szField1)
      COLUMN_ENTRY(2, szField2)
   END_ACCESSOR()
END_ACCESSOR_MAP()
};

코드를 업데이트한 경우 인터페이스를 사용하여 공급자 IRowsetLocate 를 빌드하고 실행할 수 있어야 합니다.

참고 항목

고급 공급자 기술