ActiveX Scripting Engines: Интерпретация внешнего скрипта в С++ - Передача объекта
ОГЛАВЛЕНИЕ
Страница 3 из 4
Передача объекта
Вспомним описанный ранее объект, порожденный от CCmdTarget и служащий, напомню, для позднего связывания: class CCodeObject : public CCmdTarget { \\. . . |
Пришло его время.
Один из методов интерфейса IActiveScriptSite имеет следующий прототип:
HRESULT _stdcall CScriptHost::GetItemInfo(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppunkItem, ITypeInfo** ppTypeInfo); |
Во время исполнения скрипта метод GetItemInfo будет вызван с определенными параметрами, говорящими о том, что в ответ нужно вернуть указатель на интерфейс IUnknown*. Это как раз и есть тот момент, когда для дальнейшего исполнения скрипта понадобился экземпляр объекта CCodeObject – например, чтобы «поискать» там какую-нибудь переменную, имя которой использовано в скрипте.
К этому моменту в каком-нибудь модуле трансляции уже существует экземпляр класса CCodeObjecе. Например, обьявленный как глобальный – сейчас стиль программирования не особо важен, главное – проиллюстрировать суть происходящего. Итак, где-то объявлен и находится в зоне видимости:
CCodeObject codeobj; |
Теперь в реализации CScriptHost::GetItemInfo() происходит следующее:
HRESULT _stdcall CScriptHost::GetItemInfo(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppunkItem, ITypeInfo** ppTypeInfo) { // . . . *ppunkItem = codeobj.GetIDispatch(TRUE); // . . . } |
Заметим важный ньюанс – следующая строка НЕПРАВИЛЬНАЯ:
*ppunkItem = (IUnknown*)&codeobj; // так нельзя!!! |
Компилятор проглотит, но, хотя наш обьект и унаследован от CCmdTarget, сам класс CCmdTarget не унаследован от IUnknown.
Ранее мы создавали CScriptHost, унаследованный от IActiveScriptSite, а сам IActiveScriptSite был унаследован от IUnknown. Это действительно допускает преобразование CScriptHost к IUnknown.
Но в случае с классом CCodeObject, порожденным от CCmdTarget, преобразование к типу IUnknown невозможно.
Класс CCmdTarget может вернуть указатель на интерфейс IUnknown (или интефейс IDispatch, действительно порожденный от IUnknown). Но делается это путем вызова
IUnknown* punk = CCmdTarget::GetInterface(&IID_IUnknown); |
или
IUnknown* punk = CCmdTarget::GetIDispatch(TRUE); |
В основу положен другой механизм. В очень грубом приближении, в классе CCmdTarget объявлен член класса, имеющий тип IDispatch, а метод GetIDispatch возвращает его адрес:
class CCmdTarget: { // . . . IDispatch m_xxIDispatch; IUnknown* GetIDispatch(BOOL bAddRef) { if ( bAddRef ) m_xxIDispatch.AddRef(); return &m_xxIDispatch; } }; |
На самом деле всё несколько сложнее – применена некоторая арифметика указателей и смещений. Проиллюстрируем это на примере:
class IClassA { }; class ClassB { int dummy1, dummy2, dummy3; IClassA m_xxIClassA; static int m_offs; public: IClassA* GetIClassA() { IClassA* pia = (IClassA*)((BYTE*)this + m_offs); return pia; } }; int ClassB::m_offs = (size_t)&(((ClassB *)0)->m_xxIClassA); int main(int argc, char* argv[]) { ClassB b; IClassA* pA = b.GetIClassA(); return 0; } |
Не будем углубляться дальше. Продолжим работу над главной задачей – запуском скрипта. Осталось совсем немного.