::::::::: PowerShell :::::::::
Tuesday, January 31, 2006
  [MSH] Clipboard Snapin...
Thanks to both /\/\o\/\/ and Keith Hill, I was able to somewhat improve the Clipboard snapin by adding "Get-Clipboard" cmdlet.

I was able to fix "Out-Clipboard" cmdlet to receive an input from pipe by setting type of "inputData" to that of "string" from "object"(thanks /\/\o\/\/).

Well, I just couldn't improve much from Keith's orignal Clipboard Cmdlet other than the fact that this is now a Snapin and fixed one small bug(refer to OutClipboardCommand.SetObject)...

Anyways, here is how this Clipboard Snapin can be used

Here is a simple one.

MSH> "Hello, World!" | Out-Clipboard
MSH> Get-Clipboard
Hello, World!
MSH>


Now let's try to send the output of "get-process" to the clipboard

MSH> get-process | Out-Clipboard
MSH> Get-Clipboard
System.Diagnostics.Process (ahnsd)
System.Diagnostics.Process (ahnsdsv)
...
System.Diagnostics.Process (winlogon)
System.Diagnostics.Process (wmiprvse)
MSH>


Now, what is going on here? Well, I don't want to go too deep with that because it was explained in Keith's blog on MSH: Get-Clipboard and Set-Clipboard Cmdlets already.

Anyways, the problem is that, "out-clipboard" is receiving unformatted input string so MSH user have to explicitly have to convert piped data to string (through "out-string") before sending the data to Out-Clipboard(well, that is why the cmdlet only supports data of type string, for now)

So, i was searching and reading documents on MSDN and was looking for ways to put that "out-string" behavior inside "out-clipboard". Well, I haven't found the answer yet... It seems like there is not "a recommanded way invoking a cmdlet that is a subclass of MshCmdlet from another cmdlet" according to Kevin Loo who also suggested me to create a new function defined like the following
function set-clipboard {$input | out-string | out-clipboard}


Using "set-clipboard", try to run "get-process | set-clipboard", "get-childitem | set-clipboard", etc... But the problem with "set-clipboard" approach is that, you can't explicitly pass the object to "set-clipboard"... while something like "out-clipboard $(dir)" would work(although not how you might expect it to work).
I guess a workaround for that problem might be my next topic...



For instructions on how to compile and install the snapin, please refer to Appendix D in "Getting Started" document for MSH.

Here is how to load Clipboard Snapin
MSH> set-alias installutil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil
MSH> cd [go to Snapin assembly directory]
MSH> installutil Snapins.dll
MSH> get-mshsnapin -r
Name : ClipboardSnapin
MshVersion : 1.0
Description : Provides retrieving/sending data from/to Clipboard
MSH> add-mshsnapin ClipboardSnapin
Now, ClipboardSnapin is loaded and you are good to go.

Well, the source's below... but I was deliberately trying to make each line shortern than 80 columns... so it looks longer than it should be...


using System;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
using System.Management.Automation;

