Fix BizTalk application ownership mapping

This commit is contained in:
2026-08-27 19:54:33 +02:00
parent b18adab26d
commit 61b5b7e452
21 changed files with 735 additions and 35 deletions
@@ -23,7 +23,7 @@ namespace BizTalkPlatformManagementTool.Setup
private const string ProductName = "BizTalk Platform Management Tool";
/// <summary>Aktuelle Produktversion des Installers und Uninstall-Eintrags.</summary>
private const string ProductVersion = "2.3.2";
private const string ProductVersion = "2.3.3";
/// <summary>
/// Wartezeiten zwischen Wiederholungen atomarer Verzeichnisverschiebungen.
@@ -65,7 +65,7 @@ namespace BizTalkPlatformManagementTool.Setup
{
AutoSize = true,
Font = new Font(Font.FontFamily, 14, FontStyle.Bold),
Text = "BizTalk Platform Management Tool 2.3.2"
Text = "BizTalk Platform Management Tool 2.3.3"
});
root.Controls.Add(new Label
{
@@ -8,6 +8,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyProduct("BizTalk Platform Management Tool")]
[assembly: ComVisible(false)]
[assembly: Guid("675b68a9-bd80-46a5-b8c5-3b11b0b374e2")]
[assembly: AssemblyVersion("2.3.2.0")]
[assembly: AssemblyFileVersion("2.3.2.0")]
[assembly: AssemblyVersion("2.3.3.0")]
[assembly: AssemblyFileVersion("2.3.3.0")]
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="2.3.2.0" name="BizTalkPlatformManagementTool.Setup" />
<assemblyIdentity version="2.3.3.0" name="BizTalkPlatformManagementTool.Setup" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator" uiAccess="false" /></requestedPrivileges></security>
</trustInfo>
@@ -58,6 +58,7 @@
<Compile Include="Models\DiffModels.cs" />
<Compile Include="Models\OperationModels.cs" />
<Compile Include="Services\BizTalkWmiClient.cs" />
<Compile Include="Services\BizTalkApplicationResolver.cs" />
<Compile Include="Services\AdapterAssemblyResolver.cs" />
<Compile Include="Services\CsvWriter.cs" />
<Compile Include="Services\HtmlReportWriter.cs" />
@@ -9,6 +9,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: ComVisible(false)]
[assembly: Guid("2c5b2c0a-f407-46c2-9e3b-1fa09fa8445a")]
[assembly: AssemblyVersion("2.3.2.0")]
[assembly: AssemblyFileVersion("2.3.2.0")]
[assembly: AssemblyVersion("2.3.3.0")]
[assembly: AssemblyFileVersion("2.3.3.0")]
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
@@ -241,7 +241,7 @@ namespace BizTalkPlatformManagementTool.Services
}
/// <summary>Discovers BizTalk, ScheduledTask and explicitly configured directories.</summary>
private static IEnumerable<string> DiscoverSearchDirectories()
internal static IEnumerable<string> DiscoverSearchDirectories()
{
var result = new List<string>();
AddConfiguredDirectories(result);
@@ -0,0 +1,443 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Management;
using System.Reflection;
namespace BizTalkPlatformManagementTool.Services
{
/// <summary>
/// Resolves runtime artifacts to their owning BizTalk applications by reading the
/// ExplorerOM application hierarchy without a compile-time BizTalk dependency.
/// </summary>
internal sealed class BizTalkApplicationResolver
{
/// <summary>The strong-name token used by Microsoft BizTalk product assemblies.</summary>
private static readonly byte[] BizTalkPublicKeyToken = { 0x31, 0xbf, 0x38, 0x56, 0xad, 0x36, 0x4e, 0x35 };
/// <summary>Maps group-unique send-port names to application names.</summary>
private readonly ArtifactApplicationMap _sendPorts = new ArtifactApplicationMap();
/// <summary>Maps receive-port names to application names.</summary>
private readonly ArtifactApplicationMap _receivePorts = new ArtifactApplicationMap();
/// <summary>Maps receive-location names to application names.</summary>
private readonly ArtifactApplicationMap _receiveLocations = new ArtifactApplicationMap();
/// <summary>Maps orchestration names and supported aliases to application names.</summary>
private readonly ArtifactApplicationMap _orchestrations = new ArtifactApplicationMap();
/// <summary>Initializes an empty resolver, primarily for deterministic regression tests.</summary>
internal BizTalkApplicationResolver()
{
}
/// <summary>
/// Reads the BizTalk Management database location through WMI and builds an
/// application/artifact index through the locally installed ExplorerOM assembly.
/// </summary>
/// <param name="client">Connected BizTalk WMI client.</param>
/// <param name="logger">Optional operation logger for support diagnostics.</param>
/// <returns>A resolver populated from the BizTalk application catalog.</returns>
internal static BizTalkApplicationResolver Load(BizTalkWmiClient client, OperationLogger logger)
{
if (client == null)
{
throw new ArgumentNullException("client");
}
string databaseServer;
string databaseName;
ReadManagementDatabase(client, out databaseServer, out databaseName);
var assembly = LoadExplorerOmAssembly();
var catalogType = assembly.GetType("Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer", true, false);
var catalog = Activator.CreateInstance(catalogType);
try
{
var connection = new SqlConnectionStringBuilder
{
DataSource = databaseServer,
InitialCatalog = databaseName,
IntegratedSecurity = true,
ConnectTimeout = 15,
ApplicationName = "BizTalkPlatformManagementTool"
};
SetProperty(catalog, "ConnectionString", connection.ConnectionString);
var resolver = new BizTalkApplicationResolver();
resolver.IndexApplications(GetProperty(catalog, "Applications"));
Info(logger, "BizTalk application catalog loaded through ExplorerOM. "
+ "Applications=" + resolver.ApplicationCount
+ "; SendPorts=" + resolver._sendPorts.Count
+ "; ReceivePorts=" + resolver._receivePorts.Count
+ "; ReceiveLocations=" + resolver._receiveLocations.Count
+ "; Orchestrations=" + resolver._orchestrations.Count
+ "; ManagementDatabase=" + databaseServer + "\\" + databaseName + ".");
return resolver;
}
catch (TargetInvocationException ex)
{
throw new InvalidOperationException(
"BizTalk ExplorerOM could not read the application catalog from "
+ databaseServer + "\\" + databaseName + ". "
+ ExceptionDiagnostics.Format(ex.InnerException ?? ex), ex.InnerException ?? ex);
}
finally
{
var disposable = catalog as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
}
/// <summary>Gets the number of distinct application names represented in the index.</summary>
internal int ApplicationCount
{
get
{
return _sendPorts.ApplicationNames
.Concat(_receivePorts.ApplicationNames)
.Concat(_receiveLocations.ApplicationNames)
.Concat(_orchestrations.ApplicationNames)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Count();
}
}
/// <summary>Adds a send-port ownership record to the index.</summary>
/// <param name="application">Owning BizTalk application.</param>
/// <param name="name">Send-port name.</param>
internal void AddSendPort(string application, string name)
{
_sendPorts.Add(application, name);
}
/// <summary>Adds a receive-port ownership record to the index.</summary>
/// <param name="application">Owning BizTalk application.</param>
/// <param name="name">Receive-port name.</param>
internal void AddReceivePort(string application, string name)
{
_receivePorts.Add(application, name);
}
/// <summary>Adds a receive-location ownership record to the index.</summary>
/// <param name="application">Owning BizTalk application.</param>
/// <param name="name">Receive-location name.</param>
internal void AddReceiveLocation(string application, string name)
{
_receiveLocations.Add(application, name);
}
/// <summary>Adds an orchestration ownership record to the index.</summary>
/// <param name="application">Owning BizTalk application.</param>
/// <param name="name">Orchestration name or alias.</param>
internal void AddOrchestration(string application, string name)
{
_orchestrations.Add(application, name);
}
/// <summary>Resolves a send port to its owning application.</summary>
/// <param name="name">Send-port name.</param>
/// <returns>The application name, or null when absent or ambiguous.</returns>
internal string ResolveSendPort(string name)
{
return _sendPorts.Resolve(name);
}
/// <summary>Resolves a receive location, with its receive port as a stable fallback.</summary>
/// <param name="locationName">Receive-location name.</param>
/// <param name="receivePortName">Parent receive-port name.</param>
/// <returns>The application name, or null when absent or ambiguous.</returns>
internal string ResolveReceiveLocation(string locationName, string receivePortName)
{
return _receiveLocations.Resolve(locationName) ?? _receivePorts.Resolve(receivePortName);
}
/// <summary>Resolves an orchestration to its owning application.</summary>
/// <param name="name">WMI orchestration name.</param>
/// <returns>The application name, or null when absent or ambiguous.</returns>
internal string ResolveOrchestration(string name)
{
return _orchestrations.Resolve(name);
}
/// <summary>Enumerates all applications and their runtime artifact collections.</summary>
/// <param name="applications">ExplorerOM application collection.</param>
private void IndexApplications(object applications)
{
foreach (var application in Enumerate(applications, "Applications"))
{
var applicationName = ReadString(application, "Name");
if (string.IsNullOrWhiteSpace(applicationName))
{
continue;
}
foreach (var sendPort in Enumerate(GetProperty(application, "SendPorts"), "Application.SendPorts"))
{
AddSendPort(applicationName, ReadString(sendPort, "Name"));
}
foreach (var receivePort in Enumerate(GetProperty(application, "ReceivePorts"), "Application.ReceivePorts"))
{
AddReceivePort(applicationName, ReadString(receivePort, "Name"));
foreach (var location in Enumerate(GetProperty(receivePort, "ReceiveLocations"), "ReceivePort.ReceiveLocations"))
{
AddReceiveLocation(applicationName, ReadString(location, "Name"));
}
}
foreach (var orchestration in Enumerate(GetProperty(application, "Orchestrations"), "Application.Orchestrations"))
{
AddOrchestration(applicationName, ReadString(orchestration, "FullName"));
AddOrchestration(applicationName, ReadString(orchestration, "AssemblyQualifiedName"));
}
}
}
/// <summary>Reads the BizTalk Management database endpoint from MSBTS_GroupSetting.</summary>
/// <param name="client">Connected WMI client.</param>
/// <param name="server">Resolved SQL Server name.</param>
/// <param name="database">Resolved management database name.</param>
private static void ReadManagementDatabase(BizTalkWmiClient client, out string server, out string database)
{
var settings = client.Query("MSBTS_GroupSetting");
try
{
if (settings.Count != 1)
{
throw new InvalidOperationException(
"Expected exactly one MSBTS_GroupSetting instance but found " + settings.Count + ".");
}
server = BizTalkWmiClient.SafeGetString(settings[0], "MgmtDbServerName", null);
database = BizTalkWmiClient.SafeGetString(settings[0], "MgmtDbName", null);
if (string.IsNullOrWhiteSpace(server) || string.IsNullOrWhiteSpace(database))
{
throw new InvalidOperationException(
"MSBTS_GroupSetting did not expose MgmtDbServerName and MgmtDbName.");
}
}
finally
{
foreach (ManagementObject setting in settings)
{
setting.Dispose();
}
}
}
/// <summary>Loads an identity-verified ExplorerOM assembly from the CLR/GAC or installed BizTalk folders.</summary>
/// <returns>The loaded Microsoft.BizTalk.ExplorerOM assembly.</returns>
private static Assembly LoadExplorerOmAssembly()
{
var requested = new AssemblyName("Microsoft.BizTalk.ExplorerOM");
requested.SetPublicKeyToken(BizTalkPublicKeyToken);
var loaded = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(x => string.Equals(x.GetName().Name, requested.Name, StringComparison.OrdinalIgnoreCase));
if (loaded != null)
{
RequireMicrosoftIdentity(loaded);
return loaded;
}
var explorerType = Type.GetType(
"Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer, Microsoft.BizTalk.ExplorerOM",
false);
if (explorerType != null)
{
RequireMicrosoftIdentity(explorerType.Assembly);
return explorerType.Assembly;
}
try
{
return Assembly.Load(requested);
}
catch (FileNotFoundException)
{
// The installed-directory fallback below keeps non-GAC BizTalk admin installations usable.
}
catch (FileLoadException)
{
// The fallback still enforces the Microsoft strong-name identity before loading a file.
}
#pragma warning disable 618
// ExplorerOM versions vary across supported BizTalk installations. This GAC-only
// compatibility API is followed by an explicit Microsoft public-key-token check.
var partial = Assembly.LoadWithPartialName(requested.Name);
#pragma warning restore 618
if (partial != null)
{
RequireMicrosoftIdentity(partial);
return partial;
}
var path = AdapterAssemblyResolver.FindCandidateFile(
requested.Name,
requested,
AdapterAssemblyResolver.DiscoverSearchDirectories());
if (path == null)
{
throw new FileNotFoundException(
"Microsoft.BizTalk.ExplorerOM.dll was not found through CLR/GAC resolution or in the configured/installed BizTalk directories. "
+ "The application association cannot be derived from the runtime WMI classes alone.");
}
var fromPath = Assembly.LoadFrom(path);
RequireMicrosoftIdentity(fromPath);
return fromPath;
}
/// <summary>Rejects a same-named assembly that is not signed with the BizTalk product token.</summary>
/// <param name="assembly">Loaded ExplorerOM candidate.</param>
private static void RequireMicrosoftIdentity(Assembly assembly)
{
var actual = assembly == null ? null : assembly.GetName().GetPublicKeyToken();
if (actual == null || actual.Length != BizTalkPublicKeyToken.Length)
{
throw new FileLoadException("Microsoft.BizTalk.ExplorerOM has no valid BizTalk product public key token.");
}
for (var index = 0; index < actual.Length; index++)
{
if (actual[index] != BizTalkPublicKeyToken[index])
{
throw new FileLoadException("Microsoft.BizTalk.ExplorerOM has an unexpected public key token.");
}
}
}
/// <summary>Gets a required public instance property through reflection.</summary>
/// <param name="instance">Object that owns the property.</param>
/// <param name="name">Property name.</param>
/// <returns>The property value.</returns>
private static object GetProperty(object instance, string name)
{
if (instance == null)
{
throw new InvalidOperationException("ExplorerOM object was null while reading property '" + name + "'.");
}
var property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
if (property == null)
{
throw new MissingMemberException(instance.GetType().FullName, name);
}
return property.GetValue(instance, null);
}
/// <summary>Sets a required public instance property through reflection.</summary>
/// <param name="instance">Object that owns the property.</param>
/// <param name="name">Property name.</param>
/// <param name="value">Value to assign.</param>
private static void SetProperty(object instance, string name, object value)
{
var property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
if (property == null || !property.CanWrite)
{
throw new MissingMemberException(instance.GetType().FullName, name);
}
property.SetValue(instance, value, null);
}
/// <summary>Reads an optional string property from one ExplorerOM object.</summary>
/// <param name="instance">ExplorerOM object.</param>
/// <param name="name">Property name.</param>
/// <returns>The trimmed string value, or null.</returns>
private static string ReadString(object instance, string name)
{
var property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
if (property == null)
{
return null;
}
var value = property.GetValue(instance, null) as string;
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
/// <summary>Validates and enumerates one ExplorerOM collection.</summary>
/// <param name="value">Collection object.</param>
/// <param name="description">Property path used in diagnostics.</param>
/// <returns>The collection as an enumerable sequence.</returns>
private static IEnumerable Enumerate(object value, string description)
{
var enumerable = value as IEnumerable;
if (enumerable == null)
{
throw new InvalidOperationException("ExplorerOM property '" + description + "' is not enumerable.");
}
return enumerable;
}
/// <summary>Writes an informational message when logging is available.</summary>
/// <param name="logger">Optional logger.</param>
/// <param name="message">Message text.</param>
private static void Info(OperationLogger logger, string message)
{
if (logger != null)
{
logger.Info(message);
}
}
/// <summary>Stores one artifact-type mapping and rejects cross-application ambiguity.</summary>
private sealed class ArtifactApplicationMap
{
/// <summary>Unambiguous name/application mappings.</summary>
private readonly Dictionary<string, string> _applications = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
/// <summary>Names observed under more than one application.</summary>
private readonly HashSet<string> _ambiguous = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
/// <summary>Gets the number of unambiguous artifact names.</summary>
internal int Count { get { return _applications.Count; } }
/// <summary>Gets the represented application names.</summary>
internal IEnumerable<string> ApplicationNames { get { return _applications.Values; } }
/// <summary>Adds one mapping unless the artifact name is blank or becomes ambiguous.</summary>
/// <param name="application">Application name.</param>
/// <param name="artifact">Artifact name.</param>
internal void Add(string application, string artifact)
{
if (string.IsNullOrWhiteSpace(application) || string.IsNullOrWhiteSpace(artifact))
{
return;
}
application = application.Trim();
artifact = artifact.Trim();
if (_ambiguous.Contains(artifact))
{
return;
}
string existing;
if (_applications.TryGetValue(artifact, out existing)
&& !string.Equals(existing, application, StringComparison.OrdinalIgnoreCase))
{
_applications.Remove(artifact);
_ambiguous.Add(artifact);
return;
}
_applications[artifact] = application;
}
/// <summary>Resolves one unambiguous artifact name.</summary>
/// <param name="artifact">Artifact name.</param>
/// <returns>The application name, or null.</returns>
internal string Resolve(string artifact)
{
if (string.IsNullOrWhiteSpace(artifact) || _ambiguous.Contains(artifact.Trim()))
{
return null;
}
string application;
return _applications.TryGetValue(artifact.Trim(), out application) ? application : null;
}
}
}
}
@@ -16,10 +16,10 @@ namespace BizTalkPlatformManagementTool.Services
/// <summary>
/// Current tool version written into generated snapshots.
/// </summary>
public const string Version = "2.3.2-net461";
public const string Version = "2.3.3-net461";
/// <summary>
/// Fallback application name used when WMI does not expose an application property.
/// Fallback application name used when neither WMI nor the ExplorerOM catalog can resolve ownership.
/// </summary>
private const string UnknownApplication = "(Unknown Application)";
@@ -48,7 +48,17 @@ namespace BizTalkPlatformManagementTool.Services
var ports = client.Query("MSBTS_SendPort");
try
{
_logger.Success("WMI/CIM diagnostic succeeded. Send ports visible: " + ports.Count);
var applications = TryLoadApplicationResolver(client);
var resolved = applications == null
? 0
: ports.Count(x => !string.IsNullOrWhiteSpace(applications.ResolveSendPort(BizTalkWmiClient.SafeGetString(x, "Name", null))));
_logger.Success("WMI/CIM diagnostic succeeded. Send ports visible: " + ports.Count
+ "; application associations resolved: " + resolved + ".");
if (ports.Count > 0 && resolved != ports.Count)
{
_logger.Warning("Application association is incomplete. Unresolved send ports: "
+ (ports.Count - resolved) + ". Check the preceding ExplorerOM and BizTalk Management database diagnostics.");
}
}
finally
{
@@ -74,18 +84,21 @@ namespace BizTalkPlatformManagementTool.Services
};
var apps = new Dictionary<string, ApplicationSnapshot>(StringComparer.OrdinalIgnoreCase);
var applicationResolver = TryLoadApplicationResolver(client);
var receiveLocations = client.Query("MSBTS_ReceiveLocation");
try
{
foreach (var item in receiveLocations)
{
var appName = GetApplicationName(item);
var name = BizTalkWmiClient.SafeGetString(item, "Name", string.Empty);
var receivePortName = BizTalkWmiClient.SafeGetString(item, "ReceivePortName", string.Empty);
var appName = GetApplicationName(item, applicationResolver, "ReceiveLocation", name, receivePortName);
var app = GetApplication(apps, appName);
app.ReceiveLocations.Add(new ReceiveLocationState
{
Application = app.Application,
Name = BizTalkWmiClient.SafeGetString(item, "Name", string.Empty),
ReceivePortName = BizTalkWmiClient.SafeGetString(item, "ReceivePortName", string.Empty),
Name = name,
ReceivePortName = receivePortName,
Enabled = !BizTalkWmiClient.SafeGetBoolean(item, "IsDisabled", true),
AdapterName = BizTalkWmiClient.SafeGetString(item, "AdapterName", string.Empty),
Address = BizTalkWmiClient.SafeGetString(item, "InboundTransportURL", string.Empty)
@@ -102,12 +115,13 @@ namespace BizTalkPlatformManagementTool.Services
{
foreach (var item in sendPorts)
{
var appName = GetApplicationName(item);
var name = BizTalkWmiClient.SafeGetString(item, "Name", string.Empty);
var appName = GetApplicationName(item, applicationResolver, "SendPort", name, null);
var app = GetApplication(apps, appName);
app.SendPorts.Add(new SendPortState
{
Application = app.Application,
Name = BizTalkWmiClient.SafeGetString(item, "Name", string.Empty),
Name = name,
Status = BizTalkWmiClient.SafeGetInt32(item, "Status", 0),
PrimaryTransportType = BizTalkWmiClient.SafeGetString(item, "PTTransportType", string.Empty),
PrimaryTransportAddress = BizTalkWmiClient.SafeGetString(item, "PTAddress", string.Empty)
@@ -124,12 +138,13 @@ namespace BizTalkPlatformManagementTool.Services
{
foreach (var item in orchestrations)
{
var appName = GetApplicationName(item);
var name = BizTalkWmiClient.SafeGetString(item, "Name", string.Empty);
var appName = GetApplicationName(item, applicationResolver, "Orchestration", name, null);
var app = GetApplication(apps, appName);
app.Orchestrations.Add(new OrchestrationState
{
Application = app.Application,
Name = BizTalkWmiClient.SafeGetString(item, "Name", string.Empty),
Name = name,
OrchestrationStatus = BizTalkWmiClient.SafeGetInt32(item, "OrchestrationStatus", 0)
});
}
@@ -170,6 +185,14 @@ namespace BizTalkPlatformManagementTool.Services
snapshot.HostInstances = snapshot.HostInstances.OrderBy(x => x.Server).ThenBy(x => x.InstanceName).ToList();
SnapshotValidator.Validate(snapshot);
ApplicationSnapshot unresolved;
if (apps.TryGetValue(UnknownApplication, out unresolved) && _logger != null)
{
_logger.Warning("Snapshot contains unresolved BizTalk application associations. ReceiveLocations="
+ unresolved.ReceiveLocations.Count + "; SendPorts=" + unresolved.SendPorts.Count
+ "; Orchestrations=" + unresolved.Orchestrations.Count
+ ". Review ExplorerOM and BizTalk Management database diagnostics before plan approval.");
}
_logger.Success("Snapshot created. Applications: " + snapshot.Applications.Count + ", host instances: " + snapshot.HostInstances.Count);
return snapshot;
}
@@ -873,24 +896,75 @@ namespace BizTalkPlatformManagementTool.Services
}
/// <summary>
/// Reads the application name from the available WMI properties.
/// Reads a directly exposed application name or resolves it through the
/// ExplorerOM application hierarchy because the runtime WMI classes do not
/// define a reliable application property.
/// </summary>
/// <param name="item">The WMI object being mapped into a snapshot item.</param>
/// <param name="resolver">Optional ExplorerOM application resolver.</param>
/// <param name="kind">Artifact kind used to select the catalog index.</param>
/// <param name="name">Artifact name.</param>
/// <param name="parentName">Optional parent receive-port name.</param>
/// <returns>The discovered application name or a stable fallback.</returns>
private static string GetApplicationName(ManagementObject item)
private static string GetApplicationName(ManagementObject item, BizTalkApplicationResolver resolver, string kind, string name, string parentName)
{
var names = new[] { "ApplicationName", "Application", "BizTalkApplication" };
foreach (var name in names)
foreach (var propertyName in names)
{
var value = BizTalkWmiClient.SafeGetString(item, name, null);
var value = BizTalkWmiClient.SafeGetString(item, propertyName, null);
if (!string.IsNullOrWhiteSpace(value))
{
return value;
}
}
if (resolver != null)
{
string application = null;
if (string.Equals(kind, "SendPort", StringComparison.Ordinal))
{
application = resolver.ResolveSendPort(name);
}
else if (string.Equals(kind, "ReceiveLocation", StringComparison.Ordinal))
{
application = resolver.ResolveReceiveLocation(name, parentName);
}
else if (string.Equals(kind, "Orchestration", StringComparison.Ordinal))
{
application = resolver.ResolveOrchestration(name);
}
if (!string.IsNullOrWhiteSpace(application))
{
return application;
}
}
return UnknownApplication;
}
/// <summary>
/// Loads the optional ExplorerOM application index while preserving WMI-only
/// snapshot availability and emitting an actionable warning on failure.
/// </summary>
/// <param name="client">Connected BizTalk WMI client.</param>
/// <returns>The populated resolver, or null when the catalog could not be read.</returns>
private BizTalkApplicationResolver TryLoadApplicationResolver(BizTalkWmiClient client)
{
try
{
return BizTalkApplicationResolver.Load(client, _logger);
}
catch (Exception ex)
{
if (_logger != null)
{
_logger.Warning("BizTalk application association could not be loaded. Runtime state collection remains available, "
+ "but affected artifacts will be grouped under " + UnknownApplication + ". "
+ ExceptionDiagnostics.Format(ex));
}
return null;
}
}
/// <summary>
/// Gibt alle von einer WMI-Abfrage übernommenen Objekte deterministisch frei.
/// </summary>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="2.3.2.0" name="BizTalkPlatformManagementTool" />
<assemblyIdentity version="2.3.3.0" name="BizTalkPlatformManagementTool" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>