从 IPersistMoniker 加载需要很长时间才能加载无法解析 URL
Load from IPersistMoniker takes long time to load unresolvable URL
我正在通过 IPersistMoniker
Load
方法加载本地磁盘驱动器 _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
需要很长时间才能到达 resolve/load。据我了解,CreateURLMonikerEx
应该使用 file:///path/to/executable/
并自动将其添加到这些路径之前,在这种情况下它们会立即失败 - 例如 file:///path/to/executable//test/test.js
。那不会发生。
我还尝试将 image.jpg
移动到子文件夹,然后使用 GetDisplayName
和 BindToStorage
实现创建自定义 IMoniker
接口,从自定义路径加载图像.然而,对于以 //
或 /
开头的路径,它不会执行相同的操作。即使我通过 *ppszDisplayName
参数在 GetDisplayName
中输出 file:///path/to/executable/
。
如何避免长时间加载此类不可用的链接(丢弃它们),或将它们重定向到上述本地路径?
我找到了在 *ppszDisplayName
中使用 about:blank
的部分解决方案,但它不会加载具有有效路径 image.jpg
的图像,因为它会将它们加载为 [=35] =] 这又是无效路径。
此外 - 我尝试添加 IDocHostUIHandler
接口,实现 Invoke
方法 (DISPID_AMBIENT_DLCONTROL
) 和 pVarResult->lVal = DLCTL_NO_SCRIPTS | DLCTL_NO_JAVA | DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_DLACTIVEXCTLS | DLCTL_NO_FRAMEDOWNLOAD | DLCTL_FORCEOFFLINE;
- 它阻止图像下载完全,但仍然会检查以 //
或 /
.
开头的链接的 20-30 秒
Update - this doesn't work well!
The code below doesn't work well! The problem is - it loses <BODY>
tag attributes. BODY tag turns out entirely empty after loading. I
ended up loading the message using IHTMLDocument2.write
method.
See:
在这里花费了大量时间并且没有任何形式的指导之后,我相信在链接无效时无法避免等待 20-30 秒。我找到了另一个解决方案,如果有人想补充这个解决方案,请随时这样做。
相反,我必须做的是创建一个 CLSID_HTMLDocument
的实例(IHTMLDocument3
或 IHTMLDocument2
接口),然后将文档加载到该容器中并在之前解析链接和他们一起做任何事。这描述于:
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)
解析文档URL并修复无效的URL后,实际可以saved/displayed 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
/src
、script
/src
、base
/href
等。 ) 保存并载入 TWebBrowser
.
我现在只需要看看解析后的HTMLIHTMLDocument2
是否可以直接赋值给TWebBrowser
而不用重新加载,那是另外一个问题了(见-)
我正在通过 IPersistMoniker
Load
方法加载本地磁盘驱动器 _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
需要很长时间才能到达 resolve/load。据我了解,CreateURLMonikerEx
应该使用 file:///path/to/executable/
并自动将其添加到这些路径之前,在这种情况下它们会立即失败 - 例如 file:///path/to/executable//test/test.js
。那不会发生。
我还尝试将 image.jpg
移动到子文件夹,然后使用 GetDisplayName
和 BindToStorage
实现创建自定义 IMoniker
接口,从自定义路径加载图像.然而,对于以 //
或 /
开头的路径,它不会执行相同的操作。即使我通过 *ppszDisplayName
参数在 GetDisplayName
中输出 file:///path/to/executable/
。
如何避免长时间加载此类不可用的链接(丢弃它们),或将它们重定向到上述本地路径?
我找到了在 *ppszDisplayName
中使用 about:blank
的部分解决方案,但它不会加载具有有效路径 image.jpg
的图像,因为它会将它们加载为 [=35] =] 这又是无效路径。
此外 - 我尝试添加 IDocHostUIHandler
接口,实现 Invoke
方法 (DISPID_AMBIENT_DLCONTROL
) 和 pVarResult->lVal = DLCTL_NO_SCRIPTS | DLCTL_NO_JAVA | DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_DLACTIVEXCTLS | DLCTL_NO_FRAMEDOWNLOAD | DLCTL_FORCEOFFLINE;
- 它阻止图像下载完全,但仍然会检查以 //
或 /
.
Update - this doesn't work well!
The code below doesn't work well! The problem is - it loses
<BODY>
tag attributes. BODY tag turns out entirely empty after loading. I ended up loading the message usingIHTMLDocument2.write
method.See:
在这里花费了大量时间并且没有任何形式的指导之后,我相信在链接无效时无法避免等待 20-30 秒。我找到了另一个解决方案,如果有人想补充这个解决方案,请随时这样做。
相反,我必须做的是创建一个 CLSID_HTMLDocument
的实例(IHTMLDocument3
或 IHTMLDocument2
接口),然后将文档加载到该容器中并在之前解析链接和他们一起做任何事。这描述于:
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)
解析文档URL并修复无效的URL后,实际可以saved/displayed 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
/src
、script
/src
、base
/href
等。 ) 保存并载入 TWebBrowser
.
我现在只需要看看解析后的HTMLIHTMLDocument2
是否可以直接赋值给TWebBrowser
而不用重新加载,那是另外一个问题了(见-