SQL Express 2014版呼叫拉快照复制订阅者

时间:2016-03-29 15:35:43

标签: replication sql-server-2014 database-replication sql-server-2014-express transactional-replication

我有2014年的Sql Stander版本,也是发行商和发行商。我在其他Server上创建了Sql Stander Edition上的Subscriber.Subscriber pull Snapshot Replication工作正常,我可以查看数据。

当我使用2014 Express版时,由于2014年快递版不支持SQL Server Agent,因此无法使用。

任何人都可以知道在快递版中调用pull订阅模型的任何其他选项。或者我们不能这样做 这是我用过的代码

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        //  createSubscriber();
        //disableTheServer2Distributor();
    }

    public bool CreatePublication( string publisherName, string publicationName, string publicationDbName)
    {

        ReplicationDatabase publicationDb;
        TransPublication publication;

        // Create a connection to the Publisher using Windows Authentication.
        ServerConnection conn;
        conn = new ServerConnection(publisherName);


        try
        {
            // Connect to the Publisher.
            conn.Connect();

            // Enable the AdventureWorks database for transactional publishing.
            publicationDb = new ReplicationDatabase(publicationDbName, conn);

            // If the database exists and is not already enabled, 
            // enable it for transactional publishing.
            if (publicationDb.LoadProperties())
            {
                if (!publicationDb.EnabledTransPublishing)
                {
                    publicationDb.EnabledTransPublishing = true;
                }

                // If the Log Reader Agent does not exist, create it.
                if (!publicationDb.LogReaderAgentExists)
                {
                    // Specify the Windows account under which the agent job runs.
                    // This account will be used for the local connection to the 
                    // Distributor and all agent connections that use Windows Authentication.
                    publicationDb.LogReaderAgentProcessSecurity.Login = textBox1.Text ;
                    publicationDb.LogReaderAgentProcessSecurity.Password = textBox2.Text;

                    // Explicitly set authentication mode for the Publisher connection
                    // to the default value of Windows Authentication.
                    publicationDb.LogReaderAgentPublisherSecurity.WindowsAuthentication = true;

                    // Create the Log Reader Agent job.
                    publicationDb.CreateLogReaderAgent();
                }
            }
            else
            {
                throw new ApplicationException(String.Format(
                    "The {0} database does not exist at {1}.",
                    publicationDb, publisherName));
            }

            // Set the required properties for the transactional publication.
            publication = new TransPublication();
            publication.ConnectionContext = conn;
            publication.Name = publicationName;
            publication.DatabaseName = publicationDbName;

            // Specify a transactional publication (the default).
            publication.Type = PublicationType.Snapshot;

            // Activate the publication so that we can add subscriptions.
            publication.Status = State.Active;

            // Enable push and pull subscriptions and independent Distribition Agents.
            publication.Attributes |= PublicationAttributes.AllowPull;
            //publication.Attributes |= PublicationAttributes.AllowPush;
            //publication.Attributes |= PublicationAttributes.IndependentAgent;

            // Specify the Windows account under which the Snapshot Agent job runs.
            // This account will be used for the local connection to the 
            // Distributor and all agent connections that use Windows Authentication.
            publication.SnapshotGenerationAgentProcessSecurity.Login = textBox1.Text;
            publication.SnapshotGenerationAgentProcessSecurity.Password = textBox2.Text;

            // Explicitly set the security mode for the Publisher connection
            // Windows Authentication (the default).
            publication.SnapshotGenerationAgentPublisherSecurity.WindowsAuthentication = true;
            ReplicationAgentSchedule schedule;
            if (publication.LoadProperties() || publication.SnapshotAvailable)
            {
                // Set a weekly schedule for the filtered data snapshot.
                schedule = new ReplicationAgentSchedule();
                schedule.FrequencyType = ScheduleFrequencyType.Continuously;
                schedule.FrequencyRecurrenceFactor = 1;
                schedule.FrequencyInterval = Convert.ToInt32(0x001);



            }

                if (!publication.IsExistingObject)
            {
                // Create the transactional publication.
                publication.Create();

                // Create a Snapshot Agent job for the publication.

                publication.CreateSnapshotAgent();

            }
            else
            {
                throw new ApplicationException(String.Format(
                    "The {0} publication already exists.", publicationName));
            }
            return true;
        }

        catch (Exception ex)
        {
            return false;
            // Implement custom application error handling here.
            throw new ApplicationException(String.Format(
                "The publication {0} could not be created.", publicationName), ex);
        }
        finally
        {
            conn.Disconnect();
        }



    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="distributionDbName">distribution data base Name</param>
    /// <param name="publisherName">Publisher Name </param>
    /// <param name="publicationDbName">Publication data base name</param>
    /// <param name="distributionDbPassword"> Set the password of the database</param>
    /// <param name="WorkingDirectoryPath">Network location from where it can be access </param>
    /// <returns></returns>
    public bool ConfigurationPublishreAndDistributor(string distributionDbName , string publisherName, string publicationDbName, string distributionDbPassword,string WorkingDirectoryPath)
    {
        DistributionDatabase distributionDb;
        ReplicationServer distributor;
        DistributionPublisher publisher;
        ReplicationDatabase publicationDb;

        // Create a connection to the server using Windows Authentication.
        ServerConnection conn = new ServerConnection(publisherName);

        try
        {
            // Connect to the server acting as the Distributor 
            // and local Publisher.
            conn.Connect();

            // Define the distribution database at the Distributor,
            // but do not create it now.
            distributionDb = new DistributionDatabase(distributionDbName, conn);
            distributionDb.MaxDistributionRetention = 96;
            distributionDb.HistoryRetention = 120;              

            // Set the Distributor properties and install the Distributor.
            // This also creates the specified distribution database.
            distributor = new ReplicationServer(conn);
            distributor.InstallDistributor(distributionDbPassword, distributionDb);

            // Set the Publisher properties and install the Publisher.
            publisher = new DistributionPublisher(publisherName, conn);
            publisher.DistributionDatabase = distributionDb.Name;
            publisher.WorkingDirectory = WorkingDirectoryPath;

            publisher.PublisherSecurity.WindowsAuthentication = true;

            publisher.Create();

            // Enable AdventureWorks2012 as a publication database.
            publicationDb = new ReplicationDatabase(publicationDbName, conn);

            publicationDb.EnabledTransPublishing = true;
            publicationDb.EnabledMergePublishing = true;




            return true;
        }
        catch (Exception ex)
        {
            // Implement appropriate error handling here.
            return false;
            throw new ApplicationException("An error occured when installing distribution and publishing.", ex);
        }
        finally
        {
            conn.Disconnect();
        }


    }

    private void CreateDistributorServer1_Click(object sender, EventArgs e)
    {
        if (ConfigurationPublishreAndDistributor("distribution", @"SERVER-002\SQLSERVER2014", "ReplicationDB", "Asdf1234!", @"\\SERVER-001\Replication-001"))
        {

            if (CreatePublication(@"SERVER-002\SQLSERVER2014", "ReplicationSnapShort", "ReplicationDB"))
            {

                string publisherName = @"SERVER-002\SQLSERVER2014";
                string publicationName = "ReplicationSnapShort";
                string publicationDbName = "ReplicationDB";
                string articleName = "student";
                string schemaOwner = "dbo";

                TransArticle article;

                // Create a connection to the Publisher.
                ServerConnection conn = new ServerConnection(publisherName);

                // Create a filtered transactional articles in the following steps:
                // 1) Create the  article with a horizontal filter clause.
                // 2) Add columns to or remove columns from the article.
                try
                {
                    // Connect to the Publisher.
                    conn.Connect();

                    // Define a horizontally filtered, log-based table article.
                    article = new TransArticle();
                    article.ConnectionContext = conn;
                    article.Name = articleName;

                    article.DatabaseName = publicationDbName;
                    article.SourceObjectName = articleName;
                    article.SourceObjectOwner = schemaOwner;
                    article.PublicationName = publicationName;
                    article.Type = ArticleOptions.LogBased;
                    String[] articlecolumns = new String[2];
                    articlecolumns[0] = "studentid";
                    articlecolumns[1] = "name";
                    article.AddReplicatedColumns(articlecolumns);
                    //article.FilterClause = "DiscontinuedDate IS NULL";

                    // Ensure that we create the schema owner at the Subscriber.
                    article.SchemaOption |= CreationScriptOptions.Schema;

                    if (!article.IsExistingObject)
                    {
                        // Create the article.
                        article.Create();
                    }
                    else
                    {
                        throw new ApplicationException(String.Format(
                            "The article {0} already exists in publication {1}.",
                            articleName, publicationName));
                    }

                    // Create an array of column names to remove from the article.
                    String[] columns = new String[2];
                    columns[0] = "studentid";
                    columns[1] = "name";

                    // Remove the column from the article.
                    article.AddReplicatedColumns(columns);
                   // publication.StartSnapshotGenerationAgentJob();
                }
                catch (Exception ex)
                {
                    // Implement appropriate error handling here.
                    throw new ApplicationException("The article could not be created.", ex);
                }
                finally
                {
                    conn.Disconnect();
                    startTheAgentNow();
                }
            }
        }
        MessageBox.Show("Create the Distributor and Publisher");
    }

    public bool startTheAgentNow()
    {
        string publisherName = @"SERVER-002\SQLSERVER2014";
        string publicationName = "ReplicationSnapShort";
        string publicationDbName = "ReplicationDB";
        TransPublication publication;

        // Create a connection to the Publisher using Windows Authentication.
        ServerConnection conn;
        conn = new ServerConnection(publisherName);

        try
        {
            // Connect to the Publisher.
            conn.Connect();

            // Set the required properties for an existing publication.
            publication = new TransPublication();
            publication.ConnectionContext = conn;
            publication.Name = publicationName;
            publication.DatabaseName = publicationDbName;

            if (publication.LoadProperties())
            {
                // Start the Snapshot Agent job for the publication.
                publication.StartSnapshotGenerationAgentJob();
            }
            else
            {
                throw new ApplicationException(String.Format(
                    "The {0} publication does not exist.", publicationName));
            }
        }
        catch (Exception ex)
        {
            // Implement custom application error handling here.
            throw new ApplicationException(String.Format(
                "A snapshot could not be generated for the {0} publication."
                , publicationName), ex);
        }
        finally
        {
            conn.Disconnect();
          //  createSubscriber();
        }
        return true;

    }
    public bool createSubscriber()
    {
        // Define the Publisher, publication, and databases.
        string publicationName = "ReplicationSnapShort";
        string publisherName = @"SERVER-002\SQLSERVER2014";
        string subscriberName = @"SERVER-001\SQLSERVER2014";
        string subscriptionDbName = "ReplicationDB1";
        string publicationDbName = "ReplicationDB";

        //Create connections to the Publisher and Subscriber.
        ServerConnection subscriberConn = new ServerConnection(subscriberName);
        ServerConnection publisherConn = new ServerConnection(publisherName);

        // Create the objects that we need.
        TransPublication publication;
        TransPullSubscription subscription;

        try
        {
            // Connect to the Publisher and Subscriber.
            subscriberConn.Connect();
            publisherConn.Connect();

            // Ensure that the publication exists and that 
            // it supports pull subscriptions.
            publication = new  TransPublication();
            publication.Name = publicationName;
            publication.DatabaseName = publicationDbName;
            publication.ConnectionContext = publisherConn;

            if (publication.IsExistingObject)
            {
                if ((publication.Attributes & PublicationAttributes.AllowPull) == 0)
                {
                    publication.Attributes |= PublicationAttributes.AllowPull;

                }

                // Define the pull subscription.
                subscription = new TransPullSubscription();
                subscription.ConnectionContext = subscriberConn;
                subscription.PublisherName = publisherName;
                subscription.PublicationName = publicationName;
                subscription.PublicationDBName = publicationDbName;
                subscription.DatabaseName = subscriptionDbName;

                // Specify the Windows login credentials for the Distribution Agent job.
                subscription.SynchronizationAgentProcessSecurity.Login = textBox1.Text;
                subscription.SynchronizationAgentProcessSecurity.Password = textBox2.Text;

                // Make sure that the agent job for the subscription is created.
                subscription.CreateSyncAgentByDefault = true;

                // By default, subscriptions to transactional publications are synchronized 
                // continuously, but in this case we only want to synchronize on demand.
                subscription.AgentSchedule.FrequencyType = ScheduleFrequencyType.Continuously;

                // Create the pull subscription at the Subscriber.
                subscription.Create();


                Boolean registered = false;

                // Verify that the subscription is not already registered.
                foreach (TransSubscription existing
                    in publication.EnumSubscriptions())
                {
                    if (existing.SubscriberName == subscriberName
                        && existing.SubscriptionDBName == subscriptionDbName)
                    {
                        registered = true;
                    }
                }
                if (!registered)
                {
                    // Register the subscription with the Publisher.
                    publication.MakePullSubscriptionWellKnown(
                        subscriberName, subscriptionDbName,
                        SubscriptionSyncType.Automatic,
                        TransSubscriberType.ReadOnly);
                }
            }
            else
            {
                // Do something here if the publication does not exist.
                throw new ApplicationException(String.Format(
                    "The publication '{0}' does not exist on {1}.",
                    publicationName, publisherName));
            }
        }
        catch (Exception ex)
        {
            // Implement the appropriate error handling here.
            throw new ApplicationException(String.Format(
                "The subscription to {0} could not be created.", publicationName), ex);
        }
        finally
        {
            subscriberConn.Disconnect();
            publisherConn.Disconnect();
        }
        MessageBox.Show("Subscription is Created");
        return true;
    }
    /// <summary>
    /// This is the code of the 
    /// </summary>
    public void disableTheServer2Distributor()
    {

        string publisherName = @"SERVER-002\SQLSERVER2014";


        string publicationDbName = "ReplicationDB";


        string distributorName = @"SERVER-002\SQLSERVER2014";
        string distributionDbName = "distribution";


        // Create connections to the Publisher and Distributor
        // using Windows Authentication.
        ServerConnection publisherConn = new ServerConnection(publisherName);
        ServerConnection distributorConn = new ServerConnection(distributorName);

        // Create the objects we need.
        ReplicationServer distributor =
            new ReplicationServer(distributorConn);
        DistributionPublisher publisher;
        DistributionDatabase distributionDb =
            new DistributionDatabase(distributionDbName, distributorConn);
        ReplicationDatabase publicationDb;
        publicationDb = new ReplicationDatabase(publicationDbName, publisherConn);

        try
        {
            // Connect to the Publisher and Distributor.
            publisherConn.Connect();
            distributorConn.Connect();

            // Disable all publishing on the AdventureWorks2012 database.
            if (publicationDb.LoadProperties())
            {
                if (publicationDb.EnabledMergePublishing)
                {
                    publicationDb.EnabledMergePublishing = false;
                }
                else if (publicationDb.EnabledTransPublishing)
                {
                    publicationDb.EnabledTransPublishing = false;
                }
            }
            else
            {
                throw new ApplicationException(
                    String.Format("The {0} database does not exist.", publicationDbName));
            }

            // We cannot uninstall the Publisher if there are still Subscribers.
            if (distributor.RegisteredSubscribers.Count == 0)
            {
                // Uninstall the Publisher, if it exists.
                publisher = new DistributionPublisher(publisherName, distributorConn);
                if (publisher.LoadProperties())
                {
                    publisher.Remove(false);
                }
                else
                {
                    // Do something here if the Publisher does not exist.
                    throw new ApplicationException(String.Format(
                        "{0} is not a Publisher for {1}.", publisherName, distributorName));
                }

                // Drop the distribution database.
                if (distributionDb.LoadProperties())
                {
                    distributionDb.Remove();
                }
                else
                {
                    // Do something here if the distribition DB does not exist.
                    throw new ApplicationException(String.Format(
                        "The distribution database '{0}' does not exist on {1}.",
                        distributionDbName, distributorName));
                }

                // Uninstall the Distributor, if it exists.
                if (distributor.LoadProperties())
                {
                    // Passing a value of false means that the Publisher 
                    // and distribution databases must already be uninstalled,
                    // and that no local databases be enabled for publishing.
                    distributor.UninstallDistributor(false);
                }
                else
                {
                    //Do something here if the distributor does not exist.
                    throw new ApplicationException(String.Format(
                        "The Distributor '{0}' does not exist.", distributorName));
                }
            }
            else
            {
                throw new ApplicationException("You must first delete all subscriptions.");
            }

        }
        catch (Exception ex)
        {
            // Implement appropriate error handling here.
            throw new ApplicationException("The Publisher and Distributor could not be uninstalled", ex);
        }
        finally
        {
            publisherConn.Disconnect();
            distributorConn.Disconnect();
        }
    }

    private void CreateDistributorServer2_Click(object sender, EventArgs e)
    {

    }

    private void button5_Click(object sender, EventArgs e)
    {
        createSubscriber();
    }

    private void button13_Click(object sender, EventArgs e)
    {

        string publisherName = @"SERVER-002\SQLSERVER2014";


        string publicationDbName = "ReplicationDB";




        String subscriberName = @"SERVER-001\SQLSERVER2014";

        String publicationName = "ReplicationSnapShort";

        String subscriptionDbName = "ReplicationDB1";

        // Create a connection to the Subscriber.
        ServerConnection conn = new ServerConnection(subscriberName);

        TransPullSubscription subscription;



        try
        {
            // Connect to the Subscriber.
            conn.Connect();

            // Define subscription properties.
            subscription = new TransPullSubscription();
            subscription.ConnectionContext = conn;
            subscription.DatabaseName = subscriptionDbName;
            subscription.PublisherName = publisherName;
            subscription.PublicationDBName = publicationDbName;
            subscription.PublicationName = publicationName;

            // If the pull subscription and the job exists, mark the subscription
            // for reinitialization and start the agent job.
            if (subscription.LoadProperties() && subscription.AgentJobId != null)
            {
                subscription.Reinitialize();
                subscription.SynchronizeWithJob();
            }
            else
            {
                // Do something here if the subscription does not exist.
                throw new ApplicationException(String.Format(
                    "A subscription to '{0}' does not exists on {1}",
                    publicationName, subscriberName));
            }
        }
        catch (Exception ex)
        {
            // Do appropriate error handling here.
            //throw new ApplicationException("The subscription could not be reinitialized.", ex);
        }
        finally
        {
            conn.Disconnect();
            startTheAgentNow();
            MessageBox.Show("Agents are started");
        }
    }

    private void button9_Click(object sender, EventArgs e)
    {

}

}

以上代码按预期工作我遇到问题当我将订阅服务器更改为SQL

2 个答案:

答案 0 :(得分:1)

我看到您正在使用TransPullSubscription类并调用SynchronizeWithJob()方法来同步订阅。正如您所知,SQL Server代理在SQL Server Express中不可用,因此这种方法不起作用。

但是,RMO TransSynchronizationAgent类公开了Synchronize方法,该方法可用于在没有代理作业的情况下同步提取订阅。这包含在How to: Synchronize a Pull Subscription (RMO Programming)

  

从中获取TransSynchronizationAgent类的实例   SynchronizationAgent属性,并调用Synchronize方法。这个   方法同步启动代理,并且控制保持与   运行代理工作。在同步执行期间,您可以处理   代理运行时的状态事件。

我找到了here的合并请求订阅的类似示例。

请注意,如果在创建pull订阅时为CreateSyncAgentByDefault(默认值)指定了false值,则还需要先设置其他属性,然后才能调用Synchronize()。

  

如果为CreateSyncAgentByDefault指定了值false(   默认情况下,当您创建了pull订阅时,您还需要   指定Distributor,DistributorSecurityMode和(可选)   DistributorLogin和DistributorPassword因为代理作业相关   订阅的元数据不可用于   MSsubscription_properties。

答案 1 :(得分:1)

由于 布兰登威廉姆斯, 我对代码进行了更改,如下所示

`     {

    TransSynchronizationAgent agent;

    // Sync BackgroundWorker
    BackgroundWorker syncBackgroundWorker;

    public Form1()
    {
        InitializeComponent();
        lblSubscriptionName.Text = "[" + subscriptionDbName + "] - [" + publisherName + "] - [" + publicationDbName + "]";
        lblPublicationName.Text = publicationName;
    }

    private void btnStart_Click(object sender, EventArgs e)
    {
        // Instantiate a BackgroundWorker, subscribe to its events, and call RunWorkerAsync()
        syncBackgroundWorker = new BackgroundWorker();
        syncBackgroundWorker.WorkerReportsProgress = true;
        syncBackgroundWorker.DoWork += new DoWorkEventHandler(syncBackgroundWorker_DoWork);
        syncBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(syncBackgroundWorker_ProgressChanged);
        syncBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(syncBackgroundWorker_RunWorkerCompleted);

        // Disable the start button
        btnStart.Enabled = false;

        // Initialize the progress bar and status textbox
        pbStatus.Value = 0;
        tbLastStatusMessage.Text = String.Empty;

        pictureBoxStatus.Visible = true;
        pictureBoxStatus.Enabled = true;

        // Kick off a background operation to synchronize
        syncBackgroundWorker.RunWorkerAsync();
    }

    // This event handler initiates the synchronization
    private void syncBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        // Connect to the Subscriber and synchronize
        SynchronizeMergePullSubscriptionViaRMO();
    }

    // Synchronize
    public void SynchronizeMergePullSubscriptionViaRMO()
    {
        // Create a connection to the Subscriber.
        ServerConnection conn = new ServerConnection(subscriberName);

        // Merge pull subscription
        TransPullSubscription subscription;

        try
        {
            // Connect to the Subscriber.
            conn.Connect();

            // Define the pull subscription.
            subscription = new TransPullSubscription();
            subscription.ConnectionContext = conn;
            subscription.DatabaseName = subscriptionDbName;
            subscription.PublisherName = publisherName;
            subscription.PublicationDBName = publicationDbName;
            subscription.PublicationName = publicationName;

            // If the pull subscription exists, then start the synchronization.
            if (subscription.LoadProperties())
            {
                // Get the agent for the subscription.
                agent = subscription.SynchronizationAgent;

                // Set the required properties that could not be returned
                // from the MSsubscription_properties table.
                //agent.PublisherSecurityMode = SecurityMode.Integrated;
                agent.DistributorSecurityMode = SecurityMode.Integrated;
                agent.Distributor = publisherName;

                // Enable verbose merge agent output to file.
                agent.OutputVerboseLevel = 4;
                agent.Output = "C:\\TEMP\\mergeagent.log";

                // Handle the Status event
                agent.Status += new AgentCore.StatusEventHandler(agent_Status);

                // Synchronously start the Merge Agent for the subscription.
                agent.Synchronize();
            }
            else
            {
                // Do something here if the pull subscription does not exist.
                throw new ApplicationException(String.Format(
                    "A subscription to '{0}' does not exist on {1}",
                    publicationName, subscriberName));
            }
        }
        catch (Exception ex)
        {
            // Implement appropriate error handling here.
            throw new ApplicationException("The subscription could not be " +
                "synchronized. Verify that the subscription has " +
                "been defined correctly.", ex);
        }
        finally
        {
            conn.Disconnect();
        }
    }

    // This event handler handles the Status event and reports the agent progress.
    public void agent_Status(object sender, StatusEventArgs e)
    {
        syncBackgroundWorker.ReportProgress(Convert.ToInt32(e.PercentCompleted), e.Message.ToString());
    }

    // This event handler updates the form with agent progress.
    private void syncBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // Set the progress bar percent completed
        pbStatus.Value = e.ProgressPercentage;

        // Append the last agent message
        tbLastStatusMessage.Text += e.UserState.ToString() + Environment.NewLine;

        // Scroll to end
        ScrollToEnd();
    }

    // This event handler deals with the results of the background operation.
    private void syncBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled == true)
        {
            tbLastStatusMessage.Text += "Canceled!" + Environment.NewLine;
            ScrollToEnd();
        }
        else if (e.Error != null)
        {
            tbLastStatusMessage.Text += "Error: " + e.Error.Message + Environment.NewLine;
            ScrollToEnd();
        }
        else
        {
            tbLastStatusMessage.Text += "Done!" + Environment.NewLine;
            ScrollToEnd();
        }

        btnStart.Enabled = true;
        pictureBoxStatus.Enabled = false;
    }

    private void ScrollToEnd()
    {
        // Scroll to end
        tbLastStatusMessage.SelectionStart = tbLastStatusMessage.TextLength;
        tbLastStatusMessage.ScrollToCaret();
    }

    private void btnClose_Click(object sender, EventArgs e)
    {
        this.Close();
    }
}`