从 IPersistMoniker 加载需要很长时间才能加载无法解析的 URL

Posted

技术标签:

【中文标题】从 IPersistMoniker 加载需要很长时间才能加载无法解析的 URL【英文标题】:Load from IPersistMoniker takes long time to load unresolvable URL 【发布时间】:2021-11-29 15:03:57 【问题描述】:

我正在通过IPersistMonikerLoad方法加载本地磁盘驱动器_test.htm文件。据我所知,它应该将相对 URL 的路径添加为基本路径。问题是 - 它没有这样做。相反,它需要很长时间来尝试解析来自 Internet 的路径,直到它放弃(大约 20-30 秒)。我想要的是在检测到无法解析的路径后立即放弃(因为它无论如何都是本地磁盘文件)。

这是我正在加载的 html 示例:

<html>
  <head>
    <script src="//test/test.js"></script>
  <head>
  <body>
    <img src="image.jpg">
    <img src="/image.jpg">
    <img src="//image.jpg">
  </body>
</html>

没有错误检查的简化代码(C++ Builder):

WideString      URL = "file:///" + StringReplace(ExtractFilePath(Application->ExeName), "\\", "/", TReplaceFlags() << rfReplaceAll) + "_test.htm";
TCppWebBrowser* WB  = CppWebBrowser1;

DelphiInterface<IMoniker> pMoniker;
OleCheck(CreateURLMonikerEx(NULL, URL.c_bstr(), &pMoniker, URL_MK_UNIFORM));

DelphiInterface<IHTMLDocument2> diDoc2 = WB->Document;
DelphiInterface<IPersistMoniker> pPrstMnkr;
OleCheck(diDoc2->QueryInterface(IID_IPersistMoniker, (LPVOID*)&pPrstMnkr));

DelphiInterface<IBindCtx> pBCtx;
OleCheck(CreateBindCtx(0, &pBCtx));

pPrstMnkr->Load(0, pMoniker, pBCtx, STGM_READWRITE);

问题 - image.jpg 加载正常,但路径 //test/test.js/image.jpg//image.jpg 需要很长时间才能解决/加载。据我了解,CreateURLMonikerEx 应该使用file:///path/to/executable/ 并自动将其添加到这些路径中,在这种情况下它们会立即失败——例如file:///path/to/executable//test/test.js。这不会发生。

我还尝试将 image.jpg 移动到子文件夹,然后使用从自定义路径加载图像的 GetDisplayNameBindToStorage 实现创建自定义 IMoniker 接口。但是,对于以/// 开头的路径,它不会做同样的事情。即使我通过*ppszDisplayName参数在GetDisplayName中输出file:///path/to/executable/

如何避免长时间加载此类不可用的链接(丢弃它们),或将它们重定向到上述本地路径?

我找到了在*ppszDisplayName 中使用about:blank 的部分解决方案,但它不会加载具有有效路径image.jpg 的图像,因为它会将它们加载为about:image.jpg,这又是无效路径。

另外 - 我尝试添加 IDocHostUIHandler 接口和 Invoke 方法的实现 (DISPID_AMBIENT_DLCONTROL) 与 pVarResult-&gt;lVal = DLCTL_NO_SCRIPTS | DLCTL_NO_JAVA | DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_DLACTIVEXCTLS | DLCTL_NO_FRAMEDOWNLOAD | DLCTL_FORCEOFFLINE; - 它完全阻止图像的下载,但仍然检查 20-30 秒对于以/// 开头的链接。

【问题讨论】:

【参考方案1】:

更新 - 效果不佳!

下面的代码不能正常工作!问题是 - 它失去了&lt;BODY&gt; 标签属性。加载后,BODY 标签完全为空。一世 最终使用IHTMLDocument2.write 方法加载消息。

见:Assigning IHTMLDocument2 instance to a TWebBrowser instance

