Editing excel with npoi breaks buttom with macro - c#

I have excel template that I copy and populate using npoi. I take a copy of original. This works fine but after I put set cell values the button that has macros breaks
Is there something that can be done?
I originally crated new excel and transfered the content and of course this didn't bring macro so I switched to copy. This worked on first step but now adding the cell value is problem. Values come to excel nicely. Values are just simple texts or numbers.
// Paramerters
string filename_org = "Excel_template.xlsm";
// Template file
string server_folder = HttpContext.Current.Server.MapPath("~");
string file_path_org = server_folder + "\\temp\\" + filename_org;
// New File ****************************************************************
string filename_new = "Report_for_" + root.getProperty( "name", "" ) + "_" + pval.Replace("-", "_").Replace(":", "_") + ".xlsm";
string file_path_new = server_folder + "\\temp\\" + filename_new;
// Copy file here **********************************************************
System.IO.File.Copy(file_path_org, file_path_new, true);
FileStream fs;
try {
fs = new FileStream(file_path_new, FileMode.Open, FileAccess.Read);
} catch( Exception e) {
return inn.newError("Opening the Excel Template File FAILED: " + e.Message);
}
CCO.Utilities.WriteDebug("Excel_Report", "file_Open");
if (fs != null) {
// WORKBOOK ****************************************************************
IWorkbook xssWorkbook = new XSSFWorkbook(fs);
fs.Close();
// WORKSHEET
ISheet sheet = xssWorkbook.GetSheetAt(0);
// UPDATE CELL VALUES ******************************************************
//LOTS OF THESE HERE
sheet.GetRow(9).GetCell(1).SetCellValue( root.getProperty( "name", "" ) );
// Save new result file ****************************************************
using (var fs2 = new FileStream(file_path_new, FileMode.Create, FileAccess.Write))
{
xssWorkbook.Write(fs2,false);
fs2.Close();
}
}
CCO.Utilities.WriteDebug("Excel_REPORT", "Properties added");
//Add file to vault ************************************************************
Item file = inn.newItem("File","add");
file.setProperty("filename", filename_new);
file.attachPhysicalFile(file_path_new);
Item returnItem = file.apply();
returnItem.setProperty("errors",errorMessage);
// Delete copied File
File.Delete(file_path_new);
return returnItem;
}

Related

Windows Service Filestream giving System.IO.IOException: The process cannot access the file "filename" because it is being used by another process

