簡體   English   中英

使用自定義 IDocHostUIHandler 並關閉 window 時崩潰

[英]Crashes when using custom IDocHostUIHandler and closing window

我使用附加到 TWebBrowser 的自定義IDocHostUIHandler (在設計模式下)。 有用。 但是,當程序退出(或使用它的 window 關閉)時,它會崩潰。 我想某些東西沒有被正確刪除/銷毀。 我假設自定義 IDocHostUIHandler 在被銷毀后仍在使用中,所以我應該先以某種方式銷毀 web 瀏覽器(或分離 custom.

所以我的問題是 - 我如何正確銷毀自定義IDocHostUIHandler以便在現有程序不會崩潰或分離自定義IDocHostUIHandler時?

如果我在構造函數中刪除diCustDoc->SetUIHandler(piDocUIHandler)它可以正常工作而不會發生任何崩潰,因此問題肯定是由附加自定義IDocHostUIHandler引起的。

設置diCustDoc->SetUIHandler(NULL); 在上述Release() (在析構函數中)也崩潰之前。

我的自定義IDocHostUIHandler (替換彈出菜單並自定義GetHostInfo )。

    class TDocUIHandler : public ::IDocHostUIHandler
        {
        private:
            ULONG        RefCount;
            TPopupMenu* pPopupMenu;

        public:
            //TDocUIHandler()                     : RefCount(0)                         {}
            TDocUIHandler(TPopupMenu* fPopupMenu) : RefCount(0), pPopupMenu(fPopupMenu) {}

            // IUnknown method
            HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
                {
                if (!ppv) return E_POINTER;

                if        (IsEqualIID(riid, IID_IUnknown))          *ppv = static_cast<IUnknown*>(this);
                else if (IsEqualIID(riid, ::IID_IDocHostUIHandler)) *ppv = static_cast< ::IDocHostUIHandler*>(this);
                else                                                *ppv = NULL;

                if (*ppv)
                    {
                    AddRef();                                                // Used the first time so increase the RefCount
                    return S_OK;
                    }

                return E_NOINTERFACE;
                }

            ULONG __stdcall AddRef()
                {
                return (ULONG) InterlockedIncrement((long*)&RefCount);
                }

            ULONG __stdcall Release()
                {
                ULONG res = (ULONG) InterlockedDecrement((long*)&RefCount);
                if (res == 0) delete this;

                return res;
                }

            // Returning S_OK tells the web browser that it need not display its
            // own context menu, presumably because the application hosting it has
            // displayed its own menu to replace it.
            // Since our host does not display any, no context menu is shown.

            STDMETHOD(ShowContextMenu)(         /* [in] */ DWORD dwID,
                                                /* [in] */ POINT __RPC_FAR *ppt,
                                                /* [in] */ IUnknown __RPC_FAR *pcmdtReserved,
                                                /* [in] */ IDispatch __RPC_FAR *pdispReserved)
                {
                // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa753264(v%3Dvs.85)
                switch (dwID)
                    {
                    default:                    break;
                    case CONTEXT_MENU_DEFAULT:
                    case CONTEXT_MENU_IMAGE:    pPopupMenu->Popup(ppt->x, ppt->y);
                                                break;
                    }

                return S_OK;
                }

            STDMETHOD(GetHostInfo)(                /* [out][in] */ DOCHOSTUIINFO __RPC_FAR *pInfo)
                {
                if (pInfo == NULL) return E_POINTER;

                pInfo->cbSize = sizeof(DOCHOSTUIINFO);
                pInfo->pchHostCss       = NULL;
                pInfo->pchHostNS        = NULL;
                pInfo->dwDoubleClick    = ::DOCHOSTUIDBLCLK_DEFAULT;  // default action

                pInfo->dwFlags          = 0
                                        | ::DOCHOSTUIFLAG_DIV_BLOCKDEFAULT
                                        | ::DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE
                                        | ::DOCHOSTUIFLAG_THEME
                                        | ::DOCHOSTUIFLAG_DPI_AWARE
                                        ;

                return S_OK;
                }

            STDMETHOD(ShowUI)(                  /* [in] */ DWORD dwID,
                                                /* [in] */ IOleInPlaceActiveObject __RPC_FAR *pActiveObject,
                                                /* [in] */ IOleCommandTarget __RPC_FAR *pCommandTarget,
                                                /* [in] */ IOleInPlaceFrame __RPC_FAR *pFrame,
                                                /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pDoc)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(HideUI)(void)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(UpdateUI)(void)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(EnableModeless)(          /* [in] */ BOOL fEnable)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(OnDocWindowActivate)(        /* [in] */ BOOL fActivate)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(OnFrameWindowActivate)(   /* [in] */ BOOL fActivate)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(ResizeBorder)(            /* [in] */ LPCRECT prcBorder,
                                                /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pUIWindow,
                                                /* [in] */ BOOL fRameWindow)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(TranslateAccelerator)(    /* [in] */ LPMSG lpMsg,
                                                /* [in] */ const GUID __RPC_FAR *pguidCmdGroup,
                                                /* [in] */ DWORD nCmdID)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(GetOptionKeyPath)(        /* [out] */ LPOLESTR __RPC_FAR *pchKey,
                                                /* [in] */ DWORD dw)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(GetDropTarget)(           /* [in] */ IDropTarget __RPC_FAR *pDropTarget,
                                                /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(GetExternal)(                /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDispatch)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(TranslateUrl)(            /* [in] */ DWORD dwTranslate,
                                                /* [in] */ OLECHAR __RPC_FAR *pchURLIn,
                                                /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut)
                {
                return E_NOTIMPL;
                }

            STDMETHOD(FilterDataObject)(        /* [in] */ IDataObject __RPC_FAR *pDO,
                                                /* [out] */ IDataObject __RPC_FAR *__RPC_FAR *ppDORet)
                {
                return E_NOTIMPL;
                }
        };

附加上述處理程序的代碼(在構造函數中):

TDocUIHandler* piDocUIHandler;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
piDocUIHandler = new TDocUIHandler(PopupMenu1);

WB->Navigate("about:blank");
while (WB->Busy) { Application->ProcessMessages(); Sleep(50); }

DelphiInterface<IHTMLDocument2> diDoc = WB->Document;
if (diDoc)
    {
    diDoc->designMode = "on";

    DelphiInterface< ::ICustomDoc> diCustDoc;
    if (SUCCEEDED(WB->Document->QueryInterface( ::IID_ICustomDoc,(void**)&diCustDoc)) && diCustDoc)
        {
        if (SUCCEEDED(diCustDoc->SetUIHandler(piDocUIHandler)))
        {
        //MSGBOX("Success!");
        }
    else
        {
        // FAIL
        }
    }
}

在表單析構函數中:

__fastcall TForm1::~TForm1()
    {
    piDocUIHandler->Release();
    }

在這里找到的代碼: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa753260(v%3Dvs.85)

保存默認的 IDocHostUIHandler。 不確定我是否需要,但那里的指南講述了如何避免 memory 泄漏以執行IObjectWithSite::SetSite(NULL) - 不確定這是否適用於此代碼。

您沒有對TDocUIHandler object 正確執行引用計數。

對象的引用計數最初為 0。您的表單維護對 object 的活動引用(僅供參考,它應該在 class 數據成員中,而不是在全局變量中),但不會相應地增加其引用計數。 因此,當調用SetUIHandler()時,它保存自己對 object 的引用並在其上調用AddRef() ,因此對象的引用計數為 1,即使它實際上有 2 個活動引用。

當您的 Form 析構函數被調用時,它Release()的 object 將 refcount 遞減為 0,因此 object delete本身,即使 WebBrowser 仍然具有對它的活動引用。 當 WebBrowser 正在清理時,它會嘗試Release()它對 object 的引用並崩潰,因為它不知道 object 已經被破壞。

嘗試這個:

IDocHostUIHandler* piDocUIHandler;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
    piDocUIHandler = new TDocUIHandler(PopupMenu1);
    piDocUIHandler->AddRef(); // <-- ADD THIS!!

    ...
}

__fastcall TForm1::~TForm1()
{
    piDocUIHandler->Release();
}

或者:

DelphiInterface<IDocHostUIHandler> piDocUIHandler; // <-- use DelphiInterface instead!

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
    piDocUIHandler = new TDocUIHandler(PopupMenu1); // <-- calls AddRef() for you!
    ...
}

__fastcall TForm1::~TForm1()
{
    piDocUIHandler.Release(); // <-- note '.' not '->' !

    or:

    piDocUIHandler = NULL; // <-- calls Release() for you!
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM