Invoke Win32 API via PowerShell

.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/

vsaddin2

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 :-)

This entry was posted in powerJobs, PowerShell, Visual Studio. Bookmark the permalink.

One Response to Invoke Win32 API via PowerShell

  1. Hi,
    to compile the code in memory you can also use the Add-Type cmdlet.
    Add-type -TypeDefinition $code

    Tested, and worked.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s