65.9K
CodeProject 正在变化。 阅读更多。
Home

Web Win32/WTL 混合

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (14投票s)

2002年8月30日

4分钟阅读

viewsIcon

139351

downloadIcon

2092

如何在 IExplorer 和 WTL 代码之间实现双向通信路径

引言

通常,当我们看到像 MS Money 这样的应用程序时,它看起来像 Internet Explorer 的布局,并且具有 Win32 应用程序的功能,我们不知道微软是否对我们隐藏了一些技术概念(我不知道任何,我向你保证 ;)),或者我们是否无法正确阅读微软 MSDN 文档。

本文将介绍完全重用 IExplorer 的方法,并尝试解释此示例的实现。实际上,它只是一个示例,而不是要用于“真实”应用程序的“真实”库。我只想展示一种执行此类实现的方法。代码没有完整的错误处理,因此,如果您想在“真实”应用程序中使用它,请记住,您需要审查一些关键步骤。

在进入下一部分之前,我邀请您下载示例源代码并进行编译。我认为这将有助于您更好地理解以下部分。

编译

代码有两个版本,一个是 VC6/WTL7.0,另一个是 VC7/WTL7.0。

您应该已安装最新的 Platform SDK,否则将在接口声明(如 IHTMLElement2)中收到错误。

您可以在 msdn.microsoft.com 上免费下载 Platform SDK。

编译程序后,您可能会收到“库未注册”的错误;如果发生这种情况,您只需转到项目目录并执行 RegTLib WebWin32Sample.tlb。有时,您可能需要以这种方式注册 COM 服务器:WebWin32Sample.exe /RegServer,只运行一次。

最终类

首先,我想向您展示“宿主”类是如何工作的,然后我将解释每一段代码和底层技巧。

class CWebWin32SampleView : public 
                CWindowImpl<CWebWin32SampleView,CAxWindow>
{
public:
    DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

    // Our internet explorer control
    CWTLIExplorer m_pBrowser;

    // Our vars to Exchange information with HTML page...
    CString m_FirstName;
    CString m_LastName;
    CString m_Address;
    CString m_Country;
    BOOL m_ILikeThisSample;

    // html-hosted-page-form-based DDX
    BEGIN_HTML_DDX()
        BEGIN_FORM( "testForm" ) // Our Form Name ( at HTML page )
          // Our exchange definitions...
          DDX_HTML_STRING( m_FirstName ,"firstName" ) 
          DDX_HTML_STRING( m_LastName  ,"lastName" )
          DDX_HTML_STRING( m_Address,"address" )
          DDX_HTML_LISTBOX( m_Country, "country" );
          DDX_HTML_CHECKBOX( m_ILikeThisSample, "ILikeIt" )
       END_FORM()
     END_HTML_DDX()

    // HTML Handlers
    // Here we can define HTML handlers hooks related to element at the 
    // page loaded
    BEGIN_HTML_MSG_MAP(CWebWin32SampleView,m_pBrowser)
        // Set an event handler on buttonSumbmit.onclick 
        COMMAND_HTML_HANDLER( ID_HTML_CLICK, "buttonSubmit", OnSubmit )
    END_HTML_MSG_MAP()

    LRESULT OnSubmit( BOOL & bHandled )
    {
        // Do Data Exchange with HTML page...
        DDX_HTML_EXCHANGE( m_pBrowser, false /* false=get | true=set */ )

        // MessageBox text Composite 
        CString totalMessage = m_FirstName + CString(" - ") + m_LastName + 
                                                CString(" - " ) + m_Address;

        if ( m_ILikeThisSample )
             totalMessage += CString("\n\nYou have checked it." );
        else
             totalMessage += CString("\n\nYou have not checked it." );

        totalMessage += CString("\n\nCountry Selected: ") + m_Country;

        // Show a message box
        MessageBox( totalMessage,
                "From CWebWin32SampleView::OnSubmit event handler.", 
                MB_ICONINFORMATION );

        // set handled
        bHandled = true;
 
        return 0;
    }


    BOOL PreTranslateMessage(MSG* pMsg);

    BEGIN_MSG_MAP(CWebWin32SampleView)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        MESSAGE_HANDLER(WM_SIZE, OnSize)

        // We receive this message when IExplorer is on a ready state for us
        // and we can Set handlers to the page...
        MESSAGE_HANDLER(WM_HTML_SETHANDLERS /* = WM_USER + 1 */ , 
                                                OnHTMLSetHandlers )
    END_MSG_MAP()

    LRESULT OnHTMLSetHandlers(UINT /*uMsg*/, WPARAM /*wParam*/, 
                            LPARAM /*lParam*/, BOOL& /*bHandled*/ )
    {
        // Activate event handlers map
        SET_HTML_MESSAGE_MAP();
        return 0;
    }

// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, 
//                         LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, 
//                         HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, 
//                                         BOOL& /*bHandled*/)

    LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, 
                    LPARAM /*lParam*/, BOOL& /*bHandled*/);
    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                    LPARAM /*lParam*/, BOOL& /*bHandled*/);
};

