threading problem (reading and writing to one excel file) - c#

I have application, which do something. Generaly the main their task is to analise and drawing charts after getting data from excel file. This application can do at the same time max. 10 analise and each of them are execute in a separate thread in a separate tabPage control. Everything is great to the moment when is appearing 3 problems.
I can't reading data from one excel file do a few analises. If I'm using one file to one analise and I want to use this same file to another it's not possible beacuse there is some massage that this file is acctually using by another process. To read data from excel file i'm using oleDBConnection schema. How to solve this problem.
I have this same problem to write data to one file. How to force my application to write same message from different threads to one file.
If I want to close my application (when one of analise is working) there is show me some message with communication: "Interruption lasted thread (or something like that)". I don't know why. I support this
Please help me to solve this problems beacuse I'm trying to solve it sice monday and there is no effect :(

Possible Example
string FullExcelFilePath = "C:\ExcelFile.xls";
Excel.Application xlApp = new Excel.Application()
Excel.WorkBook xlWorkBook1 = xlApp.WorkBooks.Add(FullExcelFilePath);
Excel.WorkBook xlWorkBook2 = xlApp.WorkBooks.Add(FullExcelFilePath);
//then in your first ThreadCall
foreach(Excel.WorkSheet in xlWorkBook1.Sheets)
{
//get what you want
}
//then in your 2nd ThreadCal
foreach(Excel.WorkSheet in xlWorkBook2.Sheets)
{
//get what you want
}
OTHER Option you could do is:
You Could make a copy of the Xls for the other thread, and then
use a copy of the xls file in the Other Thread.
File.Copy(strFileName, strDestination);
then Afterwards Delete it
File.Delete(strFileNameSecondVersion)

Related

unable to loop through open Excel workbooks in C#

I am currently consistently failing at a very simple task:
I want to loop through all currently open Excel-files (Workbooks)
Of course I did the usual googling for code snipes and found quite a bunch of ready-to-use code that should work according to the comments in the discussion threats and ratings. But for me they all just don’t work
I also saw people mentioning releasing COM Objects, but I didn’t really understand it and all the ready-to-go code examples don’t include it neither.
I am using:
Windows 10
Office 16
Visual Studio 2017 (V15.9.41)
COM-Reference 'Microsoft Excel 16.0 Object Library'
I modified the code snippets I found online, so my current code looks like this:
using System;
using Microsoft.Office.Interop.Excel;
private void PrintListOfOpenWB()
{
Microsoft.Office.Interop.Excel.Application xlApp;
xlApp = new Microsoft.Office.Interop.Excel.Application();
foreach (Workbook item in xlApp.Workbooks)
{
Console.WriteLine(item.Name);
}
}
I am using it as part of a WindowsFormsApp, so there is also using System.Windows.Forms etc, but I removed this part, so only the relevant code is shown here.
I do have multiple workbooks open and I do not get any error while compiling nor while running the code. But there is absolutely no output generated, because it directly jumps to the end of the foreach loop.
Any ideas what I am doing wrong?
I looked further into it and found the following solution that works for me.
using System;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
private void PrintListOfOpenWorkbooks()
{
Excel.Application xlApp;
xlApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
foreach(Excel.Workbook xlWorkBook in xlApp.Workbooks)
{
Console.WriteLine(xlWorkBook.Name);
}
}
Maybe this will help other people in the future as well.
This probably returns no output because the variable is empty, doing what you did you only created the variable that let you access the library method, and you can't populate that with an actual excel file, to do that do this:
Workbook workBook = xlApp.Workbooks.Open(yourExcelFilePath);
//Cycle through the workBook and display the sheets
workBook.Close(false, yourExcelFilePath, null);
Remember that the hierarchy of working with excel files (and this is practically the same with all the different libraries that helps you in doing this) is structured like this:
test = new Microsoft.Office.Interop.Excel.Application(); : This is used just to interface with the library methods
WorkBook : This is the actual equivalent of the full excel file you want to work on, and you need to populate it first
Worksheet: This is an entity that represent a single excel "page" inside the file
P.S: Re-reading the question I maybe have understood it wrong, can you specify if you want to show the name of excel files inside a folder or show the names of the single sheets inside a sigle excel file
To show the names of different excel files somewhere you can do this:
string[] temp = Directory.GetFiles(directoryPath);
List<string> ExcelFilesInDirectory = new();
foreach (var item in temp)
{
if (item.Contains(".xlsx"))
{
ExcelFilesInDirectory.Add(item);
}
}
Then with that list you can make the user select the file he wants to modify and then you can use the first code sample I sent to make it work

