دعم موفر لـلإشارات المرجعية‬

يضيف المثال في هذا الموضوع الواجهة IRowsetLocate إلى فئة CMyProviderRowset . في كافة الحالات تقريباً , ابدأ بإضافة واجهة إلى كائن COM موجود. ويمكن اختبار ذلك بواسطة إضافة المزيد من الاستدعاءات من قوالب المستهلك. يوضح المثال الكيفية:

  • إضافة واجهة إلى موفر.

  • تحديد الأعمدة بشكل حيوي للعودة إلى المستهلك.

  • إضافة دعم الإشارة المرجعية.

ترث واجهة IRowsetLocate من واجهة IRowset . لإضافة واجهة IRowsetLocate ، قم بـتوريث CMyProviderRowset من IRowsetLocateImpl.

إضافة واجهة IRowsetLocate يختلف قليلاً عن معظم الواجهات. لجعل VTABLEs مصطفة في خط واحد , تحتوي قوالب موفر OLE DB على معلمة قالب للتعامل مع الواجهة المشتقة. تُظهر التعليمات البرمجية التالية قائمة التوريث الجديدة:

////////////////////////////////////////////////////////////////////////
// MyProviderRS.h

// CMyProviderRowset
class CMyProviderRowset : public CRowsetImpl< CMyProviderRowset, 
      CTextData, CMyProviderCommand, CAtlArray<CTextData>, 
      CSimpleRow, 
          IRowsetLocateImpl<CMyProviderRowset, IRowsetLocate> >

يتم إضافة المعلمة الرابعة و الخامسة و السادسة. يستخدم هذا المثال الإعدادات الافتراضية للرابع ومعلمات الخامس ولكن بتحديد IRowsetLocateImplكـ المعلمة السادس. IRowsetLocateImplهو تعتبر فئة قالب OLE DB الذي يأخذ معلمتين من معلمات القالب: هذه تربط واجهة IRowsetLocate إلى فئة CMyProviderRowset . لإضافة معظم الواجهات يمكنك تخطي هذه الخطوة ثم الانتقال إلى الخطوة التالية. فقط واجهات IRowsetLocate و IRowsetScroll تحتاج للمعالجة بـهذه الطريقة.

ثم تحتاج أن تخبر CMyProviderRowset أن تستدعي QueryInterface من أجل واجهة IRowsetLocate . أضف السطر COM_INTERFACE_ENTRY(IRowsetLocate) إلى المخطط. يجب أن يظهر مخطط الواجهة لـ CMyProviderRowset كما هو موضح في التعليمات البرمجية التالية:

////////////////////////////////////////////////////////////////////////
// MyProviderRS.h

typedef CRowsetImpl< CMyProviderRowset, CTextData, CMyProviderCommand, CAtlArray<CTextData>, CSimpleRow, IRowsetLocateImpl<CMyProviderRowset, IRowsetLocate> > _RowsetBaseClass;

BEGIN_COM_MAP(CMyProviderRowset)
   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 الخاصة بك. يجب أن يكون التصريح بالدالة كما يلي:

////////////////////////////////////////////////////////////////////////
// MyProviderRS.H

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

ثم تقوم بتطبيق دالة GetColumnInfo في ملف MyProviderRS.cpp كما يلي:

////////////////////////////////////////////////////////////////////
// MyProviderRS.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(CMyProviderCommand* 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 إلى true (صواب). OLE DB به خصائص لكل من الواجهات الإختيارية خارج كائن مجموعة الصفوف. إذا أراد المستهلك استخدام إحدى هذه الواجهات الإختيارية ،فإنها تقوم بتعيين خاصية ما. يمكن بعد ذلك أن يقوم الموفر ‏‫بـفحص الخاصية واتخاذ إجراء خاص استنادا عليها.

في التطبيق, يمكنك الحصول على الخاصية عن طريق استخدام مؤشر لكائن الأمر. مؤشر pThis يمثل مجموعة الصفوف أو فئة الأمر. و لأنك تستخدم القوالب هنا عليك تمرير هذا كمؤشر void و إلا فلن يمكن تحويل ا‏لتعليمات البرمجية برمجياً.

قم بتحديد صفيفة ثابتة تحتوي على معلومات العمود. إذا كان المستهلك لا يريد عمود الإشارة المرجعية، يضيع المدخل في الصفيفة. يمكنك تخصيص هذا الصفيف بشكل حيوي ولكن عليك التأكد من إتلافه بشكل صحيح. هذا المثال يحدد ويستخدم وحدات الماكرو ADD_COLUMN_ENTRY و ADD_COLUMN_ENTRY_EX لإدراج المعلومات إلى الصفيف. يمكنك إضافة وحدات الماكرو إلى ملف MyProviderRS.H كما هو موضح في التعليمات البرمجية التالية:

////////////////////////////////////////////////////////////////////////
// MyProviderRS.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 . التغيير الأول هو إضافة تعليمة برمجية لإضافة خاصية إلى مجموعة الخصائص. تُعين التعليمات البرمجية قيمة الخاصية DBPROP_IRowsetLocate إلى true (صواب) وبالتالي تخبر الموفر أنك تُريد عمود الإشارة المرجعية. يجب أن يظهر معالج OnRun كما يلي:

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

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

   if (source.Open("MyProvider.MyProvider.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 .

راجع أيضًا:

المبادئ

أساليب موفر متقدمة