}
};
Обратите внимание, что базовый класс 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);
BStringbPath;
doc->get_FullName(bPath.GetPointer());
Теперь у Вас не должно быть каких-либо проблем при понимании кода, который определяет номер строки, на которой пользователь позиционировал курсор.
BString bType;
doc->get_Type(bType.GetPointer());
if (type.IsEqual("Text")) {
SObjFace text (docObj);
SSelectionselObj(text);
SObjFace sel(selObj);
long line;
sel->get_CurrentLine(&line);
canvas.Text(20, y, "CurrentLine:");
char buf[10];
wsprintf(buf, "%ld", line);
canvas.Text(200, y, buf);
}
Читать дальше