Compiling and Executing C#
Compiling C# into PowerShell involves using the
Add-Type cmdlet to add a C# class to the PowerShell .NET instance. Once your class is compiled you can instantiate the class and call functions straight from PowerShell. To start here is a example that prints 'Hello PowerShell' to the console by a static function or creating an object to run the function.
Add-Type -TypeDefinition @"
using System;
public class CSharpClass {
public void Hello() {
Console.WriteLine("Hello PowerShell!");
}
public static void HelloStatic() {
Console.WriteLine("Hello PowerShell, I'm static!");
}
}
"@
$csclass = New-Object CSharpClass
$csclass.Hello();
[CSharpClass]::HelloStatic();
Download Hello World Script
One key thing you need to know is that you can only add the same class once per PowerShell instance, if you change the C# and try to run it again in the same shell it will fail. This is because
once you add a type with Add-Type it cannot be removed until you exit and restart PowerShell. Because of this it's suggested you mainly run scripts through double clicking, if you need to keep the shell open
to read results use
Read-Host.
Automating Tasks
Now if your fluent in C# and .NET you probably are already full of ideas on what you can do. But I thought I'd cover some essentials that I think are especially useful.
Modifying files is definitely the most common thing I automate, so to start here is a C# function which finds and replaces specific text across all the files in a folder.
using System;
using System.IO;
public class FileMod {
public static void Apply(string path) {
//get all files in directory
string[] files = Directory.GetFiles(path);
for (int i = 0; i < files.Length; i++) {
//skip non .html files
if (!files[i].EndsWith(".html")) continue;
Console.WriteLine(files[i]);
//replace class='lk' with class='lnk'
string txt = File.ReadAllText(files[i]);
txt = txt.Replace(" class='lk'", " class='lnk'");
File.WriteAllText(files[i], txt);
}
}
}
}
One potential issue is the C# code executes relative to PowerShell.exe, not the current working directory. So you may want to combine this with
Get-Location to prepend the current directory to your file paths.
Probably the most powerful thing that C# gives you access to is direct native WinAPI interfacing. By using DllImport you can call native functions and with proper structures you can fully work with the native
WinAPI from C#. Below you can see an example of resizing and moving a window using
FindWindow and
MoveWindow.
using System;
using System.Runtime.InteropServices;
public static class WinMove {
//import native functions
[DllImport("User32.dll")] static extern Boolean MoveWindow(IntPtr handle, Int32 x, Int32 y, Int32 w, Int32 h, Boolean repaint);
[DllImport("User32.dll")] static extern IntPtr FindWindowW([MarshalAs(UnmanagedType.LPWStr)] string className, [MarshalAs(UnmanagedType.LPWStr)] string winName);
public static void Move(string name, int x, int y, int width, int height) {
//get window pointer by name
IntPtr h = FindWindowW(null,name);
if (h == IntPtr.Zero) Console.WriteLine("Error could not find window '"+name+"'!");
else {
//move window to pixel coordinates and size
MoveWindow(h,x,y,width,height,true);
Console.WriteLine("Moved '"+name+"' to "+x+","+y+","+width+","+height+".");
}
}
}
If you have any questions feel free to reach out to me at xaloezcontact@gmail.com.