The process cannot access the file because it is being used by another process in C# asp.net [duplicate]

I am trying to read an excel file every 2 seconds, This file is getting updated by other RTD application.
I am able to read this file by Oledb connection, but problem comes when i am trying to read it every 2 seconds. Out of 10 attempts it is able to read 4-5 times only and at other attempts ,it throws exception.
Connection String
Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\nids\shes.xlsm;Extended Properties="Excel 12.0 Macro;HDR=Yes;IMEX=1"
Code
//opening connection to excel file
using (OleDbConnection connection = new OleDbConnection(constr))//constr = connection string
{
try
{
connection.Open();
isconopen = true;
}
catch
{
dispatcherTimer2.Start();
connection.Close();
isconopen = false;
}
// If connection is ok , then query sheet
if (isconopen == true)
{
strcon = "SELECT * FROM [" + dsheet + "]";
using (OleDbDataAdapter adapter = new OleDbDataAdapter(strcon, connection))
{
try
{
adapter.Fill(result);
isread = true;
adapter.Dispose();
connection.Close();
}
catch
{
isread = false;
dispatcherTimer2.Start();
adapter.Dispose();
connection.Close();
}
}
}
//if able to retrieve data then call some other function
if (isread == true)
{
converToCSV(0);// for further processing
}
Please help me , i am trying this from last 1 month. Please please please please help me out
Sadly OleDB driver by default will open file exclusively then you can't open it when it's in use by someone else, even just for reading.
Two considerations:
Other application may finalize its work with the file within milliseconds so it's good to try again
Driver will always open file locked for writing (so you can't open it twice via OleDB) but it's shared for reading (so you can copy it).
That said I suggest you should first try to open it after a short pause, if it's still in use (and you can't wait more) then you can make a copy and open that.
Let me assume you have your code in a HandlExcelFile() function:
void HandleExcelFile(string path)
{
try
{
// Function will perform actual work
HandleExcelFileCore(path);
}
catch (Exception) // Be more specific
{
Thread.Sleep(100); // Arbitrary
try
{
HandleExcelFileCore(path);
}
catch (Exception)
{
string tempPath = Path.GetTempFileName();
File.Copy(path, tempPath);
try
{
HandleExcelFileCore(tempPath);
}
finally
{
File.Delete(tempPath);
}
}
}
}
Code is little bit ugly so just consider it a starting point to write your own function.
Considerations:
Retry isn't such bad thing and it's a common way to solve this kind of problems. It's, for example, what Windows shell does (and it's even more normal with networks).
If application didn't close the file then you may copy (and read) old data. If you always need most up-to-date data then you have only one choice: wait. If you can assume that unsaved data belongs to the previous time frame (T - 1, as in digital electronic when signal edge is on clock edge) then just do it and live happy.
Untested solution:
I didn't try this so you have to do it by yourself. Actuallly (I was initially wrong) you can open a read-only connection (through extended properties). It's not documented if this apply to connection only or both file handle and connection. Anyway let's try to change your connection string to:
Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\nids\shes.xlsm;Extended Properties="Excel 12.0 Macro;HDR=Yes;IMEX=1;ReadOnly=true"
Just added a ReadOnly=true at the end of Extended Properties.
Other solutions:
The only alternative solution that comes to my mind is to...manually read Excel file (so you can open it just for reading). That said even in this case the other application may have not written new data (so you'll read old one).
Don't use a timer at all. Change your design to use a FileSystemWatcher, you'll read the file only when notified it has been changed.
When applicable...just do not use a shared file as IPC mechanism! Well, you may not be able to change 2nd application so this may not be your case.
Do not use OleDB to read Microsoft Excel files, there are many 3rd part free libraries that don't open file with exclusive lock, for example this one.
Do not read data directly from files, if Microsoft Excel instance is always running you can use COM interop to get notifications. Check this general article about COM add-ins and this to see how you can attach your C# application with Office Interop to an existing Microsoft Excel instance.
Not sure , why do you want to read the excel the way you are doing.
You can try LinqToExcel for excel reading , its a nice little library for reading excel files also if you need to create excel then try to EPPLUS library. These library i personally found really effective when working with Excels
I had similar problem. Below fixes worked for me
1) Don't hold your connection to sheet. Instead open connection read data and close the connection immediately.
2) If you are using managed code in the unmanaged application then consider using object of managed type instead of pointer(using gcnew) and use Stack Semantics to make sure that memory is cleaned up when object goes out of scope.

