URL reservation check repeatedly triggers UAC/netsh

TTMSMCPWinHTTPServer.IsUrlReserved repeatedly launches elevated netsh even when the exact URL ACL already exists:

http://+:8934/

HttpQueryServiceConfiguration can return either ERROR_INSUFFICIENT_BUFFER or ERROR_MORE_DATA when querying with a zero-length output buffer. Microsoft documents both results, but IsUrlReserved currently accepts only ERROR_INSUFFICIENT_BUFFER and NO_ERROR.

Could you update the check to also accept ERROR_MORE_DATA?

Result := (res = NO_ERROR)
  or (res = ERROR_INSUFFICIENT_BUFFER)
  or (res = ERROR_MORE_DATA);

Without this, the reservation is incorrectly considered missing and ReserveUrl invokes elevated netsh on every server start.

Hi,

Thank you for reporting. We applied the improvement and the next version will contain the changes.

Hello,

Thank you for adding ERROR_MORE_DATA handling to TTMSMCPWinHTTPServer.IsUrlReserved in TMS AI Studio 1.7.4.0.

Unfortunately, the repeated UAC prompt still occurs after fully deleting and rebuilding both the TMS and application DCUs. We debugged the updated source and found that the problem happens before the new return-code check becomes relevant.

At runtime, IsUrlReserved calls:

res := HttpQueryServiceConfiguration(
  0,
  HttpServiceConfigUrlAclInfo,
  @urlAclInfoQuery,
  SizeOf(urlAclInfoQuery),
  pOutput,
  0,
  bufferLength,
  nil);

The observed debugger values are:

AURL                       = "http://+:8933/"
urlFixed                   = "http://+:8933/"
urlAclInfoQuery.QueryDesc  = HttpServiceConfigQueryExact
SizeOf(urlAclInfoQuery)    = 24
pOutput                    = nil
bufferLength               = 0
res                         = 87

Error 87 is ERROR_INVALID_PARAMETER. Consequently, IsUrlReserved returns False, and StartServer proceeds to ReserveUrl, which launches elevated netsh even though the URL ACL already exists.

The URL construction itself appears correct:

FBindingIP = ""
FPort      = 8933
FUseSSL    = False
protocol   = "http"
ip         = "+"
url        = "http://+:8933/"

We believe the invalid parameter is caused by the native Windows HTTP API enums being declared as ordinary Delphi enums. In particular:

HTTP_SERVICE_CONFIG_ID = (
  HttpServiceConfigIPListenList,
  HttpServiceConfigSSLCertInfo,
  HttpServiceConfigUrlAclInfo,
  ...
);

The imported function currently declares ConfigId using that Delphi enum type:

HttpQueryServiceConfiguration: function(
  ServiceHandle: THandle;
  ConfigId: HTTP_SERVICE_CONFIG_ID;
  ...
): ULONG; stdcall;

The corresponding Windows SDK type is a C enum and therefore occupies four bytes. Unless the Delphi unit or project forces four-byte enums, HTTP_SERVICE_CONFIG_ID can occupy only one byte. On Win64, passing a byte-sized enum to a native function expecting a 32-bit value can result in an invalid argument being received by httpapi.dll.

This would explain why the API returns ERROR_INVALID_PARAMETER even though:

  • The queried URL is exact.
  • The query descriptor is HttpServiceConfigQueryExact.
  • The query structure size is 24 bytes, as expected for Win64.
  • The URL ACL already exists.
  • A direct call using a 32-bit configuration ID successfully returns ERROR_INSUFFICIENT_BUFFER, indicating that the reservation was found.

Could you please verify SizeOf(HTTP_SERVICE_CONFIG_ID) in the affected build and ensure all Windows HTTP API enums use the native four-byte ABI?

One possible correction would be to compile the relevant native enum declarations with:

{$PUSH}
{$MINENUMSIZE 4}

type
  HTTP_SERVICE_CONFIG_QUERY_TYPE = (...);
  HTTP_SERVICE_CONFIG_ID = (...);
  // Other enums corresponding to native Windows C enums

{$POP}

Alternatively, the imported function can declare the native argument explicitly as a 32-bit type:

HttpQueryServiceConfiguration: function(
  ServiceHandle: THandle;
  ConfigId: ULONG;
  pInputConfigInformation: PVOID;
  InputConfigInformationLength: ULONG;
  pOutputConfigInformation: PVOID;
  OutputConfigInformationLength: ULONG;
  var pReturnLength: ULONG;
  pOverlapped: POverlapped
): ULONG; stdcall;

With the call using an explicit cast:

res := HttpQueryServiceConfiguration(
  0,
  ULONG(HttpServiceConfigUrlAclInfo),
  @urlAclInfoQuery,
  SizeOf(urlAclInfoQuery),
  nil,
  0,
  bufferLength,
  nil);

The first solution may be preferable because HTTP_SERVICE_CONFIG_QUERY_TYPE is also embedded in native HTTP API structures, and several other HTTP enums are used by imported functions throughout the unit.

Could you please review the enum sizes and native declarations in TMS.MCP.HTTPServer.Win.pas and provide an updated build?

Thank you.