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.
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);
}
}
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
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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)
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
Marshall.ReleaseComObject wird in mienem Beispielcode verwendet. Ind er Vorlage steht auch nix von Dispose, deswegen stand ich da ja auch am schlauch
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...?
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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:
Nicht so offensichtlich - aber dennoch zu entsorgen:
Du solltest dir meinen vorherigen Link wirklich noch mal anschauen.
LG
ja, von 2011 http://www.jarloo.com/excel-to-csv/
wie würde man dass heute lösen?
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.
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... 😄
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]);
..
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... 😄
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
...
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;
}
}
}
}
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
das dachte ich mir auch schon, nur kann ich dann nicht auf die CSV Daten ohne xlApp.Quit() zugreifen.
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);
}
}
Weitere Verbesserungen:
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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);
}
}