Читать «Применение Windows API» онлайн - страница 40

А. И. Легалов

  if (FAILED (hr)) throw HEx(hr, "get_ActiveDocument failed");

 }

};

Ниже представлена скелетная реализация DispObject, которая демонстрирует эту возможность. Она также объясняет, почему мы говорили о таких «членах», как Visible или FullName при обсуждении интерфейсов. В VB они фактически появляются как элементы данных, или как реквизиты, объектов. Здесь, я реализовал диспетчерский метод GetProperty, который используется, чтобы загрузить значение любого свойства, по его DISPID. И Вы можете получать DISPID любого свойства или метода, если Вы знаете его имя. Метод GetDispId будет делать это для Вас. Подобным способом, Вы можете реализовать и PutProperty, а также Invoke, который может использоваться, чтобы вызвать любой метод по его DISPID. Я оставляю это как упражнение для читателя.

class DispObject: public CoObject {

public:

 DispObject(CLSID const& classId) : _iDisp(0) {

  HRESULT hr = ::CoCreateInstance(classId, 0, CLSCTX_ALL, IID_IDispatch, (void**)&_iDisp);

  if (FAILED(hr)) {

   if (hr == E_NOINTERFACE) throw "No IDispatch interface";

   else throw HEx(hr, "Couldn't create DispObject");

  }

 }

 ~DispObject() {

  if (_iDisp) _iDisp->Release();

 }

 operator bool() const { return _iDisp != 0; }

 bool operator!() const { return _iDisp == 0; }

 DISPID GetDispId(WCHAR* funName) {

  DISPID dispid;

  HRESULT hr = _iDisp->GetIDsOfNames(IID_NULL, &funName, 1, GetUserDefaultLCID(), &dispid);

  return dispid;

 }

 void GetProperty(DISPID propId, VARIANT& result) {

  // In parameters

  DISPPARAMS args = { 0, 0, 0, 0 };

  EXCEPINFO except;

  UINT argErr;

  HRESULT hr = _iDisp->Invoke(propId, IID_NULL, GetUserDefaultLCID(), DISPATCH_PROPERTYGET, &args, &result, &except, &argErr);

  if (FAILED (hr)) throw HEx(hr, "Couldn't get property");

 }

 void* AcquireInterface(IID const & iid) {

  void* p = 0;

  HRESULT hr = _iDisp->QueryInterface(iid, &p);

  if (FAILED(hr)) {

   if (hr == E_NOINTERFACE) throw "No such interface";

   else throw HEx(hr, "Couldn't query interface");

  }

  return p;

 }

protected:

 DispObject(IDispatch * iDisp) : _iDisp(iDisp) {}

 DispObject() : _iDisp(0) {}

protected:

 IDispatch* _iDisp;

};

Ниже приводится небольшая иллюстрация динамической диспетчеризации. Конечно, тот же самый результат мог быть получен непосредственно, если вызвать метод get_Name интерфейса IGenericDocument. Мы рассмотрим этот непосредственный метод, использующий таблицу виртуальных фунций через мгновение, чтобы получить полный путь документа.

// Use docObj as a dispatch interface

DISPID pid = docObj.GetDispId(L"Name");

VARIANT varResult;

::VariantInit(&varResult);

docObj.GetProperty(pid, varResult);

BString bName(varResult);

CString cName(bName);

canvas.Text(20, y, "Name:");

canvas.Text(200, y, cName);

Это показывает, как Вы получаете путь, используя таблицу виртуальных функций (vtable).

BString bPath;

doc->get_FullName(bPath.GetPointer());

Теперь у Вас не должно быть каких-либо проблем при понимании кода, который определяет номер строки, на которой пользователь позиционировал курсор.