ActiveX Scripting Engines: Интерпретация внешнего скрипта в С++ - Передача объекта

ОГЛАВЛЕНИЕ


Передача объекта

Вспомним описанный ранее объект, порожденный от 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;
}

Не будем углубляться дальше. Продолжим работу над главной задачей – запуском скрипта. Осталось совсем немного.