namespace D2D.Snapins
{
/// <summary>
/// Define basic information of this snap in
/// </summary>
[RunInstaller(true)]
public class ClipboardSnapin : MshSnapIn
{
public ClipboardSnapin() : base()
{
}

public override string Name
{
get { return "ClipboardSnapin"; }
}

public override string Vendor
{
get { return "DontBotherMeWithSpam"; }
}

public override string Description
{
get { return "Provides retrieving/sending data from/to Clipboard"; }
}
}

/// <summary>
/// Cmdlet to return clipboard text to pipe
/// </summary>
/// <remarks>
/// Original Author: Keith Hill (http://spaces.msn.com/keithhill)
/// Original Source URL: http://home.comcast.net/~rkeithhill/MSH/Clipboard.cs
/// Modified by: DBMwS
/// Date: 01/31/2006 @ 12:59AM
///
/// Removed, "timeout" feature
/// </remarks>
[Cmdlet("Get", "Clipboard")]
public class GetClipboardCommand : Cmdlet
{
private Exception ex;
private string dataObject = "";

protected override void BeginProcessing()
{
Thread t = new Thread(new ThreadStart(GetObject));

try
{
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
catch (Exception ex)
{
ErrorRecord err = new ErrorRecord(ex, "GetClipboardError",
ErrorCategory.NotSpecified, t);
WriteError(err);
}

if (this.ex != null)
{
WriteError(new ErrorRecord(this.ex,
"GetClipboard::GetObjectError",
ErrorCategory.NotSpecified, null));
}

WriteVerbose(string.Format("Sending '{0}' to pipe",
this.dataObject.ToString()));
WriteObject(this.dataObject,
(this.dataObject.GetType().IsArray));
}

private void GetObject()
{
try
{
this.dataObject = Clipboard.GetText();
}
catch (Exception ex)
{
this.ex = ex;
}
}
}

/// <summary>
/// send output string to clipboard
/// </summary>
[Cmdlet("Out", "Clipboard")]
public class OutClipboardCommand : Cmdlet
{
/// <summary>
/// Unhandled exception
/// </summary>
private Exception ex;
/// <summary>
/// Actuall worker thread
/// </summary>
private Thread t;
/// <summary>
/// Holds all the necessary input data to send to Clipboard
/// </summary>
private StringBuilder sb = new StringBuilder();

/// <summary>
/// Input data received either from pipe or as an argument
/// </summary>
private string []inputData;
/// <summary>
/// Input data to send to clipboard
/// </summary>
[Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)]
public string []InputData
{
get { return inputData; }
set { inputData = value; }
}

/// <summary>
/// Create a STA thread to start later on.
/// </summary>
protected override void BeginProcessing()
{
WriteVerbose("Begin Processing(Creating a new STA thread)...");

t = new Thread(new ThreadStart(SetObject));
t.SetApartmentState(ApartmentState.STA);
}

/// <summary>
/// Append all input lines into one
/// (with a newline appended for each input data)
/// </summary>
protected override void ProcessRecord()
{
WriteVerbose("Processing Record...");

try
{
if (this.InputData != null)
{
WriteVerbose(
string.Format("\tAppending {0}", this.InputData));
foreach (string line in this.InputData)
{
this.sb.AppendLine(line);
}
}
}
catch (Exception ex)
{
// Well I don't know what happened,
// so send the error to the host...
ErrorRecord err = new ErrorRecord(ex, "OutClipboardError",
ErrorCategory.NotSpecified, t);
WriteError(err);
}
}

/// <summary>
/// Start a thread to send input data to Clipboard
/// </summary>
protected override void EndProcessing()
{
try
{
t.Start();
t.Join();
}
catch (Exception ex)
{
ErrorRecord err = new ErrorRecord(ex, "OutClipboardError",
ErrorCategory.NotSpecified, t);
WriteError(err);
}

// Handle error occurred while sending text to clipboard
// Well, try out something as stupid as "clear-host | out-clipboard"...
if (this.ex != null)
{
WriteError(new ErrorRecord(this.ex,
"OutClipboard::SetObjectError",
ErrorCategory.NotSpecified, null));
}
WriteVerbose("End Processing...");
}

/// <summary>
/// Send input data to clipboard
/// </summary>
private void SetObject()
{
try
{
// Before sending the inputData to clipboard,
// we need to drop the last newline character
string text = this.sb.ToString();
text = text.Substring(0,
(text.Length - System.Environment.NewLine.Length));

Clipboard.SetText(text);
}
catch (Exception ex)
{
this.ex = ex;
}
}

}
}




Tags :
 
Saturday, January 28, 2006
  [MSH Snapin] Need help implementing Out-Clipboard Snapin
Well, while playing around with new MSH beta 3 feature, MSH Snapin, I came across with /\/\o\/\/'s MSH Clipboard use workaround blog.

Since, Msh engine is created using Muti-Thread Apartment model, it's still not possible to create or use an object that requires STA. /\/\o\/\/'s solution uses in-memory compilation and dynamic object instantiation using CodeDom namespace.

Well, since we have a new Snapin feature, I have decided to create my own "out-clipboard" cmdlet.

In the topic i have mentioned that, there is some problems with the Snapin. I can't seem to get an input to be passed from pipe although I have specified "ValueFromPipeline=true" in "Parameter" attribute. Oh well, it is far from being complete, but posting about this code to ask about the problem and how I should be improving it at usenet or at forums...

So far, only sending text to clipboard works...


MSH>Out-Clipboard "Hello, World"
Begin Processing...
Processing Record...
Setting String
End Processing...
MSH>"Hello, World" | Out-Clipboard
Begin Processing...
Processing Record...
Setting MshObject
End Processing...


I have pasted "Hello, World" in the second MSH prompt through mouse and then piped the data to "out-clipboard". The first case works while the latter case doesn't as I have mentioned above...


Comment on source:
This snapin basically creates a new STA thread and send input argument to clipboard through callback.



using System;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;
using System.Management.Automation;
using System.Collections.Generic;
using System.Text;