C# Excel unable to copy worksheet

I am working on a web App where user can upload an Excel file and I need to copy data from the user worksheet to a destination worksheet in order to do some calculation. To do so, I have the following code:
public class ExcelCtrl
{
private Excel.Application excelApp = null;
private Excel.Workbook srcworkbook = null;
private Excel.Workbook destworkbook = null;
public void excel_init(String srcpath, String destpath, string copyrange)
{
//Open Excel
this.excelApp = new Excel.Application();
//Open user's file
this.srcworkbook = excelApp.Workbooks.Open(srcpath);
Excel.Worksheet srcworksheet = (Excel.Worksheet)srcworkbook.Worksheets.get_Item(1);
//Open calculation file
this.destworkbook = excelApp.Workbooks.Open(destpath);
Excel.Worksheet destworksheet = (Excel.Worksheet)destworkbook.Worksheets.get_Item(1);
//Range of copy
Excel.Range from = srcworksheet.get_Range(copyrange);
Excel.Range to = destworksheet.get_Range(copyrange);
//Copy
from.Copy(to);
//Close user's file
srcworkbook.Close();
//Open Xla file
excelApp.Workbooks.Open("someXlaFile.xla");
excelApp.Workbooks.Open("AnotherXlaFile.xla");
}
Basically I have to different method to run different calculations and they have the following structure. What differ one calculation method to another are just the macro called, the number of macro and the copy range.
public static string Calculation1(String InputFileName, String CurDir)
{
//The Excel calculation file and its path
string filename = "CalculationFile.xls";
string path_server = ExcelCtrl.DossierCalcs + filename;
ExcelCtrl excel = new ExcelCtrl();
excel.excel_init(CurDir + "\\" + InputFileName, ExcelCtrl.DossierCalcs + filename, "A1:Z105");
//excel.excel_run("Macro1");
//The rest is not important
path_server = CurDir + "\\Resultats - " + InputFileName;
excel.excel_run("AnotherMacro", path_server);
excel.excel_close();
return path_server;
}
Now all work fine, except one calculation (the one above). I am unable to copy data from the user worksheet.
I have tested to :
(normal usage) upload an Excel file with some new data and when I download the file after calculation, it is still the old data.
reduce the range of copy ( I though maybe it is too big) to "A1:Z50" then "A1:A2". However, I have another calculation file where I copy a range of "A1:M730" and it works fine.
copy it manually with Ctrl + C; Ctrl + V (I thought maybe the worksheet is protected from modification or something)
With the first 2 tests, I still got old data (not the user data) and the last one works, I can paste user data; though not very useful if someone need to do it manually everytime an user ask for this calculation.
I hope this is enough for you to understand my problem and hope you can guide me.
Thanks
EDIT
Hello,
Just to clarify a little bit about what I am doing. I have an web app where user can download an Excel xlsx file and they have to fill in with their data. After that, They have to upload their file to the server and choose the calculation they want to run. On the server side, I copy their data (see code above) to the corresponding Excel calculation file. So for each calculation I have one input file for the user and one calculation file to run. In total I have 14 input files and 14 calculations files and 13 of them work fine.
I have done some more tests, the following tests do not copy my data :
(And I am using Excel 2010)
Changing the name of my destination Excel file to something more common because there were a "#" in the file name.
Changing the name of my destination Excel file to one another destination file where the copy works.
Changing the worksheet color (I really don't believe that will solve my problem but I have nothing to lose)
Deleting worksheets from my destination file one by one
And the following tests work (data are copied to the destination file) :
Copying to a new Excel file
Copying to an other Excel calculation file (let's say file B) where I know the copy works
My thought for the last case is because when I open my problematic Excel file, 2 warnings come up. The first one says that the workbook contains link to other data sources and ask me if I want to update it (even after updating, it keeps asking me everytime I open it) and the second one says that the workbook contains links that can not be updated.I have also this 2 warnings when I open file B but with this file the copy works.
So I have tried to delete all links in the file and it does not work either. Futhermore, those links are used in my 13 others files.
I am kind of lost now since I have deleted all worksheet and links from my Excel file and it still won't copy. The only difference I see between my problematic file and a new file is that the former contain Macro.

open specific word doc. depending on user selection c#

i currently have a word doc. file merging program and am attempting to open a specific word doc. depending on user selection.
// sample of code used:
string outputFolder = null;
...
// file selection
...
string outcomeFolder = outputFolder;
string outputFile = "Combined Folder " + fileDate + " # " + fileTime + ".docx";
string outputFileName = Path.Combine(outcomeFolder, outputFile);
in the program, outputFolder is selected by the user via a fileBrowserDialog
currently, the program runs correctly and merges the files in the folder selected by the user however it fails to open Microsoft Word as well as the outcome document merged.
i've attempted to use:
Microsoft.Office.Interop.Word.Application officeApp =
new Microsoft.Office.Interop.Word.Application();
...
// merging code
...
Document documentTest = officeApp.Documents.Open(outputFileName);
i've noticed that though the program fails to launch Word, Task Manager continues to create a new instance of Word. The merged document formed also cannot be deleted as it claims the file is currently in use. It's as if program it's opening in the background however not physically launching. The Task Manager instance of Word must then be killed before the merged file can be edited / deleted
Any suggestions as to remedy this? Am i missing something simplistic or is the issue due to the non-static file path? - if any additional information is required please ask. thank you
Update 1:
Since implementing the officeApp.Visible = true; the program now launches the file created which can then be edited / re-saved etc. However if i immediately run the program again, attempting to create another merged file within the same folder etc. I am presented with "RPC server is unavailable. (Exception from HRESULT: 0x800706BA)"
Update 2:
As listed above, I was getting a generic HRESULT error code which I have now since remedied. I moved the "new officeApp" into the "Merge" handler which seems to be allowing multiple merges in quick succession without throwing errors.
Update 3:
To make things more simplistic, i've experimented with implementing Process.Start(outputFileName); to open the document. This is due to the additional check box I am now introducing which allows the user to decide whether the merged doc. will be launched / presented once created. This new code also prevents the additional Word.exe's from being created if the file visibility is set to false.
thank you all for your suggestions and help.
Have you tried making Word visible?
officeApp.Visible = true;

How to connect to an Excel workbook that lives in SharePoint 2013 and update it

I am creating a command line application and I would like to connect to a SharePoint site that has a workbook that I want to update.
I don't even know where to start but I guess the questions I have are.
What API can I use for this? It has to be something that is already built-in and not a third party API if possible :)
Once the connection is done and I am able to open the spreadsheet, will I be able to also switch from one sheet to another?
I've tried to use some code like this but it did not work.
private static void GetExcelSheetNames(string filename)
{
var xls = new Microsoft.Office.Interop.Excel.Application();
xls.Visible = true;
xls.DisplayAlerts = false;
var workbook = xls.Workbooks.Open(Filename: filename, IgnoreReadOnlyRecommended: true, ReadOnly: false);
try
{
// Refresh the data from data connections
workbook.RefreshAll();
// Wait for the refresh occurs - *wish there was a better way than this.
System.Threading.Thread.Sleep(5000);
// Save the workbook back again
workbook.SaveAs(Filename: filename); // This is when the Exception is thrown
// Close the workbook
workbook.Close(SaveChanges: false);
}
catch (Exception ex)
{
//Exception message is "Cannot save as that name. Document was opened as read-only."
}
finally
{
xls.Application.Quit();
xls = null;
}
}
The Exception that I get was:
Microsoft Excel cannot access the file 'https//url/bla/bla/bla/workbook.xlsx There are several possible reasons:
The file name or path does not exist.
The file is being used by another program.
The workbook you are trying to save has the same name as a currently open workbook.
Actually, it seems like it is trying to open an instance of Excel Desktop in my machine.
Sorry for such an open question but I don't really know how can I connect to a SharePoint site.
Thanks in advance!
If you're using a document in a document library, check to make sure that it is checking out the document before you try to save it. I am working with a similar process but using a report library instead of a document library and am able to save documents without check out/in.

Categories

Resources