我在这个问题上抓了几天...... 首先,让我们提出一些背景信息:
我编写了一个通过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();
}
}
答案 0 :(得分:0)
我终于修好了。它不在我发布的代码之外,你是对的。
我使用 Json序列化/反序列化通过管道传递对象。传递的对象之一是包含&#34;事件&#34;类型的公共成员。
在我查看序列化字符串后,我意识到它正在将Handle从服务器端传递到UI端。
我非常确定在主循环中某些阻塞机制在UI端关闭句柄(当资源被释放时(?),这很奇怪我以为GC负责那种事情....)
无论如何,我只是在[DataContract]
上添加[DataMember]
个 Get("prot").innerHTML = protein + ' pounds';}
属性,需要序列化,忽略其他属性。
没有更多&#34; ReleaseHandleFailed&#34;等等不再崩溃。 如果这可以帮助别人。