Laden...

Excel to CSV: Prozess sperrt die Datei nachdem sie erstellt wurde

Erstellt von Gerri3d vor 6 Jahren Letzter Beitrag vor 6 Jahren 4.007 Views
G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren
Excel to CSV: Prozess sperrt die Datei nachdem sie erstellt wurde

Hallo,
ich bin noch ziemlich unerfahren mit komplexeren Programmen in C#.
nichts destotrotz versuche ich ein Excell FIle in eine CSV Datei umzuwandeln nach folgender anleitung:



using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
 
namespace jarloo
{
    public static class ExcelHelper
    {
        public static void ConvertExcelToCsv(string source, string destination, int sheetNumber=1)
        {
            if (File.Exists(destination)) File.Delete(destination);
 
            Application xl = new Application();
 
            try
            {
                Workbook workbook = xl.Workbooks.Open(source);
                Worksheet ws = (Worksheet) workbook.Sheets[sheetNumber];
                ws.SaveAs(destination, XlFileFormat.xlCSV);
 
                Marshal.ReleaseComObject(ws);
            }
            finally
            {
                xl.DisplayAlerts = false;
                xl.Quit();
 
                Marshal.ReleaseComObject(xl);
            }
        }
    }
}

namespace Jarloo
{
    class Program
    {
        static void Main(string[] args)
        {
            ExcelHelper.ConvertExcelToCsv(@"c:tempsample.xlsx",@"c:tempsample.csv");
        }
    }
}

leider wird die CSV zwar erstellt, aber Excell behält die CSV im Prozess und ich kann die Datei nicht speichern unter, bzw weiter verarbeiten.

Nach jedem Start sehe ich im Ressouorcenmonitoring, dass Excell die Datei noch geöffnet hält und sperrt.

Ich verwende Win10Prof, VS 2117 u Excell 2016.

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

mein Code:


       private void WriteAllDataToArr(string excellPath, string actDict)
        {
            string[] sheetName = new string[3];
            string pathTmp;
            Application xl = new Application();

            try
            {


                // Thread.Sleep(2000);
                int i = 0;
             //   for (int i = 0; i < 3; i++)
             //   {

                    Workbook workbook = xl.Workbooks.Open(excellPath);

                    Worksheet ws = (Worksheet)workbook.Sheets[i+1];

                   

                    sheetName[i] = ws.Name;

                    pathTmp = actDict + @"\" + sheetName[i] + ".csv";


                    /*  if (File.Exists(pathTmp))
                      {
                          Thread.Sleep(2000);
                          File.Delete(pathTmp); // delete old csv file
                          Thread.Sleep(2000);
                      } */

                    // ws.Unprotect();
                    ws.SaveAs(pathTmp, XlFileFormat.xlCSV);
                    // Hier wird excell nicht beendet                    
                    
                    Marshal.ReleaseComObject(ws);
                   
                    switch (i)
                    {   
                        case 0: WriteCodierungDataToArr(ReadText(pathTmp)); break;
                        case 1: WriteKlopperDataToArr(ReadText(pathTmp)); break;
                        case 2: WriteFlanschDataToArr(ReadText(pathTmp)); break;
                        default: break;
                    }                    
                //}
            }            
            finally
            {
                xl.DisplayAlerts = false;
                xl.Quit();

                Marshal.ReleaseComObject(xl);
            }
        }

16.806 Beiträge seit 2008
vor 6 Jahren

Da Workbook IDisposable implementiert wirst Du mit sehr hoher Wahrscheinlichkeit auch entsprechend Dispose() (zB mit Hilfe eines using()) ausführen müssen, damit die Referenzen entfernt werden.