namespace snapins
{
[RunInstaller(true)]
public class OutClipboardSnapin : System.Management.Automation.MshSnapIn
{
public OutClipboardSnapin() : base()
{
}

public override string Name
{
get { return "OutClipboardSnapin"; }
}

public override string Vendor
{
get { return "DontBotherMeWithSpam"; }
}

public override string Description
{
get { return "This snapin receives piped output into Clipboard"; }
}
}

[Cmdlet("Out", "Clipboard")]
public class OutClipboardCommand : Cmdlet
{
private Thread t;
private object inputData;
/// <summary>
/// Input data to send to clipboard
/// </summary>
[Parameter(Mandatory = true, Position = 0, ValueFromPipeline=true)]
public object InputData
{
get { return inputData; }
set { inputData = value; }
}

protected override void BeginProcessing()
{
t = new Thread(new ThreadStart(SetObject));
t.SetApartmentState(ApartmentState.STA);

WriteObject("Begin Processing...");
}

protected override void ProcessRecord()
{
WriteObject("Processing Record...");
WriteObject("Setting " + this.InputData.GetType().Name);
t.Start();
}

protected override void EndProcessing()
{
t.Join();
WriteObject("End Processing...");
}

private void SetObject()
{
if (this.InputData.GetType() == typeof(string))
{
Clipboard.SetText(this.InputData.ToString());
}
else
{
Clipboard.SetDataObject(this.InputData);
}
}
}
}





Tags :
 
Monday, January 16, 2006
  Simple tools for complex jobs
While listening to "Inside the Net" podcast episode #5 "Jason Fried of 37Signals", I have realized something.

As an application or scripts gets more mature, they tend to do more stuff than it supposed to do. For an example, Adobe Photoshop is extremely bloated so that it includes a lot of functionalities that normal users are not even aware of.

Now, we could come up with an analogy that, if you would want to disassemble, you would normally use screw drivers. But if you have a Swiss Army knife, you do can get a job done but you would have to search for the right tool that's bundled with the Swiss Army Knife. Swiss Army KNife can get other tasks accomplished but it's bloated.

So what I am trying to accomplish in this Blog is that, I would like to create simple tools or scripts or functions to accomplish other many things.

For example, when using Windows Forms, one would normally have to import "System.Windows.Forms" through excruciatingly long statement as followed.

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")



Would you actually like to have multiple of those "[System.Reflection.Assembly]::LoadWithPartialName" functions in your script?
Coming from a C# community, i could not stand such long function names clogging up my scripts.

So I have created a such script like,

function using-library {
param($ns = $(throw "Enter Namespace to import"), [bool]$SuppressOutput = $false)

trap { throw "Could not import Namespace $($ns)" }

if ($SuppressOutput) {
[void][System.Reflection.Assembly]::LoadWithPartialName($ns)
} else {
[System.Reflection.Assembly]::LoadWithPartialName($ns)
}
}


MSH>Set-Alias using using-library


Now, in a script, one can simply use C# style "USE" statement in their scripts without having to know inner details on how to import and can simply concentrate on what his/her script needs to do.
What is more great about such abstraction is that, since "LoadWithPartialName" function is now obsolete according to WinFX document, one can simply change function implementation of "using-library" without having to change all scripts with "LoadWithPartial" name.

Well, the predicament I am in now is, HOW can I put all the scripts in one place so that it'd be easy to to use all the functions, load them or unload them with ease. Ah there is also a name collision issues that I have to cope with. It'd be nice if MSH had one of those Namespace concepts as in .NET library.
Tags :
 
Friday, January 13, 2006
  [MSH] Change Default "Get-Credential" Prompt Behavior
/\/\o\/\/ has pointed out that the default prompt mode for "get-content" in Monad Beta 3 is through "CredUI", meaning, if you type "get-content" in Monad Beta 3,
you will see one of these familiar Windows Credential UI:

Image hosted by Photobucket.com

According to Abhishek Agrawal on Usenet post,

The default implementation for get-credential now uses the Windows CredUI
functionality for collecting credentials. This is a requirement for being
complaint with Windows Common Criteria. We still retain the console mode
which can be enabled by setting the registry key "ConsolePrompting" under
HLKM:\SOFTWARE\Microsoft\MSH\1\ShellIds to "True" (string)


