提供的凭据不正确时,WMI API会挂起

时间:2011-10-17 21:51:16

标签: c# wmi

我找到了多个在线教程,用于使用c#建立与远程机器的WMI连接。这些教程描述了如下过程:

ConnectionOptions cOpts = new ConnectionOptions();
ManagementObjectCollection moCollection;
ManagementObjectSearcher moSearcher;
ManagementScope mScope;
ObjectQuery oQuery;

mScope = new ManagementScope(String.Format("\\\\{0}\\{1}", host.hostname, "ROOT\\CIMV2"), cOpts);
oQuery = new ObjectQuery("Select * from Win32_OperatingSystem");
moSearcher = new ManagementObjectSearcher(mScope, oQuery);

moCollection = moSearcher.Get();

快乐路径案例 - 连接到本地主机,或使用适当的凭据连接到远程主机 - 工作正常。我正在开发一个项目,当我们当前登录的帐户无法访问我们尝试连接的远程主机时,我们需要支持该案例。也就是说,我们需要捕获这种情况,将错误的凭据提请用户注意,并提示他们再次提供凭据。

当我在我的ConnectionOptions对象中指定远程计算机上没有上下文的凭据时,我对moSearcher.Get()的调用会无限期地挂起(看似)。同样,ManagementScope中对Connect()函数的调用也以相同的方式挂起。

我们有类似的逻辑来执行c ++中的等效WMI命令,我可以报告,如果提供了不正确的凭据,那些几乎立即返回。返回适当的“访问被拒绝”消息。我现在用于测试目的的主机与我们在测试现有c ++逻辑时使用的主机相同,因此我没有理由相信在我们的环境中WMI配置不正确。

我在c#中搜索了WMI连接的超时问题。我已经探索了ConnectionOptions和moSearcher.Options的Timeout属性。我还查看了可以与ManagementObjectSearcher实例关联的EnumerationOptions对象的ReturnImmediately属性。这些选项对我没有预期效果。

我想我可以在一个单独的线程中执行这些WMI命令,并使用监视代码包围该线程,如果它没有在合理的时间内返回,则会将其杀死。这似乎是相当多的工作,将推送给c#WMI例程的所有消费者,我希望有一个更简单的方法。另外,我不确定以这种方式杀死一个出色的线程会正确清理WMI连接。

Ping远程主机对我没有任何好处,因为知道主机已启动并且运行并不告诉我我的凭据是否合适(以及c#WMI调用是否会挂起)。是否有另一种方法来验证远程主机的凭据?

我总是可能会缺少一个明显的标志或API,因为我认为其他人已经遇到了这个问题。任何信息/协助将不胜感激。感谢您阅读这篇冗长的文章。

2 个答案:

答案 0 :(得分:0)

我不知道你的所有特殊功能是什么,但是这里有一个小例程来帮助你排除故障,应该能够将你的例程包装在一个线程中并给它5秒钟来执行:

void Fake() {
  bool ok = false;
  ConnectionOptions cOpts = new ConnectionOptions();
  ManagementObjectCollection moCollection;
  ManagementObjectSearcher moSearcher;
  ManagementScope mScope;
  ObjectQuery oQuery;
  if (cOpts != null) {
    mScope = new ManagementScope(String.Format("\\\\{0}\\{1}", host.hostname, "ROOT\\CIMV2"), cOpts);
    if (mScope != null) {
      oQuery = new ObjectQuery("Select * from Win32_OperatingSystem");
      if (oQuery != null) {
        moSearcher = new ManagementObjectSearcher(mScope, oQuery);
        if (moSearcher != null) {
          ManualResetEvent mre = new ManualResetEvent(false);
          Thread thread1 = new Thread(() => {
            moCollection = moSearcher.Get();
            mre.Set();
          };
          thread1.Start();
          ok = mre.WaitOne(5000); // wait 5 seconds
        } else {
          Console.WriteLine("ManagementObjectSearcher failed");
        }
      } else {
        Console.WriteLine("ObjectQuery failed");
      }
    } else {
      Console.WriteLine("ManagementScope failed");
    }
  } else {
    Console.WriteLine("ConnectionOptions failed");
  }
}

希望有助于或给你一些想法。

答案 1 :(得分:0)

我接受了jp的建议,将WMI API调用包含在一个单独的线程中,如果超过超时就可能被杀死。在测试时,单独的线程抛出了System.UnauthorizedAccessException类型的异常。我删除了线程逻辑并添加了一个catch语句来处理这个异常类型。果然,在调用ManagementObjectSearcher.Get()之后几乎立即捕获到异常。

try
{
    moCollection = moSearcher.Get();
}

catch (System.UnauthorizedAccessException)
{
    return Program.ERROR_FUNCTION_FAILED;
}

catch (System.Runtime.InteropServices.COMException)
{
    MessageBox.Show("Error, caught COMException.");
    return Program.ERROR_FUNCTION_FAILED;
}

(注意我的代码中已经存在System.Runtime.InteropServices.COMException catch语句)

我不知道为什么在作为父线程的一部分执行时,不会抛出此异常(或者至少不会通过VS 2010 IDE引起用户的注意)。无论如何,这正是我所寻找的,并且与c ++中WMI连接例程的行为一致。