Add-Type 是 PowerShell 中用于动态编译加载 .NET 代码的核心命令,通过嵌入 C# 类并利用 P/Invoke 调用 Windows API(如 user32.dll 中的 MessageBoxA),本质是封装非托管函数供 PowerShell 调用。
add-type 是 powershell 中用于动态编译并加载 .net 代码的核心命令,它支持通过 c# 代码调用 windows api(即 win32 api),本质是借助 .net 的 p/invoke 机制封装原生函数。关键在于:你写的不是纯 powershell,而是嵌入的 c# 类,再通过反射调用其中方法。
准备前提:理解 P/Invoke 和平台调用签名
Windows API 多为 unmanaged 函数,PowerShell 本身不能直接调用,必须通过 C# 的 [DllImport] 声明。你需要确认:
- API 所在的 DLL 名称(如
user32.dll、kernel32.dll) - 函数名、参数类型(注意 IntPtr、bool、string 编码、结构体布局)
- 调用约定(通常是
CallingConvention.Winapi,对应StdCall) - 是否需要
[StructLayout]定义结构体(如RECT、POINT)
基础示例:调用 MessageBoxA 显示系统弹窗
以下代码在 PowerShell 中定义并调用 Win32 的 MessageBoxA:
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Win32 {
[DllImport("user32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
"@ -Language CSharp
<p>说明:
-
CharSet = CharSet.Ansi对应 ANSI 版本;若需 Unicode,改用MessageBoxW+CharSet.Unicode -
SetLastError = true允许后续用[Marshal]::GetLastWin32Error()获取错误码 - 类名
Win32是自定义的,可任意命名,但调用时需保持一致
进阶技巧:传入结构体与回调函数
调用如 EnumWindows 这类需要回调的 API,需定义委托和结构体:
Add-Type @"
using System;
using System.Runtime.InteropServices;
<p>public class EnumWindowsHelper {
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);</p><pre class="brush:php;toolbar:false;">[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
public static bool PrintWindow(IntPtr hWnd, IntPtr lParam) {
Console.WriteLine($"Window handle: 0x{hWnd.ToString("X")}");
return true; // 继续枚举
}} "@
[EnumWindowsHelper]::EnumWindows([EnumWindowsHelper+EnumWindowsProc] { param($hWnd, $lParam) [EnumWindowsHelper]::PrintWindow($hWnd, $lParam) }, 0)
注意点:
- 委托必须标记
[UnmanagedFunctionPointer](上例省略因默认匹配 StdCall;复杂场景建议显式声明) - 回调中避免使用 PowerShell 变量闭包,推荐将逻辑写在 C# 类内或用静态方法
- 结构体若含指针或变长字段,需用
[MarshalAs]显式指定封送行为
常见问题与避坑提醒
实际使用中容易出错的地方:
-
类型不匹配:PowerShell 的
[int]默认是 32 位,但某些 API 参数需long(Int64)或UIntPtr,务必查 MSDN 确认 -
字符串编码:ANSI/Unicode 混用会导致乱码或崩溃,优先用
MessageBoxW+CharSet.Unicode - 内存生命周期:不要在 C# 代码中返回指向栈内存的指针;字符串传参一般由 P/Invoke 自动处理
- 权限与 UAC:调用某些 API(如操作其他进程窗口)可能受 UIPI 限制,需以管理员身份运行或调整进程完整性级别
不复杂但容易忽略细节。只要签名对、DLL 对、调用方式对,Add-Type 就能稳稳帮你打通 PowerShell 和底层 Windows API。

















