.NET provides pretty good support to for calling Win32 APIs and there’s a free Visual Studio Add-In from redgate available which helps your dealing with the Win32 API: http://www.red-gate.com/products/dotnet-development/pinvoke/
But what to do if you want to call Win32 APIs or your own .NET functionality in PowerShell?
As always there is more than one way: You can write your .NET code in Visual Studio, compile it to a class library and make it available in PowerShell using Reflection:
[System.Reflection.Assembly]::LoadFrom("path\to\your.dll")
Another – cool – way is to compile your code directly to the memory without generating a dll in the file system and consume it form within PowerShell!
The following code sample shows how to accomplish this by creating a compiler function, embed C# source code as a string, compile the code and invoke the C# function in PowerShell:
################################################################## # Compiler ################################################################## function Compile-Csharp ([string] $code, $FrameworkVersion="v4.0.30319") { $provider = New-Object Microsoft.CSharp.CSharpCodeProvider $framework = [System.IO.Path]::Combine($env:windir, "Microsoft.NET\Framework\$FrameWorkVersion") $references = New-Object System.Collections.ArrayList $references.AddRange( @("${framework}\System.dll","${framework}\System.Core.dll")) $parameters = New-Object System.CodeDom.Compiler.CompilerParameters $parameters.GenerateInMemory = $true $parameters.GenerateExecutable = $false $parameters.ReferencedAssemblies.AddRange($references) $result = $provider.CompileAssemblyFromSource($parameters, $code) if ($result.Errors.Count) { $codeLines = $code.Split("`n"); foreach ($ce in $result.Errors) { write-host "Error: $($codeLines[$($ce.Line - 1)])" $ce | out-default } Throw "Compilation of C# code failed" } } ################################################################## # C# Code ################################################################## $code = @' using System; using System.Runtime.InteropServices; using System.ComponentModel; namespace CompileTest { public class Sound { [DllImport("User32.dll", SetLastError = true)] static extern Boolean MessageBeep(UInt32 beepType); public static void Beep(BeepTypes type) { if (!MessageBeep((UInt32)type)) { Int32 err = Marshal.GetLastWin32Error(); throw new Win32Exception(err); } } } public enum BeepTypes { Simple = -1, Ok = 0x00000000, IconHand = 0x00000010, IconQuestion = 0x00000020, IconExclamation = 0x00000030, IconAsterisk = 0x00000040 } } '@ ################################################################## # Compile the code and access the .NET object within PowerShell ################################################################## Compile-Csharp $code [CompileTest.Sound]::Beep([CompileTest.BeepTypes]::IconAsterisk)
If you want to use other references than “System” in your C# code, make sure to introduce them to the compiler by adding the dll to the $references ArrayList. Also make sure to pass the appropriate framework version to the Compile-Csharp function. A list of available framework versions and the System*.dlls can be found here: C:\Windows\Microsoft.NET\Framework or C:\Windows\Microsoft.NET\Framework64
Over all this is a quite simple and very cool way to provide .NET functionality within PowerShell. Happy coding!
By the way: Make sure your speakers are turned on when running this example since it leads to a beep sound on your machine :-)
Hi,
to compile the code in memory you can also use the Add-Type cmdlet.
Add-type -TypeDefinition $code
Tested, and worked.