在这里花了很多时间并且没有任何形式的指导之后,我相信当链接无效时,无法避免等待 20-30 秒。我找到了另一个解决方案,如果有人想补充这个解决方案,请随时这样做。

相反,我要做的是创建CLSID_HTMLDocument 的实例(IHTMLDocument3IHTMLDocument2 接口),然后将文档加载到该容器中并在对链接进行任何操作之前对其进行解析。这在以下位置进行了描述:

https://docs.microsoft.com/en-us/previous-versions/aa703592(v=vs.85)

这也有帮助:

How to load html contents from stream and then how to create style sheet to display the html file in preview pane (like HTML preview handler)

解析文档网址并修复无效网址后,可以在实际的TWebBrowser中保存/显示。

粗略的解决方案(C++ Builder):

try
    
    DelphiInterface<IHTMLDocument2> diDoc2;
    OleCheck(CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (void**)&diDoc2));

    DelphiInterface<IPersistStreamInit> diPersist;
    OleCheck(diDoc2->QueryInterface(IID_IPersistStreamInit, (void**)&diPersist));
    OleCheck(diPersist->InitNew());

    DelphiInterface<IMarkupServices> diMS;
    OleCheck(diDoc2->QueryInterface(IID_IMarkupServices, (void**)&diMS));

    DelphiInterface<IMarkupPointer> diMkStart;
    DelphiInterface<IMarkupPointer> diMkFinish;

    OleCheck(diMS->CreateMarkupPointer(&diMkStart));
    OleCheck(diMS->CreateMarkupPointer(&diMkFinish));

    // ...Load from file or memory stream into your WideString here...

    DelphiInterface<IMarkupContainer> diMC;
    OleCheck(diMS->ParseString(WideString(MsgHTMLSrc).c_bstr(), 0, &diMC, diMkStart, diMkFinish));

    DelphiInterface<IHTMLDocument2> diDoc;
    OleCheck(diMC->QueryInterface(IID_PPV_ARGS(&diDoc)));

    DelphiInterface<IHTMLElementCollection> diCol;
    OleCheck(diDoc->get_all(&diCol));

    long ColLen = 0;
    OleCheck(diCol->get_length(&ColLen));

    for (int i = 0; i < ColLen; ++i)
        
        DelphiInterface<IDispatch> diItem;
        diCol->item(OleVariant(i), OleVariant(i), &diItem);

        DelphiInterface<IHTMLElement> diElem;
        OleCheck(diItem->QueryInterface(IID_IHTMLElement, (void**)&diElem));

        WideString wTagName;
        OleCheck(diElem->get_tagName(&wTagName));

        if (StartsText("img", wTagName))
            
            OleVariant vSrc;
            OleCheck(diElem->getAttribute(OleVariant("src"), 4, vSrc));

            // Make changes to vSrc here....

            // And save it back to src
            OleCheck(diElem->setAttribute(OleVariant("src"), vSrc, 0));
            
        else if (StartsText("script", wTagName)) 
            
            // More parsing here...
            
        
    
catch (EOleSysError& e)
    
    // Process exception as needed
    
catch (Exception& e)
    
    // Process exception as needed
    

在完整解析所有必需元素(img/srcscript/srcbase/href 等)后保存并加载到TWebBrowser

我现在只需要看看解析后的 H​​TML IHTMLDocument2 是否可以直接分配给 TWebBrowser 而无需再次加载,但这是另一个问题(参见 - Assigning IHTMLDocument2 instance to a TWebBrowser instance)

【讨论】:

以上是关于从 IPersistMoniker 加载需要很长时间才能加载无法解析的 URL的主要内容,如果未能解决你的问题,请参考以下文章

从图像 URL 加载图像需要很长时间才能显示

谷歌图表需要很长时间才能加载

角度Web应用程序需要很长时间才能加载

截断具有许多子分区的表需要很长时间

BigQuery - 删除重复记录有时需要很长时间

PHP页面需要很长时间才能加载