Exception using C# to ChangeLink IN Excel - c#

As part of a file storage migration project, I am trying to change some excel links in some excel workbooks to reflect the new file storage location.
I am using Winforms and C# in VS2017 RC to develop the solution that I intend to deploy.
In my solution; I am calling the ChangeLink method on the Excel Workbook object and passing in the old link, the new link and the Excel Link Type.
public string ProcessFile(string fileName)
{
// Private member variable and placeholder declarations
missingValue = Type.Missing;
string oldLink;
string newLink;
int splitLocation;
string stringToFind = "\\Finance";
//Open the specified Excel Workbook
Excel.Workbook excelWorkbook;
StringBuilder resultsOut = new StringBuilder();
if (MsOfficeHelper.IsPasswordProtected(fileName))
{
resultsOut = resultsOut.AppendLine("Password Protected - " + fileName);
}
else
{
// Open
excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: false);
Array olinks = excelWorkbook.LinkSources(Excel.XlLink.xlExcelLinks) as Array;
if (olinks != null)
{
if (olinks.Length > 0)
{
resultsOut = resultsOut.AppendLine("Contains Links - " + fileName);
foreach (var olink in olinks)
{
oldLink = olink.ToString();
splitLocation = oldLink.IndexOf(stringToFind, 0);
newLink = "C:\\SteveTest\\" + oldLink.Substring(splitLocation + 1);
resultsOut = resultsOut.AppendLine(oldLink);
resultsOut = resultsOut.AppendLine(newLink);
try
{
excelWorkbook.ChangeLink(Name: oldLink, NewName: newLink, Type: Excel.XlLinkType.xlLinkTypeExcelLinks);
}
catch (Exception whoopsy)
{
MessageBox.Show(whoopsy.Message);
//throw;
}
}
}
}
excelWorkbook.Close(SaveChanges: false);
}
return resultsOut.ToString();
}
However, when I execute the ChangeLink method I get the following exception
Does anyone have any idea what is causing the exception?
Your considered responses will be greatly welcome.

Related

c# - How to make excel auto calculate formula type cells

