-1

Error

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

 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
 using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Google.Apis;
 using Google.Apis.Auth;
 using Google.Apis.Drive.v3;
 using Google.Apis.Auth.OAuth2;
 using Google.Apis.Util.Store;
 using Google.Apis.Services;
 using System.Windows.Forms; 
 namespace GDrive_Sample 
 {
    public partial class Form1 : Form{public Form1(
    {
       InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        OpenFileDialog openFileDialog1 = new OpenFileDialog
        {
            InitialDirectory = @"D:\",
            Title = "Browse Backup Files",

            CheckFileExists = true,
            CheckPathExists = true,

            DefaultExt = "bak",
            Filter = "bak files (*.bak)|*.bak",
            FilterIndex = 2,
            RestoreDirectory = true,

            ReadOnlyChecked = true,
            ShowReadOnly = true
        };

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

    private static string GetMimeType(string fileName)
    {
        string mimeType = "application/unknown";
        string ext = System.IO.Path.GetExtension(fileName).ToLower();
        Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
        if (regKey != null && regKey.GetValue("Content Type") != null)
            mimeType = regKey.GetValue("Content Type").ToString();
        return mimeType;
    }

    public void Authorize()
    {
        string[] scopes = new string[] { DriveService.Scope.Drive,
                           DriveService.Scope.DriveFile,};
        // var clientId = "{REDACTED}";      // From https://console.developers.google.com  
        //var clientSecret = "{REDACTED}";          // From https://console.developers.google.com  
        // here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%  
        var clientId = "{REDACTED}";
        var clientSecret = "{REDACTED}";
        //var clientId = "{REDACTED}";
        //var clientSecret = "{REDACTED}";

        var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets
        {
            ClientId = clientId,
            ClientSecret = clientSecret
        }, scopes,
        Environment.UserName, CancellationToken.None, new FileDataStore("MyAppsToken")).Result;
        //Once consent is recieved, your token will be stored locally on the AppData directory, so that next time you wont be prompted for consent.   

        DriveService service = new DriveService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "uploadclient",

        });
        service.HttpClient.Timeout = TimeSpan.FromMinutes(100);
        //Long Operations like file uploads might timeout. 100 is just precautionary value, can be set to any reasonable value depending on what you use your service for  
        // team drive root https://drive.google.com/drive/folders/0AAE83zjNwK-GUk9PVA   
        //string uploadfile = @"C:\Users\hp\Downloads\settings.png";
        var responce = uploadFile(service, textBox1.Text, "");
        //var respocne = uploadFile(service, uploadfile, "");
        // Third parameter is empty it means it would upload to root directory, if you want to upload under a folder, pass folder's id here.
        MessageBox.Show("Process completed--- Response--" + responce);
    }

    public Google.Apis.Drive.v3.Data.File uploadFile(DriveService _service, string _uploadFile, string _parent, string _descrp = "Uploaded with .NET!")
    {
        if (System.IO.File.Exists(_uploadFile))
        {
            Google.Apis.Drive.v3.Data.File body = new Google.Apis.Drive.v3.Data.File();
            body.Name = System.IO.Path.GetFileName(_uploadFile);
            body.Description = _descrp;
            body.MimeType = GetMimeType(_uploadFile);
            // body.Parents = new List<string> { _parent };// UN comment if you want to upload to a folder(ID of parent folder need to be send as paramter in above method)
            byte[] byteArray = System.IO.File.ReadAllBytes(_uploadFile);
            System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
            try
            {
                FilesResource.CreateMediaUpload request = _service.Files.Create(body, stream, GetMimeType(_uploadFile));
                request.SupportsTeamDrives = true;
                // You can bind event handler with progress changed event and response recieved(completed event)
                request.ProgressChanged += Request_ProgressChanged;
                request.ResponseReceived += Request_ResponseReceived;
                request.Upload();
                return request.ResponseBody;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "Error Occured");
                return null;
            }
        }
        else
        {
            MessageBox.Show("The file does not exist.", "404");
            return null;
        }
    }

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

    private void Request_ResponseReceived(Google.Apis.Drive.v3.Data.File obj)
    {
        if (obj != null)
        {
            MessageBox.Show("File was uploaded sucessfully--" + obj.Id);
        }
    }

    public void button2_Click(object sender, EventArgs e)
    {
        Authorize();
        
    }
  }
}
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449

1 Answers1

0

Your issue essentially boils down to a very simple problem. You can only update controls from the UI thread. Not from a background thread.

Your issue is this piece of code :

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

Request_ProgressChanged is being called async from a background thread. And yet you are trying to update the textBox1 text.

You can just change it to be :

private void Request_ProgressChanged(Google.Apis.Upload.IUploadProgress obj)
{
    textBox1.BeginInvoke((Action)(() => textBox1.Text += obj.Status + " " + obj.BytesSent));
}

BeginInvoke tells your application to marshall the call back to the UI thread and make it do the work. You will need to do this anywhere you are trying to update the UI from a background thread.

This can become tedious so you can also use libraries such as PostSharp that allow you to add attributes to methods you want to run on the UI thread instead of constantly writing boilerplate https://dotnetcoretutorials.com/2020/12/10/simplifying-multithreaded-scenarios-with-postsharp-threading/

MindingData
  • 11,924
  • 6
  • 49
  • 68