2011年4月11日月曜日

更新再開

開発から遠ざかっていたのでちょっとばかり放置して来たけれど、
しばらく開発をすることになりそうなので、
こちらのブログもちょくちょくと更新していこうと思います。
10か月はちょっとじゃない?あーあー聞こえなーい聞こえなーい

最近は地震が頻発してて、
その震源をトレースするのがなんだか面白そうなので、
Google MAP APIの勉強がてら、Google MAPにトレースしてみたいなぁ、と。。

Google Maps API ファミリーのページ
によれば、
GoogleAPIを使う為のキーを入手するためには、
ホームページだかブログだかがいるとかなんとか。。。

ということで、GoogleAPIで遊ぶサイトとして、
今後ここを使っていきたいと思います。
もちろんPowerShellの話も書いていきますが、
そればっかりにはならない旨ご了承を。

2010年6月15日火曜日

CA XOsoft PowerShell

業務の関係上、しばらくPowerShellから遠ざかっていましたが、
またしばらくPowerShellと戯れることが出来る見通しとなりました。
ということで、数カ月ぶりに記事を書きます。

ここしばらく、JP1やらARCserveやらを使った運用をいろいろとやっていました。
そんなこともあり、何気なくARCserveの情報を調べてみたところ、
何かすごくwktkさせてくれるものが出てきました♪

コントロールサービスって何なのさ(それ、レプリケーションでよろしく)

↑日本CAの中の皆様によるブログですが、
ARCserveのPowerShellスナップインが紹介されています。
CA XOsoftPowerShell?と言うそうです。
ネットを探すとマニュアルも出てきました。

…ちょうどARCserveが入った環境もあることだし、
仕事の合間にちょっと遊んでみたいなぁと思います。
何か所見が得られたら、このブログでフィードバックする感じで。

2010年3月4日木曜日

インポート後の関数の挙動

ScriptBlockを引数に持つような関数を、
モジュールマニフェストファイルを介してImport-Moduleで取り込むと、
ドットソースで読み込んだ場合と違はう動作をしてしまうようです。

モジュールマニフェストの作成方法については、こちらの投稿をどうぞ。

以下、検証サンプルコードです。

ソースコード:
# ファイル名:Test-ScriptBlock.ps1
function Test-ScriptBlock
{
[ScriptBlock]$block = $args[1]
Get-ChildItem -Path $args[0] | %{ '[{0}]:[{1}][{2}]' -f $_.Name, ( $_.Name -like "*a*" ), ( &$block ) }
}

PowerShellのコンソール:

PS> # Import-Moduleでファイルを直接取り込む。
PS> Import-Module .\Test-ScriptBlock.ps1 -Verbose
詳細: パス 'Test-ScriptBlock.ps1' からモジュールを読み込んでいます。
詳細: スクリプト ファイル 'Test-ScriptBlock.ps1' をドット ソース形式で読み込んでいます。

PS> Test-ScriptBlock $Env:PROGRAMFILES { $_.Name -like "*a*" }
結果出力( [アイテム名][True][True]または[アイテム名][False][False] )

PS> # Import-Moduleで、モジュールマニフェストを介してファイルを取り込む。
PS> # Test-ScriptBlock.ps1を含むモジュールマニフェストを作成(.\manifest.psd1)
PS> New-ModuleManifest
PS> Import-Module .\manifest.psd1 -Verbose
詳細: 関数 'Test-ScriptBlock' をインポートしています。
PS> Test-ScriptBlock $Env:PROGRAMFILES { $_.Name -like "*a*" }
結果出力( [アイテム名][True][False]になるものが存在する )
PS> # ↑引数を介して渡したスクリプトブロックの実行結果は常にFalse

PS> Test-ScriptBlock $Env:PROGRAMFILES { -not ( $_ ) }
結果出力( [アイテム名][-----][True])
PS> # ↑$_にオブジェクトが正しく渡されていないことを意味する


上記サンプルだけでは詳細は分かりませんが、
とりあえずは以下のことが言えそうです。
・ドットソースで取り込んだ関数の状態≠インポート後の関数の状態
・インポート後の関数は挙動がおかしい?