Ah, now, i go to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSH\1\ShellIds(Well, I can see that Registry structure has been modified slightly from pre-Beta 3 version of Monad), you won't see a registry key named "ConsolePrompting". You can simply create that key and set the value to whichever mode you set the value of key to. I prefer "GUI" mode so mine is set to "false". But i just want to switch between two modes easily so created the following script that will simply change the prompt mode with a simple function(SetConsoleCredPrompt).


MSH> SetConsoleCredPrompt
Your Credential Prompting Mode has been changed to "Console" Mode.
MSH> SetConsoleCredPrompt($false)
Your Credential Prompting Mode has been changed to "Gui" Mode.


SetConsoleCredPrompt simply navigates to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSH\1\ShellIds and then change the value of "ConsolePrompt" to "true" or "false".

This is one of the advantage of knowing how to script... I don't have to do much manually by having to open up the registry editor, navigate to the right path and change the key(BTW, isn't Registry Provider just awesome or what?).

Well the source? Nothing interesting about it...

# author: DBMwS
# date: 01/12/2005 @ 11:14 PM
# description: "Get-Credential" prompting method between "console" and "CredUI"
# note: Works only for Beta 3 of Monad
function SetConsoleCredPrompt() {
# default is $private:false since in Monad Beta 3, CredUI is the default way to
# prompt end-user for entering username and password
# But should i throw an Exception here or not... That's the problem...
param([bool] $ConsoleMode = $private:true)

# Ready for the unexpected...(such as running this script for non beta 3 version of Monad
trap { Write-Host "Some Unknown error has occurred, Aborting Mission..."; return; }

# MSH Beta 3 ShellID
$private:path = [string] "HKLM:\SOFTWARE\Microsoft\MSH\1\ShellIds"
# A new key name to create.
$private:key = [string] "ConsolePrompting"

# Check if the location we need to reach exists or not, else abort the script
if (test-path $private:path) {
set-property $private:path -Property $private:key -Type String -Value $ConsoleMode.ToString()
} else {
throw "Could not find Registry Path($($private:path)) to change Creditial Prompt Setting`nMaybe you are running pre-beta3 MSH?"
}

Write-host -n "Your Credential Prompting Mode has been changed to `""
if($ConsoleMode) { Write-host -n -f "red" "Console" } else { Write-Host -n -f "green" "Gui" }
Write-host "`" Mode."
}



Tags :
 
Tuesday, January 10, 2006
  /\/\o\/\/ on MSH (Monad): Report MP3 count and size by User from MSH
/\/\o\/\/ on MSH (Monad): Report MP3 count and size by User from MSH

Just a great fun MSH script to play around with... As the topic says, it simply reports MP3 file statistics... on either local machine or on a network computer, etc...

But what is up with those @{expression=...;Name=...} stuff?



If you scroll down a bit, then you will run into two of "select" cmdlets with weird arguments.
Don't worry about it so much, argument is simply an array of a hashtable...

So let's see...
Let's take a look at,

# group them per user 

"`n$Extension Per User"
$Userfiles | group Username | select @{expression={$_.Name};Name="User"},
                               @{expression={($_.group | measure-object).count};Name="Count"},
                               @{expression={($_.group | measure-object -property RoundedSize -sum).sum};Name="MB"}



you will just simply see an array of hashtable object with two keys with matching values

  1. 1st element

    1. expression: {$_.Name}
    2. Name: "User"

  2. 2nd element

    1. expression: {($_.group | measure-object).count}
    2. Name: "Count"

  3. 3rd element

    1. expression: {($_.group | measure-object -property RoundedSize -sum).sum}
    2. Name: "MB"



The value of "Name" key of each Hashobject is used as a header for the output while the values of "expression" evaluate piped input data object(in this case, System.Management.Automation.Commands.GroupInfo object) and spits out the output on the console.
In /\/\o\/\/'s case,

User Count MB
---- ----- --
Usr001 141 998,52
Usr002 547 3746,62


You can see that "User", "Count", and "MB" as headers and the results are simply calculated values from the piped input data.

BTW, to save typing, you don't have to go with something like expression={$_.filename}; name="Filename"(probably for better readability?) since there is no real meaning to name your keys("expression" & "name") that long... For me being such a lazy typist... i was satisfied with the following...


$Userfiles | group Username | select @{e={$_.Name};N="User"},
                               @{e={($_.group | measure-object).count};N="Count"},
                               @{e={($_.group | measure-object -property RoundedSize -sum).sum};N="MB"}


Anyways, it was a fun fun script there /\/\o\/\/~~~

Tags :
 
