在Control.Invoke上的UI线程中抛出C#Invalid Handle

时间:2015-09-17 09:23:56

标签: c# .net multithreading

我在这个问题上抓了几天...... 首先,让我们提出一些背景信息:

我编写了一个通过NamedPipeServer / Client与Windows服务进行通信的应用程序,并在Windows窗体中显示内容。

问题出在我的System.Threading.Timer回调中。根据固定的间隔减去执行时间,在自己执行结束时动态重新调度此回调。

在这个回调中,我正在执行管道客户端请求,并根据答案我用Invoke(经典的交叉线程消息)更新UI。

代码:

private void OnTimerSamplesEvent(object data)
{
    Stopwatch watch = new Stopwatch();
    watch.Start();

    try
    {      
        PipeClient pipe = new PipeClient("MyPipe");
        PipeMessage.Message msg_struct  = new PipeMessage.Message();
        msg_struct.magic                = PipeMessage.Message.Magic;
        msg_struct.command              = PipeMessage.Message.Command.GetSamples;
        PipeMessage msg                 = new PipeMessage(msg_struct);

        byte[] reply_struct = pipe.Send(msg.ToByteArray());
        if (reply_struct != null)
        {
            PipeMessage reply = new PipeMessage(reply_struct);
            if (reply.m_Message.IsValid()
             && reply.m_Message.command == PipeMessage.Message.Command.GetSamples
             && reply.m_Message.getsamples.samples != null)
            {
                //Crashes here
                UpdateListView(this.listViewMySamples, reply.m_Message.getsamples.samples, new List<string>(), new List<string>() { "Misc" });
            }
        }
    }
    catch(Exception ex)
    {
        Logger.Log("MainForm::OnTimerSamplesEvent: " + ex.Message);
    }
    finally
    {
        // reschedule worker
        m_TimerSamples.Change(Math.Max(0, SamplesCheckIntervalms - watch.ElapsedMilliseconds), Timeout.Infinite);
    }
}

private void UpdateListView(ListView lv, List<MySample> samples, List<string> type_included, List<string> type_excluded)
{
    // Update view with modified samples / Remove out of sync
    int items_count = DelegatesUI.LVGetItemsCount(this, lv);    //<-- Crashes here

    // some other code, but commented out for testing
    ... 
}

private delegate int _LVGetItemsCount(ListView lv);
static private int _lvGetItemsCount(ListView lv) 
{ 
    return lv.Items.Count; 
}
static public int LVGetItemsCount(Form f, ListView lv)
{
    if (f.InvokeRequired)
    {
        object ret = f.Invoke(new _LVGetItemsCount(_lvGetItemsCount), lv);
        return (ret == null) ? 0 : (int)ret;
    }
    else
        return _lvGetItemsCount(lv);
}

这很好用,但由于某种原因,执行后约5分钟主线程在System.IO .__ Error.WinIOError 异常(无效句柄)崩溃。当我查看工作线程状态时,它正在等待Invoke(WaitHandle.WaitOne),可能会等待UI线程发出信号其句柄。

UI调用堆栈:

ERROR_INVALID_HANDLE   
à System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
à System.IO.__Error.WinIOError()
à System.Threading.EventWaitHandle.Set()
à System.Windows.Forms.Control.ThreadMethodEntry.Complete()
à System.Windows.Forms.Control.InvokeMarshaledCallbacks()
à System.Windows.Forms.Control.WndProc(Message& m)
à System.Windows.Forms.ScrollableControl.WndProc(Message& m)
à System.Windows.Forms.ContainerControl.WndProc(Message& m)
à System.Windows.Forms.Form.WndProc(Message& m)
à System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
à System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
à System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
à System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
à System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
à System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
à System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
à System.Windows.Forms.Application.Run(Form mainForm)
à agentui.Program.Main() dans c:\MBAM\cosmos\cosmos-agent\ui\Program.cs:ligne 17
à System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
à System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
à Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
à System.Threading.ThreadHelper.ThreadStart()

工人调用堆栈:

mscorlib.dll!System.Threading.WaitHandle.WaitOne(long timeout, bool exitContext)    Inconnu
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout, bool exitContext) Inconnu
System.Windows.Forms.dll!System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle waitHandle) Inconnu
System.Windows.Forms.dll!System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control caller, System.Delegate method, object[] args, bool synchronous) Inconnu
System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate method, object[] args) Inconnu
>   agentui.exe!SharpUtils.DelegatesUI.InvokeAndClose(System.Windows.Forms.Control c, System.Delegate invok, object[] args) Ligne 38    C#
agentui.exe!SharpUtils.DelegatesUI.LVGetItemsCount(System.Windows.Forms.Form f, System.Windows.Forms.ListView lv) Ligne 541 C#
agentui.exe!agentui.MainForm.UpdateListView(System.Windows.Forms.ListView lv, System.Collections.Generic.List<agentsvc.MySample> samples, System.Collections.Generic.List<string> type_included, System.Collections.Generic.List<string> type_excluded) Ligne 142   C#
agentui.exe!agentui.MainForm.OnTimerSamplesEvent(object data) Ligne 283 C#
mscorlib.dll!System.Threading._TimerCallback.TimerCallback_Context(object state)    Inconnu
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Inconnu
mscorlib.dll!System.Threading._TimerCallback.PerformTimerCallback(object state) Inconnu

问题是:什么可能导致WaitHandle被关闭? 此外,由于这种情况发生在UI线程我无法抓住任何内容,因为我没有这里的用户代码(框架是&#34; Application.Run(new MainForm() );&#34)

欢迎任何想法。谢谢:))

编辑:我刚刚注意到GC线程在Microsoft.Win32.SafeHandles.SafeWaitHandle 上抛出了一堆ReleaseHandleFailed,因为该管道工作线程中有代码。不知道它是否是平常的东西(看起来不合法)......

GC线程:

>   mscorlib.dll!System.Runtime.InteropServices.SafeHandle.Dispose(bool disposing)  Inconnu
mscorlib.dll!System.Runtime.InteropServices.SafeHandle.Finalize()   Inconnu

工作人员主题:

[Transition Managé à Natif] 
>   System.Core.dll!System.IO.Pipes.PipeStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafePipeHandle handle, byte[] buffer, int offset, int count, System.Threading.NativeOverlapped* overlapped, out int hr)   Inconnu
System.Core.dll!System.IO.Pipes.PipeStream.ReadCore(byte[] buffer, int offset, int count)   Inconnu
System.Core.dll!System.IO.Pipes.PipeStream.Read(byte[] buffer, int offset, int count)   Inconnu
agentui.exe!agentui.PipeClient.Send(byte[] message, int TimeOut) Ligne 172  C#
agentui.exe!agentui.MainForm.OnTimerSamplesEvent(object data) Ligne 275 C#
mscorlib.dll!System.Threading._TimerCallback.TimerCallback_Context(object state)    Inconnu
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Inconnu
mscorlib.dll!System.Threading._TimerCallback.PerformTimerCallback(object state) Inconnu

编辑2:我注意到我是否评论了从Pipe读取的代码(抛出ReleaseHandleFailed的那个)我不再有这个问题。

所以我也要发布那篇文章:

客户:

    public byte[] Send(byte[] message, int TimeOut = 1000)
    {
        byte[] answer = null;

        // Write query
        NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", m_PipeName, PipeDirection.InOut);  
        try
        {
            // The connect function will indefinitely wait for the pipe to become available
            // If that is not acceptable specify a maximum waiting time (in ms)
            pipeStream.Connect(TimeOut);
            pipeStream.ReadMode = PipeTransmissionMode.Message;
            pipeStream.Write(message, 0, message.Length);
        }
        catch (Exception ex)
        {
            Logger.Log("PipeClient: " + ex.Message);
            try { pipeStream.Close(); } catch(Exception) {}
            return answer;
        }

        // Read reply
        try
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter bw = new BinaryWriter(ms))
                {
                    // We read chunks while the message is incomplete
                    do
                    {
                        // Read the incoming chunk
                        byte[] buffer = new byte[256];
                        int bytesRead = pipeStream.Read(buffer, 0, buffer.Length);

                        // Append to the binary stream
                        bw.Write(buffer, 0, bytesRead);
                    }
                    while (!pipeStream.IsMessageComplete);

                    pipeStream.Close();                        
                    answer = ms.ToArray().Length > 0 ? ms.ToArray() : null;
                }
            }
        }
        catch (Exception ex)
        {
            Logger.Log("PipeClient: " + ex.Message);
            try { pipeStream.Close(); } catch (Exception) { }
        }

        return answer;
    }

