I have a C# console application that runs many Excel macros.
Each macro makes use of a shared read-only xlsm file that contains many helper functions "helperroutinesnetwork.xlsm". It is effectively a factory for lots of shared vba functionality.
If the C# console crashes irretrievably, when a workbook plus helperroutinesnetwork are open we are finding that the helper workbook gets effectively blacklisted and added to Excel's disabled books.
What this means is that any subsequent macros that need the helper book cannot find or use it until we manually move it out of the Disable list like so:
Is it possible to either:
Write a script that detects if it has been added to the disabled list and even to enable it?
Ensure it is always protected and never allowed to be added to the Disabled add-ins list?
Related
I have a set of Visio drawing files that contain VBA code that is no longer needed (in fact, errors occur when trying to open a file as the module referenced in the VBA no longer exists).
Due to the nature of the add-on I'm working on, my users must keep Macros and VBA enabled. However, any file they open in Visio needs to have its VBA code automatically removed first.
I can hit a breakpoint in my code when the Visio application is launched, but before a file is selected to Open. I can also hit a breakpoint in my code after the file is selected and loaded in Visio. I cannot seem to find a way to get a break after the file is selected, but before Visio loads it.
Looking at the Visio Interop assemblies in C#, I can see Visio.Application events called "OnDocumentOpened" and "OnDocumentCreated", but they aren't triggered until 'after' the file has been loaded. I couldn't find anything called "BeforeDocumentOpened" or "BeforeDocumentCreated" unfortunately, which is basically exactly what I need. There are other events with similar names like "BeforeDocumentSave" and "BeforeDocumentClose", but obviously they are not what I'm looking for.
Any help would be greatly appreciated.
My Question: Are there any methods to capture when Visio attempts to open a drawing file but before it's actually opened? This is where I intend to remove the VBA code from the file, which I've already implemented. If not, any known workarounds to achieving something similar?
Example of the bad VBA I want to remove:
Private Sub Document_DocumentCreated(ByVal doc As Visio.Document)
Call Initialize(doc)
End Sub
Private Sub Document_DocumentOpened(ByVal doc As Visio.Document)
Call Initialize(doc)
End Sub
Private Sub Initialize(doc As Visio.Document)
Dim myLib As Object
Set myLib = CreateObject("MODULE_NAME") 'Errors here, since we got rid of the module
...
End Sub
I would not overdo it. You can upgrade your users. Like, provide a script (or some sort of functionality in the installer, or some button in the application) to update the diagrams. I.e. a separate function, just don't do it on opening the diagram. Since it is one-time operation, the users probably won't cry about that. And you won't add "garbage" one-time code to the application...
Just to be clear: the event you are looking for "before document opened" does not exist.
One other tip regarding VBA macros in Visio: put the code in a stencil. When you have code in drawings, it gets proliferated in every drawing that is created. If you need to change the code, then you have to find all of the drawings and change all of the code.
Instead, you can put macros in a stencil. The stencil can be put in the user's Favorites folder, so the stencil will be easily accessible from any drawing (Shapes > More Shapes > My Shapes > Favorites, usually in the panel docked on the left).
The stencil doesn't have to contain any master shapes, it can just serve as a "code library" that users can load for any drawing. But it is a separate file. If you need to update the macros, you can then distribute a new stencil and have users overwrite the old one.
Now all of your drawing files will be free of code, and you won't have to update potentially thousands of VBA projects every time a change is required.
I have a C# ExcelDna XLL function library that I register during startup from a VSTO add-in.
this.Application.RegisterXLL(xllPath);
When I shell execute an Excel file (Process.Start the .xlsx file) then most of the time everything works and the functions evaluate when the workbook opens.
When opening the workbook in this manner Excel reuses a currently running EXCEL.EXE process if one exists. Most of the time this is fine, but under certain conditions, for example if Excel was opened through COM and then closed, then when the Excel instance is closed, it doesn't really close, but instead shuts down all of its add-ins and unregisters all XLLs but remains alive. When process invoking into one of these zombie processes the functions, obviously, no longer evaluate.
To attempt to get around this I have tried to open Excel directly, using the .xlsx file as a command line parameter, but in this case there seems to be some kind of race condition and the workbook opens before the XLL has finished registering and the functions always evaluate as #NAME. If the cell is modified and reevaluated then the function correctly evaluates. Calling Application.CalculateFull() and all possible variations has no effect.
If I open a file via Explorer (i.e. double clicking on it) then strangely it now returns #N\A rather than #NAME but still it is the same problem.
I've even tried registering the XLL so it loads on start-up (see here) and it still doesn't work.
Has anyone else encountered this and found a reliable way to get XLL functions to evaluate when opening an Excel instance?
We've found that, if the Add-In isn't loaded properly and the functions don't calculate, performing a global search and replace (e.g. search for = replace with =) forces a recalc when the regular recalc doesn't work. You could potentially automate this with VBA Application.OnTime
This question already has answers here:
How to inject VBA code into Excel .xlsm without using Interop?
(2 answers)
Closed 7 years ago.
Scope:
I am generating automated reports using excel, but since we have restrictions on our server, we can't install Excel.Interop or use any COM object to perform such action.
We have been using EPPLUS as our main helper on this task but it has some serious restrictions when it comes to more intricated things such as PivotCharts and Macros.
We have just finished the project, missing only the PivotCharts since EPPLUS has no support for them.
Question:
How can we :
A) Write Macros to a .xlsm file? (We could write one named "Auto_Open" so that excel would run it uppon opening, creating the charts and stuff)
OR
B) Run a macro within our code to generate those charts after putting all the needed data there?
Not using Excel.Interop is a must at this moment. We are open for some suggestions such as
SpreadsheetGear and EasyXLS, but we can't find any sample or piece of code that actually shows how to Write or Run macros.
Thanks in advance
I realise this question is a bit old, but in the interest of helping future visitors I'll add that the current version of EPPlus (4.0.4 at time of writing) supports the inclusion of VBA.
Where excelDoc is your ExcelPackage, you can drop it in using something along the lines of:
excelDoc.Workbook.CreateVBAProject();
StringBuilder vbaCode = new StringBuilder();
vbaCode.AppendLine("Private Sub Workbook_Open()");
vbaCode.AppendLine(" Application.DisplayFormulaBar = False");
vbaCode.AppendLine("End Sub");
excelDoc.Workbook.CodeModule.Code = vbaCode.ToString();
Hope this helps.
Open Excel
Press Alt+F11
You will now be in the VBA IDE.
To run something on Auto Open:
Select ThisWorkbook From the VBAProject in Project Explorer on the Left of your screen
There are Two drop downs near the top of the window one will say (General) and The other (Declarations) click on (General) and select workbook
Excel should automatically bring you to the Open declaration.
Anything written in this sub will execute on open.
Rather than attempting to programatically write macros to an xlsm file, why don't you create a template that already contains the macros you want. Then generate your reports from this template.
When a user opens the report (and allows macros to run), then your macro will run and do the manipultations you want (your option A). Your Auto_Open macro could check some suitable condition (e.g. presence of a value in a specific location, perhaps on a VeryHidden sheet so the user can't easily interfere with it) before doing any work, so that it doesn't run when you open an "empty" template.
As for your option B (run the macro before providing the report to the user): this isn't feasible without Excel Automation.
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.
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.