如何在Windows Vista/7/8上以编程方式启动SFC程序?

3
我一直在尝试解决Chris Iverson在另一个Stackoverflow问题中遇到的问题
我想要以编程方式启动SFC(系统文件检查器工具)。
它在Windows XP上运行:
private void RunSfc()
{
    ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/K sfc.exe /scannow");
    System.Diagnostics.Process.Start(startInfo);
}

enter image description here

其他在Windows XP下可用的变体:

//Launch SFC directly
ProcessStartInfo startInfo = new ProcessStartInfo("sfc.exe", "/scannow"); 
System.Diagnostics.Process.Start(startInfo);

//Use full path to SFC
String sfcPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "sfc.exe");
ProcessStartInfo startInfo = new ProcessStartInfo(sfcPath, "/scannow"); 

在Windows 7上(以管理员身份运行启动程序),相同的代码会失败。控制台窗口会出现,但SFC会给出错误提示:
Windows Resource Protection could not start the repair service.

enter image description here

但是,如果我从一个单独的已提升权限的命令提示符中手动运行sfc /scannow,它就会起作用:

enter image description here

很明显,Windows Vista/7/8发生了一些奇怪的事情。我不确定具体是什么原因,但很可能与UAC、UIPI、0号会话隔离有关,或者与CSRSS运行控制台窗口的事实有关。

但是,我仍然不理解这个问题。

如果我想做跟Chris一样的事情,解决他的问题就很好了。

请记住:我的代码已经作为管理员运行。我右键单击并以管理员身份运行

enter image description here

这并不意味着问题不是与UAC相关的其他微妙问题,但这并不是因为我以标准用户身份运行。

WinForms应用程序中的代码

private void button1_Click(object sender, EventArgs e)
{
    RunSfc(); 
}

32位操作失败

原来有一个32位版本的 cmd.exe 和一个32位版本的 sfc.exe:

  • C:\Windows\SysWOW64\cmd.exe
  • C:\Windows\SysWOW64\sfc.exe
如果您运行的是提升的32位cmd,则32位和64位版本的sfc都无法使用。因此,难题就变成了如何从32位进程启动64位cmd.exe。这可能意味着难题就变成了如何在32位进程中查找64位版本的cmd.exe,考虑到以下情况:您可能不在64位机器上;您可能已经在运行64位进程;Windows喜欢根据您的进程位数来伪造System32文件夹的名称。

你能否将UAC完全关闭,仅作为测试场景以确定它是否涉及该问题? - vcsjones
1
@vcsjones 没有效果。控制台窗口仍然显示“Windows资源保护无法启动修复服务”。 - Ian Boyd
其实,这有点有趣。你的所有代码示例在我的Windows 8.1上都可以正常工作。我逐字逐句将你的代码复制到一个命令行项目中,右键单击生成的可执行文件,并以管理员身份运行。扫描很顺利地开始了。 - vcsjones
@vcsjones 请尝试一个带有UI的WinForms项目。 - Ian Boyd
@IanBoyd。是的。现在我能够重现你的问题了。这确实有些奇怪。 - vcsjones
显示剩余3条评论
2个回答

3

我的实验表明,这个问题与WOW64模拟器有关。我在一个非提升的WinForms应用程序中使用了这段代码:

...
using System.Diagnostics;
...

ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/K sfc.exe /scannow");
  startInfo.UseShellExecute = true;
  startInfo.Verb = "runas";
Process.Start(startInfo);

从32位WOW64进程开始,这会在控制台窗口中显示以下消息:

Windows资源保护无法启动修复服务。

从64位进程开始,上述代码成功运行。

实际上,我认为.net或WinForms在这里都不相关。如果我从SysWOW64文件夹运行32位cmd.exe,然后调用sfc,我会遇到失败。但是我使用64位cmd.exe成功了。

甚至没有必要将cmd.exe纳入其中。从64位进程开始,没有提升权限,以下操作成功:

...
using System.Diagnostics;
...

ProcessStartInfo startInfo = new ProcessStartInfo("sfc.exe", "/scannow");
startInfo.UseShellExecute = true;
startInfo.Verb = "runas";
Process.Start(startInfo);

我猜想解决问题的方法可能涉及到将主要程序切换为64位(这可能有点极端),或者在链中插入一个64位进程,以便可以运行64位的 sfc


我不能决定JIT编译后的可执行文件是32位还是64位;也不想这样做;也不想发布一个单独的thunking可执行文件。我希望32位或64位代码知道如何启动32位或64位版本的cmd,以便启动32位或64位的sfc图像。 - Ian Boyd
那么,请启动64位cmd。从32位进程中,您可以在C:/windows/sysnative中找到它。 - David Heffernan
你为什么不问一个问题呢?或者换句话说,这个问题之后你还打算问什么其他的问题呢? - David Heffernan
看了一下我的编辑历史,我添加了一个关于32位和64位障碍的精彩见解的摘要;第二次编辑则是修正了一个单词和语法问题。我认为这并没有改变问题的意思。 - Ian Boyd
编辑历史记录非常清晰。在我写这个答案的时候,从来没有提到过不能使用64位存根的要求。我花了时间来处理这个问题,现在你还期望我回答另一个问题。你问了两个完全不同的问题。更糟糕的是,第二个问题是作为对第一个问题的编辑添加的。 - David Heffernan
显示剩余7条评论

1

在这里得到了所有的帮助后,我最终得到了这个解决方案,并且它在8.1 x64上运行良好。非常感谢大家!

    ...
    using System.Diagnostics;
    ...

    private void button4_Click(object sender, EventArgs e)
    {
        string docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        string snat = Environment.GetEnvironmentVariable("windir") + @"\sysnative\sfc.exe";

        Process a = new Process();
          a.StartInfo.FileName = snat;
          a.StartInfo.Arguments = "/SCANNOW";
          a.StartInfo.UseShellExecute = false;
          a.StartInfo.RedirectStandardOutput = true;
          a.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
          a.StartInfo.CreateNoWindow = true;
        a.Start();

        string output = a.StandardOutput.ReadToEnd();
        a.WaitForExit();

        using (StreamWriter outfile = new StreamWriter(docs + @"\Testoutput.txt"))
        {
            outfile.Write(output);
        }

        MessageBox.Show("DONE!");
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接