新しいことが分かったら関連のエントリーを作成することとします。

関連投稿:
PowerShellの「モジュール」について、その2

2010年2月28日日曜日

イベントにスクリプトブロックを指定する

PowerShellでもイベントを使えないかと思いネットで調べてみました。
できるだろうなぁ、と思っていましたが、できるようです。
こちらのサイトに、たいへん分かりやすくまとめられています。

結論のみ書かせていただきますと、以下のように書けばよいです。
[オブジェクト]. add_[イベント名]( スクリプトブロック )

何故イベントの事を調べていたかといえば、
ファイルのバックアップについて、
「更新されたファイルのみをバックアップできない??」
と知人より質問を受けたのがきっかけになります。
やり口としては、こちらのサイトに紹介されている、
FileSystemWatcherオブジェクトを使用する方法が良さそうなのですが、
ここでイベントを使う必要があるわけです。

勉強がてら、PowerShellで作ってみようとしてます。
勢いでファイルの更新情報を拾う部分だけ書いてみました。
動きはするようなので、サンプルコードとして晒してみます。

サンプルコード:

#FileWatchSample.ps1
param
( [string]$Directory = "D:\", [switch]$IncludeSubDirectory = $true )

#制御用フォーム準備
$label = New-Object -TypeName "System.Windows.Forms.Label"
$label.Text = "監視終了時は、このフォームを閉じてください。"
$label.Size = New-Object -TypeName "System.Drawing.Size" `
-ArgumentList @( $label.PreferredWidth, $label.PreferredHeight )

$form = New-Object -TypeName "System.Windows.Forms.Form"
$form.Text = "ファイル監視中"
$form.AutoSize = $true
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D
[void]$form.Controls.Add( $label )

#FileSystemWatcher準備
$fileSystemWatcher = New-Object -TypeName "System.IO.FileSystemWatcher" `
-ArgumentList @( $Directory )
$fileSystemWatcher.IncludeSubdirectories = $IncludeSubDirectory
$fileSystemWatcher.NotifyFilter = [System.IO.NotifyFilters]::CreationTime.value__ `
-bor [System.IO.NotifyFilters]::FileName.value__
$fileSystemWatcher.Filter = "*.*"

$fileSystemWatcher.add_Changed( `
{ Write-Host ( "{0} changed: {1}" -f [DateTime]::Now.ToString(), $_.FullPath ) } )
$fileSystemWatcher.add_Created( `
{ Write-Host ( "{0} created: {1}" -f [DateTime]::Now.ToString(), $_.FullPath ) } )
$fileSystemWatcher.add_Deleted( `
{ Write-Host ( "{0} deleted: {1}" -f [DateTime]::Now.ToString(), $_.FullPath ) } )
$fileSystemWatcher.add_Renamed( `
{ Write-Host ( "{0} renamed: {1} => {2}" `
-f [DateTime]::Now.ToString(), $_.OldFullPath, $_.FullPath ) } )
$fileSystemWatcher.SynchronizingObject = $form

#ファイル監視開始
$fileSystemWatcher.EnableRaisingEvents = $true
#終了時はフォームを閉じること。
$form.ShowDialog() | Out-Null



参照リンク
PowerShell Memo : VisualBaiscとPowerShellのイベント処理の比較
present : ファイルの作成・削除・変更をイベントで知る方法
MSDN : FileSystemWatch

参考コマンド

2010年2月26日金曜日

PowerShellのインストール

これまで使い方等の細かい話ばっかり書いていましたが、
どこからダウンロードできるかという話は書いていなかったので、
なんとなく書こうと思います。
一応、書くネタがないだけですわけではありません。

Windows PowerShellの最新のバージョンは2です。
そのインストーラは、以下より入手できます。
http://connect.microsoft.com/windowsmanagement/Downloads
お使いのOSおよびマシンのプロセッサアーキテクチャに応じて、
適切なものをダウンロードし、インストールすればOKです。
開発環境も付いてきますので、すぐに開発ができます。
あんなものやこんなものを作って楽しみましょう♪

===
今回本当に書きたかった話はここからです。

