Применение Windows API - страница 29

Шрифт
Интервал

стр.

> }

>};

Обратите внимание, что базовый класс SActiveDocument — не SObject. Это новый класс DispObject. Он почти подобен SObject с одним лишь различием внутри: вместо использования указателя на IUnknown, он использует указатель на IDispatch. Это имеет значение? Реально — нет, я мог бы использовать SObject и все, работало бы также. За исключением того, что IDISPATCH может использоваться для большего чем запрос только других интерфейсов. Он может использоваться для динамической диспетчеризации вызовов. Так как наша программа написана на C++, и она знает все предоставленные интерфейсы, мы в действительности можем не использовать динамическую диспетчеризацию. Но имеются ситуации, в которых Вы должны дать пользователю возможность решить в реальном времени: какой объект загрузить и какой метод вызвать. Интерпретаторы и языки сценариев позволяют делать это. В частности, Visual Basic, являющийся инструментом для написания сценариев имеет такие функциональные возможности.

Ниже представлена скелетная реализация 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).

>SObjFace doc(docObj);

>BString bPath;

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

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

>BString bType;

>doc->get_Type(bType.GetPointer());

>if (type.IsEqual("Text")) {


стр.

Похожие книги