Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Работа с ActiveX в динамике. Создание и модификация 
:(
    Опции темы
ifndef
Дата 25.5.2008, 16:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



Здравствуйте, Уважаемые!

Прошу помощи в решение следующей проблемы:

Имеется самодельный composite ActiveX control: написан с использованием ATL, оформлен в виде dll (inproc server), является контейнером для других стандартных ActiveX элементов - кнопочек, надписей и т.п..

Выглядит примерно так:

Код

class ATL_NO_VTABLE CJustCompositeControl :
    public CComObjectRootEx<CComSingleThreadModel>,
    public IDispatchImpl<IJustCompositeControl, &IID_IJustCompositeControl, &LIBID_DynPropPageLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IPersistStreamInitImpl<CJustCompositeControl>,
    public IOleControlImpl<CJustCompositeControl>,
    public IOleObjectImpl<CJustCompositeControl>,
    public IOleInPlaceActiveObjectImpl<CJustCompositeControl>,
    public IViewObjectExImpl<CJustCompositeControl>,
    public IOleInPlaceObjectWindowlessImpl<CJustCompositeControl>,
    public ISupportErrorInfo,
    public IPersistStorageImpl<CJustCompositeControl>,
    public ISpecifyPropertyPagesImpl<CJustCompositeControl>,
    public IQuickActivateImpl<CJustCompositeControl>,
#ifndef _WIN32_WCE
    public IDataObjectImpl<CJustCompositeControl>,
#endif
    public IProvideClassInfo2Impl<&CLSID_JustCompositeControl, NULL, &LIBID_DynPropPageLib>,
#ifdef _WIN32_WCE // IObjectSafety is required on Windows CE for the control to be loaded correctly
    public IObjectSafetyImpl<CJustCompositeControl, INTERFACESAFE_FOR_UNTRUSTED_CALLER>,
#endif
    public CComCoClass<CJustCompositeControl, &CLSID_JustCompositeControl>,
    public CComCompositeControl<CJustCompositeControl>



Нужно сделать так, что бы в ходе выполнения программы его можно было динамически воткнуть на форму (к примеру на форму диалога, созданного с помощью MFC wisard`a) и получить к нему доступ для ресайза и модификации, причем модификация элемента должна быть реализована в каком-нибудь переопределенном стандартном методе стандартных интерфейсов, от которого унаследован класс элемента ActiveX (я использовал метод SetExtent интерфейса IOleObject). 

Динамически воткнуть удалось с использованием ProgId ActiveX`а и метода  CreateControl класса CWnd. 

Вот пример кода - это обработчик нажатия кнопки на форме MFCшного диалога. При нажатии контрол должен просто появиться на форме:

Код

void CDynPropAppDlg::OnShowPPage()
{
     ......
    // Объект page создается в конструкторе MFCшного диалога
    page->CreateControl(L"DynPropPage.JustCompositeControl.1",L"",WS_VISIBLE,CRect(0,0,270,200),this,99);
     ......
}


Затем пытаюсь до него достучаться:
На форме диалога помещаю кнопочку, по нажатию на которую контрол должен изменить свои размеры, вот обработчик ее нажатия:

Код

void CDynPropAppDlg::OnBnClickedResize()
{
     CoInitialize(NULL);
        HRESULT hr;
        IOleObject *disp;
        CLSID   cls77;
        SIZEL *size = new SIZEL;
                size->cx = 100;
        size ->cy = 100;
        hr = CLSIDFromProgID(L"DynPropPage.JustCompositeControl.1", &cls77);
        hr = CoCreateInstance(cls77,NULL,CLSCTX_INPROC_SERVER,IID_IOleObject,(void**)&disp);
        disp->SetExtent(1,size);
     CoUninitialize();
}


Переопределяю в ActiveX`е метод SetExtent интерфейса IOleObject:
Код


    STDMETHOD(SetExtent)(DWORD dwDrawAspect,SIZEL *psizel)
    {
             // Здесь вызывается стандартный метод SetExtent
                ........
            // Далее пытаюсь достучаться до элемента по ID
         CWindow nwin =CWindow();
         nwin = GetDlgItem(IDC_BUTTON1);
         .....
         return S_OK;
    }


Когда дело доходит до метода GetDlgItem, выскакивает Assert из этой функции и выполнение программы завершается ошибкой:

   
Код

     CWindow GetDlgItem(int nID) const throw()
    {
        ATLASSERT(::IsWindow(m_hWnd));  // На этом ассерте все заканчивается...
        return CWindow(::GetDlgItem(m_hWnd, nID));
    }


Видимо, проблема в отсутствии окна, точнее его описателя, но сколько ни бился, не могу понять какого и как это решить.. :( 
Если ресайз делать по нажатию кнопочки внутри самого контрола (т.е. любой кнопки из контейнера), все работает...

Разъясните пожалуйста поподробнее в чем моя ошибка и как реализовать описанное выше!

Заранее благодарен!
С уважением,
Сергей.

PM MAIL ICQ   Вверх
xvr
Дата 26.5.2008, 14:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



У вас в void CDynPropAppDlg::OnShowPPage() создается один ActiveX control, а в void CDynPropAppDlg::OnBnClickedResize() - еще один. Нужно не создавать еще одну копию, а брать уже созданный в окне (см. метод page->QueryControl или метод CreateControlEx, 4й параметр ppUnkControl)
PM MAIL   Вверх
ifndef
Дата 26.5.2008, 16:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



xvr, спасибо за помощь! 

Объект page в моем случае - это объект класса CWnd, поэтому метода CreateControlEx не содержит.

Попробовал его сделать объектом класса CAxWindow, но так как в методе CreateControlEx нет параметра типа CWnd* pParentWnd как в методе  CWnd::CreateControl, то при запуске вылезает ассерт уже отсюда:

Код


HRESULT CreateControlEx(LPCOLESTR lpszName, IStream* pStream = NULL, 
            IUnknown** ppUnkContainer = NULL, IUnknown** ppUnkControl = NULL,
            REFIID iidSink = IID_NULL, IUnknown* punkSink = NULL)
    {
        ATLASSERT(::IsWindow(m_hWnd));
            .....
         }


Т.е., как я понимаю, мой объект page не является окошком, так как не зарегистрирован, по типу RegisterClassEx и не создано окошко (аля методом Create).
PM MAIL ICQ   Вверх
xvr
Дата 26.5.2008, 17:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цитата(ifndef @ 26.5.2008,  16:35)
xvr, спасибо за помощь! 

Объект page в моем случае - это объект класса CWnd, поэтому метода CreateControlEx не содержит.

Тогда для доступа к созданному ActiveX надо звать GetControlUnknown
PM MAIL   Вверх
ifndef
Дата 26.5.2008, 18:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



Через GetControlUnknown тоже самое - ассерт при вызове метода GetDlgItem в переопределенном методе SetExtent в контроле...

Загвоздка, видимо, вот в этом:

переопределенный метод SetExtent в контроле:

Код


    STDMETHOD(SetExtent)(DWORD dwDrawAspect,SIZEL *psizel)
    {
    CWindow nwin;
    nwin = GetDlgItem(IDC_BUTTON1);
    return S_OK;
    }



Когда вызывается этот метод, создается новый объект класса CWindow, для того, чтобы в него сохранить указатель на контрол внутри контейнера. Потом вызывается метод GetDlgItem. Если взглянуть на этот метод:

Код


    CWindow GetDlgItem(int nID) const throw()
    {
        ATLASSERT(::IsWindow(m_hWnd));
        return CWindow(::GetDlgItem(m_hWnd, nID));
    }


то можно увидеть, что он проверяет переменную m_hWnd, а потом вызывает APIшную(??) функцию, в которую этот HWND и передается.. 
Вопрос в том, что это за m_hWnd и как его сюда передать из MFCшного диалога... 

Повторюсь, что если этот метод (SetExtent), вызывается из обработчика висящего на кнопке, расположенной в контейнере, то все нормально работает и хэндл передается, если же используется обработчик кнопки на MFC диалоге - затык...  smile 

Это сообщение отредактировал(а) ifndef - 26.5.2008, 18:38
PM MAIL ICQ   Вверх
ifndef
Дата 27.5.2008, 11:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



xvr, спасибо большое за участие, все заработало!
В предыдущем посте я допустил ошибку - при создании контрола, вызывается метод SetExtent, ассерт выскакивал потому, что окна контрола еще не было на тот момент. 

Надо было писать так:

Код

STDMETHOD(SetExtent)(DWORD dwDrawAspect,SIZEL *psizel)
    {
       if(m_hWnd)
         {
            CWindow nwin;
            nwin = GetDlgItem(IDC_BUTTON1);
            nwin.MoveWindow(0,0,10,10);
         }
      return S_OK;
    }



Еще раз спасибо!  smile 
 

Это сообщение отредактировал(а) ifndef - 27.5.2008, 11:18
PM MAIL ICQ   Вверх
ifndef
Дата 4.6.2008, 14:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



Господа, кто-нибудь знаком с методом интерфейса IViewObject SetExtent?
Не могу понять как он функционирует и для чего нужен.. :(
Мне необходимо реализовать масштабирование элемента ActiveX  в контейнере.

Вычитал в умной книжке, что этот метод, вроде как, выполняется в случае, если надо изменить размеры внедренного элемента, т.е. если написать так: 

Код

CoInitialize(NULL);
IOleObject *m_pInplObj;
//Новые размеры и положение
SIZE size;
size.cx=50;
size.cy=100;
//Создаем контрол
LPUNKNOWN pUnk = page->GetControlUnknown();
// Получаем указатель на интерфейс IOleInPlaceActiveObject 
pUnk -> QueryInterface(IID_IOleObject,(LPVOID*)&m_pInplObj);
CClientDC dc(NULL);
//
//Конвертируем размеры
//
dc.DPtoHIMETRIC(&size);
// Вызываем метод для изменения зазмеров и позиции
HRESULT hrez = OleRun(pUnk);
m_pInplObj->SetExtent(DVASPECT_CONTENT,(SIZEL*)&size);
m_pInplObj->Update();
::RedrawWindow(m_hWnd,NULL,NULL,RDW_ALLCHILDREN |
                                     RDW_UPDATENOW |
                                     RDW_INVALIDATE);

CoUninitialize();


то после выполнения этого метода мой ActiveX-элемент должен быть отмасштабирован в область с размерами 50 на 100, или я ошибаюсь?

Пробую это сделать, размеры элемента не меняются.. :( Прошу совета!

Это сообщение отредактировал(а) ifndef - 4.6.2008, 15:09
PM MAIL ICQ   Вверх
xvr
Дата 4.6.2008, 20:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цитата(ifndef @ 4.6.2008,  14:47)
Господа, кто-нибудь знаком с методом интерфейса IViewObject SetExtent?
Не могу понять как он функционирует и для чего нужен.. :(
Мне необходимо реализовать масштабирование элемента ActiveX  в контейнере.

Вычитал в умной книжке, что этот метод, вроде как, выполняется в случае, если надо изменить размеры внедренного элемента, т.е. если написать так: 

Код

CoInitialize(NULL);
IOleObject *m_pInplObj;
//Новые размеры и положение
SIZE size;
size.cx=50;
size.cy=100;
//Создаем контрол
LPUNKNOWN pUnk = page->GetControlUnknown();
// Получаем указатель на интерфейс IOleInPlaceActiveObject 
pUnk -> QueryInterface(IID_IOleObject,(LPVOID*)&m_pInplObj);
CClientDC dc(NULL);
//
//Конвертируем размеры
//
dc.DPtoHIMETRIC(&size);
// Вызываем метод для изменения зазмеров и позиции
HRESULT hrez = OleRun(pUnk);
m_pInplObj->SetExtent(DVASPECT_CONTENT,(SIZEL*)&size);
m_pInplObj->Update();
::RedrawWindow(m_hWnd,NULL,NULL,RDW_ALLCHILDREN |
                                     RDW_UPDATENOW |
                                     RDW_INVALIDATE);

CoUninitialize();


то после выполнения этого метода мой ActiveX-элемент должен быть отмасштабирован в область с размерами 50 на 100, или я ошибаюсь?

Пробую это сделать, размеры элемента не меняются.. :( Прошу совета!

Может все же IOleObject::SetExtent? Читаем MSDN:
Цитата

IOleObject::SetExtent
Informs an object of how much display space its container has assigned it.
Т.е. всего лишь информируем объект о том, сколько ему отведено места, а уж как на это реагировать - ответственность объекта. Кстати, что вернул SetExtent?


PM MAIL   Вверх
ifndef
Дата 4.6.2008, 21:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



xvr, спасибо за помощь! Уже вроде разобрался! smile

Насчет IViewObject - виноват, переклинило что-то...
SetExtent вернул S_OK..

Требуется сделать так, чтобы при изменении окна внедренный элемент масштабировался.
Окошко с элементом выглядит следующим образом:
user posted image

Сам ActiveX - composite control, включающий в себя следующие элементы:
- GroupBox
- Static
- Button

Вот он:
user posted image

Думал сделать масштабирование так:
1. Создаем контрол. 
Код

CWnd* page = new CWnd;
....
page->CreateControl(L"DynPropPage.JustCompositeControl.1",L"",WS_VISIBLE|WS_BORDER,CRect(0,0,270,200),this,99);


2. В случае ресайза окна вызываем метод IOleObject::SetExtent на стороне элемента
3. Метод SetExtent выглядит примерно так:

Код

// Предварительный работающий набросок
STDMETHOD(SetExtent)(DWORD dwDrawAspect,SIZEL *psizel)
    {
    if(m_hWnd)
       {
          CWindow nwin;
          // Здесь значения произвольные - не доделал пока, в идеале должно быть завязано на размере и положении окна
          nwin = GetDlgItem(IDC_BORDER);
          nwin.SetWindowPos(m_hWnd,0,0,psizel->cx,psizel->cy,SWP_NOZORDER | SWP_NOACTIVATE);
          nwin = GetDlgItem(IDC_BUTTON1);
          nwin.SetWindowPos(m_hWnd,0,0,psizel->cx,psizel->cy,SWP_NOZORDER | SWP_NOACTIVATE);
          SetWindowPos(m_hWnd, 0, 0, 200, 200, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
    return S_OK;
    }
 

Это правильно, как по-Вашему, или можно как-нибудь пограмотнее написать?

Это сообщение отредактировал(а) ifndef - 4.6.2008, 21:37
PM MAIL ICQ   Вверх
ifndef
Дата 5.6.2008, 06:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



Кстати такой еще вопрос:
Для отображения контрола я использую метод CWnd::CreateControl().
В этом методе создается объект класса COleControlContainer и объект COleControlSite.
Пытался вручную создать контейнер, подключить к нему ControlSite и создать контрол, да что-то не выходит..

Вот код:

Код

HRESULT hr;
CLSID   cls77;
hr = CLSIDFromProgID(L"DynPropPage.JustCompositeControl.1", &cls77);
COccManager* m_pOcc = new COccManager();
COleControlContainer *m_pCont;
BOOL bSuccess = CreateControlContainer(&m_pCont);
m_pCont = m_pOcc->CreateContainer(this);
COleControlSite *m_pSite;;
bSuccess = CreateControlSite(m_pCont,&m_pSite,1000,cls77);
CControlCreationInfo info;
info.m_clsid = cls77;
info.m_hk = info.NullHandle;
info.m_nHandle = NULL;
m_pSite = m_pOcc->CreateSite(m_pCont,info);
m_pSite->CreateControl(this,cls77,L"",WS_VISIBLE,CRect(0,0,270,200),1002);


Не понимаю методки создания контрола и где какое окно подключать, например:
методы CreateContainer(this) и CreateControl(this,cls77,L"",WS_VISIBLE,CRect(0,0,270,200),1002)
Оба первым параметром имеют m_hWnd окна. Как я понимаю, в случае создания контейнера необходимо указать само окно диалога, а в случае создания контрола - окно контрола (т.е. просто любой объект типа CWnd, например тот же самый CWnd* page, см выше), или я не прав?

Программа завершается на последней строчке приведенного кода в методе COleControlSite::QuickActivate()  на строчке pWndContain->OnAmbientProperty(this, _afxAmbients[i].dwDispID, &var). Выскакивает ассерт:

Код

BOOL CWnd::OnAmbientProperty(COleControlSite* pSite, DISPID dispid,
    VARIANT* pvar)
{
    ASSERT(m_pCtrlCont != NULL);
    return m_pCtrlCont->GetAmbientProp(pSite, dispid, pvar);
}


Что я делаю не так?



Это сообщение отредактировал(а) ifndef - 5.6.2008, 06:55
PM MAIL ICQ   Вверх
xvr
Дата 5.6.2008, 11:59 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цитата(ifndef @ 5.6.2008,  06:54)
Кстати такой еще вопрос:
Для отображения контрола я использую метод CWnd::CreateControl().
В этом методе создается объект класса COleControlContainer и объект COleControlSite.
Пытался вручную создать контейнер, подключить к нему ControlSite и создать контрол, да что-то не выходит..

Вот код:

Код

HRESULT hr;
CLSID   cls77;
hr = CLSIDFromProgID(L"DynPropPage.JustCompositeControl.1", &cls77);
COccManager* m_pOcc = new COccManager();
COleControlContainer *m_pCont;
BOOL bSuccess = CreateControlContainer(&m_pCont);
m_pCont = m_pOcc->CreateContainer(this);
COleControlSite *m_pSite;;
bSuccess = CreateControlSite(m_pCont,&m_pSite,1000,cls77);
CControlCreationInfo info;
info.m_clsid = cls77;
info.m_hk = info.NullHandle;
info.m_nHandle = NULL;
m_pSite = m_pOcc->CreateSite(m_pCont,info);
m_pSite->CreateControl(this,cls77,L"",WS_VISIBLE,CRect(0,0,270,200),1002);



CreateControlSite переопределен? Его default имплементация для CWnd пустая.

Цитата

Не понимаю методки создания контрола и где какое окно подключать, например:
методы CreateContainer(this) и CreateControl(this,cls77,L"",WS_VISIBLE,CRect(0,0,270,200),1002)
Оба первым параметром имеют m_hWnd окна. Как я понимаю, в случае создания контейнера необходимо указать само окно диалога, а в случае создания контрола - окно контрола (т.е. просто любой объект типа CWnd, например тот же самый CWnd* page, см выше), или я не прав?
Посмотри в сэмплах из MSDN (VCTERM sample)

PS. С SetExtent вроде все Ок
PM MAIL   Вверх
ifndef
Дата 6.6.2008, 19:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 14
Регистрация: 25.5.2008
Где: Россия, Москва

Репутация: нет
Всего: нет



xvr, ОГРОМНЕЙШЕЕ спасибо, Вы очень мне помогли!
Вопрос снят, вроде как во всем разобрался!!  smile 
PM MAIL ICQ   Вверх
Finarfin
Дата 25.7.2010, 20:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 17
Регистрация: 9.6.2009

Репутация: нет
Всего: нет



Цитата(xvr @  5.6.2008,  11:59 Найти цитируемый пост)
Посмотри в сэмплах из MSDN (VCTERM sample)

Тоже нужен этот пример. Это должно поставляться с Visual Studio?  
Microsoft Visual Studio 10.0\Samples\1049\VC2010Samples.zip - здесь нет.  

О, сорри, нашел

Добавлено через 7 минут и 20 секунд
Блин, там то же самое. Нет там vcterm никакого

Это сообщение отредактировал(а) Finarfin - 25.7.2010, 20:48
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: COM/DCOM/ActiveX/ATL/CORBA | Следующая тема »


 




[ Время генерации скрипта: 0.0841 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.