上記リンクから取得したインストーラを使っても、
インストールに失敗してしまう事が時々あります。
私自身、仕事用マシン(WindowsXP SP3のx86)でこの問題に悩みました。
あのエラーメッセージで途方に暮れた型は私以外にもいらっしゃるはず。

その後の調査により、いつの間にかPowerShell v2がインストールされており、
重複でインストールしようとしてエラーが出ていたようでした。
今となっては、WindowsUpdateに混ざって勝手にインストールされていた
というのが真相だろうなぁと思っています。ちなみに、KB968930です。

会社では、いつの間にかPowerShellが使えるようになっていた、
という話をしてくれた人が何人かいました。
インストール時に問題が起こるとしたら、
まずはこちらの原因を疑ってみるとよろしいかと思います
(特に、WSUSを使われている皆様)。
インストールに失敗したあなたのマシンの「アクセサリ」をのぞくと、
その中にもうWindows PowerShell ISEがあるかも…。。


上記以外でインストールに失敗する原因としては、
PowerShellのv1をアンインストールしていなかったとか、
ダウンロードしたインストーラが間違っていたとか、
といった理由も考えられます。
PowerShellBlogにもエントリーが出ておりますので、
詳細はそちらをご覧ください。

2010年2月24日水曜日

PowerShellからCOMのタイプライブラリを使う

一つ前の投稿で紹介したCodePlexのサイトに、
COMのタイプライブラリを使用するためのツールが公開されています。
CodePlex : Type Library Importer in Managed Code
この機能、前からずっと欲しいと思ってました。
私以外にも欲しいと思っていた人もいらっしゃると思いますし、
喜び半分でこのサイトでもリンクのみ公開しました。

WSHでは、タイプライブラリを容易に利用することができました
VBScriptはActiveX経由で簡単にCOMオブジェクトが作成できる上、
タイプライブラリも簡単に使うことができました。
「VBScriptはCOMのネイティブ言語」という旨の話が書かれていた
サイトがありましたが、そのとおりだと私も思います。

PowerShellでも、
New-Objectコマンドレットを用いてCOMオブジェクトを取得できます。
しかし、WSHと違いタイプライブラリを読み込む方法がなかったため、
プロパティに設定されている数字が何を表すかを調べるのが、
若干面倒な作業だったりします。
しかし、Type Library Importer in Managed Codeが使用できれば、
その苦労ともおさらばできることでしょう(喜)。

参照リンク
CodePlex : Type Library Importer in Managed Code

PowerShellからWin32APIを使う

つい最近の話ですが、
PowerShellからiniファイルのデータを拾いたいと思う事がありました。
Win32APIのGetPrivateProfileString関数を
使えれば早いのですがそんなことが可能なのか。。。

実は、.NET FrameworkのP/Invoke(Platform Invokeの略?)の
仕組み機能を使用することで、PowerShellからWin32APIを使用できます。
例によって、.NET FrameworkをPowerShellから呼び出します。

今回のは、.NETにそんなに詳しくない身としては少々ハードルが高く、
一から実装することは難しいですので、
今回はこちらのサイトからソースコードを拝借しました。
ありがとうございます!

単にコードを示すだけでは私自身の勉強にならないため、
MSDNのクラスライブラリ等を使い、なるべくコードの意味を調べるようにしました。
関連リンクは後ほど。。。

サンプルコード:

# PowerShellからのPlatform APIの呼び出しは、
# .NET Frameworkの提供するP/Invoke機能を介して実施することができる。
# Invoke-Win32では、P/Invokeを実行用に一時的な型を定義し、
# そこに実装したStaticなメソッドを介してPlatformAPIを実行する。

