如何: 通过HTML文档对象模型访问文档中的ActiveX控件的属性 .

如何: 通过HTML文档对象模型访问文档中的ActiveX控件的属性

 



此文章的信息应用于:

  • Microsoft Internet Explorer (编程) 版本4.0, 4.01, 4.01 SP1, 4.01 SP2, 5, 5.01, 5.5,6.0


概要

CSDN文档中心文章

用 MSHTML 的一点经验 111222(原作)

说明了如何访问在HTML文档对象模型中的网页的元素、内容。但是,有时候开发者实际上需要访问的是网页中ActiveX控件的属性、方法和事件。例如,你在网页载入之后需要修改/获取MediaPlayer的媒体源,以及控制MediaPlayer的播放。

更多信息

为获得ActiveX控件的接口,我们需要访问文档对象模型。获得文档接口的方法多种多样,比如CHtmlView::GetHtmlDocument,IWebBrowser2::get_Document,IHTMLWindow2::get_document等等,参见111222的文档。这里我直接用一个函数GetDHtmlDocument表示获得这个接口的函数。你可以自己实现这个函数。

通常,我们给控件一个在文档中唯一的ID以便于访问。首先我们要在文档里面找到这个元素,使用ID作为参数。

示例代码: (参考了MFC7.0的源代码)

// Document modified at : Sunday, August 18, 2002 11:04:50 AM , by user : Jiangsheng , from computer : KFB
//通过名字访问元素接口

HRESULT CDHtmlDialog::GetElement(LPCTSTR szElementId, IHTMLElement **pphtmlElement)
{
    return GetElementInterface(szElementId, __uuidof(IHTMLElement), (void **) pphtmlElement);
}
//通过名字访问元素接口的辅助函数,用于返回指定类型的接口
HRESULT CDHtmlDialog::GetElementInterface(LPCTSTR szElementId, REFIID riid, void **ppvObj)
{
    HRESULT hr = E_NOINTERFACE;
    *ppvObj = NULL;
    CComPtr<IDispatch> spdispElem;

    hr = GetElement(szElementId, &spdispElem);

    if (spdispElem)
        hr = spdispElem->QueryInterface(riid, ppvObj);
    return hr;
}
//通过名字访问元素接口的辅助函数,用于访问指定ID的元素接口。如果pBCollection返回TRUE,则返回的是一个IHtmlElementCollection集合,表示指定ID/名称的网页元素不止一个。
HRESULT CDHtmlDialog::GetElement(LPCTSTR szElementId, IDispatch **ppdisp,
                                 BOOL *pbCollection /*= NULL*/)
{
    CComPtr<IHTMLElementCollection> sphtmlAll;
    CComPtr<IHTMLElementCollection> sphtmlColl;
    CComPtr<IDispatch> spdispElem;
    CComVariant varName;
    CComVariant varIndex;
    HRESULT hr = S_OK;
    CComPtr<IHTMLDocument2> sphtmlDoc;
    USES_CONVERSION;

    *ppdisp = NULL;

    if (pbCollection)
        *pbCollection = FALSE;

    hr = GetDHtmlDocument(&sphtmlDoc);
    if (sphtmlDoc == NULL)
        return hr;

    varName.vt = VT_BSTR;
    varName.bstrVal = T2BSTR(szElementId);
    if (!varName.bstrVal)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    hr = sphtmlDoc->get_all(&sphtmlAll);
    if (sphtmlAll == NULL)
        goto Error;
    hr = sphtmlAll->item(varName, varIndex, &spdispElem);
    if (spdispElem == NULL)
    {
        hr = E_NOINTERFACE;
        goto Error;
    }

    spdispElem->QueryInterface(__uuidof(IHTMLElementCollection), (void **) &sphtmlColl);
    if (sphtmlColl)
    {
        if (pbCollection)
            *pbCollection = TRUE;
#ifdef _DEBUG
        else
        {
            TRACE(traceHtml, 0, "Warning: duplicate IDs or NAMEs./n");
            ATLASSERT(FALSE);
        }
#endif

    }
Error:
    if (SUCCEEDED(hr))
    {
        *ppdisp = spdispElem;
        if (spdispElem)
            (*ppdisp)->AddRef();
    }
    return hr;
}
然后我们要访问对象的属性、方法和事件,这就需要从IHtmlElement接口获得对象的接口,这里通过IHtmlObjectElement来访问
//获得ActiveX控件接口,注意ActiveX控件接口和HTML对象元素接口不是同一个接口,你不能直接使用IHtmlObjectElement接口来访问控件
HRESULT CDHtmlDialog::GetControlDispatch(LPCTSTR szId, IDispatch **ppdisp)
{
    HRESULT hr = S_OK;
    CComPtr<IDispatch> spdispElem;

    hr = GetElement(szId, &spdispElem);

    if (spdispElem)
    {
        CComPtr<IHTMLObjectElement> sphtmlObj;

        hr = spdispElem.QueryInterface(&sphtmlObj);
        if (sphtmlObj)
        {
            spdispElem.Release();
            hr = sphtmlObj->get_object(ppdisp);
        }
    }
    return hr;
}
有了Active控件的接口,下面的工作就简单多了,举例来说,如果要访问控件的指定名字的无参数属性,只需简单的调用IDispatch接口的GetIDsOfNames获得属性的DispID,然后调用Invoke方法取得属性
//获得控件属性,通过名字访问
VARIANT CDHtmlDialog::GetControlProperty(LPCTSTR szId, LPCTSTR szPropName)
{
    CComVariant varEmpty;
    CComPtr<IDispatch> spdispElem;

    GetControlDispatch(szId, &spdispElem);
    if (!spdispElem)
        return varEmpty;

    DISPID dispid;
    USES_CONVERSION;
    LPOLESTR pPropName = (LPOLESTR)T2COLE(szPropName);
    HRESULT hr = spdispElem->GetIDsOfNames(IID_NULL, &pPropName, 1, LOCALE_USER_DEFAULT, &dispid);
    if (SUCCEEDED(hr))
        return GetControlProperty(spdispElem, dispid);
    return varEmpty;
}
//设置控件属性,通过名字访问
void CDHtmlDialog::SetControlProperty(LPCTSTR szElementId, LPCTSTR szPropName, VARIANT *pVar)
{
    CComPtr<IDispatch> spdispElem;
    GetControlDispatch(szElementId, &spdispElem);
    if (!spdispElem)
        return;
    DISPID dispid;
    USES_CONVERSION;
    LPOLESTR pPropName = (LPOLESTR)T2COLE(szPropName);
    HRESULT hr = spdispElem->GetIDsOfNames(IID_NULL, &pPropName, 1, LOCALE_USER_DEFAULT, &dispid);
    if (SUCCEEDED(hr))
        SetControlProperty(spdispElem, dispid, pVar);
}
//获得控件属性的辅助函数,通过DispID访问
VARIANT CDHtmlDialog::GetControlProperty(LPCTSTR szId, DISPID dispid)
{
    CComPtr<IDispatch> spdispElem;

    GetControlDispatch(szId, &spdispElem);
    return GetControlProperty(spdispElem, dispid);
}
//设置控件属性的辅助函数,通过DispID访问
void CDHtmlDialog::SetControlProperty(LPCTSTR szElementId, DISPID dispid, VARIANT *pVar)
{
    CComPtr<IDispatch> spdispElem;
    GetControlDispatch(szElementId, &spdispElem);

    SetControlProperty(spdispElem, dispid, pVar);
}

//获得控件属性的实现函数
VARIANT CDHtmlDialog::GetControlProperty(IDispatch *pdispControl, DISPID dispid)
{
    VARIANT varRet;
    varRet.vt = VT_EMPTY;
    if (pdispControl)
    {
        DISPPARAMS dispparamsNoArgs = { NULL, NULL, 0, 0 };
        pdispControl->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
            DISPATCH_PROPERTYGET, &dispparamsNoArgs, &varRet, NULL, NULL);
    }
    return varRet;
}

//设置控件属性的实现函数
void CDHtmlDialog::SetControlProperty(IDispatch *pdispControl, DISPID dispid, VARIANT *pVar)
{
    if (pdispControl != NULL)
    {
        DISPPARAMS dispparams = {NULL, NULL, 1, 1};
        dispparams.rgvarg = pVar;
        DISPID dispidPut = DISPID_PROPERTYPUT;
        dispparams.rgdispidNamedArgs = &dispidPut;

        pdispControl->Invoke(dispid, IID_NULL,
                LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
                &dispparams, NULL, NULL, NULL);
    }
}
实际上,这样的方法效率比较低,因为每次访问都要调用GetIDsOfNames,而GetIDsOfNames是一个很慢的调用。为了优化程序效率,你可以缓存得到的名字->DispID映射,但是推荐的方法是使用类向导(Class Wizard)的从类型库添加类(New Class->From a type library)的功能把控件导入到工程,通过类向导自动生成的COleDispatchDriver派生类来访问属性和方法。这种方法直接使用类型库中生成的DispID来访问属性、方法和事件,所以速度比前面的每次都要调用GetIDsOfNames的方法要快得多。