服务器:

    private static void WorkerThread(object data)
    {
        PipeServer pipeServer = (PipeServer)data;
        while ( true )
        {
            try
            {
                PipeSecurity pipesecurity = new PipeSecurity();

                // Who has access to the pipe?
                pipesecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow));

                // Who can control the pipe? TODO: Find a SID representing the service
                pipesecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.FullControl, AccessControlType.Allow)); 

                // Create the new async pipe 
                NamedPipeServerStream namedPipe = new NamedPipeServerStream(pipeServer.m_PipeName, 
                    PipeDirection.InOut, 
                    NamedPipeServerStream.MaxAllowedServerInstances, 
                    PipeTransmissionMode.Message,
                    PipeOptions.Asynchronous,
                    4096, 
                    4096, 
                    pipesecurity);

                // Wait for a connection with event
                var asyncResult = namedPipe.BeginWaitForConnection(_ => pipeServer.m_TerminateEvent.Set(), null);
                pipeServer.m_TerminateEvent.WaitOne();  // We block on that event until we have a client or a shutdown request
                pipeServer.m_TerminateEvent.Reset();    // We reset the event to reuse it

                if (asyncResult.IsCompleted)
                {
                    // We have a client, stop listening while we process it
                    namedPipe.EndWaitForConnection(asyncResult);

                    // Start a new thread for that client
                    Thread t = new Thread(() => ClientThread(namedPipe, pipeServer));
                    t.Start();
                }
                else
                {
                    // We requested a shutdown
                    namedPipe.Close();
                    namedPipe.Dispose();
                    break;
                }                    
            }
            catch (Exception ex)
            {
                Logger.Log("PipeServer::WorkerThread: " + ex.Message);
            }
        }

        // Notify the main thread we end the worker thread
        pipeServer.m_TerminatedEvent.Set();
    }

    private static void ClientThread(object pipe, object server)
    {
        NamedPipeServerStream namedPipe = (NamedPipeServerStream)pipe;
        PipeServer pipeServer           = (PipeServer)server;        

        try
        {     
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);

            // We read chunks while the message is incomplete
            do
            {
                // Read the incoming chunk
                byte[] buffer = new byte[256];
                int bytesRead = namedPipe.Read(buffer, 0, buffer.Length);

                // Append to the binary stream
                bw.Write(buffer, 0, bytesRead);
            }
            while (!namedPipe.IsMessageComplete);

            // Consume message.
            if (pipeServer.PipeEvent != null)
            {
                PipeEventArgs args = new PipeEventArgs( ms.ToArray() );
                pipeServer.PipeEvent(pipeServer, args);

                // Handle a possible reply
                if ( args.Reply != null && args.Reply.Length != 0 )
                    namedPipe.Write(args.Reply, 0, args.Reply.Length);
            } 
        }
        catch (Exception ex)
        {
            Logger.Log("PipeServer::ClientThread: " + ex.Message);
        }
        finally
        {
            namedPipe.Close();
            namedPipe.Dispose();
        }
    }

1 个答案:

答案 0 :(得分:0)

我终于修好了。它不在我发布的代码之外,你是对的。

我使用 Json序列化/反序列化通过管道传递对象。传递的对象之一是包含&#34;事件&#34;类型的公共成员。

在我查看序列化字符串后,我意识到它正在将Handle从服务器端传递到UI端。

我非常确定在主循环中某些阻塞机制在UI端关闭句柄(当资源被释放时(?),这很奇怪我以为GC负责那种事情....)

无论如何,我只是在[DataContract]上添加[DataMember] Get("prot").innerHTML = protein + ' pounds';} 属性,需要序列化,忽略其他属性。

没有更多&#34; ReleaseHandleFailed&#34;等等不再崩溃。 如果这可以帮助别人。