I have written a C# program which import a product list from a .xlsx file and let the user create an order based on that product list.
When the user is finished, the program builds one or more system specifications based on the order.These specifications is written to a .docx file. I have Office 2007 installed on the computer and are using the Microsoft.Office.Interop.Excel and the Microsoft.Office.Interop.Word namespaces.
The problem:
After I have runned the program, Windows Explorer crashes very often and has to restart. This happens when navigating in folders or when right- clicking on folders etc.
This also happens after the program have been closed and the only solution to make it stop is to restart the computer. It seems like it only happens when I have created the output files (.docx). If i start the program and use it like I normally do, but without creating the word files, the problem don't seem to occur.
After the program have created the output files, Word gets "Visible" to the user for manual editing. The user closes the word application when finished editing the documents.
What can make the Windows Explorer crash when running word automation?
I really need help on this one. Any suggestions are welcome.
After execution, do you have ghost excel.exe and word.exe processes remaining?
These ghost are likely to make the system unstable.
You're likely not releasing properly the COM objects you instantiated via automation.
Use Marshal.ReleaseComObject(yourobj); on each and every COM objects you instantiate. It's a real pain, I know.
Note: be sure that you don't instantiate COM objects without knowing it:
mySheet = myExcelObject.workbooks[0].Sheet[0] won't just instantiate a sheet object, but also a workbook object.
Rule of thumb: never ever use a secondary property on a COM object ( foo.bar.baz ) and release everything.
Final note: don't use office automation at all on the server, it's bad, per Microsoft own words, there are fully managed libraries for that.
Related
I'm looking for some help. I am in need of figuring out how to get an object reference to Microsoft Access 2013 so that I can (through Automation) call some of the already defined functions in the accdb. For instance; I want to automate the process of "RelinkODBCTables" function which repoints the linked tables to another data source from my .net core 3.0 c# application.
I've not been able to successfully get a reference to interop but I may not be doing it correctly.
Any help would be greatly appreciated.
D-
If you want to
Create a instnce of Access.
Call a VBA sub (say your relink code).
Close the the database.
quit access.
You could use this code:
{
object AccessApp;
AccessApp = Interaction.CreateObject("Access.Application");
AccessApp.OpenCurrentDatabase(#"c:\test\test44.accdb");
AccessApp.Run("MyLinker");
AccessApp.CloseCurrentDatabase();
AccessApp.Quit();
}
So, you don't need any referance at all. Just create a instance of the given applicatin (word, Excel or as per above Access).
At that point, you have full use of the object model, and can use run to call some VBA routine. In above, we call a VBA Sub called MyRelinker.
About the only caution here is that when you create the instance of that object, then all startup code of the application will run. So, if on startup the forms and UI that the developer of the Access program launches any prompt, then you can't "answer" that prompt. So, how well this works will VERY much depend on how nice the application plays on startup, and that calling any of those VBA routines does not trigger some kind of prompt(s) in that Access application. If it does, then you in trouble, since you can't "answer" any of the forms or code prompts that access may very well throw up at the end user.
And, if you do want some "inteli-sense" during coding, then you can add a office "interop" reference to your project. Its not required, but if you not really fluent in the Access VBA + object model, then in place of CreateObject("Access.Application"), if you do referance the office assemby, say this one:
Microsoft.Office.Interop.Access
C:\Program Files (x86)\Microsoft Visual Studio 12.0\
Visual Studio Tools for Office\PIA\Office14\
Microsoft.Office.Interop.Access.dll
Then you code becomes this:
{
Microsoft.Office.Interop.Access.Application AccessApp =
new Microsoft.Office.Interop.Access.Application();
AccessApp.OpenCurrentDatabase(#"c:\test\test44.accdb");
AccessApp.Run("MyLinker");
AccessApp.CloseCurrentDatabase();
AccessApp.Quit();
}
However, while you get stronger typing with the reference, you often will "tie" you code to a given version of Access, and a simple use of CreateObject() quite much means that you can create a instance of Any Access installed on the target computer, and it should work going back all the way to office 2000 - a good 20 years of "coverage".
Keep in mind that you CAN NOT create a instance of the Access runtime, the target computer will requite a full version to "create" a instance of the Access.Application object.
You "can" automate the runtime version. This involves launching Access runtime (via Shell()), and then grabbing an instance with "GetObject()" in place of CreateObject().
Edit
I should point out that in the 2nd above code example, and using the office interop-assembly reference, I choose office 14, which is office 2010. In your case, you have to use office 15 (2013).
There are several threads on SO that describe how to check which application creates a file with tools like Sysinternals process monitor. Is something like this possible programmatically from .net?
Background: My program has to remote-control a proprietary third party application using its automation interface, and one of the functions I need from this application has a bug where it creates a bunch of temporary files in %TEMP% that are called tmpXXXX.tmp (the same as .net's Path.GetTempFileName() does) but does not delete them. This causes the C drive to become full over time, eventually failing the application. I already filed a bug to the manufacturer, but we need a temporary workaround for the time being, so I thought of putting a FileSystemWatcher on %TEMP% that watches tmp*.tmp, collects these files, and after the operation on the third-party application finishes, deletes them. But this is risky as another application might also write files with the same file name pattern to %TEMP% so I only want to delete those created by NastyBuggyThirdPartyApplication.exe.
Is this anyhow possible?
This kind of things is possible, but maybe a bit tricky.
To know who created the file, look at the user that owns it. Therefore you might need to create a specific user, and that application will run under this specific user. In order to do that, you need to create a small application that will start your buggy app by impersonating another user, so anything done within the app will be under this user so as file creating...
I don't know how to monitor and get triggered when a file is created, but nothing can prevent you from setting a timer that wakes up every five or ten minutes, then checks if any file in the directory is owned by the application user and closed, so it deletes it.
Maybe if they react fast for this bug fixing, you won't need your app very long time. So another solution, if possible might just to change the Temp folder into another drive, which has lots of space...
One solution is that you use a FileWatcher to automatically delete all the files but before deleting you should check if the file is not currently locked or used by other process, for example the Sysinternal Suite has a tool called handle.exe that can do this. Use it from the command line:
handle.exe -a
You can invoke this from a c# program (there might be some performance issues though)
So what you would do is when a file is created you verify if it is in use or locked (for example u can use the code provided in Is there a way to check if a file is in use?) and then delete it.
Most of the time when an app is using a temp file it will lock it to prevent just what you fear, that you might delete files from other processes.
As far as I can tell there is no sure way to identify which process created a specific file.
My problem is my program crashes at run when it is opened by a computer that does not have office installed.
I am writing a program where that will be run by multiple users on different computers. Some users computers will have MS office installed, others will not. It is a multi-form program and for ease of reference can be thought of like an ERP type software.
I have included Microsoft.Office.Interop.Excel and Microsoft.Office.Interop.Outlook in the program references. Certain sub forms within my application use both excel and outlook functionality, however many do not.
I would like both users who have and do not have office installed to be able to run the program. Perhaps if they do not have office installed they receive an error stating that they do not if they were to click a button that would normally export an excel spreadsheet of data.
Is there any way to achieve this?
I would like to avoid third party libraries, having to install office on all computers as it will not be needed on those workstations, I would also like to manage one program and not have two separate programs to manage which would share forms and functionality.
With the restrictions mentioned - no third party libraries, no office on some user machines, and no 2 versions of the program // Read the Excel files as datasource into the program and deal with the data that way. For where you mentioned "export an Excel Spreadsheet" you can use ODF/ODS.
I setup a new form and tested out what specifically in my application was causing my program to crash. I found out that including the office references does not cause a problem. Also adding using Microrosoft.Office.Interop.Excel; does not cause a problem.
However I was calling a Class in my main program that that created Excel application's, workbooks and worksheets, despite not actually using those items on program load. An example of a local variable in the class that likely caused the problem was private Excel.Application XLApp = new Excel.Application();.
I will probably use a solution like #Jcl suggested before allowing a user to run anything that may crash the program on them.
This question already has answers here:
Why does rename a loaded .net assembly work?
(3 answers)
Closed 5 years ago.
We are trying to push updates to multiple servers at once and my manager has found that it is possible to rename running .exe file. Using that knowledge he wants to rename a running exe and copy over a new version of said exe such that anyone running their in memory copy of foo.exe are fine and anybody who opens a shortcut pointing to foo.exe will get a new copy with updates applied.
I guess I need to clarify, He doesn't expect the old copy to magically update, he just expects them to keep running the old copy until they open the exe again, in which case it will then open the new one that has the name of the old one.
It sometimes throws an exception that the file is in use on his program but if he tries renaming it in a loop it will eventually succeed. On my machine I have yet to be able to get it to work even in a loop.
My first and main question is this: Is it ever acceptable to do this. Should renaming a running executable ever be a valid scenario?
Secondly, if it is a valid scenario then how could one reliably do this? Our current thoughts are try a bunch of times using File.Move (C#) to do a rename and if it doesn't work then write out to an error log so it can be handled manually.
An airplane mechanic and a surgeon meet in a bar. The mechanic says "you know, we have basically the same job. We take broken stuff out and put new, better parts in." The surgeon says "yeah, but you don't have to keep the plane flying as you're making the repairs!"
Trying to update an application by moving files while the application is running seems about as dangerous as trying to fix an airplane in flight. Possible? Sure. Greatly increased risk of catestrophic crash? Yep.
If the application you are updating is a managed application, consider using ClickOnce Deployment. That way, the next time someone runs the application, if there is a new version available it will be copied down and installed automatically. That's much more safe and pleasant than trying to mess with an application while its still running.
No, this is not acceptable. Do not do this. This is not a valid deployment mechanism. This should have been yours or his first clue:
It sometimes throws an exception that the file is in use on his program but if he tries renaming it in a loop it will eventually succeed.
And it won't work, anyway. His theory is quite wrong:
Using that knowledge he wants to rename a running exe and copy over a new version of said exe such that anyone running their in memory copy of foo.exe are fine and anybody who opens a shortcut pointing to foo.exe will get a new copy with updates applied.
Specifically, the copy in memory will not be automatically replaced with the new executable just because it has the same name. The reason that you're allowed to rename the executable in the first place is because the operating system is not using the file name to find the application. The original executable will still be loaded, and it will remain loaded until you explicitly unload it and load the new, modified executable.
Notice how even modern web browsers like Chrome and Firefox with their super fancy automatic, in the background, no one ever notices that they exist, updaters still have to close and relaunch the application in order to apply the updates.
Don't worry about shooting the messenger here. It's more likely that your customers and your tech support department will shoot you first.
See number 1.
In our organization, we solved the problem of Updates by having two release folders say EXE_A and EXE_B. We also have a release folder called EXE which only has links ALL of which points to either to EXE_A or EXE_B from which the user runs the applications.
When we publish a new version of the program, we publish it to the folder that is not referenced in the links and then update the links (EXE). In this way, you do not get into exceptions that users are holding the application / assemblies. Also if a user wants to run the updated version, all he need to do is close / re-execute the link in EXE folder.
If you use Windows Vista/Server2k8 or newer you could use mklink to create a symbolic link to the folder containing your application and start the application out of the "symblic linked folder" and then at the update create a new folder, e.g. "AppV2" and change the SymLink to that folder, so the next time the user restarts the application he starts it out of the new folder without noticing.
Renaming open files is ALWAYS a bad choice!
But in general I would think of a better deployment strategy anyway, because if you need to use such "hacks" it is always a messy situation. I don't know your application, but maybee ClickOnce would be a point to start, because you can configure it to check for updates on every start...
I am currently hosting an IE Browser control in a .NET (2.0) Form and using it to load Office files such as Excel and Word thusly:
_ieCtrl.Navigate("C:\\test.xls", False);
The hosting and loading works well except whenever I navigate to a file I am presented with a dialog that asks whether I want to save or open the file. (This is standard IE file-download behavior.) I always want to open it of course and I do not want the dialog to show.
Another issue is that when I close the window that hosts the IE control and the Office doc the document does not close and remains open on disk. This means that subsequent attempts to open the same file via my app or the native office app will fail because of the sharing violation.
Is there a programmatic way of avoiding this dialog and cleaning up resources afterward? I am asking for a programmatic answer because web research has only yielded solutions that entail modifying OS-level settings.
Bounty NOTE:
I am open to any solution to this issue that will allow me to:
Host an Excel spreadsheet inside my application
Work rather transparently (avoid usability issues like the one described above)
Avoid having to make any OS-specific changes that may affect other applications (especially icluding IE)
Is zero additional cost (no licensed 3rd party libs please) Code Project and other open source resources are OK
Not mess around with the DSO Framer ActiveX control, unless a stable version is developed/discovered
Is your intention for the user to be able to work with the Excel file in an Excel-ish way (i.e. columns, rows, formulas, etc.), possibly saving it back? If this is the case, I can't see how you can solve this problem well without relying on COM Interop with the Excel object model or by integrating third-party libraries to work with the Excel sheet. I know you said no paid solutions, but there are some feature-rich 3rd-party controls out there just for working with Excel files within applications.
I noticed in your comment to SLaks that the final product is a "dashboard of sorts". If your intention is to design a a custom dashboard application, have you considered parsing the Excel file(s) to extract the data and then presenting it in a logical manner within your application. This removes the need to directly display and work with the Excel file while still allowing you to work with the data inside that file. If you are trying to get the data outside of the file, here are two approaches among many:
You might consider using the Excel object model and COM interop to read the data from the Excel file into your application. Granted, this includes a dependency on Excel being installed, but it is a possibility. This article has some great code for getting started with reading Excel files in this way.
A better way might be to use a library that doesn't have a dependency on Excel being installed on the local system. This answer suggests using the Excel Data Reader library, available on CodePlex.
I know this answer side-steps your original answer of "hosting MS Office documents in [a] custom app," but in case what you're really interested in is the data inside those Excel files, hopefully this answer will prove helpful.
This is a horrible hack and should only be considered as a last resort: SendKeys.Send("{O}");
http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys%28VS.71%29.aspx
Something similar to
_ieCtrl.Navigate("C:\\test.xls", False);
(code to sleep or wait may be needed here)
SendKeys.Send("{O}");
Basically, you send the "o" key to the dialog so it presses the "open" option. You are simulating a keyboard presses to click the "open" button. It is hackey because
1) you may need to wait in between
calls. If you send the o key before
the dialog is up it will be missed.
Hopefully the navigate call is finished when the dialog pops (dont know behavior of control in c#). You may need to experiment with the time since different computers will open faster\slower
2) If the dialog is not shown on a
computer, you will be inserting "o"s
into it. This may cause problems when
exiting because it may popup another dialog to try and save
the changes. May be able to prevent this by opening it in read-only mode
3) Different versions or windows may need different sendkeys commands. For example, you may need to send "o" and them the "{enter}" key
4) Probably more :)
If you want to open the file in a separate Excel instance (not embedded in the WebBrowser control), you can simply call
Process.Start(#"C:\Test.xls");
Office was never meant to run in embedded mode, not in a web page or in an ActiveX Document host. Microsoft had time and time again given us the warning. From pulling dsoframer from the knowledge base to skipping the BrowserFlags registry key in Office 2007.
Move to Office add-ins, Excel Web Access or Office Web Apps as quickly as you can.