I've got a windows service that I have to modify. Current code is this:
public IRecord2 GetRecord(string name)
{
string path = Path.Combine(this.DirectoryPath, name);
if (!File.Exists(path))
return null;
byte[] contents;
lock (locker) {
using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize:4096, useAsync:true)) //WHERE THE PROBLEM IS OCCURRING
{
using (BinaryReader br = new BinaryReader(fs))
{
contents = br.ReadBytes((int)fs.Length);
br.Close(); //unnecessary but threw it in just to be sure
fs.Close(); //unnecessary but threw it in just to be sure
}
}
}
return new Record2()
{
Name = name,
Contents = contents
};
}
Code that calls the function:
public void Process(string pickupFileName)
{
string uniqueId = DateTime.Now.ToString("(yyyy-MM-dd_HH-mm-ss)");
string exportFileName = Path.GetFileNameWithoutExtension(pickupFileName) + "_" + uniqueId + ".csv";
string archiveFileName = Path.GetFileNameWithoutExtension(pickupFileName) + "_" + uniqueId + Path.GetExtension(pickupFileName);
string unprocessedFileName = Path.GetFileNameWithoutExtension(pickupFileName) + "_" + uniqueId + Path.GetExtension(pickupFileName);
try
{
_logger.LogInfo(String.Format("Processing lockbox file '{0}'", pickupFileName));
IRecord2 record = _pickup.GetRecord(pickupFileName);
if (record == null)
return;
_archive.AddOrUpdate(new Record2() { Name = archiveFileName, Contents = record.Contents });
string pickupFileContents = UTF8Encoding.UTF8.GetString(record.Contents);
IBai2Document document = Bai2Document.CreateFromString(pickupFileContents);
StringBuilder sb = Export(document);
_export.AddOrUpdate(new Record2() { Name = exportFileName, Contents = Encoding.ASCII.GetBytes(sb.ToString()) });
_pickup.Delete(pickupFileName);
}
catch(Exception ex)
{
throw ex;
}
}
Function that calls Process:
public void Process()
{
foreach (ConfigFolderPath configFolderPath in _configSettings.ConfigFolderPaths)
{
IRecordRepository pickup = new FileRepository(configFolderPath.PickupFolderPath);
IRecordRepository export = new FileRepository(configFolderPath.ExportFolderPath);
IRecordRepository archive = new FileRepository(configFolderPath.ArchiveFolderPath);
IRecordRepository unprocessed = new FileRepository(configFolderPath.UnprocessedFolderPath);
Converter converter = new Converter(Logger,pickup, export, archive, unprocessed);
foreach (string fileName in pickup.GetNames())
{
if (_configSettings.SupportedFileExtensions.Count > 0 && !_configSettings.SupportedFileExtensions.Any(extension => extension.ToLower() == Path.GetExtension(fileName).ToLower()))
continue;
Action action = () => converter.Process(fileName);
_queue.TryEnqueue(action, new WorkTicket() { Description = String.Format("Processing '{0}'", fileName), SequentialExecutionGroup = fileName });
}
}
}
When 1 file is sent to the service, it processes and reads the file correctly. However, if two files are sent (difference of 3 minutes), the first file will process correctly, but the second will give me "System.IO.IOException: The process cannot access the file "filename" because it is being used by another process.
Is the solution to use a mutex as per https://stackoverflow.com/a/29941548/4263285 or is there a better solution to solve this?
Edit: More context:
Service is constantly running - as soon as files are dropped into a folder, it begins the process.
get the file data (function up above)
take the data, transform it, and put it into a different file
Delete the original file from the one up above
rinse and repeat if more files
if one file is placed in the folder, it works correctly.
if two files are placed in the folder, it breaks on the second file
if service is stopped and restarted, it works again
In your code add ".Close()" here, at the end of the line :
using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize:4096, useAsync:true).Close())

If string exists in text file then <condition> in C#

I need to check if the string exists in a text file and if it does then proceed. if not, it will display a MessageBox saying that the ID does not exist.
In my previous question, I tried to check if "testfile.txt" contains the string inputted by the user in TextBox1 then copy the line which contains the string into a new textfile. Using the approach that has been suggested, here's a snippet of what I have so far:
string emp_profile = #"EmployeeData.txt"; //file to be checked
string endata = #"EndData.txt"; //terminated employees data file
string end_tdata = #"end_tdata.txt"; //holds sample's data
//validates if eid exists and if it exists copies data of sample
//plus txt_end.Text into temporary data file
string[] dataline = File.ReadAllLines(emp_profile);
using (StreamWriter w = File.AppendText(end_tdata))
{
foreach (var line in dataline)
{
if (line.Contains(txt_un.Text))
{
w.WriteLine(txt_end.Text + "," + line);
w.Close();
}
}
}
string end_holddata = #"end_holddata.txt";
//read original file
string[] raw_data = File.ReadAllLines(endata);
using (StreamWriter r = File.AppendText(end_holddata))
{
foreach (var line in raw_data)
{
if (!line.Contains(txt_un.Text))
r.WriteLine(line);
}
r.Close();
}
//delete original file
File.Delete(endata);
//creates new data file with old data path
//and copies the temporary data held testdata
using (Stream input = File.OpenRead(end_holddata))
using (Stream output = new FileStream(endata, FileMode.Append,
FileAccess.Write, FileShare.None))
{
input.CopyTo(output);
}
//appends new data of sample into the newly created data file
using (Stream input = File.OpenRead(end_tdata))
using (Stream output = new FileStream(endata, FileMode.Append,
FileAccess.Write, FileShare.None))
{
input.CopyTo(output);
}
DialogResult result = MessageBox.Show("Data has been recorded.");
//clears all temporary files
if (result == DialogResult.OK)
{
File.Delete(end_holddata);
File.Delete(end_tdata);
}
What I'm trying to do here is that I'm copying the data from the "emp_profile.txt that does not contain the txt_un.Text into "end_holddata" and copy the line that contains the input into a different file end_tdata.txt, delete the old datafile to clear contents then merge the data into a new data with the same pathfile as the old one. Afterwards, the temporary data files would be deleted.
I tried enclosing the above snippet within this but it's not working the way I want it to.
string[] dataline = File.ReadAllLines(emp_profile);
for (int i = 0; i < dataline.Length; i++)
{
if (dataline[i].Contains(txt_un.Text))
{
//code snippet here
}
else
MessageBox.Show("ID does not exist!");
}
Any help would appreciated! Thank you in advance!

