0

My program should export certain Excel files from a folder as an image (in SVG format) into another folder. This can be done by the Methods CopyAndSaveTo and ExportRangeAsImage and they do their job. On the MainWindow I have a button that performs these two functions when it is clicked. I want the User who clicks on this button be informed (Progressbar) how far is this process (Copy + export). I have tried to realise it using a BackgroundWorker and it works only if I comment the following code (in the Method ExportRangeAsImage):

Bitmap image = new Bitmap(System.Windows.Forms.Clipboard.GetImage());
image.Save(ImagePath + Path.GetFileNameWithoutExtension(file) + ".svg");

Otherwise I get the following error message ( translated from German):

In System.NullReferenceException an exception of type "System.Drawing.dll" has occurred, but was this not processed in the user code. Additional information: Object reference not set to an object instance. If a handler is available for this exception, the program may continue to run safely.

Here is the whole code:

private void UpdateDataOfLine1(object sender, ExecutedRoutedEventArgs e)
        {
            string[] files = Directory.GetFiles(RootPath);
 
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.DoWork += worker_DoWork;
            worker.ProgressChanged += worker_ProgressChanged;
            worker.RunWorkerAsync();
        }
 

 
        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            string[] files = Directory.GetFiles(RootPath);
 
            for (int i = 0; i < files.Length; i++)
            {
                if (files[i].Contains("$") || files[i].Contains("~") || files[i].Contains("Thumb"))
                {
                    continue;
                }
 
                File.Copy(files[i], DestPath + Path.GetFileName(files[i]), true);
 
                string newFile = DestPath + Path.GetFileName(files[i]);
 
                ExPortRangeAsImage(newFile);
 
                (sender as BackgroundWorker).ReportProgress(i);
                Thread.Sleep(100);
            }
        }
 

 
        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            Status.Value = e.ProgressPercentage;
        }
 

        public void ExPortRangeAsImage(string file)
        {
            var ExcelApp = new Microsoft.Office.Interop.Excel.Application();
 
            try
            {
                if (file.Contains("BeispielDatei"))
                {
                    Workbook wb = ExcelApp.Workbooks.Open(file);
                    Worksheet ws = (Worksheet)wb.Sheets[1];
                    Range range = ws.Range["A1:U35"];
                    range.CopyPicture(XlPictureAppearance.xlScreen, XlCopyPictureFormat.xlBitmap);
                    wb.Close(SaveChanges: false);
                    Marshal.ReleaseComObject(wb);
                }
 
                else
                {
                    Workbook wb = ExcelApp.Workbooks.Open(file);
                    Worksheet ws = (Worksheet)wb.Sheets[1];
                    Range range = ws.Range["A1:U35"];
                    range.CopyPicture(XlPictureAppearance.xlScreen, XlCopyPictureFormat.xlBitmap);
                    wb.Close(SaveChanges: false);
                    Marshal.ReleaseComObject(wb);
                }
            }
 
            finally
            {
                Bitmap image = new Bitmap(System.Windows.Forms.Clipboard.GetImage());
                image.Save(ImagePath + Path.GetFileNameWithoutExtension(file) + ".svg");
 
                Marshal.ReleaseComObject(ExcelApp);
            }
        }

Can you show me what I'm doing wrong or how I can realize it? Are there other ways than BackgroundWorker?

Thank you in advance for your help!

Here is a screenshot of the Error

lindexi
  • 3,717
  • 1
  • 14
  • 59
Guilian
  • 129
  • 1
  • 11

1 Answers1

0

You should review the stacktrace, I think your inner exception is most likely due to cross thread access violation.

Null reference is probably getting triggered due to: System.Windows.Forms.Clipboard.GetImage() which likely is returning null.

You can try running this portion in UI dispatcher thread:

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>
{
   Bitmap image = new Bitmap(System.Windows.Forms.Clipboard.GetImage());
   image.Save(ImagePath + Path.GetFileNameWithoutExtension(file) + ".svg");
}

This will also most likely introduce synchronization issues (race between copy and paste). You may have to restructure your code to ensure image writing is complete before copying another image into clipboard.

loopedcode
  • 4,635
  • 1
  • 17
  • 20
  • Thanks it functioned with your code :) !!! – Guilian Oct 03 '16 at 19:13
  • Can task use the `System.Windows.Forms.Clipboard` when the task in background ? @loopedcode – lindexi Oct 05 '16 at 01:28
  • It can but it has to use UI synchronization context. See examples here: https://blogs.msdn.microsoft.com/csharpfaq/2010/06/18/parallel-programming-task-schedulers-and-synchronization-context/ – loopedcode Oct 06 '16 at 01:44