无法写入流内部事件 [英] Can't write to stream inside event

查看:231
本文介绍了无法写入流内部事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以尝试运行NHTTP(用C#.NET Framework的简单的异步HTTP服务器。可以在的的NuGet ,或查看源代码在 github上),但我写斜面我自己的响应客户从我的服务器,因为我是不能写的OutputStream

I am can try to run NHTTP(simple asynchronous HTTP server written in C# for the .NET framework. You can download this dll in nuget, or view source code on github) But i am cant write my own response to client from my server, because i am can't write to OutputStream.

我的PowerShell脚本:

My powershell script:

Add-Type -Path "NHttp.dll"

$server = New-Object NHttp.HttpServer
$server.EndPoint =  new-object System.Net.IPEndPoint ([System.Net.IPAddress]::Parse("127.0.0.1"), 8080)


$RequestReceived = {

    $request = $EventArgs.Request
    $response = $EventArgs.Response

    $global:response = $response.OutputStream.CanWrite

    $writer = New-Object System.IO.StreamWriter($response.OutputStream)
    $writer.AutoFlush = $true
    $writer.WriteLine("Hello World")    


}

Register-ObjectEvent -InputObject $server -EventName RequestReceived -SourceIdentifier RequestReceived -Action $RequestReceived

$server.Start()

总是在我的服务器从客户端接收$全局请求:响应=假

Always when my server receive request from client $global:response=False

在事件发生的错误,当服务器收到请求时出现:

An error occurring in the Event, which occurs when the server receives a request:

writeErrorStream      : True
PSMessageDetails      : 
Exception             : System.Management.Automation.MethodInvocationException: Exce
                        ption when calling the ".ctor" with "1" argument: "Stream wa
                        s not writable" ---> System.ArgumentException: Stream was no
                        t writable.
                          at System.IO.StreamWriter..ctor(Stream stream, Encoding en
                        coding, Int32 bufferSize, Boolean leaveOpen)
                          at System.IO.StreamWriter..ctor(Stream stream)
                           --- End of inner exception stack trace ---
                          at System.Management.Automation.DotNetAdapter.AuxiliaryCon
                        structorInvoke(MethodInformation methodInformation, Object[]
                         arguments, Object[] originalArguments)
                          at Microsoft.PowerShell.Commands.NewObjectCommand.CallCons
                        tructor(Type type, ConstructorInfo[] constructors, Object[] 
                        args)
TargetObject          : 
CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationExceptio
                        n
FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Comman
                        ds.NewObjectCommand
ErrorDetails          : 
InvocationInfo        : System.Management.Automation.InvocationInfo
ScriptStackTrace      : at <ScriptBlock>, C:\powershell_scripts\tmp\nhttp.ps1: line 18
PipelineIterationInfo : {}

和服务器响应干净的白页和的StatusCode 200到客户端。

And server response clean white page and StatusCode 200 to client.

但榜样C#的工作完美:用

But example for C# work perfectly:

using (var server = new HttpServer())
{
    server.RequestReceived += (s, e) =>
    {
        using (var writer = new StreamWriter(e.Response.OutputStream))
        {
            writer.Write("Hello world!");
        }
    };

    server.Start();

    Process.Start(String.Format("http://{0}/", server.EndPoint));

    Console.WriteLine("Press any key to continue...");
    Console.ReadKey();
}



所谓的事件通过注册,ObjectEvent创建时的动作块内的此类行为发生我是不是第一次了。我试图解决它,将公寓模式(STA和MTA),但它不解决问题。如果你需要我我有更多的例子。

Such behavior within the Action block when called Event created by Register-ObjectEvent occurs to me is not the first time. I try to solve it, changing "apartment" model(STA and MTA), but it not solve the problem. If you need i am have more examples.

推荐答案

在PowerShell中如何处理事件的问题。让我们写一些代码来测试它:

The problem in how PowerShell handle events. Let us write some code to test it:

Add-Type -TypeDefinition @‘
    using System;
    using System.Threading;
    public static class TestEvent {
        public static event EventHandler Event = delegate { };
        public static void RaiseEventCurrentThread() {
            Console.WriteLine("Raising Event in thread: {0}.", Thread.CurrentThread.ManagedThreadId);
            Event(null, null);
            Console.WriteLine("Raised Event in thread: {0}.", Thread.CurrentThread.ManagedThreadId);
        }
        public static void RaiseEventThreadPool() {
            ThreadPool.QueueUserWorkItem(_ => RaiseEventCurrentThread());
        }
    }
’@
Register-ObjectEvent -InputObject ([TestEvent]) -EventName Event -Action {
    Write-Host "PowerShell event thread: $([Threading.Thread]::CurrentThread.ManagedThreadId)."
}|Out-Null
Write-Host "PowerShell current thread: $([Threading.Thread]::CurrentThread.ManagedThreadId)."
# PowerShell current thread: 7.
[TestEvent]::RaiseEventCurrentThread()
# Raising Event in thread: 7.
# Raised Event in thread: 7.
# PowerShell event thread: 7.
[TestEvent]::RaiseEventThreadPool()
# Raising Event in thread: 4.
# Raised Event in thread: 4.
# PowerShell event thread: 7.

正如你可以看到,即使在事件当前的PowerShell线程提出,PowerShell的不没有援引注册清议作为事件处理程序的一部分。但从NHTTP点, RequestReceived 事件处理到底是时候,当响应可以发送回客户端。因此,当调用注册操作时,反应已经可以发送到客户端,所以你不能修改它。

As you can see, even if event raised in current PowerShell thread, PowerShell does not invoke registered -Action as part of event handler. But from NHTTP point of view, end of RequestReceived event handler is the time, when response can be send back to client. Thus, when registered action is invoked, response can be already sent to client, so you are not allowed to modify it.

要解决这个问题,你需要创建委托,这将等待事件来控制返回给调用者之前完全处理。然后你通过这个委托添加事件访问。我写了一个cmdlet,后者做的辛勤工作:

To solve this problem, you need to create delegate, which would wait for event to be fully processed before returning control to caller. And then you pass this delegate to add event accessor. I wrote a cmdlet, which do the hard work:

Add-Type -TypeDefinition @‘
    using System;
    using System.Linq.Expressions;
    using System.Management.Automation;
    using System.Reflection;
    using Microsoft.PowerShell.Commands;
    [Cmdlet(VerbsLifecycle.Register, "InlineObjectEvent"), OutputType(typeof(PSEventJob))]
    public class RegisterInlineObjectEventCmdlet : RegisterObjectEventCommand {
        private InlineObjectEventInformation info;
        public RegisterInlineObjectEventCmdlet() { }
        [Parameter(Mandatory = true, Position = 101)]
        public new ScriptBlock Action {
            get {
                return base.Action;
            }
            set {
                base.Action=value;
            }
        }
        public new SwitchParameter Forward {
            get {
                return base.Forward;
            }
            set {
                base.Forward=value;
            }
        }
        protected override object GetSourceObject() {
            return info;
        }
        protected override string GetSourceObjectEventName() {
            return "Event";
        }
        protected override void EndProcessing() {
            info=new InlineObjectEventInformation(Events, InputObject.BaseObject, EventName, MessageData);
            base.EndProcessing();
            info.ProcessEventSubscriber(NewSubscriber);
        }
        public class InlineObjectEventInformation {
            private PSEventManager eventManager;
            private PSEventSubscriber eventSubscriber;
            private object sourceObject;
            private EventInfo eventInfo;
            private PSObject messageData;
            private Delegate eventHandler;
            internal InlineObjectEventInformation(PSEventManager eventManager, object sourceObject, string eventName, PSObject messageData) {
                this.eventManager=eventManager;
                this.sourceObject=sourceObject;
                this.messageData=messageData;
                GetEventInfo(eventName);
                CreateEventHandler();
            }
            public PSEventSubscriber EventSubscriber {
                get {
                    return eventSubscriber;
                }
            }
            public object SourceObject {
                get {
                    return sourceObject;
                }
            }
            public string EventName {
                get {
                    return eventInfo.Name;
                }
            }
            public event Action Event {
                add { }
                remove { }
            }
            internal void ProcessEventSubscriber(PSEventSubscriber eventSubscriber) {
                this.eventSubscriber=eventSubscriber;
                eventInfo.AddEventHandler(sourceObject, eventHandler);
                eventSubscriber.Unsubscribed+=(sender, args) => eventInfo.RemoveEventHandler(sourceObject, eventHandler);
            }
            private void GetEventInfo(string eventName) {
                eventInfo=(sourceObject as Type??sourceObject.GetType()).
                          GetEvent(eventName, BindingFlags.Instance|BindingFlags.Static|BindingFlags.Public|BindingFlags.IgnoreCase);
                if(eventInfo==null) {
                    throw new ArgumentException(null, "eventName");
                }
            }
            private void CreateEventHandler() {
                MethodInfo invoke = eventInfo.EventHandlerType.GetMethod("Invoke");
                if(invoke.ReturnType!=typeof(void)) {
                    throw new NotSupportedException();
                }
                ParameterExpression[] parameters = Array.ConvertAll(invoke.GetParameters(), pi => Expression.Parameter(pi.ParameterType));
                eventHandler=Expression.Lambda(
                    eventInfo.EventHandlerType,
                    Expression.Call(
                        Expression.Constant(this),
                        "GenerateEvent", null,
                        Expression.NewArrayInit(typeof(object), Array.ConvertAll(parameters, pe => Expression.Convert(pe, typeof(object))))
                    ),
                    parameters
                ).Compile();
            }
            private void GenerateEvent(object[] args) {
                eventManager.GenerateEvent(eventSubscriber.SourceIdentifier, args.Length>0 ? args[0] : sourceObject, args, messageData, true, true);
            }
        }
    }
’@ -ReferencedAssemblies Microsoft.PowerShell.Commands.Utility -PassThru|Select-Object -First 1 -ExpandProperty Assembly|Import-Module
Register-InlineObjectEvent -InputObject ([TestEvent]) -EventName Event -Action {
    Write-Host "PowerShell event thread: $([Threading.Thread]::CurrentThread.ManagedThreadId)."
    Start-Sleep 10
    Write-Host "End of long event handler."
}|Out-Null
Write-Host "PowerShell current thread: $([Threading.Thread]::CurrentThread.ManagedThreadId)."
# PowerShell current thread: 7.
[TestEvent]::RaiseEventCurrentThread()
# Raising Event in thread: 7.
# PowerShell event thread: 7.
# End of long event handler.
# Raised Event in thread: 7.
[TestEvent]::RaiseEventThreadPool()
# Raising Event in thread: 4.
# PowerShell event thread: 7.
# End of long event handler.
# Raised Event in thread: 4.

这篇关于无法写入流内部事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