EPPlus Dispose Doesn't Work

I want to create workbook and then write data using EPPlus. When I create new workbook, it can create successfully. But when I want to write some data to that worksheet, it failed and error says
The process cannot access the file 'filename' because it is being
used by another process.
I have disposed previous ExcelPackage but the error still show when I write data.
//Create new Workbook
private void PengisianBaruBW_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke(new MethodInvoker(delegate
{
SetPengisianBtn.Enabled = false;
}));
FileInfo filePath = new FileInfo("D:\\Data Pengisian SLA Surabaya\\" + day + "_" + date + ".xlsx");
if (File.Exists(filePath.ToString()))
{
File.Delete(filePath.ToString());
}
using (ExcelPackage pck = new ExcelPackage(filePath))
{
var schedule = pck.Workbook.Worksheets.Add("Schedule");
var cart = pck.Workbook.Worksheets.Add("Cartridge");
var unsche = pck.Workbook.Worksheets.Add("Unschedule");
var rekap = pck.Workbook.Worksheets.Add("Rekap");
//My Code here
pck.SaveAs(filePath);
pck.Dispose(); //I have disposed ExcelPakcage here
}
}
//Write Data to Excel File
private void PrintScheduleBtn_Click(object sender, EventArgs e)
{
if (StaffATB.Text != "" && HelperTeamATB.Text != "" && StaffBTB.Text != "" && HelperTeamBTB.Text != "" && StaffCTB.Text != "" && HelperTeamCTB.Text != "" && StaffDTB.Text != "" && HelperTeamDTB.Text != "")
{
DialogResult dialogResult = MessageBox.Show("Apakah Anda yakin ingin menyimpan jadwal pengisian ?", "", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
FileInfo file = new FileInfo("D:\\Data Pengisian SLA Surabaya\\" + day + "_" + date + ".xlsx");
using (ExcelPackage pck = new ExcelPackage(file)) //error here
{
var rekap = pck.Workbook.Worksheets["Rekap"];
var data = pck.Workbook.Worksheets["Data"];
//my code to write data here
pck.SaveAs(file);
pck.Dispose();
}
}
}
else
{
MessageBox.Show("Silakan isi PIC terlebih dahulu !");
}
}
I have added this code to check whether my excel file is active or not. But the error still exsit. I set breakpoint and I see that stream value is null that indicate that my excel file is close. But why the error still exists ? Can anyone help me ?
string file = "D:\\Data Pengisian SLA Surabaya\\" + day + "_" + date + ".xlsx";
var path = Path.Combine(Path.GetTempPath(), "D:\\Data Pengisian SLA Surabaya\\" + day + "_" + date + ".xlsx");
var tempfile = new FileInfo(path);
FileStream stream = null;
try
{
stream = tempfile.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
}
finally
{
if (stream != null)
stream.Close();
}
I simplified your snippet for testing. It all worked as it should. Are you sure there is no other cause of the file access problem, like a virus scanner, backup program etc. since you also have another question with the same basic problem.
Take a look at the snippet below, try it and see if this one works. If not the problem is not in the code.
FileInfo filePath = new FileInfo("ExcelDemo.xlsx");
if (File.Exists(filePath.ToString()))
{
File.Delete(filePath.ToString());
}
using (ExcelPackage pck = new ExcelPackage(filePath))
{
var schedule = pck.Workbook.Worksheets.Add("Schedule");
var cart = pck.Workbook.Worksheets.Add("Cartridge");
var unsche = pck.Workbook.Worksheets.Add("Unschedule");
var rekap = pck.Workbook.Worksheets.Add("Rekap");
pck.SaveAs(filePath);
}
using (ExcelPackage pck = new ExcelPackage(filePath))
{
var rekap = pck.Workbook.Worksheets["Rekap"];
var schedule = pck.Workbook.Worksheets["Schedule"];
rekap.Cells[4, 1].Value = "Added data";
schedule.Cells[4, 1].Value = "Added data";
pck.SaveAs(filePath);
}
As already stated, the basic code should work just fine. However, looking at your code, I sense that you are using some kind of BackgroundWorker (PengisianBaruBW_DoWork name suggests this).
If so, you might run into accessing the same file from another thread (PengisianBaruBW_DoWork executes in parallel with PrintScheduleBtn_Click).
To help you more, you should add where (what line) do you receive this error and the call stack.
[Edit]
Based on additional comments, I think of one of these scenarios:
1) PengisianBaruBW_DoWork gets called many times and sometimes it happens to do work with the file, while PrintScheduleBtn_Click is trying to do work with the same file
2) An unhandled exception in _DoWork might get swallowed and leave the file opened (highly improbable since you have a disposable context).
Either way, put a breakpoint at the start of your _DoWork and one at beginning of PrintScheduleBtn_Click and use step over (F10).
I know this an old post, but it never got solved. I ran into the same problem, but i think i found the solution for it (at least it unlocked my excel file):
excelPackage.Dispose();
excelPackage = null;
GC.Collect();

