0

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

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

如果该方法由事件处理程序中的主 GUI 线程执行,则它可以正常工作。但是,如果此方法在新线程中执行,或者由 ThreadPool 执行,则会失败并出现访问被拒绝错误。

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

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

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

使用.Net2.0的目标框架编译以下,所有三种调用方法都可以正常工作。使用.Net4.0.3的目标框架编译,最后2个调用方法失败,拒绝访问。

我在这里想念什么?

    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 线程中工作?我想使用线程池来运行它。

4

0 回答 0