1

I'm trying to build an installer project (windows forms) using which I can select multiple excel files from a directory, copy contents from each file and append it in a new single output file. I am using Microsoft.Office.Interop.Excel package in VS 2017 Community edition. I'm still working on it but a bit push to the right direction will help a lot.

Say, I have 100's of excel files in a directory, each excel workbook contains at least 1 sheet. The structure of each sheet are identical. Using OpenFileDialog:

  1. I select the files and display the selected file name and its path in a listbox.
  2. I display the file count in a label.
  3. Read the content of each file and paste it to a new excel file. The output file is single. after reading each file I want to append the contents to the end of the output file. So, I simply want to copy all the rows in one worksheet and append at the end of another worksheet.

I don't know where exactly I'm going wrong, I'm not even able to create the output excel file. Here is my work till now:

Excel.Application excel;
    Stream myStrm;
    OpenFileDialog ofd = new OpenFileDialog();

    private void button1_Click(object sender, EventArgs e)
    {
        ofd.InitialDirectory = "c:\\";
        ofd.Title = "Please select the files to merge";
        ofd.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm";
        //ofd.FileName = "ExportedData";
        //ofd.DefaultExt = ".xlsx";
        ofd.RestoreDirectory = true;
        ofd.Multiselect = true;

        //To store file path info
        List<System.IO.FileInfo> fList = new List<System.IO.FileInfo>();

        if (ofd.ShowDialog() == DialogResult.OK)
        {
            var fileName = ofd.FileName;
            var tempFolderPath = Path.GetTempPath();
            var templateFileLocation = Path.Combine(Directory.GetCurrentDirectory(), "Model", "ExcelTemplate.xlsx");
            templateFileLocation = templateFileLocation.Replace("\\bin\\Debug\\", "\\");

            foreach (String file in ofd.FileNames)
            {
                try
                {
                    if((myStrm = ofd.OpenFile()) != null)
                    {
                        using (myStrm)
                        {
                            //to display file name in the 1st listbox
                            fileListBox.Items.Add(file.Substring(file.LastIndexOf('\\') + 1));
                            //to display the path of the file
                            pathListBox.Items.Add(file);
                        }
                    }
                    //to display the selected file count 
                    fileCntLbl.Text = "You have selected " + ofd.FileNames.Length + " files";

                    ApplicationClass app = new ApplicationClass();
                    Workbook curWorkBook = null;
                    Workbook destWorkbook = null;
                    Worksheet workSheet = null;
                    Worksheet newWorksheet = null;
                    Object defaultArg = Type.Missing;
                    //FileInfo fi = null;
                    Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();

                    try
                    {
                        // Copy the source sheet
                        curWorkBook = app.Workbooks.Open(file, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg);
                        workSheet = (Worksheet)curWorkBook.Sheets[1];
                        workSheet.UsedRange.Copy(defaultArg);

                        // Paste on destination sheet
                        destWorkbook = app.Workbooks.Open(@"c:\\Results.xlxs", defaultArg, false, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg, defaultArg);
                        newWorksheet = (Worksheet)destWorkbook.Worksheets.Add(defaultArg, defaultArg, defaultArg, defaultArg);
                        newWorksheet.UsedRange._PasteSpecial(XlPasteType.xlPasteValues, XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);


                        }
                    }
                    catch (Exception exc)
                    {
                        System.Windows.Forms.MessageBox.Show(exc.Message);
                    }
                    finally
                    {
                        if (curWorkBook != null)
                        {
                            curWorkBook.Save();
                            curWorkBook.Close(defaultArg, defaultArg, defaultArg);
                        }

                        if (destWorkbook != null)
                        {
                            destWorkbook.Save();
                            destWorkbook.Close(defaultArg, defaultArg, defaultArg);
                        }
                    }
                    app.Quit();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
        }
    }

    private void button2_Click(object sender, EventArgs e)
    {
        System.Windows.Forms.Application.Exit();
    }

This solution works but doesn't copy the contents and produces error. The error is like this:

Error Screenshot

Any help/suggestions appreciated. Thanks for help.

I have already tried all the solutions on stackexchange, codeproject and many other sites. So please do not mark it as duplicate or a homework stuff.

StackUseR
  • 884
  • 1
  • 11
  • 40
  • what error does it produce? We don't want to have to guess. BTW you might be better off using a library which actually interacts with just the files, rather than trying to use interop, which is designed to manipulate a running instance of Excel - that's an unnecessary level of indirection and complexity if you're trying to work with the data in the files. It's also notoriously crash-prone. There are several free libraries around for .NET which work directly on the files themselves. – ADyson Aug 01 '18 at 14:30
  • @ADyson: I have updated the question with the error sceenshot. BTW Thanks for the suggestion. I really appreciate it but according to my needs interop is an option. – StackUseR Aug 02 '18 at 07:33
  • well the error seems pretty clear - you're trying to interact with a file which doesn't exist. Since you've wrapped the whole thing in a try/catch, you might be best to step through it with a debugger and find exactly which line causes the error. In your code I can't see that filename mentioned, which is odd. – ADyson Aug 02 '18 at 07:39

3 Answers3

1

Instead of copy/pasting UsedRange, try using the Sheet.Copy function. It will simplify your code substantially and reduce the potential error points. C# - How to copy a single Excel worksheet from one workbook to another?

Frank Ball
  • 1,039
  • 8
  • 15
  • Thanks for your valuable time bro. But I don't want to copy the whole sheet. I just want to copy the used cells from multiple files and append it to a single excel file. – StackUseR Aug 02 '18 at 07:37
  • We've had the need in our organization on a couple of occasion including one where we had to combine over 700 files into a set of about 15. Sheet.Copy was by far the simplest and most foolproof method we found. We didn't have to create new sheets within the destination workbook and it was literally a one line solution. If you're copying the UsedCells, then for all intents and purposes, you're doing a Sheet.Copy, just putting more work into your code. Take it or leave it, but I'd suggest giving it a shot... – Frank Ball Aug 02 '18 at 14:35
  • +1 it worked pretty good, but I have errors while loading `.xls` files which says: `Zip exception.Can't locate end of central directory record. Possible wrong file format or archive is corrupt`. After googling, I came to know that the file may be corrupted or may be developed in some other software than excel. But I can open it without any error in MS Excel. Also I'm not trying to open any zip file. All are excel files. Can you help me with that? – StackUseR Aug 09 '18 at 12:04
  • All Excel files in 2010 and O365 are actually Zip files. They contain several different files that all work together to create the Excel file. Try this: Create an Excel file with some simple text and then save it. Change the extension to "ZIP" and then open it using WinZip (or whatever software you have for opening ZIP files). You'll see a collection of files that create the Excel file. – Frank Ball Aug 09 '18 at 17:53
  • Yup that's correct. But how to solve the error? Any ideas? Thanks. – StackUseR Aug 10 '18 at 09:28
1

I know I'm late to answer this but still this could help someone.

You can have two functions here, one to open the files and the other to copy the contents.

In the first function, you can open as many files as you want through a for loop, something like this:

void OpenFiles()
{
    foreach (string strFile in sourceFiles) //sourceFiles is a list containing the file paths
    {
        bool b = false;
        Excel.Workbook bookSource = app.Workbooks._Open(strFile, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
        Excel.Worksheet sheetSource = bookSource.Worksheets[1] as Excel.Worksheet;

        CopyData();

    }
    //finally save the file as your requirements and close all the open workbooks
}

void CopyData()
{
    Excel.Worksheet lastsheet = null;   //last sheet in the workbook
    int limit = 1000000; //variable to check if your sheet has exceeded
    try
    {
        var sheets = bookDest.Sheets;
        lastsheet = (Excel.Worksheet)bookDest.Sheets[sheets.Count];
        hc.ReleaseObject(sheets);
        drc = lastsheet.UsedRange.Rows.Count;       //no of rows used in result workbook
        src = sheetSource.UsedRange.Rows.Count;     //no of rows used in source workbook

        //if else loop to check if you have exceeded 1st sheet limit before start copying
        if ((drc + src) <= limit)
        {
            int sheetRowCount = sheetSource.UsedRange.Rows.Count;
            Excel.Range range = sheetSource.get_Range(string.Format("A{0}", _headerRowCount), _columnEnd + sheetRowCount.ToString());
            range.Copy(lastsheet.get_Range(string.Format("A{0}", _currentRowCount), Missing.Value));
            _currentRowCount += range.Rows.Count;
        }
        else if ((drc >= limit && src >= limit) || drc >= limit || src >= limit || (drc + src) >= limit)
        {
                Excel.Worksheet newSheet = (Excel.Worksheet)bookDest.Worksheets.Add(After: lastsheet);
                newSheet.Name = "Result " + (cnt++);
                hc.ReleaseObject(lastsheet);
                lastsheet = newSheet;
                lastsheet.Activate();
                CopyHeader(lastsheet);

                //((Excel.Worksheet) this.app.ActiveWorkbook.Sheets[lastsheet]).Select();
                int sheetRowCount = sheetSource.UsedRange.Rows.Count;
                Excel.Range range = sheetSource.get_Range(string.Format("A{0}", _headerRowCount), _columnEnd + sheetRowCount.ToString());
                range.Copy(lastsheet.get_Range(string.Format("A{0}", _currentRowCount), Missing.Value));
                _currentRowCount += range.Rows.Count;
            }
        }
        else
        {
            int sheetRowCount = sheetSource.UsedRange.Rows.Count;
            Excel.Range range = sheetSource.get_Range(string.Format("A{0}", _headerRowCount), _columnEnd + sheetRowCount.ToString());
            range.Copy(lastsheet.get_Range(string.Format("A{0}", _currentRowCount), Missing.Value));
            _currentRowCount += range.Rows.Count;
        }
    }
    catch (IndexOutOfRangeException)
    {
        MessageBox.Show("Some problem with the source file", "Copy error");
    }
    finally
    {
        ReleaseObject(lastsheet);
    }
}

I have provided the limit to exactly a million. If you feel this limit might hang your excel than you can reduce it.

You're invited for any code changes.

Thank you.

Raj
  • 199
  • 1
  • 12
0

Alternatively, using System.IO you can use the following to copy an excel file into a new file:

File.WriteAllBytes(newFilePath, File.ReadAllBytes(initialFilePath));
Giollia
  • 77
  • 4