SoftCraft
разноликое программирование

Отправная точка
Программирование
Windows API
Автоматы
Нейроинформатика
Парадигмы
Параллелизм
Проектирование
Теория
Техника кодирования
Трансляторы
Прочие вопросы

Разное

Беллетристика
Брюзжалки
Цели и задачи
Об авторе


Оболочка из классов для OLE


Рационализация OLE

Перевод А. И. Легалова

Англоязычный оригинал находится на сервере компании Reliable Software


Построив интеллектуальное OLE на вершине старого, Вы узнаете другое OLE


Прежде всего Вы должны сообщить миру, что собираетесь использовать OLE. Имеется небольшой класс, который будет делать это за Вас. Только внедрите объект этого класса в некоторый высокоуровневый сконструированный объект прежде, чем Вы что-либо сделаете с OLE и удалите его после того, как отработаете с OLE. В Windows программе первоосновой для UseOle является "Контроллер".


class UseOle
{
public:
    UseOle () { OleInitialize (0); }
    ~UseOle () { OleUninitialize (); }
};

class Controller
{
public:
    Controller(HWND hwnd, CREATESTRUCT * pCreate);
    ~Controller() { PostQuitMessage(0); }
    void Paint (HWND hwnd);
private:
    UseOle useOle;
};

Затем Вы должны создать OLE объект, который является поставщиком интерфейсов. Абстрактный класс CoObject объявляет метод AcquireInterface. Фактический класс SObject (интеллектуальный объект) обеспечивает одну специфическую реализацию CoObject, которая использует, внутри себя, позорящий OLE IUnknown.


class CoObject
{
public:
    virtual void * AcquireInterface (IID const & iid) = 0;
};

class SObject: public CoObject
{
public:
    SObject (CLSID const & classId, bool running = false);
    ~SObject ()
    {
        if (_iUnk)
            _iUnk->Release ();
    }
    void * AcquireInterface (IID const & iid);

private:
    IUnknown * _iUnk;
};

Давайте взглянем на реализации конструктора и метода AcquireInterface.

Конструктор получает объект, вызывая функцию API GetActiveObject и/или CoCreateInstance. Различие между ними в том, что GetActiveObject пытается овладеть уже выполняющимся объектом, в то время как CoCreateInstance пробует то же самое, но, если это терпит неудачу, то запускает любой exe-шник, необходимый для выполнения этого объекта. Некоторые объекты фактически определяют предпочтение: они хотят, чтобы новый сервер был заново запущен каждый раз, когда вызывается CoCreateInstance. GetActiveObject позволяет Вам обойти это.

Обратите внимание, что это - только один пример того, как Вы получаете доступ к OLE объектам. Вы можете захотеть поиграть с некоторыми из параметров. Например, я передаю CLSCTX_SERVER как параметр в CoCreateInstance. Это даст мне уверенность, что объект будет жить в отдельном от клиента процессе. Во многих случаях вам лучше иметь объект как сервер в процессе на основе DLL, которая загружается в адресное пространство клиента. Для больших подробностей ищите описание CoCreateInstance в вашей справке.


SObject::SObject (CLSID const & classId, bool running)
    :_iUnk (0)
{
    HRESULT hr = S_OK;
    if (running)
    {
        ::GetActiveObject (classId, 0, & _iUnk);
    }
    if (_iUnk == 0)
    {
        hr = ::CoCreateInstance (
                   classId,
                   0,
                   CLSCTX_SERVER,
                   IID_IUnknown,
                   (void**)& _iUnk);
    }
    if (FAILED (hr))
        throw HEx (hr, "Couldn't create instance");
}

Через мгновение я объясню странный тип исключения HEx.

В нашей реализации AcquireInterface просто вызывает метод QueryInterface из IUnknown (или, как я говорю, неудачный QueryInterface из неудачного IUnknown).


void * SObject::AcquireInterface (IID const & iid)
{
    void * p = 0;
    HRESULT hr = _iUnk->QueryInterface (iid, & p);
    if (FAILED (hr))
    {
        if (hr == E_NOINTERFACE)
            throw "No such interface";
        else
            throw HEx (hr, "Couldn't acquire interface");
    }
    return p;
}

Метод AcquireInterface - один из этих исключительных, Получающих методов Управления ресурсами, которые освобождают ресурсы. Мы не вызываем его иначе, как внутри конструктора интеллектуального указателя интерфейса. (Между прочим, параметр шаблона - это IID указатель, потому что компилятор не будет принимать ссылки как параметры шаблона. Я не уверен почему.)

Итак, имеется шаблон для интеллектуального указателя интерфейса.


template<class I, IID const * iid>class SFace
{
public:
    ~SFace ()
    {
        if (_i)
            _i->Release ();
    }
    I * operator-> () { return _i; }
protected:
    SFace () : _i (0) {}
    SFace (void * i)
    {
        _i = static_cast<I*> (i);
    }
protected:
    I * _i;
};

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

Вот один из них, который использует наш CoObject (или любой другой объект, полученный от него) как источник интерфейса.


template<class I, IID const * iid>class SObjFace: public SFace<I, iid>
{
public:
    SObjFace (CoObject & obj)
        : SFace<I, iid> (obj.AcquireInterface (*iid))
    {}
};

В заключение, позвольте мне, представит класс HEx (HRESULT Exception). Это - класс исключения, который является способным к отображению значимых сообщений об ошибках. Для достижения моих ограниченных целей, я просто отображаю сообщения непосредственно на холсте основного экрана. Не бойтесь реализовать ваш собственный метод с использованием окна сообщений или еще чего-нибудь.


class HEx
{
public:
    HEx (HRESULT hr, char const * str = 0)
        : _hr (hr), _str (str)
    {}
    void Display (int x, int y, Canvas & canvas)
    {
        if (_str != 0)
        {
            canvas.Text (x, y, _str);
            y += 20;
        }
        if (FAILED (_hr))
        {
            char * msg;
            ::FormatMessage (
                FORMAT_MESSAGE_ALLOCATE_BUFFER 
                    | FORMAT_MESSAGE_FROM_SYSTEM,
                0,
                _hr,
                MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
                reinterpret_cast<char *> (& msg),
                0,
                0);
            canvas.Text (x, y, msg);
            ::LocalFree (msg);
        }
    }
private:
    HRESULT _hr;
    char const * _str;
};

Далее: Будет представлен типичный код. Я только должен объяснить, что такое Автоматизация.