Interop SaveAs bypasses input file extension - c#

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?

Related

Exception using C# to ChangeLink IN Excel

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.

When i try to delete report from the folder this error comes

The process cannot access the file because it is being used by another process
private void DeleteReport()
{
int invid = Convert.ToInt32(Session["InvId"]);
string FileName = invid + "_Report" + ".pdf";
string path1 = Server.MapPath("~/Report/" + FileName);
if (File.Exists(path1))
{
File.Delete(path1);
}
}
The error tells you, that the file is used and can't be deleted. So nothing wrong. As you did not formulate a
real question, lets try to help you in following way.
I guess that only your program is using the report, so good possible, you block the report
somewhere else.
E.g., the following code
string path = "C:\\Temp\\test.txt";
FileStream file = File.Open(path, FileMode.OpenOrCreate);
if (File.Exists(path))
File.Delete(path);
raises the same error. It does not necessarily mean that the process is another process.
What you can do is for example, for testing purpose, install SysInternal
http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx and add following code around your
File.Delete statement. Then you will see, what process uses the file:
try
{
File.Delete(path);
}
catch (Exception)
{
using (Process tool = new Process())
{
tool.StartInfo.FileName = #"C:\Program Files (x86)\SysinternalsSuite\handle.exe"; //Your path
tool.StartInfo.Arguments = path + " /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern))
{
Process p = Process.GetProcessById(int.Parse(match.Value));
MessageBox.Show(p.ProcessName); // OR LOG IT
}
}
throw;
}
Credit for handle.exe call to https://stackoverflow.com/a/1263609/2707156

How to create and write Excel .xls file using C#

I have few tests which run three times and there average is calculated through c# code.I am able to write the three test times and there average to the xls file if created before in the below picture format . But now I have to run each test every hour everyday through a batch file using windows scheduler. I want to create the xls file dynamically in every hour in below mentioned format with a specific name so that at the first iteration the file is created and for next 19 iteration it should write in the same file then next hour new file created with a specific name.How to create and write the excel file dynamically ?????
If there is any other simple procedure plz suggest that. The code which I was using to write in already created xls file is :`/*
using System;
using System.IO;
using Ranorex;
namespace PEPI_Performance.Utility
{
/// <summary>
/// Description of ExcelWriter.
/// </summary>
public class ExcelWriter
{
/// <summary>
/// Constructs a new instance.
/// </summary>
public ExcelWriter()
{
// Do not delete - a parameterless constructor is required!
}
public void Driver(int row , int col, string time, string sheetName){
string sDataFile = "Ranorex_Reports.xls";
string sFilePath = Path.GetFullPath(sDataFile);
string sOldvalue = "Automation\\bin\\Debug\\" + sDataFile;
sFilePath = sFilePath.Replace(sOldvalue,"")+
"PEPI_Performance\\ExecutionReport\\" + sDataFile;
fnOpenExcel(sFilePath,sheetName);
writeExcel(row,col,time);
fnCloseExcel();
}
Excel.Application exlApp ;
Excel.Workbook exlWB ;
Excel.Sheets excelSheets ;
Excel.Worksheet exlWS;
//Open Excel file
public int fnOpenExcel(string sPath, string iSheet){
int functionReturnValue = 0;
try {
exlApp = new Excel.ApplicationClass();
exlApp.Visible = true;
exlWB=
exlApp.Workbooks.Open(sPath,Type.Missing,Type.Missing,
Type.Missing,Type.Missing,Type.Missing,Type.Missing,
Type.Missing,Type.Missing,Type.Missing,Type.Missing,Type.Missing,Type.Missing);
// get all sheets in workbook
excelSheets = exlWB.Worksheets;
// get some sheet
//string currentSheet = "Cycle1";
exlWS = (Excel.Worksheet)excelSheets.get_Item(iSheet);
functionReturnValue = 0;
}
catch (Exception ex) {
functionReturnValue = -1;
Report.Error(ex.Message);
}
return functionReturnValue;
}
// Close the excel file and release objects.
public int fnCloseExcel(){
//exlWB.Close();
try{
exlApp.ActiveWorkbook.Save();
exlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(exlWS);
System.Runtime.InteropServices.Marshal.ReleaseComObject(exlWB);
System.Runtime.InteropServices.Marshal.ReleaseComObject(exlApp);
GC.GetTotalMemory(false);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.GetTotalMemory(true);
}catch(Exception ex){
Report.Error(ex.Message);
}
return 0;
}
public void writeExcel(int i, int j , string time){
Excel.Range exlRange = null;
exlRange = (Excel.Range)exlWS.UsedRange;
((Excel.Range)exlRange.Cells[i,j]).Formula = time;
}
}
}
`
In all honesty you might be better off using a csv file, that way from your ranorex tests you can simply use system.IO.File to write the output text to the file, and the nice thing about the csv format is it can then be opened in excel
There is a way to handle this using a data grid.
The example below accepts a DataSet (you could pass a list or table).
Then on the FLY a GridView is created and exported to Excel. I use this method on many sites.
public static void ExportDataSetToExcel(DataSet ds, string filename)
{
try
{
HttpResponse response = HttpContext.Current.Response;
// first let's clean up the response.object
response.Clear();
response.Charset = "";
// set the response mime type for excel
response.ContentType = "application/vnd.ms-excel";
response.AddHeader("Content-Disposition", "attachment;filename=\"" + filename + "\"");
// create a string writer
using (StringWriter sw = new StringWriter())
{
using (HtmlTextWriter htw = new HtmlTextWriter(sw))
{
// instantiate a datagrid
DataGrid dg = new DataGrid();
dg.DataSource = ds;
dg.DataBind();
dg.RenderControl(htw);
response.Write(sw.ToString());
response.End();
}
}
}
catch { }
}

