IWebBrowser2: как заставить ссылки открываться в новом окне? - PullRequest
4 голосов
/ 28 мая 2010

Документация MSDN по Настройка веб-браузера объясняет, как предотвратить открытие новых окон и как отменить навигацию. В моем случае мое приложение содержит IWebBrowser2, но я не хочу, чтобы пользователь переходил на новые страницы в моем приложении. Вместо этого я хотел бы открыть все ссылки в новом окне IE. Требуемое поведение: пользователь щелкает ссылку, и открывается новое окно с этим URL.

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

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

Ответы приветствуются!

Ответы [ 5 ]

4 голосов
/ 18 июня 2010

Извините, что отвечаю на свой вопрос здесь, но в надежде, что это может кому-то помочь, в конечном итоге я использовал IHTMLDocument напрямую, а не IWebBrowser. IWebBrowser - это расширенный набор IHTMLDocument, а модель навигации, реализованная IWebBrowser, не настраивается в той степени, в которой я хотел.

На самом деле я включил MS Developer Support, и этот подход был их рекомендацией. Они говорят, что это то, что Outlook использует для электронной почты на основе HTML, и это тот пользовательский опыт, который я хотел эмулировать. Они также подтвердили, что не существует надежного способа фильтрации событий OnBeforeNavigate, возникающих в результате действий пользователя, и событий, возникающих в результате действия сценария.

Надеюсь, это поможет всем, кто сталкивается с такими же проблемами. Не было слишком сложно портировать код для использования IHTMLDocument. Если вы в конечном итоге делаете это, вы также можете найти способ выяснить, когда документ будет загружен. Для этого подключите HTMLDocumentEvents вместо DWebBrowserEvents и найдите событие DISPID_HTMLDOCUMENTEVENTS_ONREADYSTATECHANGE. Он не говорит вам, что такое состояние готовности; вам нужно вызвать IHTMLDocument :: get_readyState и проанализировать полученную строку. Тупой, но вот, пожалуйста.

2 голосов
/ 02 июня 2010

Я предполагаю здесь, но еще один подход может состоять в том, чтобы вести подсчет событий навигации, увеличивая счетчик на DISPID_BEFORENAVIGATE2 и уменьшая его в случаях DISPID_NAVIGATECOMPLETE2 и DISPID_NAVIGATEERROR. Имея это в виду, вы можете предположить, что всякий раз, когда вы получаете DISPID_BEFORENAVIGATE2 и ваш счетчик равен нулю, это фактическая навигация пользователя / вызов ссылки.

Я понятия не имею, сработает ли этот подход, или это те события, которые вам нужны, чтобы заставить его работать, но его стоит изучить.

1 голос
/ 28 мая 2010

Вместо этого вы можете попробовать другой подход и физически добавить атрибут target="_blank" ко всем <a> тегам в отображаемом документе.

Этот подход предполагает ожидание DISPID_DOCUMENTCOMPLETE и последующее использование IHTMLDocument3::getElementsByTagName() для извлечения всех элементов привязки. Затем вы должны использовать IHTMLElement::setAttribute(), чтобы установить target="_blank" для каждого из них.

0 голосов
/ 23 февраля 2012

Вы можете привязать событие onclick до завершения документа при создании браузера в OnCreate () с помощью IHTMLDocument2 :: put_onclick ():

#include <comutil.h>

ClickEvents<RootFrame> clickEvents;
_variant_t clickDispatch;
clickDispatch.vt = VT_DISPATCH;
clickDispatch.pdispVal = &clickEvents;

CComQIPtr<IDispatch> dispatch;
hr = webBrowser2->get_Document(&dispatch);
ASSERT_EXIT(SUCCEEDED(hr), "webBrowser->get_Document(&dispatch)");

CComQIPtr<IHTMLDocument2> htmlDocument2;
hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");

htmlDocument2->put_onclick(clickDispatch);

ClickEvents класс реализует IDispatch, вам нужно только реализовать метод Invoke, в остальном вернуть E_NOTIMPL:

HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    HRESULT hr;

    CComQIPtr<IWebBrowser2> webBrowser2;
    hr = rootFrame->GetDlgControl(rootFrame->rootview.GetDlgCtrlID(), IID_IWebBrowser2, (void**) &webBrowser2);
    ASSERT_EXIT(SUCCEEDED(hr), "rootframe->GetDlgControl(IID_IWebBrowser2) failed");

    CComQIPtr<IDispatch> dispatch;
    hr = webBrowser2->get_Document(&dispatch);
    ASSERT_EXIT(SUCCEEDED(hr), "webBrowser2->get_Document(&dispatch)");

    CComQIPtr<IHTMLDocument2> htmlDocument2;
    hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
    ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");

    CComQIPtr<IHTMLWindow2> htmlWindow2;
    hr = htmlDocument2->get_parentWindow((IHTMLWindow2**) &htmlWindow2);
    ASSERT_EXIT(SUCCEEDED(hr), "htmlDocument2->get_parentWindow(&htmlWindow2)");

    CComQIPtr<IHTMLEventObj> htmlEvent;
    hr = htmlWindow2->get_event(&htmlEvent);
    ASSERT_EXIT(SUCCEEDED(hr), "htmlWindow2->get_event(&htmlEvent)");

    CComQIPtr<IHTMLElement> htmlElement;
    hr = htmlEvent->get_srcElement(&htmlElement);
    ASSERT_EXIT(SUCCEEDED(hr), "htmlEvent->get_srcElement(&htmlElement)");

    CComBSTR hrefAttr(L"href");
    VARIANT attrValue;
    VariantInit(&attrValue);
    hr = htmlElement->getAttribute(hrefAttr, 0 | 2, &attrValue); // 0 = case insensitive, 2 = return BSTR
    ASSERT_EXIT(SUCCEEDED(hr), "htmlElement->getAttribute()");

    wchar_t href[2084]; // maximum url length in IE, http://support.microsoft.com/kb/208427
    wcsncpy_s(href, _countof(href), attrValue.bstrVal, _TRUNCATE);

    if (!rootFrame->IsURLAllowed(href)) {

        VARIANT variant;
        variant.vt = VT_BOOL;
        variant.boolVal = VARIANT_FALSE;
        htmlEvent->put_returnValue(variant);

        ShellExecute(0, L"open", href, 0, 0, SW_SHOWNORMAL);
    }

    return S_OK;
}

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

Это обрабатывает все ссылки, даже если они были добавлены к документу с использованием javascript.

То же самое должно быть сделано с событиями "onsubmit" для форм.

Я также думаю, что у меня есть решение для переадресации "window.location" в javascript, я еще не тестировал его, но скоро опробую и обновлю этот ответ. Вы можете использовать комбинацию событий onunload и onbeforeunload вместе с DWebBrowserEvents2 :: BeforeNavigate2 (), после вызова onunload / onbeforeunload вы будете знать, что пользователь покидает текущую страницу, поэтому теперь в BeforeNavigate2 () вы можете отменить ее. Вы можете прикрепить события выгрузки, используя IHTMLWindow2 :: put_onunload () и IHTMLWindow2 :: put_onbeforeunload ().

См. Источники полного решения для "onclick" ниже.

AttachClickEvents в BrowserFrame:

http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/browser_frame.h?r=709d00b991b5#125

Вызов в ClickEvents (IDispatch):

http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/click_events.h?r=a5b0b350c933#132

0 голосов
/ 28 мая 2010

Мне кажется, что если вы хотите «открыть все ссылки в новом окне IE», это означает, что вы хотите, чтобы открытие новых окон происходило в другом процессе. Самый простой способ сделать это: использовать способ CreateObject("InternetExplorer.Application") (см. Другой вопрос, который решает проблему, противоположную вашему вопросу: InternetExplorer.Application объект и контейнер cookie ). Таким образом, вы получите лучшую изоляцию от вашего приложения, а пользователь, который нажимает на ссылку, получает все возможности, существующие в IE. Вы должны продолжить использование событий BeforeNavigate2, чтобы узнать момент, когда должно быть открыто «новое окно IE».

...