Очередной баг?
От: _defrager Россия  
Дата: 12.08.02 12:38
Оценка: 30 (1)
Делаю себе браузер на движке ie и наткнулся на такой код в MFC:

(Mfc7, ViewHtml.cpp, ExecFormsCommand)

/////////////////////////////////////////
HRESULT hr = E_FAIL;

CComPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();
if (spDoc != NULL)
{
CComQIPtr<IOleCommandTarget> spCmdTarget = spDoc;
if (spCmdTarget != NULL)
hr = spCmdTarget->Exec(&CMDSETID_Forms3, dwCommandID,
OLECMDEXECOPT_DONTPROMPTUSER, pVarOut, pVarIn);
}

return hr;
////////////////////////////////////////

Как я понимаю, здесь теряется один Release() , потому что Release() нужно вызвать и для spDoc,
и для диспатча,возвращаемого GetHtmlDocument()

Так ведь?


ps. Любители критиковать чужие браузеры, посмотрите плз stylerbrowser.narod.ru (250kb)
Re: Очередной баг?
От: Trantor Россия  
Дата: 12.08.02 12:50
Оценка:
Здравствуйте _defrager, Вы писали:

D>Делаю себе браузер на движке ie и наткнулся на такой код в MFC:


D>(Mfc7, ViewHtml.cpp, ExecFormsCommand)


D>/////////////////////////////////////////

D> HRESULT hr = E_FAIL;

D> CComPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();

D> if (spDoc != NULL)
D> {
D> CComQIPtr<IOleCommandTarget> spCmdTarget = spDoc;
D> if (spCmdTarget != NULL)
D> hr = spCmdTarget->Exec(&CMDSETID_Forms3, dwCommandID,
D> OLECMDEXECOPT_DONTPROMPTUSER, pVarOut, pVarIn);
D> }

D> return hr;

D>////////////////////////////////////////

D>Как я понимаю, здесь теряется один Release() , потому что Release() нужно вызвать и для spDoc,

D>и для диспатча,возвращаемого GetHtmlDocument()

D>Так ведь?



D>ps. Любители критиковать чужие браузеры, посмотрите плз stylerbrowser.narod.ru (250kb)


На сколько я помню в деструкторе CComPtr есть Release(), так что его вызывать ненужно...
В жизни мало быть умным, надо еще быть не дураком.
Re: Очередной баг?
От: Dima2  
Дата: 12.08.02 14:08
Оценка:
Здравствуйте _defrager, Вы писали:

D>Делаю себе браузер на движке ie и наткнулся на такой код в MFC:


А для чего по твоему обертка CComPtr, а непросто IHTMLDocument2* ?
Re[2]: Очередной баг?
От: _defrager Россия  
Дата: 13.08.02 06:33
Оценка:
Здравствуйте Dima2, Вы писали:

D>Здравствуйте _defrager, Вы писали:


D>>Делаю себе браузер на движке ie и наткнулся на такой код в MFC:


D>А для чего по твоему обертка CComPtr, а непросто IHTMLDocument2* ?



Давай считать вместе :)

CComPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();

В этой строчке вызовется AddRef() для spDoc и когда spDoc выйдет из области видимости
вызовется Release() для spDoc. А где вызовется Release для LPDISPATCH который возвратит GetHtmlDocument?

Ведь если не заморачиваться с CComPtr пришлось бы писать так
///////////////
LPDISPATCH lpDisp = GetHtmlDocument()
if (lpDisp)
{
...
lpDisp->Release()
}
///////////////


Вот этого последнего Release() и не хватает...
Re[3]: Очередной баг?
От: Dima2  
Дата: 13.08.02 08:06
Оценка:
Здравствуйте _defrager, Вы писали:

D>Давай считать вместе


D>CComPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();


D>В этой строчке вызовется AddRef() для spDoc и когда spDoc выйдет из области видимости

D>вызовется Release() для spDoc. А где вызовется Release для LPDISPATCH который возвратит GetHtmlDocument?

Мда, вообще-то наверное ты прав. Посмотрел я реализацию GetHtmlDocument(), она просто берет св-во IWebBrowser2::get_Document. И уж если не предположить что get_Document не делает AddRef ???

Хотя MSDN изобилует подобного рода примерами:

#import <mshtml.tlb>
 
CMyAppView::LoadData()
{
   AfxOleInit();  // Initialize the COM library
 
   MSHTML::IHTMLDocument2Ptr pIDoc;
   MSHTML::IHTMLElementCollectionPtr pICollection;
 
   pIDoc = GetHtmlDocument();
....
 }


   IHTMLDocument2Ptr spDoc;

   // Get the active document
   spDoc = GetHtmlDocument();
   if ( spDoc )
   {
.....
Re[4]: Очередной баг?
От: _defrager Россия  
Дата: 13.08.02 08:53
Оценка:
Здравствуйте Dima2, Вы писали:

D>Здравствуйте _defrager, Вы писали:


D>>Давай считать вместе :)


D>>CComPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();


D>>В этой строчке вызовется AddRef() для spDoc и когда spDoc выйдет из области видимости

D>>вызовется Release() для spDoc. А где вызовется Release для LPDISPATCH который возвратит GetHtmlDocument?

D>Мда, вообще-то наверное ты прав. Посмотрел я реализацию GetHtmlDocument(), она просто берет св-во IWebBrowser2::get_Document.


Вот-вот

D>И уж если не предположить что get_Document не делает AddRef ???


Нее...это врядли...

D>Хотя MSDN изобилует подобного рода примерами:


D>
D>   IHTMLDocument2Ptr spDoc;

