Cross-thread exception when uploading database backup to Google Drive using C# WinForms

I’m trying to build a Windows Forms application that uploads database backup files to Google Drive, but I’ve run into this issue with cross-thread operations:

Error Message:

System.InvalidOperationException: ‘Cross-thread operation not valid: Control ‘textBox1’ accessed from a thread other than the thread it was created on.’

This error occurs when I attempt to update the UI controls during the upload progress. Here is my code:

using System;
using System.Windows.Forms;
using System.Threading;
using Google.Apis.Drive.v3;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;

namespace DriveUploader
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog
            {
                InitialDirectory = @"C:\",
                Title = "Select Database Backup",
                CheckFileExists = true,
                DefaultExt = "bak",
                Filter = "Backup files (*.bak)|*.bak",
                RestoreDirectory = true
            };

            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                textBox1.Text = fileDialog.FileName;
            }
        }

        private static string DetectMimeType(string filePath)
        {
            string contentType = "application/octet-stream";
            string extension = System.IO.Path.GetExtension(filePath).ToLower();
            Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(extension);
            if (key != null && key.GetValue("Content Type") != null)
                contentType = key.GetValue("Content Type").ToString();
            return contentType;
        }

        public void AuthenticateAndUpload()
        {
            string[] permissions = new string[] { DriveService.Scope.Drive };
            
            var clientId = "{YOUR_CLIENT_ID}";
            var clientSecret = "{YOUR_CLIENT_SECRET}";

            var credentials = GoogleWebAuthorizationBroker.AuthorizeAsync(
                new ClientSecrets
                {
                    ClientId = clientId,
                    ClientSecret = clientSecret
                }, 
                permissions,
                Environment.UserName, 
                CancellationToken.None, 
                new FileDataStore("DriveTokens")
            ).Result;

            DriveService driveService = new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credentials,
                ApplicationName = "BackupUploader"
            });
            
            driveService.HttpClient.Timeout = TimeSpan.FromMinutes(60);
            
            var result = PerformUpload(driveService, textBox1.Text, "");
            MessageBox.Show("Upload finished: " + result);
        }

        public Google.Apis.Drive.v3.Data.File PerformUpload(DriveService service, string filePath, string parentId)
        {
            if (System.IO.File.Exists(filePath))
            {
                var fileMetadata = new Google.Apis.Drive.v3.Data.File()
                {
                    Name = System.IO.Path.GetFileName(filePath),
                    Description = "Uploaded via C#",
                    MimeType = DetectMimeType(filePath)
                };

                byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
                System.IO.MemoryStream fileStream = new System.IO.MemoryStream(fileBytes);
                
                try
                {
                    var uploadRequest = service.Files.Create(fileMetadata, fileStream, DetectMimeType(filePath));
                    uploadRequest.SupportsTeamDrives = true;
                    uploadRequest.ProgressChanged += OnProgressUpdate;
                    uploadRequest.ResponseReceived += OnUploadComplete;
                    uploadRequest.Upload();
                    return uploadRequest.ResponseBody;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Upload Failed");
                    return null;
                }
            }
            else
            {
                MessageBox.Show("File not found", "Error");
                return null;
            }
        }

        private void OnProgressUpdate(Google.Apis.Upload.IUploadProgress progress)
        {
            textBox1.Text += progress.Status + " " + progress.BytesSent;
        }

        private void OnUploadComplete(Google.Apis.Drive.v3.Data.File file)
        {
            if (file != null)
            {
                MessageBox.Show("Upload successful: " + file.Id);
            }
        }

        private void btnUpload_Click(object sender, EventArgs e)
        {
            AuthenticateAndUpload();
        }
    }
}

Can anyone help me resolve this threading issue? The error shows up in the OnProgressUpdate method where I attempt to update the text box.

Been there with Google Drive uploads! Use Progress instead of callback events - it’s way cleaner for UI updates. Wrap your upload in Task.Run and use IProgress to report back to the UI thread automatically. Way simpler than invoke calls everywhere.

Your OnProgressUpdate method is trying to modify UI controls from a background thread - that’s what’s causing the issue. I ran into this exact same problem building a file upload utility last year.

You need to use Control.Invoke or Control.BeginInvoke to get back to the UI thread. Here’s how to fix your OnProgressUpdate method:

private void OnProgressUpdate(Google.Apis.Upload.IUploadProgress progress)
{
    if (textBox1.InvokeRequired)
    {
        textBox1.Invoke(new Action(() => {
            textBox1.Text += progress.Status + " " + progress.BytesSent;
        }));
    }
    else
    {
        textBox1.Text += progress.Status + " " + progress.BytesSent;
    }
}

Another option: run the whole upload with async/await using Task.Run, then use BeginInvoke for UI updates. This stops the UI from freezing during large uploads - learned that the hard way with database backups.

I hit this exact same cross-thread violation building backup automation tools. Google Drive API callbacks run on worker threads, so they can’t directly touch WinForms controls.

Besides the Invoke approach mentioned, try wrapping your whole upload operation in an async method:

private async void btnUpload_Click(object sender, EventArgs e) 
{ 
    await Task.Run(() => AuthenticateAndUpload()); 
}

Then modify OnProgressUpdate to use BeginInvoke - it’s faster:

private void OnProgressUpdate(Google.Apis.Upload.IUploadProgress progress) 
{ 
    this.BeginInvoke(new Action(() => { 
        textBox1.AppendText($"{progress.Status} - {progress.BytesSent} bytes\r\n"); 
    })); 
}

Use AppendText instead of concatenating to the Text property - way better performance with frequent updates. BeginInvoke won’t block either, so it won’t slow down your upload progress reporting.