/// <summary>
        /// Starts the user interface that prompts a user for a certificate.
        /// </summary>
        /// <param name="iClientChannel">The client channel.</param>
        /// <param name="callback">The callback object.</param>
        /// <param name="state">Any state data.</param>
        /// <returns>The System.IAsyncResult to use to call back when processing has completed.</returns>
        public IAsyncResult BeginDisplayInitializationUI(IClientChannel iClientChannel, AsyncCallback callback, object state)
        {
            // There are three ways of coordinating asynchronous activity.  The WCF channels appear to ignore all of them.  This
            // asynchronous interface appears to be here for future deveopment because it currently doesn't work.  A dummy
            // IAsyncResult is created here that makes it appear that the User Interface was handled immediately. The real work is
            // done when the synchronous 'End' method is called.
            ClientChannelResult clientChannelResult = new ClientChannelResult();

            clientChannelResult.IClientChannel = iClientChannel;
            return(clientChannelResult);
        }
        /// <summary>
        /// Completes the user interface that prompts a user for a certificate.
        /// </summary>
        /// <param name="iAsyncResult">Represents the status of an asynchrous operation.</param>
        public void EndDisplayInitializationUI(IAsyncResult iAsyncResult)
        {
            // Any given thread can try to use a channel.  Most will be on a Multi Threaded Apartment (MTA).  To call up a user
            // interface, a Single Threaded Apartment (STA) thread must be created.
            ClientChannelResult clientChannelResult = iAsyncResult as ClientChannelResult;

            // Only one thread at a time is allowed to prompt the user for credentials.  The credentials chosen here will be shared
            // by all other threads that try to access this type of channel.  Clearing the 'IsPrompted' flag will cause the prompt
            // to be displayed again allowing the user to change their identity.
            lock (syncObject)
            {
                // If the user cancels out of the login screen, there's no sense in trying to initialize any of the other channels
                // with what we know are bad credentials.  The channel initializer will patiently sit here until a manual event
                // tells it to try prompting the user again.
                ChannelStatus.LoginEvent.WaitOne();

                // Only prompt the user once for their credentials.  After a set of credentials is chosen, those credentials will
                // be used by all other threads that use a Basic authentication.
                if (ChannelStatus.IsPrompted)
                {
                    // Any worker thread can request a channel, so the user prompt needs to be invoked on a Single Threaded
                    // Apartment (STA) thread.  This structure is used to pass data to and from the thread where the dialog box is
                    // invoked.
                    PromptData promptData = new PromptData();
                    promptData.NetworkCredentials          = new NetworkCredential();
                    promptData.NetworkCredentials.UserName = Properties.Settings.Default.UserName;
                    promptData.NetworkCredentials.Password = ChannelStatus.Secret as String;
                    promptData.EndpointAddress             = clientChannelResult.IClientChannel.RemoteAddress;

                    // When running as a Windows application, the dialog box that prompts the user for credentials is run modally,
                    // preventing the user from accessing the main application until the credential dialog box is dismissed.  A
                    // console application doesn't have the same luxury and must spawn a dedicated thread to handle the user
                    // interface.
                    if (Application.Current == null || Application.Current.Dispatcher == null)
                    {
                        // When running in a console environment, this will prompt the user from a dedicated thread.
                        Thread thread = new Thread(PromptBasic);
                        thread.SetApartmentState(ApartmentState.STA);
                        thread.Name = "Basic Prompt Thread";
                        thread.Start(promptData);
                        thread.Join();
                    }
                    else
                    {
                        // This will create the dialog box on the main application's message loop.
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new PromptDelegate(PromptBasic),
                                                              promptData);
                    }

                    // The user has the option to cancel the login operation.  If they cancel, the current operation will be
                    // aborted and further attempts to initialize a channel will be suspended.  If not, an attempt is made to use
                    // the credentials to authorize communication with the web service.
                    if (promptData.IsCanceled == true)
                    {
                        // If no credentials are given, this channel and all others like it are suspended until an external thread
                        // resets the event.
                        clientChannelResult.IClientChannel.Abort();
                        ChannelStatus.LoginEvent.Reset();
                        return;
                    }
                    else
                    {
                        // Copy the information out of the dialog box after it has been dismissed.  This information is shared
                        // between all channels using Basic authentication.
                        UserNameChannelInitializer.networkCredentials = promptData.NetworkCredentials;

                        // The information from the dialog box is copied to a persistent store when the user selects the 'Save
                        // Credentials' check box.
                        Properties.Settings.Default.UserName = UserNameChannelInitializer.networkCredentials.UserName;

                        // This will prevent the user from being promtped again.
                        ChannelStatus.IsPrompted = false;
                    }
                }
                // the password was reset internally, modify the network credentials accordingly.
                else if (ChannelStatus.Secret != null)
                {
                    UserNameChannelInitializer.networkCredentials.Password = ChannelStatus.Secret as String;
                    ChannelStatus.Secret = null;
                }

                // This set of network credentials is now available to this channel for authentication.
                this.promptedClientCredentials.Credential = UserNameChannelInitializer.networkCredentials;
            }
        }