好的,我有一种方法可以简单地将 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 线程中工作?我想使用线程池来运行它。