Bawiąc się z PowerShellem, zauważyłem brak wysłania rezultatów do notatnika w stylu Out-GridView. Postanowiłem napisać skrypcik, który by to umożliwiał. Na samym początku jest potrzebna funkcja do znajdywania okna z aplikacją oraz wysyłania tekstu do niej. Skorzystamy z biblioteki user32.dll. Skrypt wyglada następująco:
$User32MethodDefinition = @'
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
'@
$User32 = Add-Type -MemberDefinition $User32MethodDefinition -Name 'user32' -Namespace 'Win32' -PassThru
Za każdym razem jak uruchomimy skrypt to będziemy musieli stworzyć nowy obiekt $user32 (i będziemy musieli wiedzieć czy obiekt ma wymagane metody). Dlatego potrzebna nam będzie funkcja sprawdzająca metody statyczne na obiekcie
function Test-StaticMembers
{
param(
[ValidateNotNull()]
$objectToTest
,
[string[]]
$members)
[array]$result = $objectToTest.GetMembers() |
? {$_.IsStatic -eq $true -and $_.IsPublic -eq $true} |
? {$_.IsAbstract -eq $false -and $_.IsConstructor -eq $false} |
? { $members -contains $_.Name}
if( $result.Count -eq $members.Count)
{ return $true}
throw "Not all member are in object. Searching for members: $members"
}
Sprawdzanie obiektu wygląda następująco:
if( ! $User32 -OR !(Test-StaticMembers -objectToTest $User32 -members 'FindWindowEx','SendMessage' ) )
{
$User32 = Add-Type -MemberDefinition $User32MethodDefinition -Name 'user32' -Namespace 'Win32' -PassThru
}
Teraz będziemy mogli stworzyć funkcje do wysyłania outputu tekstu do dowolnej aplikacji:
Function Out-Applciation
{
Param(
[Parameter(ValueFromPipeline = $true)]
[string]
$text
,
[switch]
$toAll,
[switch]
$passResult
,
[string]
$appName
)
Begin{
$textArray = @()
$intPtr = New-Object 'IntPtr' -ArgumentList '0'
if($toAll)
{
$appProcesses = Get-Process -Name $appName -ErrorAction SilentlyContinue
}
else
{
$appProcesses =@( Start-Process -FilePath $appName -PassThru )
}
#check if process list exist
if(! $appProcesses)
{
Start-Process -FilePath $appName
$appProcesses = Get-Process -Name $appName
}
}
Process{
$textArray += $text
}
End{
$textString = $textArray | out-string
$appProcesses | %{
$findWindowExTryCounter = 0
while(!$windowExChild -OR $windowExChild -eq 0)
{
$windowExChild = $User32::FindWindowEx($_.MainWindowHandle, $intPtr,'Edit', $null )
#if FindWindowEx will not work
$findWindowExTryCounter++
if($findWindowExTryCounter -gt 100)
{
throw "Can not find window $($_.MainWindowHandle) for $appName"
}
else
{
Start-Sleep -Milliseconds 100
}
}
$result = $User32::SendMessage($windowExChild, '0x000C', 0, $textString )
if($passResult)
{$result} #if 1 then is has been send
}
}
}
Do tego stworzymy pomocna funkcję do wysyłania wyjścia na notepad:
function Out-Notepad
{
param(
[switch]
$toAll,
[switch]
$passResult
)
Begin{
$appName = 'notepad'
}
End{
$input | Out-Applciation -toAll:$toAll -passResult:$passResult -appName $appName
}
}
Dla przykładu zastosowania out-notepad, pomocna nam będzie funkcja numerująca stringi w kolekcji:
function Numerate-String
{
Param(
[Parameter(ValueFromPipeline = $true)]
[string]
$text,
[string]
$seperator=':'
)
begin{
$count =0
}
process{
$result = "$count $seperator"+ [System.Environment]::NewLine + $text
$count++
return $result
}
}
A wywołanie może byglądać następująco:
gc applicationName.log | Numerate-String | Out-Notepad