ラベル 便利なツール の投稿を表示しています。 すべての投稿を表示
ラベル 便利なツール の投稿を表示しています。 すべての投稿を表示

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のアセンブリを呼ぶ

修正履歴:

2010年1月30日土曜日

PowerShellからWMIを使う

PowerShellの登場により、
WMIの活用への敷居が一気に低くなったように思います。

technetによれば、WMI(Windows Management Instrumentation) とは:

WMIとは、WBEM(Web-Based Enterprise Management Initiative) を Microsoft が実装したものです。WBEM とは、エンタープライズのネットワークの管理情報にアクセスしたり、管理情報を共有したりするための標準を確立する業界イニシアティブの 1 つです。WMI は、管理環境にあるオブジェクトを説明するデータ モデルであるCommon Information Model (CIM) を統合的にサポートします。

要は、Windowsの各種管理に使用できる手段のひとつ、ってことです。
私も、PowerShellを書き始めるまでWMIは名前こそ知っていましたが、
何ができるのかとか、どういう風に使えばいいのかとか、
ほとんどまったく知らなかったってのが実際のところです。

しかしながら、PowerShellのテクノロジーを使用できる今、
Get-WmiObjectに-Listオプションをつけて実行することにより、
やりたいことを実現させてくれるWMIクラスを探すのも容易であり、
Get-Memberコマンドレットを使ってメンバーも簡単に確認にできます。
もちろん、WSHでもWMIを使用することができたのですが、
言語として検索機能をサポートしている点において、
PowerShellは非常に優れている、といって間違いないかと思います。
これは、PowerShellの登場によりWMIが一気に身近なものになった、
ということに他ならないのではないでしょうか。

なお、クラスを自分で探すのが面倒な場合とか、
デフォルトの名前空間以外のWMIクラスを使用する場合については、
Windows PowerShell Scriptomaticにより、
PowerShellのコードを自動生成し、それを必要に応じて加工したものが使えます。
どんなメソッドが使えるかは、Get-Memberで確認しましょう。

ということで、PowerShellを用いてシステムの運用管理を考える場合には、
PowerShellからWMIを積極的に使っていくことを検討してみてはどうでしょうか。

実装例:
PowerShellからWMIを使用し、残り容量が10%を切ったディスクのドライブレターを取得する。

#ディスク関連のWMIクラスを探す
PS D:\> Get-WmiObject -List -Class "*Disk*"
(出力内容は省略)

#ディスク容量が残り10%を切ったディスクのドライブレターを表示
PS D:\> Get-Wmiobject -class Win32_LogicalDisk | ?{ $_.Size -ne $null } | ?{ $_.FreeSpace / $_.Size -lt 0.1} | %{ $_.DeviceID }
(出力内容は省略)


参考サイト:
WMIクエリでのLIKE演算子の使用

参考コマンド:

Get-Help about_WMI_cmdlets


便利なツール:
Windows PowerShell Scriptomatic

修正履歴: