I am using Excel Interop. In the beginning of a method I got, I'm allocating a new instance of the application and at the end of it I'm trying to free it, but when I look at the TaskManager I can still see Excel open.
This is the code:
A class member: private Excel.Application _app;
The usage:
public void MethodApp()
{
_app = new Excel.Application();
....
....
FreeApplicationResources();
}
private void FreeApplicationResources()
{
_app.Quit();
Marshal.ReleaseComObject(_app);
}
MethodApp can run several times and it opens instances at the same quantity as the number of times it's called.
Why won't Excel close?
Try releasing any worksheets and workbooks used also in the order below:
Marshal.ReleaseComObject(_worksheet);
Marshal.ReleaseComObject(_workbook);
Marshal.ReleaseComObject(_app);
Related
I have a Windows form app project (C#).
I am trying unsuccessfully to write code so that when I click a button I created, it will load an excel file that actually has macros in it and delete them all.
I know you can delete macros manually in the excel file itself but I need a way to do it programmatically (I just delete them all for the user).
I know that the macros in Excel files are written in VBA language, so I try to use the related libraries in c# but I get an error when I try to use VBProject, VBComponent.
This what I tried so far:
using Microsoft.Office.Interop.Excel;
namespace MacroRemover
{
public partial class Main : Form
{
private void Btn_Click(object sender, EventArgs e)
{
string filePath = "path\\to\\file.xlsm";
Application excel = new Application();
Workbook workbook = excel.Workbooks.Open(filePath);
VBProject vbProject = workbook.VBProject;
foreach (VBComponent component in vbProject.VBComponents)
{
vbProject.VBComponents.Remove(component);
}
workbook.Save();
workbook.Close();
excel.Quit();
}
}
}
Any way that works will help me, I would appreciate the help
Thanks in advance guys!!!
If you don't need to save xlsm format of the file, you can save this file to xlsx format and all scripts will be removed.
using Aspose.Cells;
var workbook = new Workbook("input.xlsm");
workbook.Save("Output.xlsx");
Thanks for all the replies, #tttony's response helped me to understand more deeply and #Bushuev's response here is definitely a possible and simple solution for deleting the macros.
I finally managed to delete all the macros like this:
string filePath = Path_TxtBox.Text;
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
Workbook workbook = excel.Workbooks.Open(filePath);
VBProject project = workbook.VBProject;
for (int i = project.VBComponents.Count; i >= 1; i--)
{
VBComponent component = project.VBComponents.Item(i);
try
{
project.VBComponents.Remove(component);
}
catch (ArgumentException)
{
continue;
}
}
for (int i = project.VBComponents.Count; i >= 1; i--)
{
VBComponent component = project.VBComponents.Item(i);
component.CodeModule.DeleteLines(1, component.CodeModule.CountOfLines);
}
workbook.Save();
workbook.Close();
excel.Quit();
MessageBox.Show("Macros Removed");
It should only be noted that I encountered an error: 'Programmatic access to Visual Basic Project is not trusted'
It was solved after I realized that I had to change the option to access the VBA Project object model in the Trust Center settings.
So basically my app triggers an excel macro, from a file, that updates the file and then closes it.
When I open the file I set the "DisplayAlerts = false" variable in order to ignore all popups and it works as expected in my computer... however, a colleague of mine tried to use it and for every file, he gets the popup asking if he wants to save all changes...
Checked other questions about the popups in excel but all suggested solutions use "oBook.Saved = true;" or "oBook.Close(false);", but these did not work for me.
my code is as follows:
using Microsoft.Office.Interop.Excel;
public static bool Trigger_Macro_From_File(string path)
{
ApplicationClass oExcel = null;
Workbook oBook = null;
try
{
string filename = Path.GetFileName(path);
string macro_name = "!some_macro";
string macro = #"'" + filename + #"'" + macro_name;
// Create an instance of Microsoft Excel
oExcel = new ApplicationClass
{
DisplayAlerts = false,
Visible = false
};
oBook = oExcel.Workbooks.Open(path);
RunMacro(oExcel, new Object[] { macro });
oBook.Save();
oBook.Saved = true;
return true;
}
catch (Exception ex)
{
return false;
}
finally
{
oBook?.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook);
oBook = null;
oExcel?.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel);
oExcel = null;
GC.Collect();
}
}
Does anyone know anything about this?
Thanks in advance.
You could double-check that no other "Microsoft Excel" process is running in the Task Manager.
Let's say at some point in your development process you started your program and open the workbook with something like
xlWorkbook = xlApp.Workbooks.Open(filePath);
Then you encountered an exception for some reason, and killed the program without closing the file properly (workbook.Close(..), app.Quit(..) and so on).
The Microsoft Excel process is still running in the background, and has a handle on the file you want to edit. So you cannot execute an instruction that saves the file under the same name. This is why the popup is appearing.
This scenario is taken from the point of view of the developer, but the same behavior could have happened on your coworker's computer if your app crashed without quitting properly, and gets re-started.
Also, be careful that finally statement might not always be executed, so double-check which scenario could cause your app to close without releasing the COM object.
I am having trouble with an application that uses Excel interop. With Excel 2010 and 2013 it works perfectly fine. Unfortunately it no longers works for my colleague now that he has upgraded to Office 2016. I will also upgrade very soon and need to find a solution.
What is being done?
A click event fires a background worker...
private void btnPrep_Click(object sender, EventArgs e)
{
lblWorking.Visible = true;
pBar1.Visible = true;
//...
//...
bwPrep.RunWorkerAsync(args);
}
... which in turn calls a method "Xls2Xml"...
private void bwPrep_DoWork(object sender, DoWorkEventArgs e)
{
//...
//...
foreach (var x in y)
{
var fils = di.GetFiles();
if (fils.Any(f => f.Name.Contains(".xls")))
{
bwPrep.ReportProgress(0, lang);
Xls2Xml(fils.First(f => f.Name.Contains(".xls")).FullName, dest);
}
}
}
... in which I use interop:
private void Xls2Xml(string xlsPath, string destination)
{
var pfad = xlsPath;
var xlapp = new XL.Application();
var wbooks = xlapp.Workbooks;
var wb = wbooks.Open(pfad);
var sheets = wb.Sheets;
XL.Worksheet sheet = sheets[1];
//...
//...
Marshal.ReleaseComObject(sheet);
Marshal.ReleaseComObject(sheets);
Marshal.ReleaseComObject(wb);
Marshal.ReleaseComObject(wbooks);
Marshal.ReleaseComObject(xlapp);
}
This works fine with Excel 2010 and 2013. Excel 2016 causes a COM error. First, it was RPC_E_SERVERCALL_RETRYLATER ("application busy").
Googling gave me a recommendation to add this line before calling the background worker:
System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
What this did was to change the resulting error into "Call was rejected by callee" (RPC_E_CALL_REJECTED).
I made sure that the correct versions of Excel interop and Office library are referenced (1.9 / 16).
The error occurs on the line var xlapp = new XL.Application();, no other instance of Excel application is created before this line. Excel is properly installed and activated, a repair installation did not solve the issue either.
Have any of you come across similar issues?
Solved.
It turned out to be something my colleague conveniently forgot to mention because he completely forgot about it: the culprit was the Kutools Add-In.
Although it was installed before and hadn't caused problems back then, one of the latest Office updates must have changed some behavior. Deactivating Kutools was all it took.
As what I described in title, how to insert a picture as a comment into Excel using C#?
Please offer a sample code or some reference documents.
Following is my code:
using Excel=MicroSoft.Office.Interop.Excel;
publice void ExcelEdit(string Path)
{
Excel.Application xlApp;
Excel.WorkBook xlWorkBook;
Excel.WorkSheet xlWorkSheet;
Excel.Range myRange;
xlApp=new Excel.ApplicationClass();
xlWorkBook=xlApp.WorkBooks.Open(Path, misValue, misValue,misValue, misValue,misValue, misValue,misValue, misValue,misValue, misValue,misValue, misValue,misValue, misValue)
xlApp.Visable=True;
xlWorkSheet=(Excel.WorkSheet)xlWorkBook.Sheets.get_Item(1);
myRange=WorkSheet.Range[WorkSheet.Cells[1,1],WorkSheet.Cells[1,1]);
xlWorkSheet.Cells[1,1]=InstertPictureComment(myRange,Path);
myRange=WorkSheet.Range[WorkSheet.Cells[1,2],WorkSheet.Cells[1,2]);
xlWorkSheet.Cells[1,1]=InstertPictureComment(myRange, Path);
}
public void InstertPictureComment(Excel.Range myrange, string picturepath)
{
myrange.ClearComment();
myrange.AddComment();
myrange.Comment.Shape.Fill.UserPicture(picturepath);
myrange.Comment.Shape.Width=400;
myrange.Comment.Shapes.Height=300;
}
I can successfully insert picture comments into excel. The problem is that: when I copy and paste the cells which I have just inserted comments, save the excel, and close it. The next time When I open the excel, messagebox shows "unreadable content in xxx has found. "
What to do with my code!!
The question seems to be repeat of Insert picture comment with C# Ok while copy content with comments fail
I just now posted an answer there. Copying the same answer here.
I have corrected the code so that it compiles
public void InstertPictureComment(Excel.Range myrange, string picturepath)
{
myrange.Cells.ClearComments();
myrange.AddComment();
myrange.Comment.Shape.Fill.UserPicture(picturepath);
myrange.Comment.Shape.Width = 400;
myrange.Comment.Shape.Height = 300;
}
Part of the problem is with Excel. With your code, you are probably creating a new application instance of Excel. Excel is unable to copy objects across the application instances.
If you open another workbook in the same application instance, the objects will get copied. The only way to copy data across application instances is using Paste Special functionality.
You should fetch the existing Excel application instance. If it is not there, then you may create it.
private Excel.Application GetExcelInstance()
{
Excel.Application instance = null;
try
{
instance = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
}
catch (System.Runtime.InteropServices.COMException ex)
{
instance = new Excel.Application();
appCreatedExcelInstance = true;
}
return instance;
}
You may use the appCreatedExcelInstance flag to decide whether or not to quit the instance during cleanup.
I hope this helps.
When i started my app and opened word file i see 3 process winword.exe in taskmanager.
After i called 'close' function 1 process winword.exe closed.
When i called worddoc.close() or wordapp.quit() in destructor, i got exception "COM object that has been separated from its underlying RCW cannot be used."
public class WordHelper
{
private object nullobj = System.Reflection.Missing.Value;
public string context = "";
Microsoft.Office.Interop.Word.Document doc = new Document();
Microsoft.Office.Interop.Word.Application wordApp = new Application();
public WordHelper(string FileName)
{
//Open word file
}
//somefunction fo work with file
public void CloseWord()
{
doc.Close();
wordApp.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wordApp);
}
~WordHelper()
{
//i got exception
doc.Close();
wordApp.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wordApp);
}
}
How i call my class
WordHelper wddoc = new WordHelper("C:\\Test Word\\Test.docx");
wddoc.CloseWord(); //this line i use and can close 1 process not 3
//One process close after i close application
At the end i want close all winword.exe which was opened by my application, and i want to close them in destructor. At the end, i need to close all 'winword.exe' which was opened by my application, and i need to close them in destructor.
You are doing this in a finalizer (different than a destructor). Finalizers are non-deterministic, meaning by the time they run class members may have already been finalized and thus would no longer be valid.
I would implement the Dispose pattern and explicitly control the lifetime of your word COM objects. This answer has a lot of good links that may help you.
//To find out the WINWORD.exe process and set RealTime Priority to WINWORD.exe
//This is used to quick process
Process[] proces = Process.GetProcessesByName("WINWORD");
foreach (Process proc in proces)
{
proc.PriorityClass = ProcessPriorityClass.RealTime;
}