Reputation: 381
I Know I'm able to pipe out/in using simple Console.WriteLine and Console.ReadLine methods, but that way I'm passing a string between processes (which must be parsed to recreate the object).
What I'm wondering is if I would be able to pipe my own types, so that I could retrieve them easily in destiny process. What I expect is to do something like:
myProgram | get-member
And the output would something like MyNameSpace.MyType and the list of its members (currently it shows the typeName System.String)
Is that possible in a console app or could I only achieve this using cmdlets?
Upvotes: 1
Views: 605
Reputation: 1391
The easiest way to do this is to use serialization to turn the objects you wish to send from one to the other into a pipeable format to send them from one to the other. There are, however, a number of constraints on doing this:
First, the implementation of the types you're passing back and forth have to be available to all the apps that may handle them. (That's not a problem for PowerShell because all the cmdlets run inside the same process.) So the easiest way to do this is to create the types you're going to pipe around inside a class library that's referenced by all the console apps. This class, for example, I put in my sample shared library:
[Serializable]
public class TestClass
{
public string Test { get; set; }
public string TestAgain { get; set; }
public string Cheese { get; set; }
}
The [Serializable] attribute marks it as serializable, which is sufficient for simple classes. For more complex classes, more may be required - see MSDN, starting here: http://msdn.microsoft.com/en-us/library/4abbf6k0(v=VS.71).aspx
Then, in the program you're piping from, you serialize it to XML and write it out to console like this:
using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using Shared;
namespace Out
{
class Program
{
static void Main(string[] args)
{
// Create the object.
TestClass test = new TestClass();
test.Test = "Monkey";
test.TestAgain = "Hat";
test.Cheese = "Fish";
// Serialize it.
XmlSerializer serializer = new XmlSerializer(typeof (TestClass));
StringBuilder sb = new StringBuilder();
using (var writer = new StringWriter(sb))
serializer.Serialize(writer, test);
// And write it to console.
Console.WriteLine(sb.ToString());
}
}
}
When run, this outputs the instance's properties encoded in XML, thus:
<?xml version="1.0" encoding="utf-16"?>
<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http
://www.w3.org/2001/XMLSchema">
<Test>Monkey</Test>
<TestAgain>Hat</TestAgain>
<Cheese>Fish</Cheese>
</TestClass>
Then, in your receiving application, you reverse the process, reading from the console, thus:
using System;
using System.IO;
using System.Xml.Serialization;
using Shared;
namespace In
{
class Program
{
static void Main(string[] args)
{
// Read the input XML; until complete.
string input = Console.In.ReadToEnd();
TestClass passedIn;
// Deserialize it.
var serializer = new XmlSerializer(typeof (TestClass));
using (var reader = new StringReader(input))
passedIn = (TestClass) serializer.Deserialize(reader);
// Do something with the object.
Console.WriteLine("Test: {0}", passedIn.Test);
Console.WriteLine("TestAgain: {0}", passedIn.TestAgain);
Console.WriteLine("Cheese: {0}", passedIn.Cheese);
}
}
}
And voila!
C:\Working\PipeExample\In\bin\Debug>..\..\..\Out\bin\Debug\Out.exe | in
Test: Monkey
TestAgain: Hat
Cheese: Fish
You'll need some additional code, of course, to make sure that the receiving application knows what type(s) to expect - or can handle anything it gets - and since the intermediate XML is not very human-parsable, you'll need a way to make sure that the sending application knows when it's talking to a pipe and when it's talking to a human. In .NET 4.5, the Console.IsOutputRedirected() method will do that for you ( http://msdn.microsoft.com/en-us/library/system.console.isoutputredirected%28v=VS.110%29.aspx ), but in earlier versions, there's not an easy way to get at this information programmatically.
But this is the core of the thing, and looking at the documentation for and around XmlSerializer should give you the rest.
Upvotes: 3
Reputation: 72660
Why don't you write your own cmdlet instead of a console program?
A PowerShell module can be a binary module (a DLL assembly) composed by cmdlets writen in C#. Have a look to Installing the Windows PowerShell SDK.
Upvotes: 1