How to extract text data from MS-Word doc file

i am developing a resume archive where people upload their resume and that resume will be saved in a specific location. the most important things is people may use any version of MS-word to prepare their resume and resume file extension could be doc or docx. so i just like to know is there any free library available which i can use to extract text data from doc or docx file which will work in case of all ms-word version and also work if ms-word is not install in pc. i search google and found some article to extract text data from doc file but i am not sure does they work in case of all ms-word version. so please guide me with info that which library i should use to extract data from ms-word irrespective of ms-word version also give me some good article link on this issue.
also guide me is there any viewer available which i can use to show doc file content from my c# apps irrespective of ms-word version.
thanks
i got the answer
**Need to add this reference Microsoft.Office.Interop.Word**
using System.Runtime.InteropServices.ComTypes;
using System.IO;
public static string GetText(string strfilename)
{
string strRetval = "";
System.Text.StringBuilder strBuilder = new System.Text.StringBuilder();
if (File.Exists(strfilename))
{
try
{
using (StreamReader sr = File.OpenText(strfilename))
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
strBuilder.AppendLine(s);
}
}
}
catch (Exception ex)
{
SendErrorMail(ex);
}
finally
{
if (System.IO.File.Exists(strfilename))
System.IO.File.Delete(strfilename);
}
}
if (strBuilder.ToString().Trim() != "")
strRetval = strBuilder.ToString();
else
strRetval = "";
return strRetval;
}
public static string SaveAsText(string strfilename)
{
string fileName = "";
object miss = System.Reflection.Missing.Value;
Microsoft.Office.Interop.Word.Document doc = null;
try
{
Microsoft.Office.Interop.Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();
fileName = Path.GetDirectoryName(strfilename) + #"\" + Path.GetFileNameWithoutExtension(strfilename) + ".txt";
doc = wordApp.Documents.Open(strfilename, false);
doc.SaveAs(fileName, Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatDOSText);
}
catch (Exception ex)
{
SendErrorMail(ex);
}
finally
{
if (doc != null)
{
doc.Close(ref miss, ref miss, ref miss);
System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
doc = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
return fileName;
}
See the following:
http://msdn.microsoft.com/en-us/library/cc974107%28office.12%29.aspx
How can i read .docx file?
Microsoft Interop Word Nuget
string docPath = #"C:\whereEverTheFileIs.doc";
Application app = new Application();
Document doc = app.Documents.Open(docPath);
string words = doc.Content.Text;
doc.Close();
app.Quit();

File.Move throws error when used with BackgroundWorkerr in C#

Solved
I figured out that the GetNewFolderNameBasedOnDate method internally didn't close the file. I have that method fixed and it working normal now
I am trying to move selected files from one folder to another using BackgroundWorker process in C#. Here is my DoWork() method that determine whether to move the files or just copy. My File.Move() throws an exception that "The process cannot access the file because it is being used by another process". I tried different methods as mentioned in the threads here in stackoverflow.
private void FileProcessor_DoWork(object sender, DoWorkEventArgs e)
{
// Copy files
long bytes = 0;
string destSubFolder = String.Empty;
string destFile = string.Empty;
foreach (FileInfo file in oSettings.SourceFiles)
{
try
{
this.BeginInvoke(OnChange, new object[] { new UIProgress(file.Name, bytes, oSettings.MaxBytes) });
destSubFolder = GetNewFolderNameBasedOnDate(file);
//Create a new subfolder under the current active folder
string newPath = Path.Combine(oSettings.TargetFolder, destSubFolder);
// Create a new target folder, if necessary.
if (!System.IO.Directory.Exists(newPath))
{
System.IO.Directory.CreateDirectory(newPath);
}
destFile = Path.Combine(oSettings.TargetFolder, destSubFolder, file.Name);
if (chkDeleteSourceFiles.Checked)
{
FileInfo f = new FileInfo(file.FullName);
if (f.Exists)
{
File.Move(file.FullName, destFile);
}
}
else
{
File.Copy(file.FullName, destFile, true);
}
//Thread.Sleep(2000);
}
catch (Exception ex)
{
UIError err = new UIError(ex, file.FullName);
this.Invoke(OnError, new object[] { err });
if (err.result == DialogResult.Cancel) break;
}
bytes += file.Length;
}
}
I tried to delete the files in "RunWorkerCompleted" method too. But didn't resolve the problem. This fails when it tries to delete the last file in the list.
private void FileProcessor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Operation completed, update UI
ChangeUI(false);
foreach (FileInfo file in oSettings.SourceFiles)
{
File.Delete(file.FullName);
}
}
GetNewFolderNameBasedOnDate() calls GetDateTaken() which was the culprit. Earlier I didn't use FileStream object but used Image myImage = Image.FromFile(filename); I didn't know that Image.FromFile locks the file.
private DateTime GetDateTaken(string fileName)
{
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
Image myImage = Image.FromStream(fs);
PropertyItem propItem = myImage.GetPropertyItem(36867);
DateTime dtaken;
//Convert date taken metadata to a DateTime object
string sdate = Encoding.UTF8.GetString(propItem.Value).Trim();
string secondhalf = sdate.Substring(sdate.IndexOf(" "), (sdate.Length - sdate.IndexOf(" ")));
string firsthalf = sdate.Substring(0, 10);
firsthalf = firsthalf.Replace(":", "-");
sdate = firsthalf + secondhalf;
dtaken = DateTime.Parse(sdate);
return dtaken;
}
}
catch (Exception ex)
{
return DateTime.Now;
}
}
Instead of creating new FileInfo objects, keep it simple and re-use the same one. I suspect the problem is that you have multiple references to the same file in your code, which prevents it from being removed. Try something like this to move it:
if (chkDeleteSourceFiles.Checked)
{
if (file.Exists)
{
file.MoveTo(destFile);
}
}
My guess is that it is the BeginInvoke call to OnChange and the new UIProgress() object that is holding onto the file. Does UIProgress open the file? You could try just using Invoke() and see if that helps.

Categories

Resources