下面是生成的COleDispatchDriver派生类部分代码示例:

CString CSomeObject::GetText()
{
 CString result;
 InvokeHelper(0x18, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
 return result;
}

void CSomeObject::SetText(LPCTSTR lpszNewValue)
{
 static BYTE parms[] =
  VTS_BSTR;
 InvokeHelper(0x18, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
   lpszNewValue);
}

LPDISPATCH CSomeObject::createNode(const VARIANT& type, LPCTSTR name, LPCTSTR namespaceURI)
{
 LPDISPATCH result;
 static BYTE parms[] =
  VTS_VARIANT VTS_BSTR VTS_BSTR;
 InvokeHelper(0x36, DISPATCH_METHOD, VT_DISPATCH, (void*)&result, parms,
  &type, name, namespaceURI);
 return result;
}

另外一个好处是显而易见的,你可以把麻烦的工作(查找DispID并且调用Invoke)扔给类向导来做,你只需要使用类向导自动生成的类就可以了。

如果你还要处理控件的事件,你应该参考MSDN中的这篇文章

Handling HTML Element Events

捕获ActiveX控件的事件的方法基本和文章中一样,只是你需要捕获事件对象的接口应该是控件接口,而不是元素接口。获得控件的IDispatch接口的方法前面已经说过了。

顺便说一下,在HTML编程中容易犯的错误是混用不同类型的接口,比如

IHTMLElement *pElem = NULL;
if(pAllElem->item(name, name, (LPDISPATCH*)&pElem)==S_OK)
    ......
注意,虽然微软的文档说IHTMLElement是从IDispatch派生的(Inherits from IDispatch),但是这并不代表一些返回IDispatch的方法返回的就是派生的接口,上面的代码就是犯了这个错误,把返回的接口直接当IHTMLElement接口用,可能会出错。正确的访问方式应该是调用返回的IDispatch的QueryInterface接口以获得指定类型的接口指针。参见CDHtmlDialog::GetElement的代码。

参考

要更多信息,单击下面的连接查看CSDN文档库中的文章

用 MSHTML 的一点经验 111222(原作)

如何 :在ActiveX控件中获得顶层IWebBrowser2接口    jiangsheng(翻译)

在对话框中使用网页输入数据 (jiangsheng原创)

单击下面的连接查看MSDN文档库中的文章

Handling HTML Element Events (英文站点)

要更多关于开发基于Web的Internet Explorer解决方案,请访问下列站点:

http://msdn.microsoft.com/workshop/entry.asp(英文站点)

http://msdn.microsoft.com/ie/(英文站点)

http://support.microsoft.com/highlights/iep.asp?FR=0&SD=MSDN(英文站点)

额外的查询关键字:MFC Internet Explorer MSHTML IHTMLElement IHTMLElementCollection IHTMLDocument2 IHTMLObjectElement

关键字: kbActiveX kbCtrl kbIE kbIE400 kbGrpDSInet kbie500 kbDSupport kbie501 kbie550 
文章类型 : kbhowto 
技术 : kbIEsearch kbAudDeveloper kbSDKIESearch kbIE500Search kbSDKIE400 kbSDKIE401 kbSDKIE401SP1 kbSDKIE401SP2 kbSDKIE500 kbSDKIE501 kbSDKIE550 kbIE550Search

 

 

from:http://blog.csdn.net/jiangsheng/article/details/3788

时间: 2024-08-03 09:44:44

如何: 通过HTML文档对象模型访问文档中的ActiveX控件的属性 .的相关文章

能预览word、ppt、pps、txt文档,是什么控件?

问题描述 能预览word.ppt.pps.txt文档,是什么控件? 能预览word.ppt.pps.txt文档,是什么控件!!! 解决方案 楼主指的是在网页中预览吧? 就是实现Office在线编辑网页插件,有时候说的office在线编辑控件也是指的同一类产品,目前有好几家做在线编辑类产品的,PageOffice产品不错,楼主可以看看. 解决方案二: 应该是webview吧 解决方案三: Pages可以预览word.ppt.pps.txt文档

打开 Office XP 或 Office 2003 文档时,系统提示您授予权限的 ActiveX 控件

故障现象: 当一个"适用于"一节中列出的 Microsoft Office 程序中打开文档时,您可能会提示您授予权限的 ActiveX 控件加载或允许这些控件维护的数据保持不变.某些 ActiveX 控件根本不加载.   此外,导入到另一个程序 (如 Microsoft SharePoint 工作组服务网站包含 ActiveX 控件的 Office 文档时,您可能会收到与以下内容类似的警告消息:   此应用程序时可能不安全的 ActiveX 控件初始化.如果您信任此文档的来源,请选择是

在Word 2007文档中选中整个表格

在Word2007文档中,如果需要设置表格属性或删除整个表格,首先需要选中 整个表格.将鼠标指针从表格上划过,然后单击表格左上角的全部选中按钮即可 选中整个表格,或者可以通过在表格内部拖动鼠标选中整个表格,如图 2009010810所示. 图2009010810 单击全部选中按钮 用户还可以在Word2007表格内单击任意单元格,然后在"表格工具 "功能区切换到"布局"选项卡,单击"表"分组 中的"选择"按钮,并在打开的下拉菜

有关用 Swift 访问后端服务器的 API 文档中

本文讲的是有关用 Swift 访问后端服务器的 API 文档中, 我最近开始开发一个全新的项目,并且我正尝试一些新的设计模式,因为我开始投身于 Swift 3.我正使用的一个模式是"请求和响应模型".这个"酷炫"的名字是我为记录这个后台 API 文档中的 Struct (结构体).让我们来看一个例子: import Alamofire protocol Request { var path : String { get } var method : Method {

Word2010文档中应用自动套用格式

  "自动套用格式"功能可以帮助用户根据"自动套用格式"选项中预设的格式迅速格式化选中的Word文档,用户首先需要将"自动套用格式"按钮放置到Word2010文档窗口的快速访问工具栏(参考教程<将"自动套用格式"命令放在快速访问工具栏>).在Word2010文档中应用自动套用格式的步骤如下所述: 第1步,打开Word2010文档窗口,选中需要应用自动套用格式的文本(如果不选中文本,自动套用格式将作用于整篇Word文

如何在java中实现读取一个txt文档中的随机一行

问题描述 如何在java中实现读取一个txt文档中的随机一行 如题,如何在java中实现读取一个txt文档中的随机一行? 主要就是怎么随机读取 解决方案 根据楼上的说法,来总结一下吧,总体来说,就是将文件全部都读取出来,每一行存储到一个数组或集合中,然后再通过产生随机数,来对这个数组或是 集合进行随机的访问.这样一来就解决了 解决方案二: 文本文件只能顺序读,不能随机读.你的需求只能是读取文本文件每一行到一个arraylist,然后得到下标范围,产生一个随机数,取那一行 解决方案三: http:

如何在Word2007文档中批量设置图片格式

很多时候,我们需要在Word文档中插入一些图片,但如何对这些图片进行处理,却是一个比较复杂的问题.例如现在需要将其设置为居中,如果一个一个手工设置居中的话,那效率之低是可想而知,有没有办法也能批量完成呢? 我们可以分两种不同的情况来进行解决,这里以Word 2007为主同时适当结合Word 2003进行说明: 1.嵌入式图片 如果文档中的图片,选中后四周出现了8个黑色的控制块,如图1所示,那么就表示这些图片是属于嵌入式的. 图1 对于嵌入式的图片,无论是Word 2007或是Word 2003,

怎样显示Word 2013文档中的所有格式

通常情况下,Word2013文档中包含有多种格式,包括字体.字号.字符间距等格式设置.用户可以在"显示格式"任务窗格中查看当前Word文档或选定文本的格式.默认情况下,Word2013的功能区中未显示"显示格式"按钮.用户可以将其放置到"快速访问工具栏"中,操作步骤如下所述: 第1步, 打开Word2013文档窗口,依次单击"文件"→"选项"按钮,如图2013080209所示. 图2013080209 单击

在Word 2010文档中设置默认粘贴选项

用户可以在Word 2010文档中设置默认粘贴选项,以适应在各种条件下的粘贴需要,操作步骤如下所述: 第1步,打开Word 2010文档窗口,依次单击"文件"→"选项"按钮,如图2010010802所示. 图2010010802单击"选项"按钮 第2步,在打开的"Word选项"对话框中切换到"高级"选项卡,在"剪切.复制和粘贴"区域可以针对粘贴选项进行设置.默认粘贴选项各项目的含义简述如