/// <inheritdoc />
        public override async Task <TransportInfrastructure> Initialize(HostSettings hostSettings, ReceiveSettings[] receivers, string[] sendingAddresses, CancellationToken cancellationToken = default)
        {
            Guard.AgainstNull(nameof(hostSettings), hostSettings);
            Guard.AgainstNull(nameof(receivers), receivers);
            Guard.AgainstNull(nameof(sendingAddresses), sendingAddresses);

            CheckMachineNameForCompliance.Check();
            ValidateIfDtcIsAvailable();

            var queuesToCreate = new HashSet <string>(sendingAddresses);

            var requiresDelayedDelivery = DelayedDelivery != null;

            string      timeoutsErrorQueue         = null;
            MessagePump delayedDeliveryMessagePump = null;


            if (requiresDelayedDelivery)
            {
                QueueAddress timeoutsQueue;
                if (receivers.Length > 0)
                {
                    var mainReceiver = receivers[0];
                    timeoutsQueue      = new QueueAddress(mainReceiver.ReceiveAddress.BaseAddress, qualifier: TimeoutsQueueQualifier);
                    timeoutsErrorQueue = mainReceiver.ErrorQueue;
                }
                else
                {
                    if (hostSettings.CoreSettings != null)
                    {
                        if (!hostSettings.CoreSettings.TryGetExplicitlyConfiguredErrorQueueAddress(out var coreErrorQueue))
                        {
                            throw new Exception("Delayed delivery requires an error queue to be specified using 'EndpointConfiguration.SendFailedMessagesTo()'");
                        }

                        timeoutsErrorQueue = coreErrorQueue;
                        timeoutsQueue      = new QueueAddress(hostSettings.Name, qualifier: TimeoutsQueueQualifier); //Use name of the endpoint as the timeouts queue name.
                    }
                    else
                    {
                        throw new Exception("Timeouts are not supported for send-only configurations outside of an NServiceBus endpoint.");
                    }
                }

                delayedDeliveryMessagePump = new MessagePump(mode => SelectReceiveStrategy(mode, TransactionScopeOptions.TransactionOptions),
                                                             MessageEnumeratorTimeout, TransportTransactionMode, false, hostSettings.CriticalErrorAction,
                                                             new ReceiveSettings("DelayedDelivery", timeoutsQueue, false, false, timeoutsErrorQueue));

                queuesToCreate.Add(delayedDeliveryMessagePump.ReceiveAddress);
                queuesToCreate.Add(timeoutsErrorQueue);
            }

            var messageReceivers = CreateReceivers(receivers, hostSettings.CriticalErrorAction, queuesToCreate);

            var dispatcher = new MsmqMessageDispatcher(this, delayedDeliveryMessagePump?.ReceiveAddress, OnSendCallbackForTesting);

            if (hostSettings.CoreSettings != null)
            {
                // enforce an explicitly configured error queue when using MSMQ transport with NServiceBus
                if (receivers.Length > 0 && !hostSettings.CoreSettings.TryGetExplicitlyConfiguredErrorQueueAddress(out _))
                {
                    throw new Exception("Faults forwarding requires an error queue to be specified using 'EndpointConfiguration.SendFailedMessagesTo()'");
                }

                bool outBoxRunning = hostSettings.CoreSettings.IsFeatureActive(typeof(Features.Outbox));
                if (hostSettings.CoreSettings.TryGetAuditMessageExpiration(out var auditMessageExpiration))
                {
                    TimeToBeReceivedOverrideChecker.Check(
                        TransportTransactionMode != TransportTransactionMode.None,
                        outBoxRunning,
                        auditMessageExpiration > TimeSpan.Zero);
                }

                if (CreateQueuesForUser == null)
                {
                    // try to use the configured installer user in Core:
                    CreateQueuesForUser = hostSettings.CoreSettings.GetOrDefault <string>("Installers.UserName");
                }
            }

            if (hostSettings.SetupInfrastructure && CreateQueues)
            {
                var installerUser = GetInstallationUserName();
                var queueCreator  = new MsmqQueueCreator(UseTransactionalQueues, installerUser);

                if (requiresDelayedDelivery)
                {
                    await DelayedDelivery.DelayedMessageStore.Initialize(hostSettings.Name, TransportTransactionMode, cancellationToken).ConfigureAwait(false);
                }

                queueCreator.CreateQueueIfNecessary(queuesToCreate);
            }

            foreach (var address in sendingAddresses.Concat(messageReceivers.Select(r => r.Value.ReceiveAddress)))
            {
                QueuePermissions.CheckQueue(address);
            }

            DelayedDeliveryPump delayedDeliveryPump = null;

            if (requiresDelayedDelivery)
            {
                QueuePermissions.CheckQueue(delayedDeliveryMessagePump.ReceiveAddress);
                QueuePermissions.CheckQueue(timeoutsErrorQueue);

                var staticFaultMetadata = new Dictionary <string, string>
                {
                    { Headers.ProcessingMachine, RuntimeEnvironment.MachineName },
                    { Headers.ProcessingEndpoint, hostSettings.Name },
                    { Headers.HostDisplayName, hostSettings.HostDisplayName }
                };

                var dueDelayedMessagePoller = new DueDelayedMessagePoller(dispatcher, DelayedDelivery.DelayedMessageStore, DelayedDelivery.NumberOfRetries, hostSettings.CriticalErrorAction, timeoutsErrorQueue, staticFaultMetadata, TransportTransactionMode,
                                                                          DelayedDelivery.TimeToTriggerFetchCircuitBreaker,
                                                                          DelayedDelivery.TimeToTriggerDispatchCircuitBreaker,
                                                                          DelayedDelivery.MaximumRecoveryFailuresPerSecond,
                                                                          delayedDeliveryMessagePump.ReceiveAddress);

                delayedDeliveryPump = new DelayedDeliveryPump(dispatcher, dueDelayedMessagePoller, DelayedDelivery.DelayedMessageStore, delayedDeliveryMessagePump, timeoutsErrorQueue, DelayedDelivery.NumberOfRetries, hostSettings.CriticalErrorAction, DelayedDelivery.TimeToTriggerStoreCircuitBreaker, staticFaultMetadata, TransportTransactionMode);
            }

            hostSettings.StartupDiagnostic.Add("NServiceBus.Transport.MSMQ", new
            {
                ExecuteInstaller = CreateQueues,
                UseDeadLetterQueue,
                UseConnectionCache,
                UseTransactionalQueues,
                UseJournalQueue,
                UseDeadLetterQueueForMessagesWithTimeToBeReceived,
                TimeToReachQueue   = GetFormattedTimeToReachQueue(TimeToReachQueue),
                TimeoutQueue       = delayedDeliveryMessagePump?.ReceiveAddress,
                TimeoutStorageType = DelayedDelivery?.DelayedMessageStore?.GetType(),
            });

            var infrastructure = new MsmqTransportInfrastructure(messageReceivers, dispatcher, delayedDeliveryPump);
            await infrastructure.Start(cancellationToken).ConfigureAwait(false);

            return(infrastructure);
        }
 public MsmqTransportInfrastructure(IReadOnlyDictionary <string, IMessageReceiver> receivers, MsmqMessageDispatcher dispatcher, DelayedDeliveryPump delayedDeliveryPump)
 {
     this.delayedDeliveryPump = delayedDeliveryPump;
     Dispatcher = dispatcher;
     Receivers  = receivers;
 }