I have an excel file that has around 40 to 50 sheets of size 8mb and will be increasing may be 15 to 20mb and many are inter-linked with multiple formulas.
So basically if one cell values changes then it will affect multiple sheets because of function and vlookup's etc.
Now I am trying to manipulate the excel sheet dynamically using c#. But I see that the are not calculating instantly.
I tried using ExcelLibrary's Calculate function to do this.
Problems with ExcelLibrary are:
a. It is very very slow with Calculate, ExcelPackage(Open) and Save functions**(Makes it unusable)**.
b. Need to know all the dependent cell to trigger calculate on them(not preferring workbook or worksheet calculate(the debugger never comes to next line)) which is not efficient way to manage code in feature.
I believe the problem is because it works with ooxml and actually not directly with excel(not sure we can work with excel directly so that it will be like inserting data manually using code).
Note: Trying to test with Excel interop(hoping it will do the job because it opens excel file in background while manipulating.)
private void TestExcelWithSimultaneousReadOfTwoSheetsFromFile(string fileName)
{
try
{
int count = 0;
int rowIndex = 1702;
for (int rowCount = 0; rowCount < 3; rowCount++)
{
count = ReadCountFromProdReport(fileName);
WriteRowToProd(fileName, rowIndex + rowCount);
count = ReadCountFromProdReport(fileName);
}
}
catch (Exception ex)
{
throw ex;
}
}
private void WriteRowToProd(string fileName, int rowIndex)
{
try
{
string logDirectory = System.Configuration.ConfigurationManager.AppSettings["LogDirectory"].ToString().Trim();
string filePath = logDirectory + fileName + ConfigurationManager.AppSettings["ExcelSaveFileExtension"].ToString().Trim();
using (ExcelPackage excelPackage = new ExcelPackage(new FileInfo(filePath)))
{
foreach (ExcelWorksheet workSheet in excelPackage.Workbook.Worksheets)
{
if (workSheet.Name.Trim() == "Sample")
{
workSheet.Cells["C" + rowIndex].Value = "15.05.2017";
workSheet.Cells["D" + rowIndex].Value = 21701503;
workSheet.Cells["E" + rowIndex].Value = "21701503109W";
workSheet.Cells["F" + rowIndex].Value = 304;
workSheet.Cells["G" + rowIndex].Value = 200;
workSheet.Cells["H" + rowIndex].Value = 1520;
workSheet.Cells["I" + rowIndex].Value = 11350;
workSheet.Cells["J" + rowIndex].Formula = "=7.85*G1701*H1701*I1701/1000000000";
workSheet.Cells["K" + rowIndex].Value = 27.080;
excelPackage.Save();
break;
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
private int ReadCountFromProdReport(string fileName)
{
try
{
string logDirectory = System.Configuration.ConfigurationManager.AppSettings["LogDirectory"].ToString().Trim();
string filePath = logDirectory + fileName + ConfigurationManager.AppSettings["ExcelSaveFileExtension"].ToString().Trim();
using (ExcelPackage excelPackage = new ExcelPackage(new FileInfo(filePath)))
{
object count = null;
foreach (ExcelWorksheet workSheet in excelPackage.Workbook.Worksheets)
{
if (workSheet.Name.Trim() == "Report")
{
count = workSheet.Cells["E23"].Value;
break;
}
}
return Convert.ToInt32(count);
}
}
catch (Exception ex)
{
throw ex;
}
}

Getting path or file name of Excel workbook

How do I get the Path or the file name of workbook in the below code so that I can get the work book that is edited. I have used ExpertXLS – Excel Spreadsheet Library for .NET - C#/VB.NET here
using ExpertXls;
namespace IQC
{
public class CSFB
{
public static string GenerateTemplateForCurrentGridView(IQO[] items, string colname, int icol)
{
/*Some Code here*/
string pathSource = HttpContext.Current.Server.MapPath("~/pdf/ExportTemplate.xlsx");
ExpertXls.ExcelLib.ExcelWorkbook workbook = new ExpertXls.ExcelLib.ExcelWorkbook(#pathSource);
workbook.LicenseKey = Inq.Configuration.Settings.Value("ExpertPdfLicenseKey");
ExpertXls.ExcelLib.ExcelWorksheet ws = workbook.Worksheets["ImportTemplate"];
ExpertXls.ExcelLib.ExcelCellStyle Style1 = workbook.Styles.AddStyle("Style1");
Style1.Fill.FillType = ExpertXls.ExcelLib.ExcelCellFillType.SolidFill;
Style1.Fill.SolidFillOptions.BackColor = Color.Yellow;
foreach (string cols in colname.Split(','))
{
ws[cols].Style = Style1;
}
/*Some Code here*/
}
}
}
You may use Application.ActiveWorkbook.FullName if the workbook is active workbook. Also you can try using workbook.Path. Refer the link.
Adding below code worked. I have saved the worked book in "pathsource"
System.IO.FileStream fs = new FileStream(pathSource, FileMode.OpenOrCreate, FileAccess.ReadWrite);
string fileName = fs.Name;
try
{
workbook.Save(fs);
}
catch (Exception ex)
{
Logger.Error(" Error:", ex);
}
finally
{
workbook.Close();
}

Using C# to open a non password protected Excel Workbook that contains links to password protected Excel workbooks

As part of a file storage migration project, I am trying to change some excel links in some excel workbooks to reflect the new file storage location.
I am using Winforms and C# in VS2017 RC to develop the solution that I intend to deploy.
At the top of my code, I have the following code so that alerts are turned off and auto updating of links is turned off.
excelApp.Visible = true;
excelApp.DisplayAlerts = false;
excelApp.AskToUpdateLinks = false;
In my solution; I am calling the ChangeLink method on the Excel Workbook object and passing in the old link, the new link and the Excel Link Type.
If I open a non password protected Workbook that contains links to other Workbooks that are not password protected, I don't get a problem and my solution goes on to successfully change the links as requested.
If I open a non password protected Workbook that contains links to other Workbooks that are password protected, Excel issues a prompt to enter a password for that linked Workbook.
Does anyone have any idea on suppressing this secondary prompt for the password of a linked Workbook? My code is below and I await your considered responses.
if (MsOfficeHelper.IsPasswordProtected(fileName))
{
while ((excelApp.Workbooks.Count == 0) && (!allPasswordUsed))
{
// Open workbook - trying each password from password list in turn
foreach (var excelPassword in excelPasswords)
{
try
{
excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword);
allPasswordUsed = true;
resultsOut = resultsOut.AppendLine(fileName + " - Opened");
}
catch (Exception WTF)
{
//MessageBox.Show(WTF.Message);
}
}
// Open workbook - trying each password from password list in turn
foreach (var excelPassword in excelPasswords)
{
try
{
excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword.ToLower());
allPasswordUsed = true;
resultsOut = resultsOut.AppendLine(fileName + " - Opened");
//
}
catch (Exception WTF)
{
//MessageBox.Show(WTF.Message);
}
}
allPasswordUsed = true;
resultsOut = resultsOut.AppendLine(fileName + " - All known passwords used - Unable to Open File");
}
}
else
{
// Open Workbook - no password required
excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever);
resultsOut = resultsOut.AppendLine(fileName + " - Opened");
}
OK.
I've not been able to find any information regarding resolving this issue so I have been left with no option but to develop a workaround using calls to the Windows API.
This is the solution or workaround that I have developed.
On my winform, I have added the following declarations to the Windows API.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
I have also added the following to the top of my winform.
public bool fileOpenInProgress = false;
To my winform, I have added a BackgroundWorker control.
On this BackgroundWorker control, I set the WorkerSupportsCancellation property to True.
In the DoWork event handler of the BackgroundWorker control, I have specified the following method be called.
private void workerXLPwdDialogCheck_DoWork(object sender, DoWorkEventArgs e)
{
while (fileOpenInProgress)
{
IntPtr hwndExcel = FindWindow(lpClassName: "XLMain", lpWindowName: null);
SetForegroundWindow(hwndExcel);
try
{
IntPtr hwndPasswordDialog = FindWindow(lpClassName: null, lpWindowName: "Password");
if (hwndPasswordDialog != IntPtr.Zero)
{
// Make the Password Dialog the active window
SetForegroundWindow(hwndPasswordDialog);
SendMessage(hwndPasswordDialog, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
IntPtr hwndSelectSheetDialog = FindWindow(lpClassName: null, lpWindowName: "Select Sheet");
if (hwndSelectSheetDialog != IntPtr.Zero)
{
// Make the Password Dialog the active window
SetForegroundWindow(hwndSelectSheetDialog);
SendMessage(hwndSelectSheetDialog, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
}
catch (Exception WTF)
{
MessageBox.Show(WTF.Message);
}
}
}
In the rest of my code where I do the the opening of Excel files and changing links, I have the following code
fileOpenInProgress = true;
workerXLPwdDialogCheck.RunWorkerAsync();
StringBuilder resultsOut = new StringBuilder();
if (MsOfficeHelper.IsPasswordProtected(fileName))
{
while ((excelApp.Workbooks.Count == 0) && (!allPasswordUsed))
{
// Open workbook - trying each password from password list in turn
foreach (var excelPassword in excelPasswords)
{
try
{
excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword);
allPasswordUsed = true;
resultsOut = resultsOut.AppendLine(fileName + " - Opened");
}
catch (Exception WTF)
{
//MessageBox.Show(WTF.Message);
}
}
// Open workbook - trying each password from password list in turn
foreach (var excelPassword in excelPasswords)
{
try
{
excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword.ToLower());
allPasswordUsed = true;
resultsOut = resultsOut.AppendLine(fileName + " - Opened");
//
}
catch (Exception WTF)
{
//MessageBox.Show(WTF.Message);
}
}
allPasswordUsed = true;
resultsOut = resultsOut.AppendLine(fileName + " - All known passwords used - Unable to Open File");
}
}
else
{
// Open Workbook - no password required
excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever);
resultsOut = resultsOut.AppendLine(fileName + " - Opened");
}
// Assuming there is an openwork book object
// check to see if it contains links and attempt to update them.
if (excelApp.Workbooks.Count > 0)
{
excelWorkbook = excelApp.ActiveWorkbook;
#pragma warning disable IDE0019 // Use pattern matching
Array olinks = excelWorkbook.LinkSources(Excel.XlLink.xlExcelLinks) as Array;
#pragma warning restore IDE0019 // Use pattern matching
if (olinks != null)
{
if (olinks.Length > 0)
{
resultsOut = resultsOut.AppendLine(" " + fileName + " - " + olinks.Length.ToString() + " links.");
foreach (var olink in olinks)
{
oldLink = olink.ToString();
// Search through list of linked files to find the oldLink
foreach (LinkedFile linkedFile in linkedFiles)
{
if (oldLink == linkedFile.OldLink)
{
newLink = linkedFile.NewLink;
break;
}
}
try
{
excelWorkbook.ChangeLink(Name: oldLink, NewName: newLink, Type: Excel.XlLinkType.xlLinkTypeExcelLinks);
resultsOut = resultsOut.AppendLine(" SUCCESS - ChangeLink from " + oldLink + " to " + newLink);
Application.DoEvents();
}
catch (Exception whoopsy)
{
resultsOut = resultsOut.AppendLine(" FAILURE - ChangeLink from " + oldLink + " to " + newLink);
Application.DoEvents();
}
//resultsOut = resultsOut.AppendLine(" " + oldLink);
} // End For loop
}
else
{
resultsOut = resultsOut.AppendLine(" No links.");
}
}
excelWorkbook.Close(SaveChanges: true);
resultsOut = resultsOut.AppendLine(fileName + " - Closed");
resultsOut = resultsOut.AppendLine(" ");
}
// Stop the background worker that checks for the existence of a
// Excel Password Dialog
fileOpenInProgress = false;
workerXLPwdDialogCheck.CancelAsync();
return resultsOut.ToString();
This has the effect of clicking the cancel button on any 'Password' or 'Select Sheet' dialogs that are displayed.
It might not be the prettiest workaround or solution but it is functional.

Writing to Excel: Cannot access closed stream with EPPLUS

I've looked around, and for the most part I see examples for more complex problems than my own.
So, I've been suggested to use EPPLUS as opposed to EXCEL INTEROP because of the performance improvement. This is my first time using it, and the first time I've encountered memory streams, so I'm not exactly sure what's wrong here.
I'm trying to write to an Excel file and convert that excel file into a PDF. To do this, I installed through NUGET the following:
EPPLUS
EPPLUSExcel
This is my code:
if (DGVmain.RowCount > 0)
{
//Source
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Excel Files|*.xls;*.xlsx";
openFileDialog.ShowDialog();
lblSuccess.Text = openFileDialog.FileName;
lblPathings = Path.ChangeExtension(openFileDialog.FileName, null);
int count = DGVmain.RowCount;
int current = 0;
int ballast = 0;
For each row in a DataGridView, perform write to Excel, then convert to PDF.
foreach (DataGridViewRow row in DGVmain.Rows)
{
//Drag
if (lblSuccess.Text == null)
return;
string drags = Convert.ToString(row.Cells[0].Value);
string dragsy = Convert.ToString(row.Cells[1].Value);
Persona = drag;
generateID();
//Initialize the Excel File
try
{
Here is where I expect something to be wrong:
using (ExcelPackage p = new ExcelPackage())
{
using (FileStream stream = new FileStream(lblSuccess.Text, FileMode.Open))
{
ballast++;
lblItem.Text = "Item #" + ballast;
p.Load(stream);
ExcelWorkbook WB = p.Workbook;
if (WB != null)
{
if (WB.Worksheets.Count > 0)
{
ExcelWorksheet WS = WB.Worksheets.First();
WS.Cells[82, 12].Value = drag13;
WS.Cells[84, 12].Value = "";
WS.Cells[86, 12].Value = 0;
//========================== Form
WS.Cells[95, 5].Value = drag26;
WS.Cells[95, 15].Value = drag27;
WS.Cells[95, 24].Value = drag28;
WS.Cells[95, 33].Value = drag29;
//========================== Right-Seid
WS.Cells[14, 31].Value = drag27;
WS.Cells[17, 31].Value = drag27;
}
}
Byte[] bin = p.GetAsByteArray();
File.WriteAllBytes(lblPathings, bin);
}
p.Save();
}
}
catch (Exception ex)
{
MessageBox.Show("Write Excel: " + ex.Message);
}
Separate method to convert to PDF, utilizing EPPLUSEXCEL and SpireXLS.
finally
{
ConvertToPdf(lblSuccess.Text, finalformat);
}
}
}
The compiler is not throwing any errors except the one mentioned in the title.
You already saved the ExcelPackage here:
Byte[] bin = p.GetAsByteArray();
So when you later try and save it again here:
p.Save();
the ExcelPackage is already closed. I.e. remove the Save() call in your code and you're good.

Interop SaveAs bypasses input file extension

I am trying to develop an extension method which uses excel interop and converts any given input file into a new file in accord with an additional xlfileFormat input parametre.
The problem that I have found so far is that SaveAs method bypasses any arbitrary extension that I set, and sets It in accord with xlFileFormat options.
For example:
xlFileFormat = xlCsv, fileName= foo.arbitrary => saves it as
foo.arbitrary.csv
xlFileFormat = xlExcel8, fileName= extensionLessFoo => saves it
as extensionLessFoo.xls
xlFileFormat = xlOpenWorkbook, fileName= foo.xlsx => saves it as foo.xlsx (this one is OK)
I have been able to overcome this problem by specifying random GUID-based file names, and introducing this name as a SaveAs FileName parametre. Later, i will read final input workbook fullName, and return the recently created FileInfo
I would prefer not to depend on temporary files, but allow for specifying the file name AND the extension. so far, nor SaveCopyAs nor SaveAs have provided me a proper solution.
This is the method have been developing so far:
public static FileInfo InteropConvertTo(this FileInfo inputFile, XlFileFormat format)
{
var outputFileName = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Random SaveAs File -" + System.Guid.NewGuid().ToString());
var outputFile = new FileInfo(outputFileName);
try
{
//creation of a new, silent application
var hiddenApp = new Application();
hiddenApp.Visible = false;
hiddenApp.ScreenUpdating = false;
hiddenApp.DisplayAlerts = false;
//adding workbook, saving as new format, closing
var inputWorkbook = hiddenApp.Workbooks.Add(inputFile);
inputWorkbook.DoNotPromptForConvert = true;
inputWorkbook.SaveAs(Filename: outputFileName,
FileFormat: format , AccessMode:XlSaveAsAccessMode.xlNoChange, CreateBackup: false);
outputFile = new FileInfo(inputWorkbook.FullName);
outputFile.IsReadOnly = false;
xlWorkBook.Close(false);
xlApp.Quit();
releaseObject(hiddenApp );
releaseObject(inputWorkbook);
}
finally
{
GC.Collect();
}
return outputFile;
}
private static void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
}
catch (Exception ex)
{
}
finally
{
obj = null;
GC.Collect();
}
}
Is there any way to use SaveAs forcing your own output file extension?

Categories

Resources