基于Internet Explorer的WPF WebBrowser控件会遇到一些键盘和焦点问题以及内存泄漏问题.作为这些问题的替代解决方案,我们正在考虑在基于HTML编辑的WPF/C#项目中托管Chromium而不是WebBrowser控件的可用选项.此前已经提出了类似的问题.我已经阅读了答案并完成了自己的研究,但我希望从生产质量项目中实际使用以下任何选项的人那里获得更多反馈:
它看起来非常合适,但我不喜欢这个项目不是开源的事实,并且不能轻易获得完整的资源.此外,它可能对我们的项目来说太过分了,因为屏幕外渲染不是我们真正依赖的东西.
用于CEF的Chromium Embedded Framework(CEF)和.NET绑定
这可能是目前可用的最佳选择.该项目似乎活跃且活跃,目前与Chrome v27同步.CEF3使用Chrome多进程架构.看起来Adobe似乎正在给它一些支持.
虽然它的最初目的是成为IE和Firefox的HTML5插件,但它实际上也可以作为独立的ActiveX控件使用,所以我可以将它包装起来用于WPF.它公开了足够的API以与内部网页(onmessage, addEventListener/removeEventListener, postMessage)进行交互.我知道谷歌将停止使用 Chrome Frame,但我认为这些来源将保留在Chromium存储库中.使用最新的Chromium代码更新它并不困难,我们可以完全控制它.
不完全是基于Chromium而且不使用V8引擎,所以它不是一个真正的选择.
还有其他我可能忽略的选择吗?
如果有人与上述任何选项分享她/他在实际生产质量的WPF项目中的经验,我将不胜感激.您是否有任何集成,许可或部署影响?谢谢.
[编辑] 我还要感谢artlung通过提供慷慨的赏金提议来提升这个问题.
这不是"如何在没有等待的情况下安全地调用C#中的异步方法"的副本.
我如何很好地抑制以下警告?
警告CS4014:由于未等待此调用,因此在完成调用之前,将继续执行当前方法.考虑将'await'运算符应用于调用的结果.
一个简单的例子:
static async Task WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}
static async Task StartWorkAsync()
{
    WorkAsync(); // I want fire-and-forget 
    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}
