开发过基于MFC的windows程序设计的朋友都知道,MFC提供的窗体效果和windows本身一样,无法提供很丰富的表现形式,也没有办法随便更改,
虽然有一些界面组件和界面控件,例如cj60,activeSkin之类的,我觉得也不够灵活。有没有直接用HTML做界面,利用DHTML内部元素与应用程序交互的可能性呢?我已经看到一些程序使用了这些技术,通过IHTMLDocument接口,通过HTML hook技术来进行交互。可我很难在网上找到比较详尽的技术介绍,请问大家有什么自己的想法吗?!
大家积极讨论,分大大的有!

解决方案 »

  1.   

    sure
    Norton Antivirus UI is HTML pages
    use CDHtmlDialog for dialog based app and CHtmlView for SDI/MDI
      

  2.   

    to jiangsheng:
    我也猜想norton杀毒是html的UI,
    但是我觉得CDHtmlDialog和CHtmlView并未解决他们的交互问题。我在网上只查到过hook html部分消息,利用navigate2来模拟按钮提交信息,但有没有更成体系的交互方式呢?我查找不到比较详尽的IHtmlDocument2的用法,总觉得MSDN的函数太过于简单。
      

  3.   

    http://www.csdn.net/develop/read_article.asp?id=21702
      

  4.   

    谢谢jiangsheng!
    学习研究中to arvid_gs: 一起研究 :)
      

  5.   

    看这个软件,是用java编的,但是采用了DHTML技术,不知道它怎么能结合的那么好
    http://www.mobilediyer.com/
      

  6.   

    我找到我要的东西了。研究中http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/mshtml/tutorials/sink.asp
      

  7.   

    HRESULT hr;
    IHTMLDocument2 * pDoc2 = NULL; // Is this the DocumentComplete event for the top frame window?
    // Check COM identity: compare IUnknown interface pointers.
    hr = m_web.GetDocument()->QueryInterface(IID_IHTMLDocument2,(void**)&pDoc2);
    if(SUCCEEDED(hr))
    {
    IHTMLElementCollection* pElemColl = NULL; hr = pDoc2->get_all(&pElemColl);
    if (SUCCEEDED(hr))
    { IDispatch* pElemDisp = NULL;
    IHTMLElement* pElem = NULL;
    _variant_t varID("T1");
    _variant_t varIdx(0l); hr = pElemColl->item(varID, varIdx, &pElemDisp); if (SUCCEEDED(hr))
    {
    hr = pElemDisp->QueryInterface(IID_IHTMLElement, (void**)&pElem); if (SUCCEEDED(hr))
    { IHTMLInputTextElement * pInputElem;
    hr = pElem->QueryInterface(IID_IHTMLInputTextElement,(void**)&pInputElem);
    BSTR p;
    pInputElem->get_value(&p);
    AfxMessageBox(CString(p));
    pElem->Release();
    }
    pElemDisp->Release();
    } pElemColl->Release();
    }
    pDoc2->Release(); }
    如果我页面是正常的,那就一切正常但如果页面是about blank之类的,或找不到服务器之类的,只要一到
    hr = pElemDisp->QueryInterface(IID_IHTMLElement, (void**)&pElem);这句
    就报错呢?而且 try也没用,一到这程序马上退出!? 高手点解?
      

  8.   

    学习!!!这才是csdn应该多多看到的帖子。
      

  9.   

    的确,使用Web方式做的确很好,但是安全性真的很头疼。
    我不想把页面暴露给用户,但是又没有办法...我想将页面和页面里用到的css、image、js都做成资源封装在exe程序中,不知是否可行?
      

  10.   

    每一步都需要检查操作结果和返回的指针
    可以用res协议访问
      

  11.   

    to ccnuxjg:我看到有内存html技术,这样就不会有html在程序外部,但是这样做对只有一个html倒是好说,如果有其他独立的js,css之类的就不方便了。to ablefirst: 我看到vs.net里面的CDHTMLDialog,确实就是我想要的,但是我不想装vs.net..  CHtmlView并不能让你很好的控制里面的元素,捕获里面的消息啊to jiangsheng:我能查到错误的地点,但它居然是QueryInterface时候直接报的access violation,虽然这个错误可以通过防止页面出错来避免,但好像还是不方便。
    to all:谢谢大家的关注!
    我还发现一个很怪的问题,就是一个html页面在IE下面会根据操作系统的不同而不同,例如XP,按钮等会很好看,但是到了我的webbrowser里面,就恢复那种普通的样子,是怎么回事?还有,border="0"对webbrowser没有用?? 我能通过什么方式来设置它的border=null?
      

  12.   

    to jiangsheng & all:
    看完下面的资料,我的疑问是难道要写一个 com组件并实现IDispatch接口才能完成消息通知的回调吗?Receiving Element Events
    Each element in the DHTML Object Model supports an outgoing HTMLElementEvents2 interface. This interface defines the events that an HTML element can fire. You implement this interface to provide an event sink, which is a Component Object Model (COM) object that implements an outgoing interface and is used as the mechanism for firing events.Note  Interfaces implemented by a server usually have their methods called by the client, but to fire an event, the server calls the respective method on the client event sink. These interface are called outgoing interfaces. A COM object that implements an outgoing interface is also known as a connectable object.
    The following steps are required to receive events from an outgoing interface:Implement the event sink.The event sink implements the appropriate outgoing interface and methods. Internet Explorer event interfaces are dispinterfaces, so calls to event methods are made through IDispatch::Invoke. This means that you only need to implement the IDispatch interface to handle events.Determine if the server is a connectable object.Call QueryInterface to retrieve a pointer to the IConnectionPointContainer interface.Find the appropriate connection point. Call the IConnectionPointContainer::FindConnectionPoint method to find the connection point you need. For Internet Explorer WebBrowser Control events, such as DWebBrowserEvents2::DocumentComplete, this is DWebBrowserEvents2. For element events, this is HTMLElementEvents2. You can also call the IConnectionPointContainer::EnumConnectionPoints to enumerate through all the connection points a server supports.Advise the connection point that you want to receive events.Using the IConnectionPoint interface pointer returned in the previous step, call IConnectionPoint::Advise, passing the IUnknown interface pointer of your event sink. 
    Note  The connectable object will use the IUnknown interface pointer to query the client for the event sink interface. If the event sink does not support the outgoing interface, Internet Explorer will query the client for the IDispatch interface.
    When you no longer want to receive events, you can call the IConnectionPoint::Unadvise method, passing the cookie you received from the call to IConnectionPoint::Advise.
    The following sample code demonstrates how to begin receiving HTML element events for an element on an HTML page.Example:void CMyClass::ConnectEvents(IHTMLElement* pElem)
    {
        HRESULT hr;
        IConnectionPointContainer* pCPC = NULL;
        IConnectionPoint* pCP = NULL;
        DWORD dwCookie;    // Check that this is a connectable object.
        hr = pElem->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);    if (SUCCEEDED(hr))
        {
            // Find the connection point.
            hr = pCPC->FindConnectionPoint(DIID_HTMLElementEvents2, &pCP);        if (SUCCEEDED(hr))
            {
                // Advise the connection point.
                // pUnk is the IUnknown interface pointer for your event sink
                hr = pCP->Advise(pUnk, &dwCookie);            if (SUCCEEDED(hr))
                {
                    // Successfully advised
                }            pCP->Release();
            }        pCPC->Release();
        }

    The following sample code demonstrates how you would detect the firing of an HTMLElementEvents2::onclick event in your implementation of IDispatch::Invoke.Example:STDMETHODIMP CEventSink::Invoke(DISPID dispidMember,
                                    REFIID riid,
                                    LCID lcid,
                                    WORD wFlags,
                                    DISPPARAMS* pdispparams,
                                    VARIANT* pvarResult,
                                    EXCEPINFO* pexcepinfo,
                                    UINT* puArgErr)
    {
        switch (dispidMember)
        {
            case DISPID_HTMLELEMENTEVENTS2_ONCLICK:
            OnClick();
            break;        default:
            break;
        }    return S_OK;
    }
      

  13.   

    老外偷懒,用变通的方法来响应事件  --___--!http://www.chrismaunder.net/shell/dlgdhtmlevents.asp
      

  14.   

    Yes
    see BEGIN_EVENTSINK_MAP in MFC
      

  15.   

    to  jiangsheng:
    我特意装了.net去看VC7里面的CDHTMLDialog怎么用,发现好像需要自己添加宏来完成HTML到application的绑定,为什么没有了class wizzard呢?我还是不知道eventsink该怎么写,现在糊里糊涂的下面是从VC7里面拿来的代码,这个可以用吗?
    class CDHtmlEventSink : public CDHtmlSinkHandler
    {
    public:
    HRESULT ConnectToConnectionPoint(IUnknown *punkObj, REFIID riid, DWORD *pdwCookie);
    void DisconnectFromConnectionPoint(IUnknown *punkObj, REFIID riid, DWORD& dwCookie);
    STDMETHOD(CDHtmlSinkHandlerQueryInterface)(REFIID iid, LPVOID* ppvObj);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
    STDMETHOD(GetTypeInfoCount)(UINT *pctinfo);
    STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
    STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
    STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
    virtual BOOL DHtmlEventHook(HRESULT *phr, DISPID dispIdMember, DISPPARAMS *pDispParams,
    VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
    virtual const DHtmlEventMapEntry* GetDHtmlEventMap() = 0;
    virtual HRESULT GetDHtmlDocument(IHTMLDocument2 **pphtmlDoc) = 0;
    int FindDHtmlEventEntry(const DHtmlEventMapEntry *pEventMap, DISPID dispIdMember,
       IHTMLElement **ppsrcElement);
    };class CDHtmlElementEventSink : public IDispatch
    {
    public:
    CDHtmlEventSink *m_pHandler;
    CComPtr<IUnknown> m_spunkElem;
    DWORD m_dwCookie; CDHtmlElementEventSink(CDHtmlEventSink *pHandler, IDispatch *pdisp);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
    STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj);
    STDMETHOD(GetTypeInfoCount)(UINT *pctinfo);
    STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
    STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
    STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS *pdispparams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
    HRESULT Advise(LPUNKNOWN pUnkObj, REFIID iid);
    HRESULT UnAdvise(LPUNKNOWN pUnkObj, REFIID iid);
    };
      

  16.   

    VC7没有class wizzard的确很不方便。比较简单的方法是用MFC的宏,不然自己写处理类实在太麻烦
      

  17.   

    to jiangsheng:
    是啊,在vc7里面有宏可以将消息映射过来,我打算仔细研究一下看看到底是怎么处理的,并在VC6中实现它。等待我的好消息吧 :)vc7的代码
    HRESULT OnButtonTest(IHTMLElement *pElement);
    BEGIN_DHTML_EVENT_MAP(CHTMLUINETDlg)
    DHTML_EVENT_ONCLICK(_T("Button1"), OnButtonTest)
    END_DHTML_EVENT_MAP()
    HRESULT CHTMLUINETDlg::OnButtonTest(IHTMLElement* pElement)
    {
    CComBSTR p;
    pElement->get_tagName(&p);
    AfxMessageBox(CString(p));
    return S_OK;
    }
      

  18.   

    摘自潘爱民的<<ATL Internals>>书评在实际应用中,用ActiveX控制或者Windows的标准控制构造新的控制是一项很有用的技术,这就是复合控制,ATL也支持复合控制,它把对话框的功能和ActiveX控制的功能结合起来。在构造复合控制的时候,我们可以指定一个对话框模板,把设计阶段完成的界面模板引入到复合控制中,这是一个非常简便的构造界面单元的方法。
    另一个功能更为强大的构造界面单元的方法是HTML控制,它利用Web浏览器控制直接把HTML页面封装成一个新的ActiveX控制。由于它把HTML页面作为界面内容,所以使用的时候非常灵活,我们可以在HTML页面中嵌入脚本,可以使用多种字体,可以访问HTML文档的对象模型。
    这一章的内容也比较广泛,但是它把上一章介绍的ActiveX控制与实际的应用结合起来了。而且通过这些内容的介绍,我们可以拓宽视野把ActiveX控制应用得更加灵活,把Web引入到我们的桌面程序上来,或者把桌面程序的功能引入到Web当中去。这两章的内容相对比较独立,它们构成了用ATL开发和使用ActiveX控制的主体。如果读者关注MSJ杂志的话,可以在1999年的2、3、4月期上找到有关这些内容的一个连载,文章的名字为:“Write ActiveX Controls Using Custom Interfaces Provided by ATL 3.0”。
      

  19.   

    我已经可以用VC6完成消息的绑定,内容的获取,代码如下/////////////////////////////////////SinkHandler.h//////////////
    #include <mshtml.h>
    #include <mshtmdid.h>
    #include <mshtmhst.h>
    #include <atlbase.h>
    class CEventSink : IDispatch
    {
    public:
    HRESULT ConnectToConnectionPoint(IUnknown *punkObj, REFIID riid, DWORD *pdwCookie);
    void DisconnectFromConnectionPoint(IUnknown *punkObj, REFIID riid, DWORD& dwCookie);
    STDMETHOD(CSinkHandlerQueryInterface)(REFIID iid, LPVOID* ppvObj);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
    STDMETHOD(GetTypeInfoCount)(UINT *pctinfo);
    STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj);
    STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
    STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
    STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
    };
    /////////////////////////////////////SinkHandler.cpp//////////////#include "SinkHandler.h"
    HRESULT CEventSink::ConnectToConnectionPoint(IUnknown *punkObj, REFIID riid, DWORD *pdwCookie)
    {
    return AtlAdvise(punkObj, (IDispatch *) this, riid, pdwCookie);
    }
    void CEventSink::DisconnectFromConnectionPoint(IUnknown *punkObj, REFIID riid, DWORD& dwCookie)
    {
    AtlUnadvise(punkObj, riid, dwCookie);
    }
    STDMETHODIMP CEventSink::CSinkHandlerQueryInterface(REFIID iid, LPVOID* ppvObj)
    {
    if (!ppvObj)
    return E_POINTER; *ppvObj = NULL;
    if (IsEqualIID(iid, __uuidof(IDispatch)) || IsEqualIID(iid, __uuidof(IUnknown)))
    {
    *ppvObj = (IDispatch *) this;
    AddRef();
    return S_OK;
    }
    return E_NOINTERFACE;
    }
    STDMETHODIMP CEventSink::QueryInterface(REFIID iid, LPVOID* ppvObj)
    {
    return CSinkHandlerQueryInterface(iid,ppvObj);
    }
    STDMETHODIMP_(ULONG) CEventSink::AddRef()
    {
    return 1;
    }
    STDMETHODIMP_(ULONG) CEventSink::Release()
    {
    return 1;
    }
    STDMETHODIMP CEventSink::GetTypeInfoCount(UINT *pctinfo)
    {
    *pctinfo = 0;
    ATLTRACENOTIMPL(_T("CEventSink::GetTypeInfoCount"));
    }
    STDMETHODIMP CEventSink::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
    {
    *ppTInfo = NULL;
    ATLTRACENOTIMPL(_T("CEventSink::GetTypeInfo"));
    }
    STDMETHODIMP CEventSink::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
    {
    ATLTRACENOTIMPL(_T("CEventSink::GetIDsOfNames"));
    }
    STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
    {
        switch ( dispIdMember )
        {
        case DISPID_HTMLELEMENTEVENTS_ONCLICK:
    ::MessageBox(NULL,"点击了此处","提示",NULL);
            break;
        default:
            break;
        }
        return S_OK;
    }
    //为id为T1的elment增加onclick事件,m_web为webbrowser包装类(classwizzard自动生产)void CHTMLUIDlg::SetEventHook() 
    {
    HRESULT hr;
    IHTMLDocument2 * pDoc2 = NULL;
    hr = m_web.GetDocument()->QueryInterface(IID_IHTMLDocument2,(void**)&pDoc2);
    if(SUCCEEDED(hr))
    {
    IHTMLElementCollection* pElemColl = NULL;
    hr = pDoc2->get_all(&pElemColl);
    if (SUCCEEDED(hr))
    {
    IDispatch* pElemDisp = NULL;
    IHTMLElement* pElem = NULL;
    _variant_t varID("T1");
    _variant_t varIdx(0l);
    hr = pElemColl->item(varID, varIdx, &pElemDisp);
    if (SUCCEEDED(hr))
    {
    hr = pElemDisp->QueryInterface(IID_IHTMLElement, (void**)&pElem);
    if (SUCCEEDED(hr))
    {
    IHTMLInputTextElement * pInputElem;
    hr = pElem->QueryInterface(IID_IHTMLInputTextElement,(void**)&pInputElem);
    //CComBSTR p("hello world!");
    //pInputElem->put_value(p);
    ConnectEvents((IHTMLElement *)pInputElem);
    pElem->Release();
    }
    pElemDisp->Release();
    }
    pElemColl->Release();
    }
    pDoc2->Release();
    }
    }//函数里面的dwCookie其实需要保存下来,用来unadvise用,CEventSink也需要保存下来,记得要delete它. 因演示,所以这里未加处理.void CHTMLUIDlg::ConnectEvents(IHTMLElement *pElem)
    {
    HRESULT hr;
    IConnectionPointContainer* pCPC = NULL;
    IConnectionPoint* pCP = NULL;
    DWORD dwCookie;
    CEventSink * sink = new CEventSink;
    // Check that this is a connectable object.
    hr = pElem->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
    if (SUCCEEDED(hr))
    {
    AfxMessageBox("QueryInterface Successfully!");
    // Find the connection point.
    hr = pCPC->FindConnectionPoint(DIID_HTMLButtonElementEvents, &pCP);
    if (SUCCEEDED(hr))
    {
    AfxMessageBox("FindConnectionPoint Successfully!");
    // Advise the connection point.
    hr = pCP->Advise((IUnknown *)sink, &dwCookie);
    if (SUCCEEDED(hr))
    {
    AfxMessageBox("Connect to the Object Successfully!");
    }
    else
    {
    if(hr == E_POINTER)
    AfxMessageBox("E_POINTER");
    if(hr == CONNECT_E_ADVISELIMIT )
    AfxMessageBox("CONNECT_E_ADVISELIMIT");
    if(hr == CONNECT_E_CANNOTCONNECT  )
    AfxMessageBox("CONNECT_E_CANNOTCONNECT ");
    }
    pCP->Release();
    }
    pCPC->Release();
    }
    }