1

I have extended the System.Collections.Concurrent.ConcurrentQueue to raise events as objects are enqueued. I now need to be able to use System.Management.Automation.WriteObject/WriteVerbose/WriteDebug methods to write the objects from the aforementioned events. However, I receive the following error when attempting to use System.Management.Automation.WriteObject/WriteVerbose/WriteDebug in the event handler.

Does anyone know how I can marshal the events back to the main thread so that I can use the System.Management.Automation.WriteObject/WriteVerbose/WriteDebug methods?

Here is my extended ConcurrentQueue class.

public class ConcurrentQueueEx<T> : ConcurrentQueue<T>
{
    #region Private Variables

    private Guid _id;

    #endregion

    #region Public Accessors

    public Guid Id
    {
        get { return this._id; }
    }

    #endregion

    #region Event Declarations

    public event PSObjectAddedEventHandler PSObjectAdded;

    protected virtual void OnPSObjectAdded(Guid parentId, object obj)
    {
        PSObjectAddedEventHandler handler = PSObjectAdded;
        if (handler != null)
        {
            PSObjectAddedEventArgs oae = new PSObjectAddedEventArgs();
            oae._ParentId = parentId;
            oae._Object = obj;
            handler(oae);
        }
    }

    #endregion

    #region Public Functions

    public new virtual void Enqueue(T item)
    {
        base.Enqueue(item);

        OnPSObjectAdded(this._id, item);
    }

    public virtual void Push(T item)
    {
        base.Enqueue(item);

        OnPSObjectAdded(this._id, item);
    }

    public virtual T Pop()
    {
        T obj;

        base.TryDequeue(out obj);

        return obj;
    }

    #endregion
}

Here are the relevant sections from the cmdlet.

protected override void BeginProcessing()
    {
        base.BeginProcessing();

        _messageQueue = new ConcurrentQueueEx<object>();
        _messageQueue.PSObjectAdded += _messageQueue_PSObjectAdded;

        _resultQueue = new ConcurrentQueueEx<object>();
        _resultQueue.PSObjectAdded += _resultQueue_PSObjectAdded;
    }

private void _resultQueue_PSObjectAdded(PSObjectAddedEventArgs e)
    {
        WriteObject(e._Object);
    }

    private void _messageQueue_PSObjectAdded(PSObjectAddedEventArgs e)
    {
        WriteVerbose(e._Object.ToString());
    }

Here are the exception details.

System.Management.Automation.PSInvalidOperationException was unhandled by user code
HResult=-2146233079
Message=The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services.
Source=System.Management.Automation
2
  • The error is telling you that you only have the output channel (and debug, warning, etc.) while you're executing a pipeline (i.e. begin/process/end). There's nowhere to write the object to outside of that context. Commented Dec 22, 2015 at 1:55
  • Possible duplicate of Reporting Powershell progress from event handler. Not an exact duplicate, but the same exception caused by calling WriteObject/WriteError in an event handler invoked on a thread other than the one on which BeginProcessing/ProcessRecord/EndProcessing was called. Commented May 7, 2018 at 3:53

1 Answer 1

0

What is the "main thread" doing while the thread on which the events are raised is queued?

If the main thread is blocked then you could make it wait on a synchronization object and then have it dequeue the objects.

If the thread is off doing something else, then you need to need it to either get it interrupted (by, say an event) or else have it poll the queue. I assume you'll want to do the first. In which case you'll need to register for an event in your cmdlet and fire the event from the other thread. The second answer here shows how to do this.

Sign up to request clarification or add additional context in comments.

1 Comment

This is a more elegant solution than what I did (I'll update my code once I get a chance). I just sent the processing to a third thread and monitored for the events until both threads have reported a completed message on a messaging queue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.