Run Code Online (Sandbox Code Playgroud)
我尝试过但不喜欢的事情:
static async Task StartWorkAsync()
{
    #pragma warning disable 4014
    WorkAsync(); // I want fire-and-forget here
    #pragma warning restore 4014
    // ...
}
static async Task StartWorkAsync()
{
    var ignoreMe = WorkAsync(); // I want fire-and-forget here
    // ...
}
Run Code Online (Sandbox Code Playgroud)
更新后,由于原始接受的答案 …
此问题已由EF数据上下文 - 异步/等待和多线程触发.我已经回答了那个,但没有提供任何最终的解决方案.
最初的问题是,有许多有用的.NET API(如Microsoft Entity Framework   DbContext),它们提供了设计用于的异步方法await,但它们被记录为不是线程安全的.这使它们非常适合在桌面UI应用程序中使用,但不适用于服务器端应用程序.[编辑]  这可能实际上并不适用DbContext,这是微软关于EF6线程安全的声明,自己判断. [/ EDITED] 
还有一些已建立的代码模式属于同一类别,例如调用WCF服务代理OperationContextScope(在此处和此处询问),例如:
using (var docClient = CreateDocumentServiceClient())
using (new OperationContextScope(docClient.InnerChannel))
{
    return await docClient.GetDocumentAsync(docId);
}
Run Code Online (Sandbox Code Playgroud)
这可能会失败,因为OperationContextScope在其实现中使用线程本地存储.
问题的根源AspNetSynchronizationContext是在异步ASP.NET页面中使用,以便从ASP.NET线程池中使用更少的线程来满足更多HTTP请求.使用时AspNetSynchronizationContext,await可以在与启动异步操作的线程不同的线程上对延续进行排队,同时将原始线程释放到池中,并可用于提供另一个HTTP请求.这大大提高了服务器端代码的可扩展性.该机制在It's All About the SynchronizationContext中有详细描述,必须阅读.因此,虽然没有涉及并发API访问,但潜在的线程切换仍然阻止我们使用上述API.
我一直在考虑如何在不牺牲可扩展性的情况下解决这个问题.显然,恢复这些API的唯一方法是维护可能受线程切换影响的异步调用范围的线程关联.
假设我们有这样的线程亲和力.这些调用中的大多数都是IO绑定的(没有线程).当异步任务处于挂起状态时,它所源自的线程可用于提供另一个类似任务的延续,该结果已经可用.因此,它不应该过多地损害可伸缩性.这种方法并不新鲜,事实上,Node.js成功使用了类似的单线程模型 …
我注意到using我的代码中嵌套语句的级别最近有所增加.究其原因,可能是因为我使用越来越多的async/await格局,这往往增加了至少一个以上using的CancellationTokenSource或CancellationTokenRegistration.
那么,如何减少嵌套using,所以代码看起来不像圣诞树?之前已经提出了类似的问题,我想总结一下我从答案中学到的东西.
using而不缩进.一个假的例子:using (var a = new FileStream())
using (var b = new MemoryStream())
using (var c = new CancellationTokenSource())
{
    // ... 
}
Run Code Online (Sandbox Code Playgroud)
这可能有用,但通常会有一些代码using(例如,创建另一个对象可能为时过早):
// ... 
using (var a = new FileStream())
{
    // ... 
    using (var b = new MemoryStream())
    {
        // ... 
        using (var c = new CancellationTokenSource())
        {
            // ... 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
IDisposable)的对象组合成单个对象using,例如:// …Run Code Online (Sandbox Code Playgroud) 如果任何正在运行的任务抛出异常,我想让Task.WaitAll()爆发,这样我就不必等待60秒才能完成.我该如何实现这种行为?如果WaitAll()无法实现,那么还有其他c#功能或解决方法吗?
Task task1 = Task.Run(() => throw new InvalidOperationException());
Task task2 = ...
...
try
{
    Task.WaitAll(new Task[]{task1, task2, ...}, TimeSpan.FromSeconds(60));
}
catch (AggregateException)
{
    // If any exception thrown on any of the tasks, break out immediately instead of wait all the way to 60 seconds.
}
Run Code Online (Sandbox Code Playgroud) 我想等待手动重置事件,超时并观察取消.我想出了类似下面的东西.手动重置事件对象由我无法控制的API提供.有没有办法在不接受和阻止ThreadPool线程的情况下实现这一点?
static Task<bool> TaskFromWaitHandle(WaitHandle mre, int timeout, CancellationToken ct)
{
    return Task.Run(() =>
    {
        bool s = WaitHandle.WaitAny(new WaitHandle[] { mre, ct.WaitHandle }, timeout) == 0;
        ct.ThrowIfCancellationRequested();
        return s;
    }, ct);
}
// ...
if (await TaskFromWaitHandle(manualResetEvent, 1000, cts.Token))
{
    // true if event was set
}
else 
{
    // false if timed out, exception if cancelled 
}
Run Code Online (Sandbox Code Playgroud)
将帖子显然,这是有道理的使用RegisterWaitForSingleObject.我试试看.
显然,WPF WebBrowser控件存在一些严重的键盘和焦点问题.我整理了一个简单的WPF应用程序,只是一个WebBrowser和两个按钮.该应用程序加载一个非常基本的可编辑HTML标记(<body contentEditable='true'>some text</body>)并演示以下内容:
标签是行为不端的.用户需要按两次Tab键才能在WebBrowser中查看插入符号(文本光标)并能够键入.
当用户切换远离应用程序时(例如,使用Alt-Tab),然后返回,插入符号消失,她根本无法键入.需要物理鼠标单击WebBrowser的窗口客户端区域才能取回插入符号和击键.
不一致的是,一个虚线的焦点矩形出现在WebBrowser周围(当标签时,但不是点击时).我找不到摆脱它的方法(FocusVisualStyle="{x:Null}"没有帮助).
在内部,WebBrowser永远不会得到关注.对于逻辑焦点(FocusManager)和输入焦点(键盘)都是如此.将Keyboard.GotKeyboardFocusEvent和FocusManager.GotFocusEvent从不事件被解雇的web浏览器(虽然他们都对按钮做在同一个焦点范围).即使当插入符号是web浏览器内,FocusManager.GetFocusedElement(mainWindow)指向先前聚焦元件(按钮)和Keyboard.FocusedElement是null.同时,((IKeyboardInputSink)this.webBrowser).HasFocusWithin()回报true.
我会说,这样的行为几乎太功能无法实现,但这就是它的运作方式.我可能想出一些黑客来解决它并将其与原生WPF控件一起排成一行TextBox.我仍然希望,也许我在这里错过了一些模糊而简单的东西.有没有人处理过类似的问题?任何有关如何解决这个问题的建议将不胜感激.
此时,我倾向于为基于HwndHost的 WebBrowser ActiveX控件开发内部WPF包装器.我们还在考虑 WebBrowser的其他替代方案,例如Chromium Embedded Framework(CEF).
VS2012项目可以从这里下载,以防有人想玩它.
这是XAML:
<Window x:Class="WpfWebBrowserTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Width="640" Height="480" Background="LightGray">
    <StackPanel Margin="20,20,20,20">
        <ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
        <WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="300"/>
        <Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" …Run Code Online (Sandbox Code Playgroud) 我们有一些遗留的HTML内容,我们必须在兼容模式下呈现.这些要求来自我们的客户,他们希望他们的基于HTML的报告(其中一些是在IE6天内创建的)无论浏览器版本或底层技术如何都能完全相同地查看和打印.与此同时,我们希望将标准模式和HTML5用于我们的其他网络应用程序.
一个明显的解决方案是以<iframe>兼容模式托管旧内容.以下似乎跨浏览器工作:
main.html(在标准模式下):
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title></title>
    <style type="text/css">
        body {
            font-family: Arial;
            font-size: 9pt;
            font-style: italic;
            font-weight: bold;
        }
    </style>
    <script type="text/javascript">
        window.onload = function () {
            info.firstChild.data = "document.compatMode: " + document.compatMode;
            // test frame's HTML5 API: document.getSelection()
            setInterval(function () {
                var selection = document.getElementById("contentFrame").contentDocument.getSelection();
                var selectedNode = selection.focusNode;
                if (selectedNode)
                    info2.firstChild.data = "Selected node: " + selectedNode.nodeName + ", offset: " + selection.focusOffset;
                else
                    info2.firstChild.data = "";
            }, 500); …Run Code Online (Sandbox Code Playgroud) 我发现TaskCompletionSource.SetResult();在返回之前调用等待任务的代码.在我的情况下,导致死锁.
这是一个在普通版本中启动的简化版本 Thread
void ReceiverRun()
    while (true)
    {
        var msg = ReadNextMessage();
        TaskCompletionSource<Response> task = requests[msg.RequestID];
        if(msg.Error == null)
            task.SetResult(msg);
        else
            task.SetException(new Exception(msg.Error));
    }
}
Run Code Online (Sandbox Code Playgroud)
代码的"异步"部分看起来像这样.
await SendAwaitResponse("first message");
SendAwaitResponse("second message").Wait();
Run Code Online (Sandbox Code Playgroud)
Wait实际上嵌套在非异步调用中.
SendAwaitResponse(简化)
public static Task<Response> SendAwaitResponse(string msg)
{
    var t = new TaskCompletionSource<Response>();
    requests.Add(GetID(msg), t);
    stream.Write(msg);
    return t.Task;
}
Run Code Online (Sandbox Code Playgroud)
我的假设是第二个SendAwaitResponse将在ThreadPool线程中执行,但它会在为ReceiverRun创建的线程中继续.
无论如何设置任务的结果而不继续等待代码?
应用程序是一个控制台应用程序.
c# multithreading asynchronous task-parallel-library async-await
我正在处理一个托管对象在async方法中间过早完成的情况.
这是一个业余爱好家庭自动化项目(Windows 8.1,.NET 4.5.1),我向非托管第三方DLL提供C#回调.在某个传感器事件时调用回调.
为了处理这个事件,我使用async/await了一个简单的自定义awaiter(而不是TaskCompletionSource).我这样做的部分原因是为了减少不必要的分配数量,但主要是出于好奇心作为学习练习.
下面是我所拥有的非常剥离的版本,使用Win32计时器队列计时器来模拟非托管事件源.让我们从输出开始:
Press Enter to exit... Awaiter() tick: 0 tick: 1 ~Awaiter() tick: 2 tick: 3 tick: 4
请注意我的等待者在第二次打勾后如何最终确定.这是出乎意料的.
代码(控制台应用程序):
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        static async Task TestAsync()
        {
            var awaiter = new Awaiter();
            //var hold = GCHandle.Alloc(awaiter);
            WaitOrTimerCallbackProc callback = (a, b) =>
                awaiter.Continue();
            IntPtr timerHandle;
            if (!CreateTimerQueueTimer(out timerHandle, 
                    IntPtr.Zero, 
                    callback, 
                    IntPtr.Zero, 500, 500, 0))
                throw new System.ComponentModel.Win32Exception( …Run Code Online (Sandbox Code Playgroud) .net c# garbage-collection task-parallel-library async-await
c# ×8
async-await ×6
.net ×3
wpf ×2
asp.net ×1
asynchronous ×1
focus ×1
html ×1
html5 ×1
iframe ×1
javascript ×1
keyboard ×1
task ×1
wpf-controls ×1