Skip to content

ManagementClient constructor throws NotImplementedException when AppDomain.SetupInformation.TargetFrameworkName is null or empty #386

@jeanliberarel

Description

@jeanliberarel

Describe the bug

We encountered this defect in a .NET custom action executing inside a WiX installer. The custom action loads a .NET Framework 4.8.1 assembly that constructs a ManagementClient, and the constructor throws a NotImplementedException.

When we investigated, we found the root cause: the WiX managed custom-action shim does not set AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName, leaving it null or empty. When TargetFrameworkName is missing, the .NET System.Uri class falls back to legacy behavior and decodes percent-encoded slashes and dots. ManagementClient's constructor calls DisableUriParserLegacyQuirks, which detects this via a URI round-trip test, then attempts a reflection-based workaround. In these environments, the workaround also fails, and the method throws.

The exception message confirms the root cause: TargetFramework= is followed by nothing, because TargetFrameworkName is empty:

System.NotImplementedException: Preserving slashes and dots escaped in System.Uri is not
supported in TargetFramework=. Expected=http://localhost/%2F., actual=http://localhost//.

During the investigation we also found that the same failure occurs when calling the ManagementClient constructor from a Windows PowerShell 5.1 session, which likewise does not configure TargetFrameworkName. We use PowerShell 5.1 as the reproduction vehicle in the steps below because it is far easier to set up than a WiX installer with a custom action.

Relevant source: ManagementClient.cs lines 49–72


To Reproduce

The crash happens inside the constructor — before any network call — so no live RabbitMQ server is required to reproduce it.

Prerequisites:

  • Windows with Windows PowerShell 5.1 (powershell.exe, not pwsh)
  • A compiled copy of EasyNetQ.Management.Client.dll (v3.0.1) and its dependencies (System.Text.Json.dll, etc.) in a single folder

Save the following script as Reproduce-ManagementClientBug.ps1 and run it from PowerShell 5.1:

#Requires -Version 5.1

param(
    [Parameter(Mandatory)][string] $ManagementClientDllPath
)

$ErrorActionPreference = 'Stop'

try {
    $libDir = Split-Path -Parent $ManagementClientDllPath
    $resolverScript = {
        param($sender, $resolveArgs)
        $simpleName = $resolveArgs.Name.Split(',')[0].Trim()
        $candidate  = Join-Path $libDir "$simpleName.dll"
        if (Test-Path $candidate) { return [System.Reflection.Assembly]::LoadFrom($candidate) }
        return $null
    }.GetNewClosure()
    [System.AppDomain]::CurrentDomain.add_AssemblyResolve([System.ResolveEventHandler]$resolverScript)
    [System.Reflection.Assembly]::LoadFrom($ManagementClientDllPath) | Out-Null

    # Diagnose the environment
    $tfn = ([System.AppDomain]::CurrentDomain.SetupInformation).TargetFrameworkName
    Write-Host "TargetFrameworkName: $(if ([string]::IsNullOrEmpty($tfn)) { '(empty)' } else { $tfn })"

    $testInput  = 'http://localhost/' + [System.Uri]::EscapeDataString('/.')
    $testOutput = (New-Object System.Uri $testInput).ToString()
    Write-Host "URI round-trip ok: $($testInput -eq $testOutput)"
    Write-Host "  expected : $testInput"
    Write-Host "  actual   : $testOutput"
    Write-Host ""

    # This line throws
    $client = New-Object EasyNetQ.Management.Client.ManagementClient(
        (New-Object System.Uri 'http://localhost:15672'),
        'guest',
        'guest'
    )
    Write-Host 'SUCCESS: ManagementClient created.'
}
catch {
    $ex = $_.Exception
    $depth = 0
    while ($ex) {
        Write-Host "  [$depth] $($ex.GetType().FullName): $($ex.Message)"
        $ex = $ex.InnerException
        $depth++
    }
    exit 1
}

Run it from PowerShell 5.1:

powershell.exe -File .\Reproduce-ManagementClientBug.ps1 -ManagementClientDllPath "C:\path\to\EasyNetQ.Management.Client.dll"

Expected behavior

ManagementClient constructs successfully. If the URI quirks cannot be disabled via reflection, the constructor should either tolerate the condition gracefully (log a warning, no-op) or document that the reflection fix is best-effort and not throw.


Actual behavior

TargetFrameworkName: (empty)
URI round-trip ok: False
  expected : http://localhost/%2F.
  actual   : http://localhost//
  [0] System.NotImplementedException: Preserving slashes and dots escaped in System.Uri is
      not supported in TargetFramework=. Expected=http://localhost/%2F., actual=http://localhost//.

Environment

EasyNetQ.Management.Client version 3.0.1
.NET Framework version of calling assembly 4.8.1
Host process Windows PowerShell 5.1 / WiX managed custom-action shim
OS Windows 11 Enterprise

Additional context

The DisableUriParserLegacyQuirks method is trying to work around a known .NET Framework quirk where System.Uri decodes percent-encoded slashes and dots. That quirk is real, and the detection logic is correct. The problem is that when the reflection-based fix cannot be applied (because TargetFrameworkName is empty), the method throws instead of degrading gracefully.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions