This question has been asked by others several years ago but no answers yet (https://go4answers.webhost4life.com/Example/excel-web-browser-control-locks-other-206723.aspx), we have the similar issue and need the solution, so I paste here. Note we are using System.Windows.Forms.WebBrowser, this is different from his question:
background: In order to automate and embed Excel in a windows form application I've used the webBrowser control.
I am able to navigate to the Excel file without a problem.To navigate I am using
System.Windows.Forms.WebBrowser WebBrowserExcel;
this.WebBrowserExcel.Navigate(filename,false);
After navigating to the Excel file, I am querying the running Object table to attach to the workBook and then manipulate the cells in the workbook.
public Workbook GetActiveWorkbook(string xlfile) {
IRunningObjectTable prot=null;
IEnumMoniker pmonkenum=null;
try {
//return m_Workbook;
IntPtr pfetched = IntPtr.Zero;
// Query the running object table (ROT)
if (GetRunningObjectTable(0, out prot) != 0 || prot == null) return null;
prot.EnumRunning(out pmonkenum);
pmonkenum.Reset();
IMoniker[] monikers = new IMoniker[1];
while (pmonkenum.Next(1, monikers, pfetched) == 0)
{
IBindCtx pctx; string filepathname;
CreateBindCtx(0, out pctx);
// Get the name of the file
monikers[0].GetDisplayName(pctx, null, out filepathname);
// Clean up
Marshal.ReleaseComObject(pctx);
// Search for the workbook
if (filepathname.IndexOf(xlfile) != -1)
{
object roval;
// Get a handle on the workbook
prot.GetObject(monikers[0], out roval);
return roval as Workbook;
}
}
} catch {
return null;
} finally {
// Clean up
if(prot!=null) Marshal.ReleaseComObject(prot);
if(pmonkenum!=null) Marshal.ReleaseComObject(pmonkenum);
}
return null;
}
The code works fine and I am able to work with the Excel workbook UNTIL no other workbook is open in the system(another workbook opened by double clicking the file in the local system).
The following is the scenario:
1) I opened a workbook from explorer by double clicking it. Let's call it Excel A. This started an EXCEL.EXE process.
2) I navigated to another Excel workbook from my Windows Form web browser. Let's call it Excel B. Excel B opens in the Form.It uses the already existing EXCEL.EXE started in step 1.
3) Now if I try to edit Excel A(opened in step 1).It does not allow me. The focus is always there on Excel B(navigated in step 2). I cannot edit the cells, select text or even close Excel A.
Please kindly let me know any solution to solve it.
This issue was solved with the hard work of the team and with the help from Microsoft deep support. I share the final solution in Google open source projects https://code.google.com/p/form-based-excel-solution/
Related
I have been asked to write a script to crawl through a load of folder locations and list out all the Excel spreadsheets that have connections to a set of SQL and other data sources that are due to be upgraded to a new server.
In order to do this, I need to open each file, then check the connections and return those that match the criterion set. All this happens fine until I hit any file where the end user has made a macro to run on open that refers to a non-existent file - As the C# script opens the file, the file presents the following message:
If I manually click "End", the script moves on to the next file and all is ok, but I would much rather avoid any user input and record the fact that there was a problem with the macro... How would I go about doing that?
I have set the Excel property "Disable all macros without notification" to true on the computer that will be running the script, using the same username as will run it, which I thought would prevent this kind of thing happening. I also open Excel with DisplayAlerts=false, so that isn't the problem...
I don't need to run the macro at all and would rather not..!
for context, the code snippet that opens each file looks like this:
var app = new Application
{
Visible = false,
DisplayAlerts = false,
ScreenUpdating = false
};
Workbook thisFile = null;
try
{
//send a false password to stop Excel asking for one - when it is wrong, the error will be caught.
thisFile = app.Workbooks.Open(file.FullName, ReadOnly: true, Password: "FakePassword");
foreach (WorkbookConnection connection in thisFile.Connections)
{
EDIT: It occurs to me that maybe I could do something with a timeout..? If there were some way to close the popup box from the script, that would do the job - I could just record that the timer expired in the output, which would be enough. So... alternatively is there a way to just close the box after it has popped up?
I have been able to disable startup macros when I open the workbook by holding down shift when opening the file.
I believe the interop way to handle this is to use the application AutomationSecurity property:
Excel.Application app = new Excel.Application();
app.Visible = true;
app.AutomationSecurity = Microsoft.Office.Core.MsoAutomationSecurity.msoAutomationSecurityForceDisable;
I tested this on a simple workbook that popped up a message box and put the current time in A1, and it seemed to work properly.
Excel.Workbook wb = app.Workbooks.Open("c:/cdh/foot.xlsm");
Default, the message box popped up and A1 had a value, and when I set it to disable neither happened.
I am trying to close an excel file named TestReport.xlsx using the below code. It is working when only one excel process is running but when I have multiple excel windows open, the MainWindowTitle changes and the code is not killing the desired excel process.
Process[] plist = Process.GetProcessesByName("Excel",".");
foreach(Process p in plist)
{
if (p.MainWindowTitle.Contains("TestReport.xlsx") && p.ProcessName == "EXCEL")
{
p.Kill();
}
}
The explanation you are giving in your question is not accurate enough. You must distinguish two cases:
Case 1: You have more than one Excel process each with one workbook. That means that e.g. all your excel workbooks each run in an own hosting excel window. You achieve this when e.g right-clicking on the excel icon, getting an empty excel workbook and loading the workbook you want into that process. In this case your approach works, as every excel process has its own title naming the workbook file name. The requested process is killed when iterating through the excel processes.
Case 2, which is the more "normal" case when working with excel, and to which you probably refer to: You have one excel process hosting more than one workbook, each possibly with several worksheets. In that case Excel acts as you describe and changes its window title (Multiple document interface).
In case 2, when there is only one single excel process present, you can close the workbook in the following way, using COM Interop:
private void button1_Click(object sender, EventArgs e)
{
CloseExcelWorkbook("TestReport.xlsx");
}
//put the following abbreviation to the "using" block: using Excel = Microsoft.Office.Interop.Excel;
internal void CloseExcelWorkbook(string workbookName)
{
try
{
Process[] plist = Process.GetProcessesByName("Excel", ".");
if (plist.Length > 1)
throw new Exception("More than one Excel process running.");
else if (plist.Length == 0)
throw new Exception("No Excel process running.");
Object obj = Marshal.GetActiveObject("Excel.Application");
Excel.Application excelAppl = (Excel.Application)obj;
Excel.Workbooks workbooks = excelAppl.Workbooks;
foreach (Excel.Workbook wkbk in workbooks )
{
if (wkbk.Name == workbookName)
wkbk.Close();
}
//dispose
//workbooks.Close(); //this would close all workbooks
GC.Collect();
GC.WaitForPendingFinalizers();
if (workbooks != null)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(workbooks);
//excelAppl.Quit(); //would close the excel application
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excelAppl);
GC.Collect();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This approach works in case you have only ONE Excel process running. When there are more than one present, the situation is more complicated as you must get access to all Excel processes. For a discussion for that case, see here.
Another point to observe is to release excel objects properly to avoid stale excel objects. See there.
If you omit the "dispose" activities, it may happen that on closing Excel and continuing to run your app the Excel process runs silently on, as an inspection in Task Manager shows.
We have a VSTO addin for Excel. The main functionality creates reports that are used to generate workbooks. When I run a batch of reports, I get a System.AccessViolationException when using Excel.Worksheet.Copy, which also crashes Excel. Here's how I recreate it:
1) Open and run report #1 with a single parameter which creates one workbook. We close the workbook.
2) Open and run the same report with several parameters. This create 5 workbooks but crashes when creating the second, but ONLY if we have run the first single output report (see step 1). If we remove the report from step 1 from the batch, this creates all 5 workbooks without error.
I've checked to make sure that the sheet we are copying is from the workbook is open, and is not referencing the first report. In fact, we close the first one so I know that it's not. Again, this ONLY happens if we have the report in step one, which it does not access at all, so how could that be affecting a sheet from a completely different workbook?
This doesn't even finish out my try/catch so that I can get more info. It simply blows up Excel and I have to restart.
UPDATE:
Here's the basic code:
function void ReplaceSheets(Dictionary<Excel.Worksheet, IReportSheet> sheetReports)
{
List<string> oldNames = new List<string>(sheetReports.Count);
foreach (Excel.Worksheet oldSheet in sheetReports.Keys)
{
Excel.Worksheet veryHiddenSheet = null;
Excel.Worksheet newSheet = null;
try
{
string sheetName = oldSheet.Name;
veryHiddenSheet = WorkbookHelper.FindSheet(this.DocumentView, MakeHiddenSheetName(sheetName, "--VH--"));
veryHiddenSheet.Visible = Excel.XlSheetVisibility.xlSheetVisible; //Sheet has to be visible to get the copy to work correctly.
veryHiddenSheet.Copy(this.DocumentView.Sheets[1], Type.Missing);//This is where it crashes
newSheet = (Excel.Worksheet)this.DocumentView.Sheets[1]; //Get Copied sheet
/* do other stuff here*/
}
finally
{
veryHiddenSheet = null;
newSheet = null;
}
}
}
I never found a way in VSTO to "fix" this. I switched code to NetOffice, and I was able to get some better error message. Excel/Com was not releasing the memory attached to the spreadsheets. I rebuilt the reports from blank 2010 spreadsheets and it took care of it. I think it was a corrupted 2007 spreadsheet that may have occured on converting to 2010 or something like that. I recommend NetOffice over VSTO because the exception handling is far superior, and you have access to the source code, but it does have it's quirks. (You'll need to pay attention to loading order for taskpanes.)
I'm working on a C# project that uses Microsoft.Office.Interop.Excel.Application to read values from an Excel file:
try {
Application xlApp = new Application();
Workbook workbook = xlApp.Workbooks.Open(filename)
// ... Load Excel values ...
} finally {
// ... Tidy up ...
}
In the finally block I'd like to make sure everything is closed and disposed of properly so nothing hangs around in memory and Excel closes cleanly. Have seen various threads about what this code should look like (more complex than I thought!) but one thing it might include is:
if (workbook != null) {
workbook.Close();
// ... possibly also Marshal.FinalReleaseComObject(workbook);
}
However, this throws an error if the workbook is already closed so how can I safely check this? Would prefer not to just catch the error if possible as this type of thing tends to distort debugging. Is there a clean way of finding out the workbook state before closing?
One more q - am wondering if workbook.Close() is needed if xlApp.Quit(); is done afterwards - would quitting the Excel application cause workbook.Close() (and any COM object releasing) to happen implicitly?
As you are opening the workbook the best advice is to keep track of the workbook and close it when appropriate. If your code is quite detailed then you could store a Boolean value indicating whether the file is currently open or closed.
In Excel there is no property such as IsOpen. You could try to reference the workbook:
Workbook wbTest = xlApp.Workbooks.get_Item("some.xlsx");
but this creates a COM error if the book is not open, so gets quite messy.
Instead, create your own function IsOpen that returns a boolean and loops through the currently open workbooks (the Workbooks collection) checking the name, using code like this:
foreach (_Workbook wb in xlApp.Workbooks)
{
Console.WriteLine(wb.Name);
}
workbook.Close() would not be necessary if the workbook has been saved - reflecting the normal behaviour of Excel. However, all Excel object references need to be released. As you have discovered, this is a little fiddly, and Close and Quit do not achieve this on their own.
static bool IsOpen(string book)
{
foreach (_Workbook wb in xlApp.Workbooks)
{
if (wb.Name.Contains(book))
{
return true;
}
}
return false;
}
Im just starting to use EPPLus Lib to create "complex" workbooks via C#, and i just ran into some trouble while trying to create two pivot tables.
The first one creates fine, but when i try to create the second one it doesnt throw any exceptions but when i try to open the worknook using excel it says
"Excel found unreadable content in 'myworkbook.xlsx'. Do you want to
recover the contents of this workbook? If you trust the source of this
workbook, clickYes"
And when i press 'yes':
Repair log ->
Removed Feature: PivotTable report from /xl/pivotTables/pivotTable2.xml part (PivotTable > view) Removed
Records: Workbook properties from /xl/workbook.xml part (Workbook)
Repaired Records: Workbook properties from /xl/workbook.xml part
(Workbook)
Here's the code that i build:
CreatePivotTable("Pivot1", "Pivot1", rng1);
CreatePivotTable("Pivot2", "Pivot2", rng2);
public void CreatePivotTable(string pivotSheet, string pivotName, ExcelRangeBase srcRange)
{
if (m_wb.Worksheets[pivotSheet] != null)
m_wb.Worksheets.Delete(pivotSheet);
var ws = m_wb.Worksheets.Add(pivotSheet);
var pivot = ws.PivotTables.Add(ws.Cells["A1"], srcRange, pivotName);
}
Any ideas?
Thanks!
What was wrong and i didnt put it in my question was that i was reopening the workbook on step before, like this:
CreatePivotTable("Pivot1", "Pivot1", rng1);
Save();
CreatePivotTable("Pivot2", "Pivot2", rng2);
private void Save()
{
m_writer.Save();
m_writer.OpenWorkbook ();
}
And since the save method of epplus closes the workbook, the program lost some sort of reference or just got lost with some info.
In short, to use epplus correctly, you should write everything u need before saving and closing the workbook, its not good to reopen.
Thank you.