在我的WinForms项目中,我打开一个CefSharp浏览器并加载一个不受我控制的外部URL。通常,此URL在外部应用程序中作为iframe打开。
该页面使用window.frameElement
与其父窗口进行交互,并在window.frameElement
上调用某个函数。
在CefSharp中,我无法删除window.frameElement
。
还有其他方法可以知道页面何时在window.frameElement
上调用某个函数。
答案 0 :(得分:1)
这将是相当hacky,但我无论如何分享。我创建了一个对我有用的简单示例,我希望它也能为您提供帮助。
JavaScript方面只是一个普通的HTML,带有一个按钮,可以在点击时调用事件处理程序:
<强>的index.html 强>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<script src="app.js"></script>
</head>
<body>
<button onclick="onClick()">Click me</button>
</body>
</html>
事件处理程序只使用一些参数调用随机函数(甚至不必存在frameElement
):
<强> app.js 强>
function onClick() {
var x = window.frameElement.greet("Hello", "world");
}
C#端有一个ChromiumWebBrowser
指向我页面的地址:
<强> Form1.Designer.cs 强>
this.browser = new CefSharp.WinForms.ChromiumWebBrowser("http://localhost:3000/index.html");
我订阅FrameLoadEnd
事件:
this.browser.FrameLoadEnd += BrowserOnFrameLoadEnd;
处理程序将一些JavaScript代码注入主/顶部框架(我们的主页):
<强> Form1.cs的强>
private void BrowserOnFrameLoadEnd(object sender, FrameLoadEndEventArgs frameLoadEndEventArgs)
{
if (frameLoadEndEventArgs.Frame.IsMain)
{
frameLoadEndEventArgs.Frame.ExecuteJavaScriptAsync(@"
(function() {
Object.defineProperty(window, 'frameElement', {
writable: true
});
window.frameElement = {};
window.frameElement = new Proxy(window.frameElement, {
get: function (target, propKey, receiver) {
return function(...args) {
window.frameElementInterceptor.intercept(propKey, args);
};
}
});
})();");
}
}
注入的代码使用ES6 Proxy修改window.frameElement
以捕获属性getter。这样,当原始页面window.frameElement.someMethod(arg1, arg2)
时,我可以注入我的自定义逻辑。
我注入的自定义逻辑是使用被调用函数的名称和传入的参数调用frameElementInterceptor
的{{1}}方法。
回到C#方面,我们使用CEFSharp浏览器注册一个JS对象,并在JS的全局范围内将其作为intercept
提供:
frameElementInterceptor
现在所有部分都已到位,每当我点击页面中的按钮时,我的C#拦截器都会收到消息:
警告:虽然这个简单的例子有效,但我确信这里没有考虑各种边缘情况,所以我会非常谨慎地将它投入生产。
此外,我认为更好的解决方案将涉及CefSharp方面的代码更改。他们可以注册从[ComVisible(true)]
public class FrameElementInterceptor
{
public void Intercept(string propKey, object[] args)
{
MessageBox.Show($@"Function '{propKey}' was called with arguments: [{string.Join(", ", args)}]",
@"Function call intercepted");
}
}
public Form1()
{
InitializeComponent();
// .....
browser.RegisterJsObject("frameElementInterceptor", new FrameElementInterceptor());
}
派生的类型,并启用覆盖DynamicObject
上的现有对象(如window
)。据我所知,自this question以来没有任何进展。