I have 2 distinct VBA projects that make use of 8 identical classes.
If i export those classes to a directory in my disk, is there a way to instantiate them programatically to avoid code duplication?
I don't see any problem by having the same code in 2 distinct projects, but my boss asked if it is possible and so far i haven't found anything that could lead me to an answer in the microsoft online help and in some books i've got.
Now that I understand your question, a central library of sorts may be your best option, there are others but I'm always a fan of libraries.
To achieve this:
Create a new workbook, import all of your class modules in to the VBA project in that workbook. Name your project something like MyCentralLibrary (something different than VBAProject)
Set the class module's Instancing Property to PublicNotCreatable
(change this in the properties window)
Now here comes the slightly
stupid part (I never was a fan of VBA). Projects referencing this
library will be able to see all the classes but never instantiate a
new instance of them. So what you'll have to do is for each class you put in the library,
write a function with the class as the return type, like so:
Public Function GetMyClass() As MyClass
Set GetMyClass = New MyClass
End Function
Now in the project you want to reference your central library. Go to Tools -> References and choose your library name from the list. Now you can instantiate classes from that library in your project like so:
Public c As MyCentralLibrary.MyClass
Set c = MyCentralLibrary.GetMyClass()
Again, there's other ways of doing it, but I'm always a proponent of libraries (at least in other languages)
As a note, if/when you move between computers,, you'll obviously have to move the workbook with your library in it around and re-do the references for any projects referencing the library on the new computer.
My two cents, if this were to scale more I'd go ahead and grab the free version of Visual Studio and turn this into a class library that can be referenced from Excel. But that's just me and my dislike of VBA shining through ;).
Yes, it is possible, but you have to change some security settings. I do something similar to this in all my VBA projects so I can use source control.
The code to import a class module into your project would be
ThisWorkbook.VBProject.VBComponents.Import "C:\path\to\myclass.cls"
In order for this to work, you have to change your security settings to allow access to the VBA project object model. This is because in the past some viruses has spread via this mechanism. The setting is in File > Options > Trust Center > Trust Center Settings... > Macro Settings > Trust access to the VBA project object model.
As a workaround I exported the methods in those classes to a DLL file and saved it in the server. Whenever the user opens the .xlsm file, a method is called to register the DLL in their computers and then I can use the methods and properties contained in it without further problems.
I also considered the other answers to this question. But sending 2 files (one that contains the datasheet that will be used and another one for the library) would not be a good idea (users would not understand what is the reason for that) because those programs will be used by a lot of users.
Related
Is it possible to have modules be external to the actual Excel file and call the functions/subs within them externally?
My thinking is if there are multiple Excel files that use the same module, instead of updating each one of those files separately when I make a change, can I just update the one module stored on a server or something?
I have doing something like you describe for years. You can move your VBA code to a VB6 ActiveX dll, organize it into classes, and load that dll as a reference from Excel VBA.
This is a good way to reuse non-workbook specific code. For instance, I have code that queries a mainframe. I like to call it from Excel, but the details of the connection and how data is passed are contained in a dll that I can load from Excel, VB6, Word, .NET, wherever. I have a similar dll for reading data from AutoCAD drawings, one for interfacing with a product DB on a MySQL server, etc.
The code that remains in Excel tends to be simple formatting stuff. Say I return a variant array of strings (technically a COM SAFEARRAY) from some library that I wrote. I would then output it into Excel, maybe do a text-to-columns, and have a list of results returned to the user.
You can also pass and return more complex data structures. The beauty of VB6/COM Automation (and I didn't appreciate this until I learned to do it the harder way in VB.NET or C#) is that the data will flow in and out of your components seamlessly and all the necessary interfaces will be created for you.
The main change to your code will be replacing things like ThisWorkbook or ActiveSheet with explicit parameters like (Byval sht as Excel.Worksheet). These will be caught at compile time in VB6 (since it doesn't know what ThisWorkbook is), so you cannot overlook them; you are forced to pass an explicit reference.
I also notice that my code inside the dll becomes more paranoid if it receives a Worksheet or other Excel object as a parameter. In VBA you might have had more assurance that you were passing a good object since it was private to a given workbook. The dll does not know who is calling it, so I view the passed-in object with more suspicion (check if Nothing, sheet name, formatting clues to ensure I am using what I think I am using).
The only downside I see is that you will have to get a copy of Visual Basic 6.0. I bought mine in 1998. It is no longer available from Microsoft, but surely there is someone out there who will sell it to you. The latest service pack is SP6.
You will also have to become familiar with "regsvr32" and "regsvr32 /u" to deal with the "ActiveX can't create component" errors as you open your workbooks on various computers. I just publish my dlls to a mapped network drive and re-register them on the user's computers whenever there is a significant change. Obviously this is a intranet/single company solution. Publishing the updated versions is much more of a pain the farther you are distributed.
Not sure if this would satisfy your needs, but you could create your common module as an "add-in" and so install it so that all files that you open in the same instance of excel would have access to the add-in code.
It would not be my recommended way of doing it because I would be worried about suitable testing of all the excel files that use it, when you make a change, plus the added complexity of getting users to install your add-in (this may not be an issue for you). I have a "developersToolkit" module I use across 8 different Workbooks, but I import the module into each workbook so its stand alone and I can also test changes for compatibility with each of the 8 workbooks.
I'm a complete VBA newbie, having decided to teach myself over a weekend, so forgive the stupid question(s). I'm trying to automate some routine tasks involving generating Word documents or emails from an Excel Spreadsheet. Because there will be multiple software versions involved, I am using late binding to open Word and Outlook. My question is: Where can I find a simple reference telling me what the index numbers are that correspond to the application constants? I have killed a lot of time googling to learn that, for example, the Outlook foldertype for "Contacts" is "10". Maybe someone knows of a web link that could save me countless hours of searching?
Update: http://msdn.microsoft.com/en-us/library/office/gg278936%28v=office.14%29.aspx seems to have some of the information I need, although it's not always intuitive where the information is. For example, if it contains the outlook folder type constants, I haven't found them yet.
See here
Enumeration http://msdn.microsoft.com/en-us/library/office/ff860961(v=office.15).aspx
OlDefaultFolders Enumeration http://msdn.microsoft.com/en-us/library/office/ff861868(v=office.15).aspx
I would recommend to add the relevant object libraries to your project as References during development time. You do this by using the Tools - References Menu in the VBA Editor. This makes developing a lot easier as you can use intellisense while writing the code.
If you need only a few Enums or single Constants in your code the easiest way to get their values is to hit [F2] in in VBA Editor while the object libraries are still referenced. Then search for the constants name and copy its value to your code.
Just using the numeric values of the constants in your code makes the code pretty hard to read. So I would recommend to re-declare all the Enums/Constants you actually use in a module in your own project. That massively improves the readability of your code.
So, instead of just copying the value from the VBA Object Browser, I suggest you copy the name and the value and put it your own code as a constant declaration. For your example of the Outlook contacts folder this will look like this:
Public Const olFolderContacts = 10
You can then use the constant in your procedures as you would do with Early Binding.
Should you work on a larger automation project using many of the constants from any one of the Office Object Libraries, you can download ready-made VBA modules containing all the Office constants from my website. You can then just import the relevant modules into your project and are ready to go.
After you finished the main development work, you remove the linked libraries from your project and declare the relevant object variables As Object instead of the actual type.
Always remember to compile your project not to miss any declaration that does not work late binding.
I'm looking for a free Visual Studio feature, extension or macro. that can help with the following situation.
When I prototype I tend to keep all my classes in one file (bad practice I know, but yeah it a prototype). Then comes the point the where the files is too hard to navigate. So I breakout the classes into separate files inside the project, the folder structure reflecting the namespaces.
To achieve the is;-
1. Add new Folder
2. Add new Class
3. Name class
4. Cut and paste corresponding section into new class file.
For me, Steps 2 through 4 are prime fodder for a new Menu entries.
Cut Class as New Class File
Cut as New Partial Class File.
I've seen this feature in C# but not VB.net.
So does know any how to achieve this for VB.net?
Here's a macro that does what you want in C#... looking at the code it's probably fairly straight forward to modify it to work in VB...
http://plisky.net/main/macros/documentation
Also, I'm pretty sure all the commercial refactoring tools (Resharper, CodeRush, etc.) support this...
Resharper can do this using Move Type to Another File or Namespace
I just stumbled across this and can point you to an updated version of the macro that scrappy kindly linked. Its at http://plisky.net/main/plisy.net-visual-studio-productivity-macros.
If you still want it and wish to test it for VB I can happily make the changes to support VB.net but as I don't use VB I'd need a tester :) As its a while since this post you probably have something working already though.
There seemes to be a consistent problem with the following situation:
Say you have a VS2008 solution, consisting of a (say console) application written in vb.net, and a class library written in c#. The application references the class library
project.
This, of course, complies and works well. However, when you right-click (in the vb.net application code) a function prototype/object type defined in the class library, and select "Go to definition", the object browser opens providing you with the the list of methods available for the class the class library consists of. BAD. However, if you try to do the same when both the application and cl are in c#, this works just fine and you navigate driectly to the relevant function/class.GOOD.
Known issue? Solvable?
Did you try adding a project reference instead of a DLL reference? A project reference should take you to the actual relevant code in the other assembly.
It's a known issue, the workaround are two: use ctrl+, or use some plugin that add this function, like resharper.
I'm new to VBA and have been throwing together a small macro application for the Office. We've got about 80 users on essentially identical PC setups, and it will be accessed by all but a few users.
I've been playing around with some automation of accessing web pages using the Web Services references, and I've also loaded the Microsoft Scripting Runtime references into the project. I attempted to run it on a test PC and it complained that there were missing references.
I don't particularly want to go around 80 PCs and manually load the references.
My question, basically, is how should I manage the distribution of this macro-app to 80 odd users so as to ensure that the references will load every time for every user.
Thanks!
For the most part, late binding will solve problems with references in VBA, unless you have some unusual references. Most problems are caused by differences in library versions that can be overcome with late binding. With VBA, it is often recommended that you develop with early binding but release with late binding. The main disadvantage of late binding is changing built-in constants to values (speed is no longer the issue it used to be.)
So:
Dim fs As Object 'Instead of FileSystemObject '
Dim xl As Object 'Instead of Excel.Application '
Set fs=CreateObject("Scripting.FileSystemObject")
Set xl=CreateObject("Excel.Application")
'Value instead of built-in constant '
ForReading=2
Set f = fs.OpenTextFile("c:\testfile.txt", ForReading)
If you have references that your application depends on, that you know are not going to be on the target PCs, then I would strongly recommend you investigate some installer technology.
Using the installer you should be able to install your macro, and install and register all appropriate references / libraries.
There are generally two flavours on windows, Windows Installer based technology and Script based technology.
We use InstallShield for all of our deployment, although there are several options for you to use (there are several discussion on Stack Overflow).
Using windows installer technology, you can build MSI install files, which you are then able to deploy automatically using Group Policy.
Instead of having the documents expose the functionality, make it an add-in for Office (the suite, or the individual apps, your choice). This way, you don't have to deal with references.
Then, just distribute an install package with the add-in which registers the components and registers the add-ins with the appropriate Office apps.
VB6 might be a good idea here, given it's similarity to VBA.
In addition to this answer, which is the bullet-proof solution to solve this kind of issue, but which is quite complex to implement, you can also write some code to be executed when your VBA application starts, checking the 'references' collection of the 'application' object. You can then check (1) if requested files (dll, ocx, tlb) are available on the computer and (2) if reference can be created (application.references.addFromFile ...).
Be careful: object declarations that might be 'reference dependent', such as:
Dim cat as ADOX.catalog
will raise a compilation bug if the reference is not active when the corresponding module is 'compiled'. I then advise you to isolate your 'reference checking procedure' in a startup module (equivalent to an 'autoexec') which deals only with VBA and basic application objects. Check it with your Help Files (Example: in Access, default references that can be used without external references are VBA, Access and DAO).
EDIT:
in case external references depend on other software package and (1) cannot be distributed with a MSI file or (2) can have multiple versions, I think the 'references.addFromFile' is the only solution that can apply. Example:
You have an VBA/Access runtime client
app that needs to refer to Word
(msword.olb file).
For licensing issues, you cannot freely distribute this file with your msi pack
the olb file can be either the 'XP version or a newer one
Our solution is to have 2 tables on the client Access file. One lists all the references that have to be checked or added at startup time (Word will be one of them), and the other one lists all the possible locations of the file (depending if the user has the 'office11' version or a newer one), with a one to many relations between the 2 tables.
So, the best strategy could be a mix between msi packs and management through code:
msi is great for distributing independant dll's or other files that are totally 'embedded' in your app, such as activeX controls (like scanners controls, report or file viewers, etc)
code is the best solution where your app will have to communicate with other applications (word, excel, outlook, etc) that can exist in different versions on your user's machines.