Sunday, January 08, 2006
  [FUN] (Poor Man's) SONY Rootkit Revealer
I was listening to "Security Now" episode #12 (Transcript for that episode and links to the podcast on the top of the page) and SONY's Rootkit can be revealed by creating any file that starts with "$sys".

NOTE:To those who still haven't heard of the term Rootkit it's advised that you either "google" about it or check out Security Now Episode #9

Well.. I was sitting down and see if i had rootkit or not...
Ah.. all this does is just simply creating a file that starts with $sys$($sys$test.txt) and check if exists after creating it(and deletes later on). There is no fancy stuff done here...


# author: dance2die
# title: Ghetto SONY Rootkit Revealer~ :)
# date: 01/08/2005 @ 20:05
# comment: LOL, this is quite funny...

function RevealSONYRootkit {
# create a file(in a current dir)
# that starts with "$sys$" which is what SONY used to hide their files.
$private:testFile = ".\`$sys`$test.txt"

trap [System.IO.FileNotFoundException] {
# well something happend so i am guessing that the a rootkit is on the machine..
Write-Host -foregroundColor "red" -backgroundcolor "white" "You might have SONY rootkit installed... I am chickening out..."
if ([System.IO.File]::Exists($private:TestFile)) remove-item $private:testFile
break
}

If (![System.IO.File]::Exists($private:testFile)) {
# i am not using [void] here since new-item will display the file created on console... to make sure that people get to see the result
new-item -type file $private:testFile
} else {
Write-Host -foregroundColor "green" -backgroundcolor "white" "Your system is clean..."
# i don't usually like to have multiple exit points in a function but wth...
return
}

if ([System.IO.File]::Exists($private:testFile)) {
Write-Host -foregroundColor "green" -backgroundcolor "white" "Your system is clean..."
} else {
Write-Host -foregroundColor "red" -backgroundcolor "white" "FATAL:You have a SONY Rootkit installed!!!`nReinstall your Windows!!!"
}

remove-item $private:testFile
}



If you don't have SONY Rootkit then you will see:
Image hosted by Photobucket.com

Or.. if you happened to be one of those unlucky ones...:
Image hosted by Photobucket.com

Wouldn't it be great if someone could modify this to run this function over network? :)
Ah.. an instance of "System.Management.Automation.MshCredential" class argument can come in handy..

Well.. Run it at your own risk.. :)

Tags:
 
Saturday, January 07, 2006
  [TechNet] Do Scripters Dream of Magenta-Colored Text?
I was going over one of the highlighted Microsoft TechNet MSH Scripting article
Do Scripters Dream of Magenta-Colored Text?


The highlighted article talks about how to "Display Output in Color Using the Microsoft Shell". The gist of the whole discussion was about "-foreground" flag of "write-host" cmdlet(pronounced as Commandlet) which would let the scripter to decide which color(any of System.ConsoleColor retrievable through "MSH>$host.ui.rawui.foregroundcolor | gm -static")

The script seems to be from the guys who has written many VBScript tech scripts because the author has used WMI Class called "Win32_Process" which holds current process information of local or remote machine. I had a feeling that the author had no idea about "get-process".

Anyways, the following is the Scripting Guy's solution for displaying current process information in "Magenta" color.

$strComputer = "." 

$colItems = get-wmiobject -class "Win32_Process" -namespace "root\CIMV2" `
-computername $strComputer | write-object

foreach ($objItem in $colItems) {
write-host $objItem.Name, $objItem.WorkingSetSize -foregroundcolor "magenta"
}

The following is the shorter version using "get-process"

MSH>get-process | foreach { write-host $_.name, $_.WorkingSet -foregroundcolor "magenta" }

Woah, wasn't that much shorter and easier to write and read?
Anyways, one draw back of the latter approach is that it does not display "System Idle Process"... But not like anyone cares about that none process... :)

Tags:
 
Let's get lazy with PowerShell!

Name:
Location: Flushing, NY, United States

Experimenting with a different format of blogs...

Links
ARCHIVES
10/01/2005 - 11/01/2005 / 11/01/2005 - 12/01/2005 / 12/01/2005 - 01/01/2006 / 01/01/2006 - 02/01/2006 / 02/01/2006 - 03/01/2006 / 03/01/2006 - 04/01/2006 / 04/01/2006 - 05/01/2006 / 05/01/2006 - 06/01/2006 / 06/01/2006 - 07/01/2006 / 07/01/2006 - 08/01/2006 / 08/01/2006 - 09/01/2006 / 10/01/2006 - 11/01/2006 / 11/01/2006 - 12/01/2006 /


Powered by Blogger