I have an Excel workbook with a lot going on – macros, external and realtime data sources, etc. – that has been breaking itself roughly once a week for the last month.
The breakage usually manifests itself when running a macro and getting:
Run-time error '-2147319767 (80028029)':
Automation error
Invalid forward reference, or reference to uncompiled type.
The point of failure identified by the debugger never makes sense – the same code has been working for weeks. And the fix I have been using has been to roll back to a saved version of the workbook that didn't throw errors running macros, and it always contains the exact same VBA code that was breaking. So I conclude that something behind the scenes is getting corrupted.
What's going on? Is there a way to avoid this? Is there a way to fix it that's better than rolling back to an earlier saved version of the workbook?
There are plenty of questions regarding this error, and the code changes that fix them never make sense either. The one thing they all have in common is that they make a change to the VBA code that, one deduces, forces Excel to regenerate its pseudo-code.
Further research leads one to frequent mentions of Excel workbook corruption, as well as to a free utility called Excel VBA Code Cleaner. The authors of that utility explain what's going on:
During the process of creating VBA programs a lot of junk code builds
up in your files. If you don't clean your files periodically you will
begin to experience strange problems caused by this extra baggage.
Cleaning a project involves exporting the contents of all its
VBComponents to text files, deleting the components and then importing
the components back from the text files.
Unfortunately they haven't published a version of their utility that works in 64-bit Excel. But one can perform the same thing manually – save all VB code, delete all modules, then recreate them and past the code back in.
UPDATE: VBA Code Decompiler is another freeware utility that appears to accomplish the same thing. There is also a more detailed description of how Office compiles and persists VBA code in its files.
Related
I'm someone who solves problems by looking, not asking. So this is new to me. This has been an issue for years, and it crops up with different computers, networks, versions and completely different code. There is a lot here, so, thank you in advance if you are willing to read the whole thing.
Generally speaking, I write MS Access programs that will open Excel and then create multiple worksheets inside of a workbook using data from Access tables and/or Excel sheets. The process can take a couple of minutes to run and occasionally, it will get an error. I could tell you the error message, but it doesn't matter because it will be different depending where the error occurs. When it occurs I simply click debug and click continue and it... continues. If it errors out again (many loops later), it will happen in the exact same spot.
So, what I start with is to make minor changes to the code. In the current program I'm working on, the error happens when I write to a cell and the value is a value directly from a table. I created a variable, copied the value to the variable and then wrote to the cell. The error moved to a completely different part of the program and it became a "paste" error. Generally what fixes it is to put a wait function at the spot where the error occurs. One second is usually good enough. Sometimes it takes a couple of these, but that usually solves it. It only took one delay per loop this time, so it is working. I just hate causing delays in my program. So... Has anyone seen anything like this before, or is it just me. It feels like a timing issue between Access and Excel since the delays are usually helpful. Thanks in advance.
I dug up my last major Access project that interacted with Word (ca. 2016) where I struggled with similar issues. I see many, many Debug.Print statements (some commented, some still active), but unlike what I recalled earlier in my comments, I don't see any "wait" statements anymore! From what I now recall and after re-inspecting the code, most problems were resolved by
implementing robust error handling and best practices for always closing automation objects (and/or releasing the objects if I wanted the instances to persist)
subscribing to and utilizing appropriate automation object events to detect and handle interaction rather than trying to force everything into serialized work-then-wait code. To do this, I placed all automation code in well-structured classes that declared automation objects WithEvents (in VBA of course) and then defined relevant event handlers for actions I was effecting. I now recall finally being able to avoid weird errors and application hangs, etc.
You also may never get a good answer to a question like this, so despite that I am not an absolute expert on Office development, I have had my own experience with frustrating bugs like this and so I'll share my 2 cents. This may not be satisfying, but after experiencing similar behavior using office automation objects, my general understanding is that interaction between OS processes are not deterministic. Especially since VBA generally has no threading or parallelism concerns, it can be strange to deal with objects that behave in unpredictable ways. The time slices given to each process separately is at the mercy of the OS and it will vary greatly with multiple processors/cores, running processes, memory management, etc. Despite the purpose of the automation objects--to control instances of office apps--the API's are not designed well for inter-application processes.
Although it would be great if old automation code would produces more useful errors, perhaps nested exceptions (like in .Net and other modern environments), something that indicates delays and timeouts within callbacks between automation objects, instead you get hodgepodge of various context errors.
My hardware is old, but still ticking. I often get delays, even if only for a second, when switching between apps, etc. Instead of thinking of it as an error, I just perceive it as a slow machine, just wait and continue. It may be useful to consider these type of random errors as similar delays. If a wait call here or there resolves the issue, however annoying, that may just be the best solution... wait and continue.
Every now and then after debugging these types of issues I would actually discover the underlying problem and be able to fix it. At the least I would be able to avoid actual problems with the data, despite errors being raised, just like you describe. But even when I felt that I understood the problem, the answer was still often to do exactly as you have done and just add a short wait.
I do believe now this is a timing issue. After thinking things through, I realized that I could easily (well 3 hours later) separate the database info from the spreadsheet info and then move the updated code that is causing problems into an Excel Macro. I then called that macro from Access. Not only do the errors go away, but it runs about 4 times faster. It's not surprising, I just hadn't thought of that direction before.
We are doing Excel JavaScript add-ins development and testing. Excel keeps crashing 1-2 times per day randomly. It may crash with action from JavaScript Pane or without it just using other Excel methods from its ribbon. There is no repeating pattern we have found.
We use Excel Tables, but that's about it. Not even huge tables just some tens or hundreds of lines mostly. One table in a sheet and below 20 sheets. So nothing special. Sometimes pivots and graphs, too. No VBA or COM add-ons installed. 32-bit Excel on 64-bit Windows. On monthly upgrade cycle so latest build and this has been going on since May 2018 so for several months/builds (at least).
This repeats on multiple developers and not a day without a crash. It feels that it is cumulating something, so it does not crash immediately but maybe after 2-3 hours of use. One idea is that could it be related to network change (between home and office)?
So how could we study this if it is our code and Async calls, or if it is something that we could report as a problem for MS to take a look?
We have already changed the way we do async calls to TypeScript style, but no change.
We are approaching the date when this goes to customer hands...so naturally this must be solved first. So will be thankful for any tips and suggestions.
Sami
I had a similar problem with Excel crashing when using a macro. It wasn't the macro because it worked fine on other systems. I tried everything suggested on the forums -- running in safe mode, checking/unchecking 'Ignore other applications that use Dynamic Data Exchange (DDE)', disabling add-ons, etc -- nothing worked. However, eventually I found that uninstalling Office 32-bit on a 64-bit version of Windows and reinstalling 64-bit Office fixed the problem -- finally! Now it works flawlessly. I hope this also works for you!
I've put together a simple Excel database that performs a few macro functions and I need to distribute this database to a few people - but they cannot see how the macro function actually works (stupid rules I have to follow!). What is the best way to achieve this?
I've done a bit of research and I found two ways:
Password protect the VBA project; but this is apparently very easy to break using readily available tools online (it would be in the best interest to the people I'm sending this to find out how the macros and functions work; so I'm almost 100% sure they will try to get into it.. hence a password protection seems inadequate.
Move to a fully compiled language like C++; my skills are very limited to VBA on Excel and Access so this being the ideal solution; isn't a solution for me :(
Are there any other ways? I thought of having a 'master excel document' with all the macros in that and then send 'children' databases to the end users and have the 'children' databases connect to the 'master' - is something like this possible? By hosting the master online or even sending the end users the master but making it completely inaccessible unless accessed by the 'children' databases?
You can create Automation Add In.
An Automation Add In provides several advantages
Execution Speed : An Automation Add In written in VB6 is compiled to
native machine code which runs much faster than the interpreted VBA
languange.
Security : Unlike an XLA add in, you never distribute the source code to the end users. If your code has proprietary information or
intellectual property value, that remains safely protected on your
own computer. It is never distributed to the user. Your code can
never be compromised.
http://www.cpearson.com/excel/automationaddins.aspx
There aren’t too many ways to protect your Excel VBA code reliably.
You can try to use passwords or VBA obfuscators, but all of that protection is limited. Passwords are easy to break, and obfuscated VBA code can still be traced back and recovered.
The other answer to this question mentions using VB6, but that limits your code to 32bit Excel. Also VB6 can be decompiled by VB decompiler.
Converting your code to a compiled language is indeed the best way to protect your VBA code, but be warned that in this case not all code is created equal.
Converting your code to VBA.NET is a flawed solution because the compiled code of .NET assembly can be converted back into the original source code.
Converting your VBA code to C or C++ is the most effective form of protection, but this takes a lot of experience and effort since C/C++ and VBA are very different languages.
I would suggest you have a look at a tool called VBA Compiler for Excel (https://vbacompiler.com/how-to-compile/). It compiles your Excel VBA code into a DLL file with a click. You do not need any knowledge of C or C++ languages to use it.
If you're still looking for a good way to protect your Excel VBA code from copying or tampering, then you should consider the Excel VBA compiler of XLS Padlock.
Because XLS Padlock offers an integrated VBA compiler that can compile your VBA code into binary code, making it completely secure and inaccessible to others.
it's really binary code and not simple obfuscation.
You open the VBA compiler of XLS Padlock, move some vital parts of your existing VBA code into it (subs or functions) and it compiles live. You can then invoke this compiled code directly from your normal VBA project using dedicated functions.
It's like having several modules, except that some modules are compiled into bytecode and thus secure.
No need to learn a new language C or C++ or VB, nor COM add-ins.
Plus, XLS Padlock can wrap your Excel workbook into a secure shell application, which you can then distribute and even sell to your customers.
It does not create DLL files that must then be distributed among your workbook files (imagine if your other non-tech users lose the DLL or don't know what it is), the software compiles spreadsheets + compiled code into a single EXE file.
It is even possible to add licensing features, online activation if you want to sell your workbooks or control who uses them, while keepking your VBA code secret.
For security purpose, I want to save my macros from VBA in Excel workbook to DLL library file I'd be calling from the file later.
This way it is possible to deploy any version of my macros to company's PCs but noone would ever see the real code behind it.
How is this possible? I've read on converting to C++ and creating DLL. Is there any step-by-step solution for this?
First off, let me warn you against this in general. We did this once with a corporate app and the time it took to port was approximately the same as the time it took to write the app originally. A step-by-step guide won't exist for this because you're basically rewriting the app from scratch.
Secondly, not everything your Excel VBA can do is necessarily available in COM, or XLL. The events available are different; in the case of XLL there are entirely new structures to use for all of the data types. In COM there are no UDFs; in XLL there are no macro actions. You will no longer be able to attach code to worksheets (only Excel sessions) and the install procedures are much more complex. I would spent a long time researching what your VBA does exactly before embarking down one particular path.
It depends how large your app is, but I would encourage you to invest the money in getting watertight licenses drawn up and doing some basic code obfuscation (as per the original comments) rather than starting from scratch with a different architecture.
You can protect your VBA, code by converting it to a password-protected XLA and obfuscating the code. but the password security is weak. Its easy to make the XLA work with all Excel versions.
An easy alternative is to convert to a compiled VB6 Automation addin. Security is good, its easy to convert, it will handle both UDFs and Command addins. But it won't work with Excel 2010-64 or early versions of Excel like XL 2000. You will need a VB6 compiler, which can be hard to find.
A harder alternative is to convert to .NET. If you use one of the many obfuscators then security is reasonable. Performance will be poor unless you use one of the tools that allow .NET access via the XLL interface, such as Excel DNA or Addin Express. 64-bit capability is also available.
If performance and security is critical you would need to convert to a C++ XLL, but this is hard work when coming from VBA.
a) You can save your macro in a separate file and protect it with password but, exist program for "recover password" that can break such protection.
b) While is possible to do a c++ dll, is smart to do instead using visual basic 6 because the syntax is pretty similar to vba. In vb6 you can create a COM component, register it and later add to you excel sheet.
You did not mention your target Excel version, but I will give a general view.
Make sure your code is appropriate for an AddIn. E.g: instead of ThisWorkBook, use ActiveWorkbook.
Also decide if you need to create a menu or use shortcut keys, and modify your code accordingly, and protect it.
In VBE, change to IsAddin property to True.
"Save As" your file and select Excel add-in. Alternatively, just save it and rename the extension from .xls to .xla
Now either inform your users to "open" that .xla, or reference it in your projects, or put it in the autostart folder or....
You're done
http://www.ozgrid.com/VBA/excel-add-in-create.htm
http://excel.tips.net/Pages/T002927_Creating_AddIns.html
http://msdn.microsoft.com/en-us/library/aa140936(v=office.10).aspx
Converting VBA code to C++ is a pretty arduous process, since C++ and VBA are very different languages, but such a conversion with compilation to a DLL file really does provide the best VBA code protection out there.
If you want a tool that does exactly that, I would recommend the VBA Compiler for Excel. It converts your VBA code to C and compiles that C code into a DLL file.
It's easier and faster than converting your VBA code to C by hand.
VBA Compiler website has a simple 10-step guide on how to compile VBA code into a Windows DLL file https://vbacompiler.com/how-to-compile/ and it doesn’t even require any knowledge of C or C++.
Although I've done VBA projects within a single application for both MS Access 2007 and Excel 2007, I haven't automated multiple applications at the same time. The generalized project is to open access, run some update queries that appends data to various tables. Then Excel needs to get the data. Some formating changes are needed in Excel, such as grouping that doesn't automatically change the date range. Finally, I plan to build it out such that the excel file will be emailed automatically.
Some parts of this are clear how to accomplish it, such as Excel will be getting the data by ODBC from Access. But where should the master VBA live? I could have a button in Access that would start running Access VBA, but is it a good practice to have the Access VBA start manipulating Excel? Does that make it difficult to debug?
To get started from Access, add a reference to the Excel object library. Then use the object browser to familiarize yourself with how the Excel object hierarchy looks from within Access. It is going to be somewhat difference, because the top-level object in Excel code is implicit (as it is in Access), and has to be explicitly referenced when coding in Access.
The Access Developers Handbook has excellent chapters on automating the rest of Office from Access.
Last of all, it's best once you've coded using the reference to the other app's automation library to help you program, you want to switch to late binding so you can remove the reference. This means not using any of the external library's specific data types (you mostly use plain object variables) and using none of the constants defined in the external library. My production code with late binding usually includes the early binding version commented out, alongside the late binding version.
I think the keywords you are looking for is "microsoft office automation".
Make an application in you favorite programming language that supports COM interfacing and then use automation to do the manipulations in the different office applications.
Look here c# How to access an excel cell? and here How to read data of an excel file using c# ? and Google.
I have recently done something quite similar to this, and have found that I can output HTML with built-in CSS for formatting that loads quite nicely into Excel. I used Access to allow users to build their required output, only opening Excel to display the results. You may find that HTML output makes for nicer emails.
Do all the work in Access VBA. See the following URLs for some sample code
Modules: Sample Excel Automation
Modules: Transferring Records to Excel with Automation
Also note that if you are dealing with multiple versions of Excel late binding becomes a necessity. Late binding means you can safely remove the reference and only have an error when the app executes lines of code in question. Rather than erroring out while starting up the app and not allowing the users in the app at all. Or when hitting a mid, left or trim function call.
This also is very useful when you don't know version of the external application will reside on the target system. Or if your organization is in the middle of moving from one version to another.
For more information including additional text and some detailed links see the Late Binding in Microsoft Access page.
As far as emailing goes there are a number of options at the Microsoft Access Email FAQ
You may put the code in Access, or Excel. In my experience, that is easier to work with than splitting the code between the two (which also works).
If you find that the automation code runs too slowly, you can split the code, so that Access is run by functions in Access, and Excel from functions in Excel, and the master code just runs routines in both. In this case, you can put the master code where-ever you get the nicest user interface for starting things. I've used C,Access,Word and Excel, and where you put the start button doesn't matter a whole lot if all the code is somewhere else.
If your users or maintainers are more familiar with one application, you may wish to put your main or master code there, but more often I find it is better to put the main or master code with the guts of the application, so that I can have no code at all in the other partner.
From the brief description, it sounds like more work will be done in Excel, so I would put all the code there.
Pulling the data out of Access doesn't even need an Access Application object - a DAO or ADO or ODBC object will work (or even DDE), which will be much faster, much more robust, much easier, much better than having both applications open and automating one from the other.
If you start from Access, it sounds like this project will require an Excel automation object. That works well now, but it is still much slower and more fragile doing it with just Excel and a DAO object.
I haven't done VBA for ages although Access was my entry-point into the world of programming. Moving from Access 97 to Visual Basic was easy and I remember that I wrote a lot of stuff using Access more like a VB-form for doing many kinds of tasks (not necessarily database-stuff).
This is the reason why I think you should stick to Access and from there, with help of VBA, do your stuff in Excel etc..
Good luck