WMI调用在GUI线程(STA线程)中工作,为什么它在后台线程(MTA线程)中失败?

时间:2013-09-20 20:26:04

标签: c# multithreading wmi apartments

好的,我有一个方法可以简单地将Windows事件日志备份为* .evt文件。 在Windows XP x86和x64上的.net2.0中,这很好用,没问题。

我们最近升级到.net4.0(特别是4.0.3),现在这段代码以非常具体的方式失败了。 (在XP x86和x64上失败,Win7工作正常)

如果方法是由事件处理程序中的主GUI线程执行的,那么它可以正常工作。 但是,如果此方法在新的Thread或ThreadPool中执行,则它会因Access Denied错误而失败。

此应用程序以管理员身份运行并访问本地计算机wmi提供程序。

我猜想.net4中有一些新的线程安全性,我不满意或类似的东西。

以下是一些说明问题的示例代码。 这是一个带有3个按钮的表单,处理程序位于最后,说明它何时工作以及何时失败。 它将所有Windows事件日志的副本保存到.. \ Log文件夹。

使用.Net2.0的目标框架编译下面的内容,所有三种调用方法都可以正常工作。 使用.Net4.0.3的目标框架进行编译,最后2个调用方法因Access Denied而失败。

我在这里缺少什么?

    public void ExportWindowsEventLogs(object ignored)
    {
        try
        {
               ManagementObjectSearcher searcher =
                new ManagementObjectSearcher("root\\CIMV2", 
                "SELECT * FROM Win32_NTEventlogFile");

               searcher.Scope.Options.EnablePrivileges = true;    // Defaults to False which gives AccessDenied errors so
               foreach (ManagementObject oEventLogFile in searcher.Get())
               {
                   string LogName = oEventLogFile["LogfileName"] as string;
                   if (!String.IsNullOrEmpty(LogName))
                   {
                       // Obtain in-parameters for the method
                       ManagementBaseObject inParams =
                           oEventLogFile.GetMethodParameters("BackupEventlog");
                       // Add the input parameters.
                       string strPath = Application.StartupPath + @"\..\Log\" + LogName + "-Exported.Evt";
                       inParams["ArchiveFileName"] = strPath;

                       //Delete the old backup if it exists, as the WMI BackupEventLog method does not overwrite old files.
                       FileInfo backupFile = new FileInfo(strPath);
                       if (!backupFile.Directory.Exists)
                           backupFile.Directory.Create();
                       else if (backupFile.Exists)
                           backupFile.Delete();

                       InvokeMethodOptions options = new InvokeMethodOptions();

                       // Execute the method and obtain the return values.
                       //FAILS ON NEXT LINE WITH ACCESS DENIED ERROR,
                       //but oEventLogFile.ToString() works fine.
                       ManagementBaseObject outParams =
                           oEventLogFile.InvokeMethod("BackupEventlog", inParams, null);
                       object returnValue =  oEventLogFile.InvokeMethod("BackupEventlog", new object[] { strPath });
                   }
               }
        }
        catch(Exception err)
        {
            MessageBox.Show("An error occurred while trying to execute the WMI method: " + err.ToString());
        }

    }

    private void button1_Click(object sender, EventArgs e)
    {
        //WORKS
        ExportWindowsEventLogs(null);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        //Access Denied error
        Thread workerThread = new Thread(new ParameterizedThreadStart(ExportWindowsEventLogs));
        workerThread.Priority = ThreadPriority.Lowest;
        workerThread.Start(true);
    }

    private void button3_Click(object sender, EventArgs e)
    {
        //Access Denied error
        System.Threading.ThreadPool.QueueUserWorkItem(ExportWindowsEventLogs, null);
    }

更新 这似乎与执行线程的Thread.ApartmentState设置直接相关。 默认情况下,线程是使用ApartmentState.MTA创建的,而主GUI线程是STA线程。 在.Net 4.0中,我的WMI代码在STA线程中运行时有效,但在MTA线程上不起作用。在.Net 2.0中,它适用于两者。

如何让它在.Net 4.0上的MTA线程中运行?我想使用线程池来运行它。

0 个答案:

没有答案