BEGIN_HTML_MAP() ... END_HTML_MAP()

实际上,IExplorer 中的所有 HTML 元素都派生自 IHTMLElement。在此接口中,我们可以找到使用某些方法在元素级别获取/设置事件处理程序的可能性,例如 put_onclick( VARIANT handler),variant 应包含一个 VT_IDISPATCH 变量,该变量引用一个 COM 对象。当触发此事件时,IExplorer 会尝试调用此 IDispatch 对象的默认方法。这非常适合我们的目的,因此我们需要定义一个包含默认方法的 COM 对象,当调用此方法时,我们只需“跳转”到我们的事件处理程序。

我用来执行此工作的对象是 WebWin32.WebWin32EventHandler,它只有两个方法 SetHandler(LONG,LONG)CallHandler,后者是对象的默认方法。

SetHandler(LONG,LONG) 用于设置“回调”地址。为此,我们需要传递我们的对象地址(this)和回调入口点的成员地址。

我们需要进行一些代码级别的“破解”,将持有成员地址的 Pointer 成员变量转换为 LONG

// ( This is a simulation )
[...]
DWORD pointer_part_2 = (DWORD) this;
DWORD pointer_part_1;
[...]

// pointer member prototype
LRESULT (CWebWin32SampleView::*pVar) ( BOOL & );

// load our member pointer
pVar = OnSubmit;

// extract address
memcpy( &pointer_part_1, ( char * )  &pVar, 4 );
[...]
pHandler->SetHandler( pointer_part_1, pointer_part_2 );
[...]
SetHandlerIntoHtmlElementOnClick( pHandler, "addressField" );
[...]

稍后,当 IExplore 触发我们的方法时,我们需要执行另一个“技巧”来“跳转”到我们的“回调”。

STDMETHODIMP CWebWin32EventHandler::CallHandler(void)
{
    // __thiscall asm implementation

    DWORD pMember;
    DWORD pThis;

    pThis   = m_pThis;
    pMember = m_pMember;

    BOOL bHandled=false;
        
    // this the same like do pObject->MemeberToCall( bParam ) 
    // but we don't have
    // problems about what type of object or method prototype was defined, 
    // this is a generic "thiscall"
    _asm
    {
        lea eax,[bHandled]      ; BOOL & 
        push eax                ; push it
        mov ecx,[pThis]         ; "this" object into ecx
        call pMember            ; call the member pointer
    }
    return S_OK;
}

这就是我们所需要知道的,以便将事件从 HTML 元素触发到我们的代码。

BEGIN_HTML_DDX() ... END_HTML_DDX()

由于我们拥有 IExplorer 实例的引用,因此这是最简单的事情,我们只需要查询当前文档的集合以找到正确的元素并查询其中的值。

Web -> Win32 路径

正如您在示例中看到的,通过按下 HTML 按钮,您可以隐藏/显示树视图,设置状态栏文本等。

好了,我们可以通过两种方式完成这项工作:使用上面描述的处理程序,或者使用另一种技术。

您可以使用 CAxWindow::SetExternalDisptach() 方法放置对象引用(COM 对象双接口)。一旦您将引用传递给此方法,您就可以通过 Explorer 页面的脚本代码访问此对象的 o方法 external.MyMethod)()

为此所需的步骤是

EVENTFN CWTLIExplorer::OnDocumentComplete( IDispatch* pDisp,  VARIANT* URL )
{
    [.....]

    CAxWindow::SetExternalDispatch( (IDispatch*) _Module.m_LibraryObject );

    // ignore about:blank
    if ( _bstr_t( bstrLocation ) != _bstr_t("about:blank") )
        // Notify parent window we are ready and loaded...
    CWindow(GetParent()).SendMessage( WM_HTML_SETHANDLERS ,0,0 );

}

一旦设置了主对象库,正如您在 sample.html 脚本代码中所见,您就可以调用 o方法。

<script>
     [...]
     window.external.MyMessageBox "Hello!","Bye!"
     [...]
     window.external.HideTree() 
     [...]
     window.external.ShowTree() 
     [...]
     window.external.statusbartext = text
     [...]
     window.external.AddChildToTree( window.document.all("newitem").value )
</script>

您可以使用 VBScript 或 JScript,这没问题。

结束语

我确信我很快就会更新这篇文章,所以任何评论都将不胜感激。

好了,就这样,我希望您觉得这篇文章有用且有帮助。如果您认为您发现了任何错误,请随时与我联系或自行解决,我将非常乐意修复任何问题。再见!!

© . All rights reserved.