D>   // Get the active document
D>   spDoc = GetHtmlDocument();
D>   if ( spDoc )
D>   {
D>.....
D>



Тут еще есть вторая СЕРЬЕЗНАЯ проблема:

Я по наивности скопировал mfcшный код "CСomPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument(); "
и получил эксепшен когда пытался вызвать put_charset у документа не поддерживающего IHTMLDocument2
из-за нелепого преобразования типа (IDispatch*) => (IHTMLDocument2*)
Re[5]: А какое описание у GetHtmlDocument в Mfc7
От: Vi2 Удмуртия http://www.adem.ru
Дата: 13.08.02 09:04
Оценка:
Здравствуйте _defrager, Вы писали:

D>>>В этой строчке вызовется AddRef() для spDoc и когда spDoc выйдет из области видимости

D>>>вызовется Release() для spDoc. А где вызовется Release для LPDISPATCH который возвратит GetHtmlDocument?

D>>Мда, вообще-то наверное ты прав. Посмотрел я реализацию GetHtmlDocument(), она просто берет св-во IWebBrowser2::get_Document.


D>Вот-вот


Единственное объяснение может быть в том, что LPDISPATCH — это не IDispatch*, а IDispatchPtr, как в #import-е генерится. У меня текстов 7 нет, посмотрите. Интересно ведь.

D>Тут еще есть вторая СЕРЬЕЗНАЯ проблема:


D>Я по наивности скопировал mfcшный код "CСomPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument(); "

D>и получил эксепшен когда пытался вызвать put_charset у документа не поддерживающего IHTMLDocument2
D>из-за нелепого преобразования типа (IDispatch*) => (IHTMLDocument2*)

Это не преобразование нелепое, а использование spDoc в дальнейшем. NULL, однако!
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[6]: А какое описание у GetHtmlDocument в Mfc7
От: Dima2  
Дата: 13.08.02 09:33
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>Здравствуйте _defrager, Вы писали:


D>>>>В этой строчке вызовется AddRef() для spDoc и когда spDoc выйдет из области видимости

D>>>>вызовется Release() для spDoc. А где вызовется Release для LPDISPATCH который возвратит GetHtmlDocument?

D>>>Мда, вообще-то наверное ты прав. Посмотрел я реализацию GetHtmlDocument(), она просто берет св-во IWebBrowser2::get_Document.


D>>Вот-вот


Vi2>Единственное объяснение может быть в том, что LPDISPATCH — это не IDispatch*, а IDispatchPtr, как в #import-е генерится. У меня текстов 7 нет, посмотрите. Интересно ведь.



typedef IDispatch* LPDISPATCH;
...
LPDISPATCH CHtmlView::GetHtmlDocument() const
{
    ASSERT(m_pBrowserApp != NULL);

    LPDISPATCH result;
    m_pBrowserApp->get_Document(&result);
    return result;
}
Re[7]: Да что же это делается-то !
От: Vi2 Удмуртия http://www.adem.ru
Дата: 13.08.02 09:38
Оценка:
Здравствуйте Dima2, Вы писали:

D>typedef IDispatch* LPDISPATCH;
D>...
D>LPDISPATCH CHtmlView::GetHtmlDocument() const
D>{
D>    ASSERT(m_pBrowserApp != NULL);

D>    LPDISPATCH result;
D>    m_pBrowserApp->get_Document(&result);
D>    return result;
D>}


Т.е. как в VC 6.0. Но там ведь было верное использование. Неужели так сильно переделали?!
 LPDISPATCH lpDisp = GetHtmlDocument();
 if (lpDisp != NULL)
 {
         // the control will handle all printing UI
         if (SUCCEEDED(lpDisp->QueryInterface(IID_IOleCommandTarget, (LPVOID*) &lpTarget)))
         {
                 lpTarget->Exec(NULL, OLECMDID_PRINT, 0, NULL, NULL);
                 lpTarget->Release();
         }
         lpDisp->Release();
 }
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[6]: А какое описание у GetHtmlDocument в Mfc7
От: _defrager Россия  
Дата: 13.08.02 11:01
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>Здравствуйте _defrager, Вы писали:



D>>Тут еще есть вторая СЕРЬЕЗНАЯ проблема:


D>>Я по наивности скопировал mfcшный код "CСomPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument(); "

D>>и получил эксепшен когда пытался вызвать put_charset у документа не поддерживающего IHTMLDocument2
D>>из-за нелепого преобразования типа (IDispatch*) => (IHTMLDocument2*)

Vi2>Это не преобразование нелепое, а использование spDoc в дальнейшем. NULL, однако!


А вот и нет!!! ССоmPtr не переходит к другому интерфейсу, т.е

при вызове CСomPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();
spDoc не будет равен NULL даже если IHTMLDocument2 не поддерживается, другое дело CСomQIPtr<IHTMLDocument2> spDoc = GetHtmlDocument().

Поэтому делать "(IHTMLDocument2*) GetHtmlDocument()" крайне опасно.
Re[7]: Для этого есть QI
От: Vi2 Удмуртия http://www.adem.ru
Дата: 13.08.02 11:13
Оценка:
Здравствуйте _defrager, Вы писали:

D>А вот и нет!!! ССоmPtr не переходит к другому интерфейсу, т.е


D>при вызове CСomPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();

D>spDoc не будет равен NULL даже если IHTMLDocument2 не поддерживается, другое дело CСomQIPtr<IHTMLDocument2> spDoc = GetHtmlDocument().

D>Поэтому делать "(IHTMLDocument2*) GetHtmlDocument()" крайне опасно.


А! Ну да! Видимо в примере IHTMLDocument2 гарантировалось используемым объектом.

А вообще это порочная практика от МФС — кастировать к потомку от базы, для этого есть QI. В обратную сторону (от IHTMLDocument2* к IDispatch*) безопасно.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re: Очередной баг?
От: Tom Россия http://www.RSDN.ru
Дата: 13.08.02 14:17
Оценка:
Господа. Вы не правы. Привожу код ATL.

T* operator=(T* lp)
{
    return (T*)AtlComPtrAssign((IUnknown**)&p, lp);
}


там же

ATLINLINE ATLAPI_(IUnknown*) AtlComPtrAssign(IUnknown** pp, IUnknown* lp)
{
    if (lp != NULL)
        lp->AddRef();
    if (*pp)
        (*pp)->Release();
    *pp = lp;
    return lp;
}


как видите Release() вызывается.
Народная мудрось
всем все никому ничего(с).
Re[2]: Очередной баг?
От: _defrager Россия  
Дата: 13.08.02 18:15
Оценка:
Здравствуйте Tom, Вы писали:

Tom>Господа. Вы не правы. Привожу код ATL.


Tom>
Tom>T* operator=(T* lp)
Tom>{
Tom>    return (T*)AtlComPtrAssign((IUnknown**)&p, lp);
Tom>}
Tom>


Tom>там же


Tom>
Tom>ATLINLINE ATLAPI_(IUnknown*) AtlComPtrAssign(IUnknown** pp, IUnknown* lp)
Tom>{
Tom>    if (lp != NULL)
Tom>        lp->AddRef();
Tom>    if (*pp)
Tom>        (*pp)->Release();
Tom>    *pp = lp;
Tom>    return lp;
Tom>}
Tom>


Tom>как видите Release() вызывается.


Опять мимо. :no:

1). в этом куске
CComPtr<IHTMLDocument2> spDoc = (IHTMLDocument2*) GetHtmlDocument();
вызывается конструктор копирования, а не оператор присваения
2) даже если бы вызывался оператор присваения, Release() не вызывался бы, так как spDoc был бы равен NULL.

Вобщем похоже в 7м MFC код дорабатывался с больщого бодуна — чуть пониже нашел еще такую штуку

*pbEnabled = (cmdInfo.cmdf & OLECMDF_ENABLED) ? TRUE : FALSE;

налядно конечно, но не сильно професианально :-\
хотя компилятор наверное все-равно из-этого сделает: *pbEnabled = cmdInfo.cmdf & OLECMDF_ENABLED;
...надо проверить
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.