脚本也玩P/Invoke
既然MSH是基于.net的,那么理所当然也应该支持P/Invoke了。据我所知,原来在win32平台上,脚本能调用API的好像只有Perl可以,而官方的脚本从来没有这种能力。现在的话,MSH可以,IronPython应该也行吧。
不过MSH脚本不是programming language,不可能定义类啊,特性啊什么的。(貌似IronPython可以),那么怎么P/Invoke呢?答案显然就是:反射。
下面看脚本:
$domain = [AppDomain]::CurrentDomain
$name = New-Object Reflection.AssemblyName 'TestAssembly'
$assembly = $domain.DefineDynamicAssembly($name, 'Run')
$module = $assembly.DefineDynamicModule('TestModule')
$type = $module.DefineType('TestType')
[Type[]]$parameterTypes = [IntPtr], [string],[string], [int]
$method = $type.DefineMethod("MessageBox", 'Public,Static,PinvokeImpl', [int], $parameterTypes)
$ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string])
$attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, 'user32'
$method.SetCustomAttribute($attr)
$realType = $type.CreateType()
[object[]]$args = [IntPtr]0, [string]'Bar',[string]'Foo', [int]0
$realType.InvokeMember('MessageBox', 'Public,Static,InvokeMethod', $null, $null, $args)
$name = New-Object Reflection.AssemblyName 'TestAssembly'
$assembly = $domain.DefineDynamicAssembly($name, 'Run')
$module = $assembly.DefineDynamicModule('TestModule')
$type = $module.DefineType('TestType')
[Type[]]$parameterTypes = [IntPtr], [string],[string], [int]
$method = $type.DefineMethod("MessageBox", 'Public,Static,PinvokeImpl', [int], $parameterTypes)
$ctor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([string])
$attr = New-Object Reflection.Emit.CustomAttributeBuilder $ctor, 'user32'
$method.SetCustomAttribute($attr)
$realType = $type.CreateType()
[object[]]$args = [IntPtr]0, [string]'Bar',[string]'Foo', [int]0
$realType.InvokeMember('MessageBox', 'Public,Static,InvokeMethod', $null, $null, $args)
这个脚本显示一个MessageBox。
不过,这段脚本一点也不优雅,甚至用处不大,它不是脚本应该做的事情,如果真的需要这种能力,我们应该用VB,C#,或者为MSH写一个CmdLet。
但是它体现了Monad的哲学,让复杂的事情变的可能。
PS:脚本产生的Assembly是在当前的AppDomain里面,只有这个AppDomain卸载时,新的Assembly才会卸载。你也可以尝试建立一个新的AppDomain,最后卸载那个domain。但是这就涉及到AppDomain交互和序列化的问题了。利用反射你也可以做到。