Mehr zum Thema Dispose im Allgemeinen hier:
[Dispose Muster

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

Das wusste ich nicht, hab mich jetzt versucht, durch Dispose durchzuarbeiten...
Ist für mich leider so komplett neu, aber kannst du mir bitte hierbei helfen, das auf mein Programm umzuwälzen?

* soll ich in IDisposable in der Klasse implementieren, also class xy: IDisposible?

* Die Dispose Methode selbst wäre dann ja leer oder?

* wo wende ich using an (Ich weiß, dass die Dispose Methode nach der Ausführung von Using
aufgerufen wird)

1.029 Beiträge seit 2010
vor 6 Jahren

Hi,

viel einfacher - du musst IDisposable nicht implementieren - sondern benutzen, also:


var myDisposable = new ...();
// dein code, der das objekt braucht
myDisposable.Dispose();

Das erwähnte using macht das automatisch - nutzt man folgendermaßen:


using (var myDisposable = new ...())
{
// dein code, der das objekt braucht
}
// hier wurde dispose bereits ausgeführt

Für COM-Interop mit Office verwendet man allerdings eigentlich Marshal.ReleaseComObject. **Und zwar für jedes einzelne Objekt!!! **
Siehe https://stackoverflow.com/questions/158706/how-do-i-properly-clean-up-excel-interop-objects

Wenn dir das zu doof ist (ist es mir auch) - dann schau dir mal NetOffice an.

LG

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

Marshall.ReleaseComObject wird in mienem Beispielcode verwendet. Ind er Vorlage steht auch nix von Dispose, deswegen stand ich da ja auch am schlauch

16.806 Beiträge seit 2008
vor 6 Jahren

Könnte sein, dass die Macher sich gedacht haben, dass Dispose verwendet wird, um Marshal.ReleaseComObject auszuführen.
Schließlich steckt dahinter ein COM Objekt, das auf Excel verweist.

Aus welcher Zeit stammt denn das Beispiel?
Hat das evtl. schon ein paar Jahre auf dem Buckel und der Code hat sich evtl. weiter entwickelt...?

1.029 Beiträge seit 2010
vor 6 Jahren

Hi,

meine Beispiele für IDisposable sind auf Excel-Interop (V15) gar nicht anwendbar, da es nicht implementiert ist. (Sry - das ist wohl nicht richtig rübergekommen)

Zudem - du verwendest zwar Marshal.ReleaseComObject - aber nicht wie von mir extra fett markiert auf jedes Objekt. Als Beispiel mal ein CSV-Export, der Excel ordentlich beendet:


static void Main(string[] args)
		{
			var sourceFilePath = @"";
			var targetFilePath = @"";

			var xlApp = new Excel.Application();
			var xlWkb = xlApp.Workbooks.Open(sourceFilePath);
			var xlWks = (Excel.Worksheet)xlWkb.Sheets[1];
			xlWks.SaveAs(targetFilePath);

			xlApp.Quit();
			Marshal.ReleaseComObject(xlWks);
			Marshal.ReleaseComObject(xlWkb.Sheets);
			Marshal.ReleaseComObject(xlWkb);
			Marshal.ReleaseComObject(xlApp.Workbooks);
			Marshal.ReleaseComObject(xlApp);
		}

Offensichtlich zu entsorgen sind:

  • xlApp
  • xlWkb
  • xlWks

Nicht so offensichtlich - aber dennoch zu entsorgen:

  • xlApp.Workbooks
  • xlWkb.Sheets

Du solltest dir meinen vorherigen Link wirklich noch mal anschauen.

LG

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

ja, von 2011 http://www.jarloo.com/excel-to-csv/
wie würde man dass heute lösen?

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren
Hinweis von Abt vor 6 Jahren

bitte keine full quotes

vielen danke

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

Im Anhang sieht man, dass die Anwendung (obwohl vorher kein CSV da war, dieses nochmals Speichern will - es poppt auch ein Dialog auf aus Excell).

Da ich save as ja Ausführe, sollte das nicht passieren oder?
Wenn ich diesen Dialog schließe ist die Datei auch wieder frei.

T
461 Beiträge seit 2013
vor 6 Jahren

Aslo in der Version V15 gibt es laut Bild mehr Parameter zum Angeben.

Ist es bei V16 wirklich derart reduziert?

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

ja klar, nur die lasse ich default.

habs jetzt so gelöst:


...
  xlWks.SaveAs(pathTmp, XlFileFormat.xlCSV);
                xlWbk.Save();



                xlApp.Quit();
                
                Marshal.ReleaseComObject(xlWks);
                
                // Marshal.ReleaseComObject(xlWbk.Sheets[i+1]);

..

T
461 Beiträge seit 2013
vor 6 Jahren

Also ich suchte und habe gefunden: (gleich der erste Treffer...)
https://stackoverflow.com/questions/7012705/excel-interop-saving-workbook-without-showing-save-dialog

Ist denk ich genau das was du suchst...

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

vielen Dank,
hatte jemand das Problem, dass der Worksheet Name zwar KOrrekt, der Inhalt aber von einem anderen Worksheet ist (also vom nächsten)?



        private void WriteAllDataToArr(string excellPath, string actDict)
        {            
            string pathTmp;
             
            for (int i = 0; i < 3; i++)
            {
                Application xlApp = new Application(); // excell application
                Workbook xlWbk = xlApp.Workbooks.Open(excellPath);
                Worksheet xlWks = (Worksheet)xlWbk.Sheets[i+1];

                Console.WriteLine( ((Worksheet)xlWbk.Sheets[i + 1]).Name + " / " + ((Worksheet)xlWbk.Sheets[i + 2]).Name + " / " + ((Worksheet)xlWbk.Sheets[i + 3]).Name );
                pathTmp = actDict + @"\" + xlWks.Name + ".csv";

                if (File.Exists(pathTmp))
                {                     
                    File.Delete(pathTmp); // delete old csv file                                               
                }
            
                xlWks.SaveAs(pathTmp, XlFileFormat.xlUnicodeText); // Trennzeichen = Tab
                ...


G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

OK - man muß das Sheet noch aktivieren ->
xlWks.Activate();



using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

        private void WriteAllDataToArr(string excellPath, string actDict)
        {            
            string pathTmp;

            for (int i = 0; i < 3; i++)
            {
                Application xlApp = new Application(); // excell application
                Workbook xlWbk = xlApp.Workbooks.Open(excellPath);
                Worksheet xlWks = (Worksheet)xlWbk.Sheets[i+1];
                xlWks.Activate();
             
                pathTmp = actDict + @"\" + xlWks.Name + ".csv";

                try
                {
                    if (File.Exists(pathTmp))
                    {
                        File.Delete(pathTmp); // delete old csv file                                               
                    }

                    xlWks.SaveAs(pathTmp, XlFileFormat.xlUnicodeText); // Trennzeichen = Tab
                    xlApp.DisplayAlerts = false;
                    xlWbk.Save();
                    xlApp.Quit();

                }
                finally
                {

                    xlApp.DisplayAlerts = true;
                    // xlApp.Quit();

                    Marshal.ReleaseComObject(xlWks);
               //     Marshal.ReleaseComObject(xlWbk.Sheets[i + 1]);
                    Marshal.ReleaseComObject(xlWbk);
                    Marshal.ReleaseComObject(xlApp.Workbooks);
                    
                    Marshal.ReleaseComObject(xlApp);
                    
                    // replace tab with ;
                    string txt = File.ReadAllText(pathTmp);
                    txt = txt.Replace("\t", ";").Replace("{tab}", "\t");
                    File.WriteAllText(pathTmp, txt, Encoding.UTF8);

                    switch (i)
                    {
                        case 0: WriteCodierungDataToArr(ReadText(pathTmp)); break;
                        case 1: WriteKlopperDataToArr(ReadText(pathTmp)); break;
                        case 2: WriteFlanschDataToArr(ReadText(pathTmp)); break;
                        default: break;
                    }
                }
            }
                        
        }

1.029 Beiträge seit 2010
vor 6 Jahren

Hi,

ist zwar schön wenn's funktioniert - aber mal ganz ehrlich:

Es ist doch sicher auch für dich offensichtlich, dass du hier jede Menge unnötige Arbeit vom Rechner ausführen lässt.

Tu dir selbst den Gefallen und verlege


Application xlApp = new Application();
Workbook xlWbk = xlApp.Workbooks.Open(excellPath);

VOR die for-Schleife. Halte dir einfach vor Augen, dass derzeit jedes Mal ein neuer Prozess gestartet wird, jedes mal das komplette Workbook geladen wird - und zwar 3 x obwohl 1 x reichen würde.

Zusätzlich finde ich es mies, dass deine Schleife statisch bis 3 läuft. Das geht doch sicher besser oder?

LG

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

das dachte ich mir auch schon, nur kann ich dann nicht auf die CSV Daten ohne xlApp.Quit() zugreifen.

G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

oh mann.. jetzt klappts 😃


        private void WriteAllDataToArr(string excellPath, string actDict)
        {            
            string[] pathTmp = new string[3];
            Application xlApp = new Application(); // excell application
            Workbook xlWbk = xlApp.Workbooks.Open(excellPath);

            try
            {
                int i = 0;
                
                foreach (Worksheet xlworksheet in xlWbk.Worksheets)
                {
                    xlApp.DisplayAlerts = false;
                    Worksheet xlWks = (Worksheet)xlWbk.Sheets[i + 1];
                    xlWks.Activate();                 
                    pathTmp[i] = actDict + @"\" + xlWks.Name + ".csv";
                    if (File.Exists(pathTmp[i]))
                    {
                        File.Delete(pathTmp[i]); // delete old csv file                                               
                    }

                    xlWks.SaveAs(pathTmp[i], XlFileFormat.xlUnicodeText); // Trennzeichen = Tab
                    Marshal.ReleaseComObject(xlWks);
                    i++;
                }                
                xlWbk.Save();
                Marshal.ReleaseComObject(xlWbk);
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp.Workbooks);

                // replace tab with ;
                for (int y = 0; y < i; y++)
                {
                    string txt = File.ReadAllText(pathTmp[y]);
                    txt = txt.Replace("\t", ";").Replace("{tab}", "\t");
                    File.WriteAllText(pathTmp[y], txt, Encoding.UTF8);
                    
                    switch (y)
                    {
                        case 0: WriteCodierungDataToArr(ReadText(pathTmp[y])); break;
                        case 1: WriteKlopperDataToArr(ReadText(pathTmp[y])); break;
                        case 2: WriteFlanschDataToArr(ReadText(pathTmp[y])); break;
                        default: break;
                    }
                }                
            }
            finally
            {
                xlApp.DisplayAlerts = true;
                Marshal.ReleaseComObject(xlApp);
            }
        }

16.806 Beiträge seit 2008
vor 6 Jahren

Weitere Verbesserungen:

  • Pfade bitte nicht mit String-Frickelei zusammen setzen, sondern mit Path.Combine()
  • Ein for() wäre hier praktischer als ein foreach()
  • Ressourcen / Releases sollten immer in einem finally-Block erfolgen, sodass diese sicher ausgeführt werden
G
Gerri3d Themenstarter:in
57 Beiträge seit 2017
vor 6 Jahren

vielen Dank für das Feedback


       private void WriteAllDataToArr(string excellPath, string actDict)
        {            
            string[] pathTmp = new string[3];
            Application xlApp = new Application(); // excell application
            Workbook xlWbk = xlApp.Workbooks.Open(excellPath);
            int SheetsCount = xlWbk.Worksheets.Count;

            try
            {                  
                for (int i = 0; i < SheetsCount; i++)
                {
                    xlApp.DisplayAlerts = false;
                    Worksheet xlWks = (Worksheet)xlWbk.Sheets[i + 1];
                    xlWks.Activate();                 
                    pathTmp[i] = Path.Combine(actDict, xlWks.Name) + ".csv";
                    if (File.Exists(pathTmp[i]))
                    {
                        File.Delete(pathTmp[i]); // delete old csv file                                               
                    }

                    xlWks.SaveAs(pathTmp[i], XlFileFormat.xlUnicodeText); // Trennzeichen = Tab
                    Marshal.ReleaseComObject(xlWks);                    
                }                
                xlWbk.Save();
                xlApp.Quit();
                Marshal.ReleaseComObject(xlWbk);
                Marshal.ReleaseComObject(xlApp.Workbooks);
                
                // replace tab with ;
                for (int y = 0; y < SheetsCount; y++)
                {
                    string txt = File.ReadAllText(pathTmp[y]);
                    txt = txt.Replace("\t", ";").Replace("{tab}", "\t");
                    File.WriteAllText(pathTmp[y], txt, Encoding.UTF8);
                    
                    switch (y)
                    {
                        case 0: WriteCodierungDataToArr(ReadText(pathTmp[y])); break;
                        case 1: WriteKlopperDataToArr(ReadText(pathTmp[y])); break;
                        case 2: WriteFlanschDataToArr(ReadText(pathTmp[y])); break;
                        default: break;
                    }
                }                
            }
            finally
            {
                xlApp.DisplayAlerts = true;
                Marshal.ReleaseComObject(xlWbk);
                Marshal.ReleaseComObject(xlApp.Workbooks);
                Marshal.ReleaseComObject(xlApp);
            }
        }