如何在PowerShell v5.1中配置ConPTY?代码报错求助
用ConPTY在PowerShell 5.1中向其他控制台窗口发送输出(无WinForms/WPF/IPC)
报错根源
你遇到的System.RuntimeType无法编组的错误,是因为调用[Marshal]::SizeOf时传入了错误参数——大概率是误传了类型的运行时内部实例(RuntimeType),而非标记了[StructLayout]的结构体类型本身。正确调用需要传入结构体的类型对象或其实例。
修复后的完整实现
以下是适配PowerShell 5.1的轻量实现,无需额外依赖,普通用户权限即可运行:
1. 定义Interop结构体与系统函数
Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public class ConPtyHelper { [StructLayout(LayoutKind.Sequential)] public struct COORD { public short X; public short Y; } [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetStdHandle(int nStdHandle); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool WriteConsole( IntPtr hConsoleOutput, string lpBuffer, int nNumberOfCharsToWrite, out int lpNumberOfCharsWritten, IntPtr lpReserved); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool DuplicateHandle( IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, bool bInheritHandle, uint dwOptions); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool GetConsoleProcessList(out uint[] lpdwProcessList, uint dwProcessCount); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject); } "@
2. 获取目标控制台窗口的句柄与进程ID
# 获取当前用户所有控制台进程ID $processListBuffer = New-Object uint[] 1024 $consoleProcessCount = [ConPtyHelper]::GetConsoleProcessList([ref]$processListBuffer, 1024) $consoleProcesses = $processListBuffer[0..($consoleProcessCount-1)] | ForEach-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue } # 排除当前PowerShell窗口,选第一个目标控制台 $currentConsolePid = [ConPtyHelper]::GetConsoleProcessList([ref]$null, 0) $targetProcess = $consoleProcesses | Where-Object { $_.Id -ne $currentConsolePid } | Select-Object -First 1 if (-not $targetProcess) { Write-Error "未找到其他控制台窗口,请先打开一个cmd/PowerShell窗口" exit 1 } # 获取目标窗口句柄 $targetWindowHandle = [ConPtyHelper]::FindWindow("ConsoleWindowClass", $targetProcess.MainWindowTitle) if ($targetWindowHandle -eq [IntPtr]::Zero) { Write-Error "无法找到目标窗口句柄" exit 1 }
3. 复制目标窗口输出句柄并发送内容
# 打开目标进程(普通用户权限仅需查询+句柄复制权限) $PROCESS_QUERY_INFO = 0x0400 $PROCESS_DUP_HANDLE = 0x0008 $targetProcessHandle = [ConPtyHelper]::OpenProcess( $PROCESS_QUERY_INFO -bor $PROCESS_DUP_HANDLE, $false, $targetProcess.Id ) if ($targetProcessHandle -eq [IntPtr]::Zero) { Write-Error "无法打开目标进程,错误代码: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" exit 1 } # 获取目标进程的标准输出句柄并复制到当前进程 $STDOUT_HANDLE = -11 $sourceStdOut = [ConPtyHelper]::GetStdHandle($STDOUT_HANDLE) $targetStdOut = [IntPtr]::Zero $duplicateSuccess = [ConPtyHelper]::DuplicateHandle( $targetProcessHandle, $sourceStdOut, [System.Diagnostics.Process]::GetCurrentProcess().Handle, [ref]$targetStdOut, 0, $false, 2 # DUPLICATE_SAME_ACCESS ) if (-not $duplicateSuccess) { Write-Error "复制输出句柄失败,错误代码: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" [ConPtyHelper]::CloseHandle($targetProcessHandle) exit 1 } # 修复SizeOf调用示例(正确传入结构体类型) $coordSize = [System.Runtime.InteropServices.Marshal]::SizeOf([ConPtyHelper+COORD]) Write-Host "COORD结构体大小验证: $coordSize" # 向目标窗口发送内容 $outputContent = "来自另一个PowerShell窗口的消息:`r`nHello, Target Console!`r`n" $charsWritten = 0 $writeSuccess = [ConPtyHelper]::WriteConsole( $targetStdOut, $outputContent, $outputContent.Length, [ref]$charsWritten, [IntPtr]::Zero ) if ($writeSuccess) { Write-Host "成功写入 $charsWritten 个字符到目标窗口" } else { Write-Error "写入失败,错误代码: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" } # 清理资源 [ConPtyHelper]::CloseHandle($targetStdOut) [ConPtyHelper]::CloseHandle($targetProcessHandle)
关键修复与说明
- 所有Interop结构体均标记
[StructLayout(LayoutKind.Sequential)],确保CLR能正确编组到非托管内存 - 调用
[Marshal]::SizeOf时传入结构体类型(如[ConPtyHelper+COORD]),而非类型的RuntimeType实例 - 使用普通用户权限允许的进程访问权限,无需管理员身份即可操作同用户下的控制台窗口
- 通过句柄复制复用目标窗口的标准输出,避免复杂的ConPTY创建流程,保持轻量高效
内容的提问来源于stack exchange,提问作者Dennis
相关产品推荐
相关产品推荐