function Invoke-Win32()
{
param
( [string]$dllName, [Type]$returnType, [string]$methodName, [Object[]]$parameterInfos )

$parameterTypes = $parameterInfos | %{ $_[ 0 ] }
$parameters = $parameterInfos | %{ $_[ 1 ] }

# カレントのアプリケーションドメインに、
# P/Invokeを実行するメソッドを持つ独自の型を定義する。

$domain = [AppDomain]::CurrentDomain
$name = New-Object Reflection.AssemblyName 'PInvokeAssembly'
$assembly = $domain.DefineDynamicAssembly( $name, 'Run' )
$module = $assembly.DefineDynamicModule( 'PInvokeModule')
$type = $module.DefineType( 'PInvokeType', "Public,BeforeFieldInit" )

# P/Invokeを呼び出すためのパラメーターを準備する。
# PlatformAPIに渡すパラメーターを保持する配列

$inputParameters = @()
# PSReference型パラメーターの位置を保持する配列
$refParameters = @()

for( $counter = 0; $counter -lt $parameterTypes.Length; $counter++ )
{
# パラメーターの型が「PSReference」のものについては、関数から戻ってきた値を拾えるようにする必要がある。
if( $parameterTypes[ $counter ] -eq [Ref] )
{
# 呼び出しの際に[out]をつける必要があるため、そのパラメーターの位置を退避しておく。
# 配列の数より1つ大きな数を保持しておく必要がある。(理由は後述)

$refParameters += $counter

# PSReference型を、.NETオブジェクトの参照型に書き換える。
$parameterTypes[ $counter ] = $parameters[ $counter ].Value.GetType().MakeByRefType()

# 関数呼び出し時に使用するパラメーター一覧に値を追加する
$inputParameters += $parameters[ $counter ].Value
}
# そうでないものについては、関数呼び出し時に使用するパラメーター一覧にただ追加するのみ
else
{
$inputParameters += $parameters[ $counter ]
}
}

# アセンブリーの動的メソッドとして、PlatformAPIを定義する
$method = $type.DefineMethod( $methodName, `
'Public,HideBySig,Static,PinvokeImpl', `
$returnType, `
$parameterTypes )
# PSReference型のパラメーターは、out属性のパラメーターとする。
foreach( $refParameter in $refParameters )
{
# 0番目の要素は戻り値の情報になるため、配列の要素番号+1を指定する必要がある。
$method.DefineParameter( ( $refParameter + 1 ), "Out", $null )
}

# P/Invokeのコンストラクターを設定する
$ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor( [string] )
$attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, $dllName
$method.SetCustomAttribute( $attr )

# 一時的な型を作成し、そのメソッドとしてPlatformAPIを実行する。
$realType = $type.CreateType()
$returnObject = $realType.InvokeMember( $methodName, `
'Public,Static,InvokeMethod', `
$null, `
$null, `
$inputParameters )

# PSReference型で受け取ったパラメーターの値を更新する。
foreach( $refParameter in $refParameters )
{
$parameterInfos[ $refParameter ][ 1 ].Value = $inputParameters[ $refParameter ]
}
# PlatformAPIの戻り値を返す
return $returnObject
}

# GetPrivateProfileStringを呼び出してみる。
$iniFilePath = 'C:\test.ini'
$returnValue = New-Object System.Text.StringBuilder 500
$parameterInfos = @( @( [string], [string]"FrontOtherService1" ), `
@( [string], [string]"Name" ), `
@( [string], [string]"" ), `
@( [System.Text.StringBuilder], [System.Text.StringBuilder]$returnValue ), `
@( [int], [int]$returnValue.Capacity ), `
@( [string], [string]$file ) )

$returnValue = Invoke-Win32 -dllName "kernel32.dll" `
-returnType ( [UInt32] ) `
-methodName "GetPrivateProfileString" `
-parameterInfos $parameterInfos
$returnValue.ToString()



他にもいろいろなWin32API関数が使えるはずです。
その際、.NET上で動くコード(マネージコード)と
Win32APIを動かすときのコード(ネイティブコード)との対応を
理解しておく必要がありますが、ツールで調べることもできます
P/Invoke Interop Assistant)。

で、今新たに知りたいと思っているのは、
PowerShellからDelegateを使用する方法です。
イベントを使ったり、マルチスレッドを実装する場合には欠かせません。
これについても、おいおい勉強していければいいかなぁ。。。

参考コマンド:


参考リンク:
Precision Computing : Get the Owner of a Process in PowerShell – P/Invoke and Ref/Out Parameters
MSDN : A Closer Look at Platform Invoke
P/Invoke.NET
CodePlex : P/Invoke Interop Assistant

関連投稿:
PowerShellから.NETのアセンブリを呼ぶ

修正履歴: