I have two Excel files, parent and child, where child contains a library of functions used by parent's functions. For versioning purposes I keep them in the same folder and copy and rename the folder in exactly the same location to keep track of my versions. I also want the references to be dynamically updated so that when i move to a new version, parent always points to child in the same location.
So in order to accomplish this I have implemented two routines in parent.
One, is in ThisWorkbook I used Workbook_Open sub:
Private Sub Workbook_Open()
'Force the location of the shared library to the current project folder irrespective where the project is located
Call reloadSharedLibrary
End Sub
Two, in Modules.Libraries I added another sub reloadSharedLibrary:
Public librName As Variant
Public isRefReloaded As Boolean
Sub reloadSharedLibrary()
isRefReloaded = True
Dim VBAEditor As VBIDE.VBE
Dim vbProj As VBIDE.VBProject
Dim chkRef As VBIDE.Reference
Dim BoolExists As Boolean
Dim librPath As String
Set VBAEditor = Application.VBE
Set vbProj = ActiveWorkbook.VBProject
librName = "lib_emtm"
librPath = Application.ActiveWorkbook.Path & "\lib.xlsm"
' delete any shared lib (if exists)
For Each chkRef In vbProj.References
If chkRef.Name = librName Then
vbProj.References.Remove chkRef
BoolExists = True
End If
Next
' you can only add it to VBAProject only after you quit the above loop
On Error Resume Next
vbProj.References.AddFromFile librPath
If Err.Number <> 0 Then
MsgBox "FATAR ERROR: Cannot find shared library file in project root": End
End If
Set vbProj = Nothing
Set VBAEditor = Nothing
End Sub
Now, the problem with this is that when I copy the project folder to a new version folder the reference to child does not get updated. The child the version uses is from the old version.
What am I doing wrong?
The problem is that when the VBA project loads a document and its references, it assigns them a name, which is lib_emtm in your case. When you uncheck the reference to it, the reference is removed from the VBA project but the project editor will keep the name in its cache. This name will remain in the cache until you close the workbook and reopen it.
You can verify this in the project references menu: you will see that even if you uncheck the reference, the name of the library lib_emtm will still appear there.
Then when you try to add a reference to the "other" child workbook (the one in the same folder), the editor will find that the name is lib_emtm which is the same as the one in the cache, so instead of opening the new document and parsing it, it will use the cached version, which is the old one!
If you close then re-open the application, the name of the library will disappear from the cache, so you can install the correct version. To be complete, this pattern occurs only with references to other workbooks, not with regular DLL's installed on the system.
I tried but couldn't find a VBA way to remove the Cached library from the editor's cache before re-installing it. If someone finds a way it would complete the solution. Therefore at the moment we must close the document before re-opening it and installing the lib. This process might be automated but I suggest a solution that prompts the user for that.
' Module ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
'Force the location of the shared library to the current project folder irrespective where the project is located
Dim check As Boolean: check = checkSharedLibrary
If check Then Exit Sub
Dim prompt
prompt = MsgBox("The installed lib_emtm library was uninstalled because it was not the correct version." & vbCrLf & _
"If you click Ok, document will close and the correct version will be automatically installed when you reopen it." & vbCrLf & _
"If you click Cancel, library will not be available in this session but will be installed next time you open the document", vbOKCancel)
If prompt = vbOK Then ThisWorkbook.Close True
End Sub
' Regular module
Option Explicit
Private librName As String, librpath As String
' if correct version already installed (correct path) return true
' if library installed with incorrect version, uninstall it and return false
' if library not installed, install it and return true
Public Function checkSharedLibrary() As Boolean
librName = "lib_emtm"
librpath = ThisWorkbook.Path & "\lib_emtm.xlsm"
Dim chkRef As VBIDE.Reference
For Each chkRef In ThisWorkbook.VBProject.References
If chkRef.name = librName Then Exit For
Next
If chkRef Is Nothing Then
install_emtm
checkSharedLibrary = True
ElseIf Left(chkRef.FullPath, InStrRev(chkRef.FullPath, "\") - 1) = ThisWorkbook.Path Then
checkSharedLibrary = True ' we have the correct version
Else
ThisWorkbook.VBProject.References.Remove chkRef ' return false
End If
End Function
Private Sub install_emtm()
On Error Resume Next
ThisWorkbook.VBProject.References.AddFromFile(librpath)
If Err.Number <> 0 Then MsgBox "FATAR ERROR: Could not install lib_emtm:" & vbCrLf & Err.Description & vbCrLf & vbCrLf & _
"Please verify that the library's file is present in the same folder or try a manual install"
End Sub
A final remark, the process can be automated without user intervention if we close directly the application but before that we can schedule the re-opening of the workbook . But things might get complicated because the user may have other Excel documents open, so we cannot oblige her to close everything.
Related
I have a workbook that is password protected and I’d like to create a read only copy that other users can view on a different location on the network drive.
I know it’s a strange request as the other people could open the original as read only, but we don’t want them to know the location of the original or have anything to do with it, should they figure out my colleagues password.
The other issue we had was that people were opening as read only and it was still telling my colleague that it was locked by another user and he needs it for most of the day so that issue is annoying
Thanks in advance
What you could do is add the following event procedure to the ThisWorkbook module:
Const RemotePath As String = "D:\YourRemoteLocation\"
Private Sub Workbook_AfterSave(ByVal Success As Boolean)
On Error GoTo CleanUp
If Success And InStr(ThisWorkbook.Name, "Copy of ") = 0 Then
Dim CopyFullName As String
CopyFullName = RemotePath & "Copy of " & ThisWorkbook.Name
Application.EnableEvents = False
Dim fso As FileSystemObject 'Requires the Microsoft Scripting Runtime Library
Set fso = New FileSystemObject
fso.CopyFile Source:=ThisWorkbook.FullName, Destination:=CopyFullName
Dim ReadOnlyWorkbook As Workbook
Set ReadOnlyWorkbook = Workbooks.Open(Filename:=CopyFullName)
Application.DisplayAlerts = False
ReadOnlyWorkbook.SaveAs Filename:=CopyFullName, Password:=""
Application.DisplayAlerts = True
ReadOnlyWorkbook.Close SaveChanges:=False
End If
CleanUp:
Application.EnableEvents = True
Application.DisplayAlerts = True
Set fso = Nothing
End Sub
This code will run every time the workbook is saved and export the current file to the remote location. Then it will open the copy and save it as a workbook without password.
Note that I've added InStr(ThisWorkbook.Name, "Copy of ") = 0 as a condition to the If-statement. Instr returns the position where a substring (arg2) appears in the main string (arg1) or zero if the substring is absent from the main string. In this context, we want it to be zero since we don't want to run the code in the workbook copy.
In this method, the owner of the original file will have to supply their password every time they save. You could automate this by passing the password as an argument to the Open method like this:
Set ReadOnlyWorkbook = Workbooks.Open( _
Filename:=CopyFullName, _
Password:="MyPassword")
However, the password would then be accessible by people looking into the VBA code.
Alternatively you could get the password from a local file that wouldn't be accessible from the Network, but then the file path would be visible.
And if the remote folder is not already set to be Read-only mode, you can make sure that people opening the remote version of the file do so in Read-Only mode by adding the following event procedure after the previous one.
Private Sub Workbook_Open()
If InStr(ThisWorkbook.Name, "Copy of ") = 0 Then
ThisWorkbook.ChangeFileAccess Mode:=xlReadOnly
End If
End Sub
Obviously this will only work if they enable macros.
I've written a program that runs and messages Skype with information when if finishes. I need to add a reference for Skype4COM.dll in order to send a message through Skype. We have a dozen or so computers on a network and a shared file server (among other things). All of the other computers need to be able to run this program. I was hoping to avoid setting up the reference by hand. I had planned on putting the reference in a shared location, and adding it programmatically when the program ran.
I can't seem to figure out how to add a reference programmatically to Excel 2007 using VBA. I know how to do it manually: Open VBE --> Tools --> References --> browse --_> File Location and Name. But that's not very useful for my purposes. I know there are ways to do it in Access Vb.net and code similar to this kept popping up, but I'm not sure I understand it, or if it's relevant:
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:="{0002E157-0000-0000-C000-000000000046}", _
Major:=5, Minor:=3
So far, in the solutions presented, in order to add the reference programmatically I will need to add a reference by hand and change the Trust Center - which is more than just adding the reference. Though I guess if I follow through with the solutions proposed I will be able to add future references programmatically. Which probably makes it worth the effort.
Any further thoughts would be great.
Ommit
There are two ways to add references via VBA to your projects
1) Using GUID
2) Directly referencing the dll.
Let me cover both.
But first these are 3 things you need to take care of
a) Macros should be enabled
b) In Security settings, ensure that "Trust Access To Visual Basic Project" is checked
c) You have manually set a reference to `Microsoft Visual Basic for Applications Extensibility" object
Way 1 (Using GUID)
I usually avoid this way as I have to search for the GUID in the registry... which I hate LOL. More on GUID here.
Topic: Add a VBA Reference Library via code
Link: http://www.vbaexpress.com/kb/getarticle.php?kb_id=267
'Credits: Ken Puls
Sub AddReference()
'Macro purpose: To add a reference to the project using the GUID for the
'reference library
Dim strGUID As String, theRef As Variant, i As Long
'Update the GUID you need below.
strGUID = "{00020905-0000-0000-C000-000000000046}"
'Set to continue in case of error
On Error Resume Next
'Remove any missing references
For i = ThisWorkbook.VBProject.References.Count To 1 Step -1
Set theRef = ThisWorkbook.VBProject.References.Item(i)
If theRef.isbroken = True Then
ThisWorkbook.VBProject.References.Remove theRef
End If
Next i
'Clear any errors so that error trapping for GUID additions can be evaluated
Err.Clear
'Add the reference
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:=strGUID, Major:=1, Minor:=0
'If an error was encountered, inform the user
Select Case Err.Number
Case Is = 32813
'Reference already in use. No action necessary
Case Is = vbNullString
'Reference added without issue
Case Else
'An unknown error was encountered, so alert the user
MsgBox "A problem was encountered trying to" & vbNewLine _
& "add or remove a reference in this file" & vbNewLine & "Please check the " _
& "references in your VBA project!", vbCritical + vbOKOnly, "Error!"
End Select
On Error GoTo 0
End Sub
Way 2 (Directly referencing the dll)
This code adds a reference to Microsoft VBScript Regular Expressions 5.5
Option Explicit
Sub AddReference()
Dim VBAEditor As VBIDE.VBE
Dim vbProj As VBIDE.VBProject
Dim chkRef As VBIDE.Reference
Dim BoolExists As Boolean
Set VBAEditor = Application.VBE
Set vbProj = ActiveWorkbook.VBProject
'~~> Check if "Microsoft VBScript Regular Expressions 5.5" is already added
For Each chkRef In vbProj.References
If chkRef.Name = "VBScript_RegExp_55" Then
BoolExists = True
GoTo CleanUp
End If
Next
vbProj.References.AddFromFile "C:\WINDOWS\system32\vbscript.dll\3"
CleanUp:
If BoolExists = True Then
MsgBox "Reference already exists"
Else
MsgBox "Reference Added Successfully"
End If
Set vbProj = Nothing
Set VBAEditor = Nothing
End Sub
Note: I have not added Error Handling. It is recommended that in your actual code, do use it :)
EDIT Beaten by mischab1 :)
There are two ways to add references using VBA. .AddFromGuid(Guid, Major, Minor) and .AddFromFile(Filename). Which one is best depends on what you are trying to add a reference to. I almost always use .AddFromFile because the things I am referencing are other Excel VBA Projects and they aren't in the Windows Registry.
The example code you are showing will add a reference to the workbook the code is in. I generally don't see any point in doing that because 90% of the time, before you can add the reference, the code has already failed to compile because the reference is missing. (And if it didn't fail-to-compile, you are probably using late binding and you don't need to add a reference.)
If you are having problems getting the code to run, there are two possible issues.
In order to easily use the VBE's object model, you need to add a reference to Microsoft Visual Basic for Application Extensibility. (VBIDE)
In order to run Excel VBA code that changes anything in a VBProject, you need to Trust access to the VBA Project Object Model. (In Excel 2010, it is located in the Trust Center - Macro Settings.)
Aside from that, if you can be a little more clear on what your question is or what you are trying to do that isn't working, I could give a more specific answer.
Browsing the registry for guids or using paths, which method is best. If browsing the registry is no longer necessary, won't it be the better way to use guids?
Office is not always installed in the same directory. The installation path can be manually altered. Also the version number is a part of the path.
I could have never predicted that Microsoft would ever add '(x86)' to 'Program Files' before the introduction of 64 bits processors.
If possible I would try to avoid using a path.
The code below is derived from Siddharth Rout's answer, with an additional function to list all the references that are used in the active workbook.
What if I open my workbook in a later version of Excel? Will the workbook still work without adapting the VBA code?
I have already checked that the guids for office 2003 and 2010 are identical. Let's hope that Microsoft doesn't change guids in future versions.
The arguments 0,0 (from .AddFromGuid) should use the latest version of a reference (which I have not been able to test).
What are your thoughts? Of course we cannot predict the future but what can we do to make our code version proof?
Sub AddReferences(wbk As Workbook)
' Run DebugPrintExistingRefs in the immediate pane, to show guids of existing references
AddRef wbk, "{00025E01-0000-0000-C000-000000000046}", "DAO"
AddRef wbk, "{00020905-0000-0000-C000-000000000046}", "Word"
AddRef wbk, "{91493440-5A91-11CF-8700-00AA0060263B}", "PowerPoint"
End Sub
Sub AddRef(wbk As Workbook, sGuid As String, sRefName As String)
Dim i As Integer
On Error GoTo EH
With wbk.VBProject.References
For i = 1 To .Count
If .Item(i).Name = sRefName Then
Exit For
End If
Next i
If i > .Count Then
.AddFromGuid sGuid, 0, 0 ' 0,0 should pick the latest version installed on the computer
End If
End With
EX: Exit Sub
EH: MsgBox "Error in 'AddRef'" & vbCrLf & vbCrLf & err.Description
Resume EX
Resume ' debug code
End Sub
Public Sub DebugPrintExistingRefs()
Dim i As Integer
With Application.ThisWorkbook.VBProject.References
For i = 1 To .Count
Debug.Print " AddRef wbk, """ & .Item(i).GUID & """, """ & .Item(i).Name & """"
Next i
End With
End Sub
The code above does not need the reference to the "Microsoft Visual Basic for Applications Extensibility" object anymore.
Here is how to get the Guid's programmatically! You can then use these guids/filepaths with an above answer to add the reference!
Reference: http://www.vbaexpress.com/kb/getarticle.php?kb_id=278
Sub ListReferencePaths()
'Lists path and GUID (Globally Unique Identifier) for each referenced library.
'Select a reference in Tools > References, then run this code to get GUID etc.
Dim rw As Long, ref
With ThisWorkbook.Sheets(1)
.Cells.Clear
rw = 1
.Range("A" & rw & ":D" & rw) = Array("Reference","Version","GUID","Path")
For Each ref In ThisWorkbook.VBProject.References
rw = rw + 1
.Range("A" & rw & ":D" & rw) = Array(ref.Description, _
"v." & ref.Major & "." & ref.Minor, ref.GUID, ref.FullPath)
Next ref
.Range("A:D").Columns.AutoFit
End With
End Sub
Here is the same code but printing to the terminal if you don't want to dedicate a worksheet to the output.
Sub ListReferencePaths()
'Macro purpose: To determine full path and Globally Unique Identifier (GUID)
'to each referenced library. Select the reference in the Tools\References
'window, then run this code to get the information on the reference's library
On Error Resume Next
Dim i As Long
Debug.Print "Reference name" & " | " & "Full path to reference" & " | " & "Reference GUID"
For i = 1 To ThisWorkbook.VBProject.References.Count
With ThisWorkbook.VBProject.References(i)
Debug.Print .Name & " | " & .FullPath & " | " & .GUID
End With
Next i
On Error GoTo 0
End Sub
I am doing a user application that needs an AddIn which I want to update in the workbook_open event.
Here is my plan:
Add Reference to "Microsoft Visual Basic for Applications Extensibility 5.3"
Delete Reference to old AddIn
Delete old AddIn if existing
Copy AddIn from different Folder
Add Reference to new AddIn
This should be done all while the vbProject is password protected and hidden. Here is my code:
Private Sub Workbook_Open()
Dim wb As Workbook
Set wb = Thisworkbook
'
If common_DB.AddFSOref(wb) Then Debug.Print "Added Extensibility"
If common_DB.AddRegEx(wb) Then Debug.Print "Added Regular Expressions"
UpdateDBAddin
End Sub
That is my update code that works without password protection but when adding in the reference in the vbProject password is required. I want to suppress that because my users do not have to worry about that.
Public Sub UpdateDBAddin()
Dim UserPath As String
Dim AI As AddIn
Dim AddinSourcePath As String
Dim AddinName As String
Dim Addintitle As String
Dim RefName As String
Dim ref As Reference
RefName = "Ex_Ample_Name"
AddinName = "ExampleName.xlam"
Addintitle = "Example AddIn"
UserPath = Application.UserLibraryPath
AddinSourcePath = "E:\Xample\Path\"
Application.DisplayAlerts = False
For Each ref In Thisworkbook.VBProject.References
If ref.Name = RefName Then
Thisworkbook.VBProject.References.remove ref
End If
Next ref
If Application.AddIns(Addintitle).IsOpen Then
Workbooks(AddinName).Close False
End If
If common_DB.IsFile(UserPath & "\" & AddinName) Then
Application.AddIns(Addintitle).Installed = False
Kill (UserPath & "\" & AddinName)
End If
Application.AddIns.Add (AddinSourcePath & AddinName)
Application.AddIns(Addintitle).Installed = True
Application.DisplayAlerts = True
' Here the VBProject Password is requested from the user, I want to suppress that
Thisworkbook.VBProject.References.AddFromFile (UserPath & "\" & AddinName)
End Sub
Question: How do I suppress the password when I want to add a new reference to my Vbproject?
EDIT1: Dilemma because it only occures when the project is hidden and I cannot 'debug' in the hold-mode. I narrowed the issue down to the adding of the reference to the addin on the last line of UpdateAddin.
EDIT2: This has some funnny behaviour. If you just Cancel the password prompt it works just fine. So the reference gets added. It doesn't matter if you put in the password or if you just cancle it the line gets executed.
So far I do not have a solution, but I found the workaround.
Just leave user to cancel password for a first time.
Reference will be added from file.
Then force the main xlsm to be Saved (can't be opened as ReadOnly)
Upon next run, check if correct reference with correct name and path is already referenced. if yes skip adding reference from file .. no password popup.
Anyway I am still looking for a solution how to avoid the first popup. Especially because our users are opening main XLSM in ReadOnly mode ..
I need some way to update an excel addin shared among my staffs so as everyone don't have to download & install it manually.
I have googled and see that we can write file to the OS file system so the task ends up with writing the new-version addin, i.e. the .xlam file, to overwrite itself.
I have no idea on how to do this. If you do have ones, please share! Thank you!
I don't know if there's a less crude way of doing it, but I have "hacked" a solution that involves SendKeys. Yuck, I know. Hopefully someone else will have a better solution.
As I recall, you need to uninstall an addin before you can overwrite the .xla(m) file and I couldn't find a way to do this purely using built-in objects.
The code below basically uninstalls the add-in, invokes the "Add-ins" dialog box and uses SendKeys to remove it from the list, before copying the new file and reinstalling the add-in.
Amend it for your circumstances - it will depend on your users having their security settings low enough to let it run, of course.
Sub UpdateAddIn()
Dim fs As Object
Dim Profile As String
If Workbooks.Count = 0 Then Workbooks.Add
Profile = Environ("userprofile")
Set fs = CreateObject("Scripting.FileSystemObject")
AddIns("MyAddIn").Installed = False
Call ClearAddinList
fs.CopyFile "\\SourceOfLatestAddIn\MyAddIn.xla", Profile & "\Application Data\Microsoft\AddIns\", True
AddIns.Add Profile & "\Application Data\Microsoft\AddIns\MyAddIn.xla"
AddIns("MyAddIn").Installed = True
End Sub
Sub ClearAddinList()
Dim MyCount As Long
Dim GoUpandDown As String
'Turn display alerts off so user is not prompted to remove Addin from list
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Do
'Get Count of all AddIns
MyCount = Application.AddIns.Count
'Create string for SendKeys that will move up & down AddIn Manager List
'Any invalid AddIn listed will be removed
GoUpandDown = "{Up " & MyCount & "}{DOWN " & MyCount & "}"
Application.SendKeys GoUpandDown & "~", False
Application.Dialogs(xlDialogAddinManager).Show
Loop While MyCount <> Application.AddIns.Count
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
I use a reversioning addin-manager to do this: basically its a small xla/xlam that never changes thats installed on each users machine. It checks a network share for the latest version of the real addin(s) and opens it as if it was an ordinary workbook: this has the effect of loading the real Addin(s) for the user.
There is a downloadable working example which you can customise here
Another option, this is what I do.
Key points.
Addin version is "some number", file name is always the same.
Installation directory must be known
When asked, the current addin, looks to see if a new version is available. I do this via a system that has a version number in the file name of the "update" and a version number as a const in the code.
Having established I we can update, I go and get the update "package" - in my case I am using an installer and a small vb.net app. If you cant do this then you might want to spin up an insatnce of PPT or word, and use that complete the install.
Next close yourself, or ask the user to close Excel.
Now all we need to do is save the new addin over the old one, with the same file name.
Tell the user its updated, and they should re-open Excel, close the install program.
This works well for me - although you need to remember the numbering system , in the file name and how that code works.
The below is the main guts of the code bit messy, but might help you out.
Private Sub CommandButton1_Click()
Dim RetVal As Long
MsgBox "To install the update, follow the installer programes directions." & vbNewLine & vbNewLine & _
"You will need to then closed down and restart Excel.", vbInformation + vbOKOnly, "::::UPDATE TRS:::::"
RetVal = Shell(gsDataBase1 & "\" & gsUpdatefolder & "\" & GetUpdateFileName(), 1)
ThisWorkbook.Close
Unload Me
End Sub
Private Sub CommandButton2_Click()
gbInUpdate = False
Unload Me
End Sub
Private Sub UserForm_Initialize()
Me.lbNew = GetServerVersion2
Me.lbCurrent.Caption = gcVersionNumber
'CheckVersionNumbers
End Sub
'''This method might be better, but is quite slow.
Public Sub GetServerVersion()
Set objshell = CreateObject("Shell.Application")
Set objFolder = objshell.Namespace(gsDataBase1 & "\" & gsUpdatefolder)
For Each strFileName In objFolder.Items
Me.lbNew.Caption = objFolder.GetDetailsOf(strFileName, 11)
Next
Set objshell = Nothing
End Sub
Public Function IsNewer() As Boolean
Dim curVer As Long
Dim newVer As Long
On Error GoTo Catch
curVer = CLng(Left(Replace(Me.lbCurrent, ".", ""), 2))
newVer = CLng(Left(Replace(Me.lbNew, ".", ""), 2))
If curVer < newVer Then
IsNewer = True
Else
IsNewer = False
End If
Exit Function
Catch:
IsNewer = False
End Function
Private Function GetServerVersion2() As String
On Error GoTo LEH
Dim strDocPath As String
Dim strCurrentFile As String
strDocPath = gsDataBase1 & "\" & gsUpdatefolder & "\"
strCurrentFile = Dir(strDocPath & "*.*")
'gets last file - randomly? should onl;y be one anyway!
'Do While strCurrentFile <> ""
GetServerVersion2 = Right(strCurrentFile, 11)
GetServerVersion2 = Left(GetServerVersion2, 7)
'Loop
Exit Function
LEH:
GetServerVersion2 = "0.Error"
End Function
'Basiclly a coop of GetSeverVerion, but just get the file name so the exe can be called by the shell operation under the update button
''seems clumbys, but works!
Private Function GetUpdateFileName() As String
On Error GoTo LEH
Dim strDocPath As String
Dim strCurrentFile As String
strDocPath = gsDataBase1 & "\" & gsUpdatefolder & "\"
GetUpdateFileName = Dir(strDocPath & "*.*")
Exit Function
LEH:
GetUpdateFileName = "0.Error"
End Function
I've written a program that runs and messages Skype with information when if finishes. I need to add a reference for Skype4COM.dll in order to send a message through Skype. We have a dozen or so computers on a network and a shared file server (among other things). All of the other computers need to be able to run this program. I was hoping to avoid setting up the reference by hand. I had planned on putting the reference in a shared location, and adding it programmatically when the program ran.
I can't seem to figure out how to add a reference programmatically to Excel 2007 using VBA. I know how to do it manually: Open VBE --> Tools --> References --> browse --_> File Location and Name. But that's not very useful for my purposes. I know there are ways to do it in Access Vb.net and code similar to this kept popping up, but I'm not sure I understand it, or if it's relevant:
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:="{0002E157-0000-0000-C000-000000000046}", _
Major:=5, Minor:=3
So far, in the solutions presented, in order to add the reference programmatically I will need to add a reference by hand and change the Trust Center - which is more than just adding the reference. Though I guess if I follow through with the solutions proposed I will be able to add future references programmatically. Which probably makes it worth the effort.
Any further thoughts would be great.
Ommit
There are two ways to add references via VBA to your projects
1) Using GUID
2) Directly referencing the dll.
Let me cover both.
But first these are 3 things you need to take care of
a) Macros should be enabled
b) In Security settings, ensure that "Trust Access To Visual Basic Project" is checked
c) You have manually set a reference to `Microsoft Visual Basic for Applications Extensibility" object
Way 1 (Using GUID)
I usually avoid this way as I have to search for the GUID in the registry... which I hate LOL. More on GUID here.
Topic: Add a VBA Reference Library via code
Link: http://www.vbaexpress.com/kb/getarticle.php?kb_id=267
'Credits: Ken Puls
Sub AddReference()
'Macro purpose: To add a reference to the project using the GUID for the
'reference library
Dim strGUID As String, theRef As Variant, i As Long
'Update the GUID you need below.
strGUID = "{00020905-0000-0000-C000-000000000046}"
'Set to continue in case of error
On Error Resume Next
'Remove any missing references
For i = ThisWorkbook.VBProject.References.Count To 1 Step -1
Set theRef = ThisWorkbook.VBProject.References.Item(i)
If theRef.isbroken = True Then
ThisWorkbook.VBProject.References.Remove theRef
End If
Next i
'Clear any errors so that error trapping for GUID additions can be evaluated
Err.Clear
'Add the reference
ThisWorkbook.VBProject.References.AddFromGuid _
GUID:=strGUID, Major:=1, Minor:=0
'If an error was encountered, inform the user
Select Case Err.Number
Case Is = 32813
'Reference already in use. No action necessary
Case Is = vbNullString
'Reference added without issue
Case Else
'An unknown error was encountered, so alert the user
MsgBox "A problem was encountered trying to" & vbNewLine _
& "add or remove a reference in this file" & vbNewLine & "Please check the " _
& "references in your VBA project!", vbCritical + vbOKOnly, "Error!"
End Select
On Error GoTo 0
End Sub
Way 2 (Directly referencing the dll)
This code adds a reference to Microsoft VBScript Regular Expressions 5.5
Option Explicit
Sub AddReference()
Dim VBAEditor As VBIDE.VBE
Dim vbProj As VBIDE.VBProject
Dim chkRef As VBIDE.Reference
Dim BoolExists As Boolean
Set VBAEditor = Application.VBE
Set vbProj = ActiveWorkbook.VBProject
'~~> Check if "Microsoft VBScript Regular Expressions 5.5" is already added
For Each chkRef In vbProj.References
If chkRef.Name = "VBScript_RegExp_55" Then
BoolExists = True
GoTo CleanUp
End If
Next
vbProj.References.AddFromFile "C:\WINDOWS\system32\vbscript.dll\3"
CleanUp:
If BoolExists = True Then
MsgBox "Reference already exists"
Else
MsgBox "Reference Added Successfully"
End If
Set vbProj = Nothing
Set VBAEditor = Nothing
End Sub
Note: I have not added Error Handling. It is recommended that in your actual code, do use it :)
EDIT Beaten by mischab1 :)
There are two ways to add references using VBA. .AddFromGuid(Guid, Major, Minor) and .AddFromFile(Filename). Which one is best depends on what you are trying to add a reference to. I almost always use .AddFromFile because the things I am referencing are other Excel VBA Projects and they aren't in the Windows Registry.
The example code you are showing will add a reference to the workbook the code is in. I generally don't see any point in doing that because 90% of the time, before you can add the reference, the code has already failed to compile because the reference is missing. (And if it didn't fail-to-compile, you are probably using late binding and you don't need to add a reference.)
If you are having problems getting the code to run, there are two possible issues.
In order to easily use the VBE's object model, you need to add a reference to Microsoft Visual Basic for Application Extensibility. (VBIDE)
In order to run Excel VBA code that changes anything in a VBProject, you need to Trust access to the VBA Project Object Model. (In Excel 2010, it is located in the Trust Center - Macro Settings.)
Aside from that, if you can be a little more clear on what your question is or what you are trying to do that isn't working, I could give a more specific answer.
Browsing the registry for guids or using paths, which method is best. If browsing the registry is no longer necessary, won't it be the better way to use guids?
Office is not always installed in the same directory. The installation path can be manually altered. Also the version number is a part of the path.
I could have never predicted that Microsoft would ever add '(x86)' to 'Program Files' before the introduction of 64 bits processors.
If possible I would try to avoid using a path.
The code below is derived from Siddharth Rout's answer, with an additional function to list all the references that are used in the active workbook.
What if I open my workbook in a later version of Excel? Will the workbook still work without adapting the VBA code?
I have already checked that the guids for office 2003 and 2010 are identical. Let's hope that Microsoft doesn't change guids in future versions.
The arguments 0,0 (from .AddFromGuid) should use the latest version of a reference (which I have not been able to test).
What are your thoughts? Of course we cannot predict the future but what can we do to make our code version proof?
Sub AddReferences(wbk As Workbook)
' Run DebugPrintExistingRefs in the immediate pane, to show guids of existing references
AddRef wbk, "{00025E01-0000-0000-C000-000000000046}", "DAO"
AddRef wbk, "{00020905-0000-0000-C000-000000000046}", "Word"
AddRef wbk, "{91493440-5A91-11CF-8700-00AA0060263B}", "PowerPoint"
End Sub
Sub AddRef(wbk As Workbook, sGuid As String, sRefName As String)
Dim i As Integer
On Error GoTo EH
With wbk.VBProject.References
For i = 1 To .Count
If .Item(i).Name = sRefName Then
Exit For
End If
Next i
If i > .Count Then
.AddFromGuid sGuid, 0, 0 ' 0,0 should pick the latest version installed on the computer
End If
End With
EX: Exit Sub
EH: MsgBox "Error in 'AddRef'" & vbCrLf & vbCrLf & err.Description
Resume EX
Resume ' debug code
End Sub
Public Sub DebugPrintExistingRefs()
Dim i As Integer
With Application.ThisWorkbook.VBProject.References
For i = 1 To .Count
Debug.Print " AddRef wbk, """ & .Item(i).GUID & """, """ & .Item(i).Name & """"
Next i
End With
End Sub
The code above does not need the reference to the "Microsoft Visual Basic for Applications Extensibility" object anymore.
Here is how to get the Guid's programmatically! You can then use these guids/filepaths with an above answer to add the reference!
Reference: http://www.vbaexpress.com/kb/getarticle.php?kb_id=278
Sub ListReferencePaths()
'Lists path and GUID (Globally Unique Identifier) for each referenced library.
'Select a reference in Tools > References, then run this code to get GUID etc.
Dim rw As Long, ref
With ThisWorkbook.Sheets(1)
.Cells.Clear
rw = 1
.Range("A" & rw & ":D" & rw) = Array("Reference","Version","GUID","Path")
For Each ref In ThisWorkbook.VBProject.References
rw = rw + 1
.Range("A" & rw & ":D" & rw) = Array(ref.Description, _
"v." & ref.Major & "." & ref.Minor, ref.GUID, ref.FullPath)
Next ref
.Range("A:D").Columns.AutoFit
End With
End Sub
Here is the same code but printing to the terminal if you don't want to dedicate a worksheet to the output.
Sub ListReferencePaths()
'Macro purpose: To determine full path and Globally Unique Identifier (GUID)
'to each referenced library. Select the reference in the Tools\References
'window, then run this code to get the information on the reference's library
On Error Resume Next
Dim i As Long
Debug.Print "Reference name" & " | " & "Full path to reference" & " | " & "Reference GUID"
For i = 1 To ThisWorkbook.VBProject.References.Count
With ThisWorkbook.VBProject.References(i)
Debug.Print .Name & " | " & .FullPath & " | " & .GUID
End With
Next i
On Error GoTo 0
End Sub