Fixed host instances WMI query error when dealing with shutdown / restart

This commit is contained in:
2026-06-01 13:47:59 +02:00
parent 6c837f35f5
commit ae468e9f19
10 changed files with 290 additions and 54 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>
@@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>BizTalkPlatformManagementTool</RootNamespace>
<AssemblyName>BizTalkPlatformManagementTool</AssemblyName>
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
@@ -9,7 +9,7 @@ namespace BizTalkPlatformManagementTool.Services
{
public sealed class BizTalkOperationService
{
public const string Version = "2.0.0-net471";
public const string Version = "2.0.0-net461";
private const string UnknownApplication = "(Unknown Application)";
private readonly OperationLogger _logger;
@@ -116,7 +116,7 @@ namespace BizTalkPlatformManagementTool.Services
{
foreach (var item in app.ReceiveLocations.Where(x => x.Enabled))
{
plan.Steps.Add(Step("ReceiveLocation", app.Application, item.Name, null, "Disable receive location", "MSBTS_ReceiveLocation", "Name", item.Name, "Disable", null, false));
plan.Steps.Add(Step("ReceiveLocation", app.Application, item.Name, null, "Disable receive location", "MSBTS_ReceiveLocation", "Name", item.Name, "Disable", null, null));
}
foreach (var item in app.Orchestrations.Where(x => x.OrchestrationStatus == ArtifactStates.OrchestrationStarted))
@@ -232,14 +232,22 @@ namespace BizTalkPlatformManagementTool.Services
continue;
}
using (var instance = client.FindByProperty(step.WmiClass, step.KeyProperty, step.KeyValue))
try
{
if (instance == null)
_logger.Info("Executing step: " + DescribeStep(step));
using (var instance = client.FindByProperty(step.WmiClass, step.KeyProperty, step.KeyValue))
{
throw new InvalidOperationException(step.Kind + " not found: " + step.Name);
}
if (instance == null)
{
throw new InvalidOperationException(step.Kind + " not found: " + step.Name);
}
ExecuteStep(client, instance, step, options);
ExecuteStep(client, instance, step, options);
}
}
catch (Exception ex)
{
throw new InvalidOperationException("Step failed: " + DescribeStep(step) + ". Error: " + ex.Message, ex);
}
}
}
@@ -387,6 +395,23 @@ namespace BizTalkPlatformManagementTool.Services
return result;
}
private static string DescribeStep(OperationStep step)
{
if (step == null)
{
return "<unknown step>";
}
return step.Action
+ " '" + step.Name + "'"
+ " [kind=" + step.Kind
+ ", class=" + step.WmiClass
+ ", key=" + step.KeyProperty + "=" + (step.KeyValue ?? string.Empty)
+ ", method=" + step.MethodName
+ (string.IsNullOrWhiteSpace(step.Server) ? string.Empty : ", server=" + step.Server)
+ "]";
}
private static ApplicationSnapshot GetApplication(Dictionary<string, ApplicationSnapshot> apps, string appName)
{
ApplicationSnapshot app;
@@ -34,44 +34,88 @@ namespace BizTalkPlatformManagementTool.Services
}
public List<ManagementObject> Query(string className)
{
return Query(className, true);
}
private List<ManagementObject> Query(string className, bool logQuery)
{
EnsureConnected();
var result = new List<ManagementObject>();
var query = new ObjectQuery("SELECT * FROM " + className);
using (var searcher = new ManagementObjectSearcher(_scope, query))
using (var collection = searcher.Get())
var queryText = "SELECT * FROM " + className;
if (logQuery)
{
foreach (ManagementObject item in collection)
_logger.Info("Executing WMI query: " + queryText);
}
try
{
var query = new ObjectQuery(queryText);
using (var searcher = new ManagementObjectSearcher(_scope, query))
using (var collection = searcher.Get())
{
result.Add(item);
foreach (ManagementObject item in collection)
{
result.Add(item);
}
}
}
catch (ManagementException ex)
{
throw new InvalidOperationException("WMI query failed. Query: " + queryText + ". WMI error: " + ex.Message, ex);
}
return result;
}
public ManagementObject FindByProperty(string className, string propertyName, string value)
{
return FindByProperty(className, propertyName, value, true);
}
private ManagementObject FindByProperty(string className, string propertyName, string value, bool logLookup)
{
EnsureConnected();
var query = string.Format(
var queryText = "SELECT * FROM " + className;
var filter = string.Format(
CultureInfo.InvariantCulture,
"SELECT * FROM {0} WHERE {1} = \"{2}\"",
className,
"{0} = \"{1}\"",
propertyName,
EscapeWql(value));
using (var searcher = new ManagementObjectSearcher(_scope, new ObjectQuery(query)))
using (var collection = searcher.Get())
value ?? string.Empty);
if (logLookup)
{
foreach (ManagementObject item in collection)
{
return item;
}
_logger.Info("Resolving WMI object: " + queryText + " with client-side filter " + filter);
}
return null;
try
{
ManagementObject match = null;
var items = Query(className, false);
foreach (var item in items)
{
if (MatchesProperty(item, className, propertyName, value))
{
match = item;
break;
}
}
foreach (var item in items)
{
if (!object.ReferenceEquals(item, match))
{
item.Dispose();
}
}
return match;
}
catch (ManagementException ex)
{
throw new InvalidOperationException("WMI lookup failed. Query: " + queryText + ". Filter: " + filter + ". WMI error: " + ex.Message, ex);
}
}
public uint InvokeMethod(ManagementObject instance, string methodName, params object[] arguments)
@@ -84,11 +128,20 @@ namespace BizTalkPlatformManagementTool.Services
var methodToCall = ResolveMethodName(instance, methodName);
_logger.Info("Calling " + instance.Path.ClassName + "." + methodToCall + " on " + SafeGetString(instance, "Name", SafeGetString(instance, "InstanceName", "<unknown>")));
object result = instance.InvokeMethod(methodToCall, arguments == null || arguments.Length == 0 ? null : arguments);
object result;
try
{
result = instance.InvokeMethod(methodToCall, arguments == null || arguments.Length == 0 ? null : arguments);
}
catch (ManagementException ex)
{
throw new InvalidOperationException("WMI method failed. Class: " + instance.Path.ClassName + ", method: " + methodToCall + ", object: " + SafeObjectName(instance) + ". WMI error: " + ex.Message, ex);
}
var returnCode = ExtractReturnCode(result);
if (returnCode != 0)
{
throw new InvalidOperationException(methodToCall + " returned HRESULT/ReturnValue " + returnCode.ToString(CultureInfo.InvariantCulture));
throw new InvalidOperationException("WMI method returned an error. Class: " + instance.Path.ClassName + ", method: " + methodToCall + ", object: " + SafeObjectName(instance) + ", ReturnValue: " + returnCode.ToString(CultureInfo.InvariantCulture));
}
return returnCode;
@@ -101,7 +154,7 @@ namespace BizTalkPlatformManagementTool.Services
while (DateTime.UtcNow <= deadline)
{
using (var current = FindByProperty(className, keyProperty, keyValue))
using (var current = FindByProperty(className, keyProperty, keyValue, false))
{
if (current != null && isReached(current))
{
@@ -113,7 +166,7 @@ namespace BizTalkPlatformManagementTool.Services
Thread.Sleep(TimeSpan.FromSeconds(delay));
}
throw new TimeoutException("Timeout while waiting for " + description);
throw new TimeoutException("Timeout while waiting for " + description + " [" + className + "." + keyProperty + "=" + keyValue + "]");
}
public static string SafeGetString(ManagementBaseObject item, string propertyName, string fallback)
@@ -191,13 +244,58 @@ namespace BizTalkPlatformManagementTool.Services
return false;
}
private static bool MatchesProperty(ManagementBaseObject item, string className, string propertyName, string expectedValue)
{
foreach (var candidate in CandidatePropertyNames(className, propertyName))
{
if (!HasProperty(item, candidate))
{
continue;
}
var actual = SafeGetString(item, candidate, string.Empty);
if (string.Equals(actual, expectedValue ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static IEnumerable<string> CandidatePropertyNames(string className, string propertyName)
{
yield return propertyName;
if (string.Equals(className, "MSBTS_HostInstance", StringComparison.OrdinalIgnoreCase)
&& string.Equals(propertyName, "InstanceName", StringComparison.OrdinalIgnoreCase))
{
yield return "Name";
}
}
private static string SafeObjectName(ManagementObject instance)
{
try
{
return instance.Path == null ? "<unknown>" : instance.Path.RelativePath;
}
catch
{
return SafeGetString(instance, "Name", SafeGetString(instance, "InstanceName", "<unknown>"));
}
}
private static string ResolveMethodName(ManagementObject instance, string requestedName)
{
foreach (MethodData method in instance.Methods)
using (var managementClass = new ManagementClass(instance.Scope, new ManagementPath(instance.Path.ClassName), null))
{
if (string.Equals(method.Name, requestedName, StringComparison.OrdinalIgnoreCase))
foreach (MethodData method in managementClass.Methods)
{
return method.Name;
if (string.Equals(method.Name, requestedName, StringComparison.OrdinalIgnoreCase))
{
return method.Name;
}
}
}
@@ -220,11 +318,6 @@ namespace BizTalkPlatformManagementTool.Services
return Convert.ToUInt32(result, CultureInfo.InvariantCulture);
}
private static string EscapeWql(string value)
{
return (value ?? string.Empty).Replace("\\", "\\\\").Replace("\"", "\\\"");
}
public void Dispose()
{
}
@@ -1,4 +1,7 @@
using System;
using System.Globalization;
using System.IO;
using System.Threading;
namespace BizTalkPlatformManagementTool.Services
{
@@ -19,11 +22,28 @@ namespace BizTalkPlatformManagementTool.Services
public sealed class OperationLogger
{
private const string LogFilePrefix = "BizTalkPlatformManagementTool-";
private const string LogFileExtension = ".log";
private const int RetentionDays = 5;
private static readonly object FileLock = new object();
private static int _cleanupDone;
private readonly Action<LogEntry> _sink;
private readonly string _logDirectory;
public OperationLogger(Action<LogEntry> sink)
{
_sink = sink;
_logDirectory = AppDomain.CurrentDomain.BaseDirectory;
CleanupOldLogs();
}
public string LogFilePath
{
get
{
return Path.Combine(_logDirectory, LogFilePrefix + DateTime.Now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + LogFileExtension);
}
}
public void Info(string message)
@@ -48,17 +68,69 @@ namespace BizTalkPlatformManagementTool.Services
private void Write(LogLevel level, string message)
{
var entry = new LogEntry
{
Timestamp = DateTime.Now,
Level = level,
Message = message
};
WriteToFile(entry);
if (_sink == null)
{
return;
}
_sink(new LogEntry
_sink(entry);
}
private void WriteToFile(LogEntry entry)
{
try
{
Timestamp = DateTime.Now,
Level = level,
Message = message
});
var line = string.Format(
CultureInfo.InvariantCulture,
"[{0:yyyy-MM-dd HH:mm:ss}][{1}] {2}{3}",
entry.Timestamp,
entry.Level.ToString().ToUpperInvariant(),
entry.Message,
Environment.NewLine);
lock (FileLock)
{
File.AppendAllText(LogFilePath, line);
}
}
catch
{
// Logging must never interrupt BizTalk operations.
}
}
private void CleanupOldLogs()
{
if (Interlocked.Exchange(ref _cleanupDone, 1) == 1)
{
return;
}
try
{
var cutoff = DateTime.Now.Date.AddDays(-(RetentionDays - 1));
foreach (var file in Directory.GetFiles(_logDirectory, LogFilePrefix + "*" + LogFileExtension))
{
var lastWrite = File.GetLastWriteTime(file);
if (lastWrite.Date < cutoff)
{
File.Delete(file);
}
}
}
catch
{
// Log retention cleanup is best-effort.
}
}
}
}
@@ -20,6 +20,7 @@ namespace BizTalkPlatformManagementTool.Ui
private readonly DataGridView _logGrid = new DataGridView();
private readonly StatusStrip _statusStrip = new StatusStrip();
private readonly ToolStripStatusLabel _statusLabel = new ToolStripStatusLabel();
private readonly OperationLogger _logger;
private readonly BizTalkOperationService _service;
private Button _diagnoseButton;
@@ -37,8 +38,10 @@ namespace BizTalkPlatformManagementTool.Ui
MinimumSize = new Size(980, 640);
StartPosition = FormStartPosition.CenterScreen;
_service = new BizTalkOperationService(new OperationLogger(AppendLog));
_logger = new OperationLogger(AppendLog);
_service = new BizTalkOperationService(_logger);
BuildUi();
_logger.Info("Log file: " + _logger.LogFilePath);
}
private void BuildUi()
@@ -264,12 +267,12 @@ namespace BizTalkPlatformManagementTool.Ui
try
{
work();
AppendLog(new LogEntry { Timestamp = DateTime.Now, Level = LogLevel.Success, Message = "Operation completed." });
_logger.Success("Operation completed.");
SetBusy(false, "Ready.");
}
catch (Exception ex)
{
AppendLog(new LogEntry { Timestamp = DateTime.Now, Level = LogLevel.Error, Message = ex.Message });
_logger.Error(FormatException(ex));
SetBusy(false, "Failed: " + ex.Message);
}
});
@@ -458,5 +461,27 @@ namespace BizTalkPlatformManagementTool.Ui
grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
grid.BackgroundColor = SystemColors.Window;
}
private static string FormatException(Exception ex)
{
if (ex == null)
{
return "Operation failed.";
}
var message = ex.Message;
var inner = ex.InnerException;
while (inner != null)
{
if (!string.IsNullOrWhiteSpace(inner.Message) && message.IndexOf(inner.Message, StringComparison.OrdinalIgnoreCase) < 0)
{
message += " Inner error: " + inner.Message;
}
inner = inner.InnerException;
}
return message;
}
}
}