Export to excel from xlsm template

Requirement
I need to create a windows application in C# where the output is an excel file (xlsm) which is created from a template in xlsm format (contains macros).
In the template file, "IneTemplate.xlsm" there is a hidden sheet, "Data". I have to fill the sheet with data (no headings for the columns. only data) from database and save using a Save File Dialog.
What I done so far ?
I have a button. In the Button click I wrote this.
using OfficeOpenXml;
using (var ms = new MemoryStream())
{
//Default filename for new excel
String newFileName = string.Concat("ExcelExport", '(',
DateTime.Now.ToString("dd-MM-yyyy h:mm:ss tt")
.Replace(':', '_')
.Replace('/', '-')
.Replace(' ', '_'), ')', ".xlsm");
FileInfo existingFile = new FileInfo(Environment.CurrentDirectory + #"\App_Data\IneTemplate.xlsm");
if (existingFile.Exists)
{
using (var MyExcel = new ExcelPackage(existingFile))
{
ExcelWorksheet worksheet = MyExcel.Workbook.Worksheets["Data"];
int tripCount = 1;
//I have few trip data in a checked list box which I need to fill in the first column (column "A")
foreach (object item in clbTrip.CheckedItems)
{
DropDown trip = (DropDown)item;
worksheet.Cells[tripCount, 1].Value = trip.Value;
tripCount++;
}
int stopcount = 2;
int rowCount = 1;
//I have to fill the remaining columns with stops from each Trip in each column.(stops from Trip1 in column "B", from Trip 2 in "C" and so on)
foreach (object item in clbTrip.CheckedItems)
{
DropDown trip = (DropDown)item;
//Get the stops in a Trip from database
DataSet dsStopNames = objTrip.GetStopNames(trip.Id);
foreach (DataRow row in dsStopNames.Tables[0].Rows)
{
worksheet.Cells[rowCount, stopcount].Value = Convert.ToString(row["StopAliasName"]);
rowCount++;
}
stopcount++;
rowCount = 1;
}
try
{
//Create a save file Dialog
SaveFileDialog saveFileDialogExcel = new SaveFileDialog();
saveFileDialogExcel.Filter = "Excel files (*.xlsm)|*.xlsm";
saveFileDialogExcel.Title = "Export Excel File To";
saveFileDialogExcel.FileName = newFileName;
if (saveFileDialogExcel.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
if (saveFileDialogExcel.FileName != "")
{
string path = saveFileDialogExcel.FileName;
MyExcel.SaveAs(ms);
File.WriteAllBytes(path, ms.ToArray());
}
}
}
catch (Exception)
{
MessageBox.Show("Exception Occured While Saving , Check Whether the file is open");
}
}
}
}
Other Info
Used EPPlus for excel generation.
There is no Microsoft Office installed in the system.
Problem
A file got created. But when I open, Its shows file is not in proper format or corrupted or something.
Is there any other way to achieve this?
Will EPPlus able to handle xlsm files?
Please Help.

Saving 2 copies of PDF template, 2nd file corrupt - iTextSharp

I have a PDF template file for 60 labels per page. My goal was to make copies of the template as needed, fill in the form data and then merge the files into a single PDF (or provide links to individual files...either works)
The problem is that the 2nd PDF copy comes out corrupt regardless of date.
The workflow is user selects a date. The lunch orders for that day are gathered into a generic list that in turn is used to fill in the form fields on the template. At 60, the file is saved as a temp file and a new copy of the template is used for the next 60 names, etc...
09/23/2013 through 09/25 have data. On the 25th there are only 38 orders, so this works as intended. On 09/24/2013 there are over 60 orders, the first page works, but the 2nd page is corrupt.
private List<string> CreateLabels(DateTime orderDate)
{
// create file name to save
string fName = ConvertDateToStringName(orderDate) + ".pdf"; // example 09242013.pdf
// to hold Temp File Names
List<string> tempFNames = new List<string>();
// Get path to template/save directory
string path = Server.MapPath("~/admin/labels/");
string pdfPath = path + "8195a.pdf"; // template file
// Get the students and their lunch orders
List<StudentLabel> labels = DalStudentLabel.GetStudentLabels(orderDate);
// Get number of template pages needed
decimal recCount = Convert.ToDecimal(labels.Count);
decimal pages = Decimal.Divide(recCount, 60);
int pagesNeeded = Convert.ToInt32(Math.Ceiling(pages));
// Make the temp names
for (int c = 0; c < pagesNeeded; c++)
{
tempFNames.Add(c.ToString() + fName); //just prepend a digit to the date string
}
//Create copies of the empty templates
foreach (string tName in tempFNames)
{
try
{ File.Delete(path + tName); }
catch { }
File.Copy(pdfPath, path + tName);
}
// we know we need X pages and there is 60 per page
int x = 0;
// foreach page needed
for (int pCount = 0; pCount < pagesNeeded; pCount++)
{
// Make a new page
PdfReader newReader = new PdfReader(pdfPath);
// pCount.ToString replicates temp names
using (FileStream stream = new FileStream(path + pCount.ToString() + fName, FileMode.Open))
{
PdfStamper stamper = new PdfStamper(newReader, stream);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
StudentLabel lbl = null;
string lblInfo = "";
// fill in acro fields with lunch data
foreach (string fieldKey in fieldKeys)
{
try
{
lbl = labels[x];
}
catch
{
break;
} // if we're out of labels, then we're done
lblInfo = lbl.StudentName + "\n";
lblInfo += lbl.Teacher + "\n";
lblInfo += lbl.MenuItem;
form.SetField(fieldKey, lblInfo);
x++;
if (x % 60 == 0) // reached 60, time for new page
{
break;
}
}
stamper.Writer.CloseStream = false;
stamper.FormFlattening = true;
stamper.Close();
newReader.Close();
stream.Flush();
stream.Close();
}
}
return tempFNames;
}
Why are you pre-allocating your files? My guess is that's your problem. You're binding a PdfStamper to a PdfReader for input and an exact copy of the same pdf to a FileStream object for output. The PdfStamper will generate your output file for you, you don't need to help it. You're trying to append new data to an existing file and I'm not quite sure what happens in that case (as I've never actually seen anyone do it.)
So drop your whole File.Copy pre-allocation and change your FileStream declaration to:
using (FileStream stream = new FileStream(path + pCount.ToString() + fName, FileMode.Create, FileAccess.Write, FileShare.None))
You'll obviously also need to adjust how your return array gets populated, too.

Categories

Resources