在WinForm C#中用PowerShell RunSpace执行两脚本结果一致问题求助
问题与解决方案
问题描述
使用Visual Studio 2017开发WinForm C#程序时,通过PowerShell RunSpace执行以下两个PowerShell脚本后,生成的InstalledSoftwareList1.txt和InstalledSoftwareList2.txt内容完全相同:
# 脚本1 Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName | Format-Table –AutoSize > C:\Temp\InstalledSoftwareList1.txt # 脚本2 Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName | Format-Table –AutoSize > C:\Temp\InstalledSoftwareList2.txt
但在PowerShell ISE中执行相同脚本,能得到预期的两个不同结果。当前WinForm中执行PowerShell的代码如下:
private string RunScript(string script) { Runspace runspace = RunspaceFactory.CreateRunspace(); runspace.Open(); Pipeline pipeline = runspace.CreatePipeline(); pipeline.Commands.AddScript(script); pipeline.Commands.Add("Out-String"); Collection<PSObject> results = pipeline.Invoke(); runspace.Close(); StringBuilder stringBuilder = new StringBuilder(); foreach (PSObject pSObject in results) stringBuilder.AppendLine(pSObject.ToString()); return stringBuilder.ToString(); }
调用方式:
RunScript(@"Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName | Format-Table –AutoSize > C:\Temp\InstalledSoftwareList1.txt"); RunScript(@"Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName | Format-Table –AutoSize > C:\Temp\InstalledSoftwareList2.txt");
原因分析
核心问题是注册表重定向机制:
- 32位进程访问
HKLM:\Software路径时,Windows会自动将请求重定向到HKLM:\Software\Wow6432Node。 - Visual Studio默认编译WinForm项目时,
AnyCPU配置下会勾选“首选32位”选项,导致程序以32位模式运行。此时不管脚本中指定的是HKLM:\Software还是HKLM:\Software\Wow6432Node,实际访问的都是同一个注册表路径,所以生成的文件内容完全一致。 - 而PowerShell ISE默认以64位模式运行(在64位系统上),能正确区分两个不同的注册表路径,因此得到不同结果。
解决方案
方案1:修改项目编译配置
- 在Visual Studio中右键项目 → 选择“属性”。
- 切换到“生成”选项卡。
- 取消勾选“首选32位”(仅适用于
AnyCPU平台),或者直接将目标平台改为x64。 - 重新编译运行程序,此时程序以64位模式运行,不会触发注册表重定向,两个脚本将访问不同的注册表路径,生成预期的不同结果。
方案2:在RunSpace中指定64位PowerShell
如果需要保持程序为32位,但要访问64位注册表,可以创建64位的RunSpace:
private string RunScript(string script) { var runspaceConfig = RunspaceConfiguration.Create(); var runspace = RunspaceFactory.CreateRunspace(runspaceConfig); // 强制启用64位模式(仅当程序以32位运行时生效) runspace.Open(new RunspaceConnectionInfo { ProcessArchitecture = Architecture.X64 }); Pipeline pipeline = runspace.CreatePipeline(); pipeline.Commands.AddScript(script); pipeline.Commands.Add("Out-String"); Collection<PSObject> results = pipeline.Invoke(); runspace.Close(); StringBuilder stringBuilder = new StringBuilder(); foreach (PSObject pSObject in results) stringBuilder.AppendLine(pSObject.ToString()); return stringBuilder.ToString(); }
方案3:禁用注册表重定向(仅适用于32位进程)
在访问注册表前禁用重定向,访问后恢复:
// 导入Windows API函数 [DllImport("kernel32.dll", SetLastError = true)] static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr); [DllImport("kernel32.dll", SetLastError = true)] static extern bool Wow64RevertWow64FsRedirection(IntPtr ptr); // 修改RunScript方法,添加禁用重定向逻辑 private string RunScript(string script) { IntPtr ptr = IntPtr.Zero; bool disableSuccess = Wow64DisableWow64FsRedirection(ref ptr); Runspace runspace = RunspaceFactory.CreateRunspace(); runspace.Open(); Pipeline pipeline = runspace.CreatePipeline(); pipeline.Commands.AddScript(script); pipeline.Commands.Add("Out-String"); Collection<PSObject> results = pipeline.Invoke(); runspace.Close(); // 恢复重定向 if (disableSuccess) Wow64RevertWow64FsRedirection(ptr); StringBuilder stringBuilder = new StringBuilder(); foreach (PSObject pSObject in results) stringBuilder.AppendLine(pSObject.ToString()); return stringBuilder.ToString(); }
内容的提问来源于stack exchange,提问作者Jo Srm
相关产品推荐
相关产品推荐

