공급자의 책갈피 지원
이 항목의 예제에서는 클래스에 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
를 빌드하고 실행할 수 있어야 합니다.