Excel VBA prevent from importing missing references - vba

I am working on a sheet on Excel 2016, but when I want my collegues to test it on their machine which runs on Excel 2013, it throws error :
"Can't find project or library error"
I know you can easily get rid of this error by going in Tools->References and uncheking the "MISSING" library.
But the problem is it's too complicated when you have too many people that don't necessarily know what VBA is and that get pissed off when asking them to follow a simplistic tutorial.
Is there a trick to get rid of all missing references at the start?

Yes, this is possible.
However, you will open up a host of new problems since you'll need one more reference to the "Microsoft Visual Basic for Application Extensibility" (to access the VBIDE objects) and you'll need to have the security settings to enable "Trust Access To Visual Basic Project" (which is disabled by default, for good reasons).
That being said:
If you abide by those two criteria, you can run the following routine to remove any broken references from the active workbook:
Option Explicit
Sub RemoveReferences()
Dim VBAEditor As VBIDE.VBE
Dim vbProj As VBIDE.VBProject
Dim chkRef As VBIDE.Reference
Set VBAEditor = Application.VBE
Set vbProj = ActiveWorkbook.VBProject
For Each chkRef In vbProj.References
If chkRef.IsBroken Then
vbProj.References.Remove chkRef
End If
Next
Set vbProj = Nothing
Set VBAEditor = Nothing
End Sub

Related

Getting UCase, Trim, Left libary errors in VBA

I have a application that opens excel files. When I run a macro function in my excel that was opened with the application. I'm getting Compile error "Can't find project or library" on the "UCase" "Trim" "Left" just to name a few. In my macro functions, I have multiply cases of using the above functions. I also have references to "Visual Basic For Application", "Microsoft Excel 12.0 Object Library", "OLE Automation", "Microsoft Office 12.0 Library", Microsoft Forms 2.0 Object Library."
If I run excel by itself without the application, there is no errors. Is there any explanation to why this is happening? Libraries mix match? Works fine for the developer and a few but as for the rest of users, they will get these errors.
It's because those methods belong to Excel Application so that you must call them by preceding their name with the Excel object name (and possibly with its relevant member, too) you must have instantiated before
for instance
example 1: late binding
Option Explicit
Sub LateBindingExcel()
Dim xlApp As Object 'declaring your application object as of "Object" type doesn't require any reference to Excel library
' open an Excel session
Set xlApp = CreateObject("Excel.Application")
' call Excel application WorksheetFunction.Trim()
MsgBox xlApp.WorksheetFunction.Trim(" see how spaces get trimmed by this function ")
End Sub
example 2: early binding
Option Explicit
Sub EarlyBindingExcel()
Dim xlApp As Excel.Application 'declaring your application object as of "Excel.Application" type requires adding Excel library reference to your project
' open an Excel session
Set xlApp = CreateObject("Excel.Application")
' call Excel application WorksheetFunction.Trim()
MsgBox xlApp.WorksheetFunction.Trim(" see how spaces get trimmed by this function ")
End Sub
one sensible difference between the two binding "styles" is that the latter allows you exploiting IntelliSense features while the former doesn't
It is likely due to the 'Visual Basic for Applications' in the Tool reference refers to non-presence library. This may happen if the developer use the library in their customized directory instead of default library.
There are 2 means to solve the issue.
Make global replacement of all string related functions (Format, Left, Right, Mid, Trim etc) with prefix VBA. e.g. VBA.Left. This will force the use of functions within standard library.
Move all your excel sheets to another new workbook and then, select the 'Visual Basic for Applications' from Tool reference.

Why doesn't code work in VB.net, but works in VBA; GetObject

VBA code works great:
Sub testVBA()
Dim wb As Object ' Lotus123.Document
Set wb = GetObject("S:\Temp\T\0375D.WK3", "Lotus123.Workbook")
End Sub
VB.net code fails:
Sub TestVBNet()
Dim wb As Object ' Lotus123.Document
wb = GetObject("S:\Temp\T\0375D.WK3", "Lotus123.Workbook")
End Sub
In VB.net I get a FileNotFoundException: "File name or class name not found during Automation operation."
As I can run it from VBA that means the file exists and that the class name exists. So why doesn't it work and how can I fix it in VB.net.
EDIT: I guess I'm not sure how to start diagnosing this: Obviously the class exists on my computer but somehow VB.net doesn't manage to find it. Maybe VB.net uses a different method to activate the class. Maybe a registry entry is missing. I am glad for any suggestions.
Edit 2: I also tried using CreateObject and got this error: "Cannot create ActiveX component." Not unexpected.
For some reason VB.net cannot find the class name "Lotus123.Workbook" so I tried getting the file without the class name and it works fine in XP.
Dim wb As Object ' Lotus123.Document
wb = GetObject("S:\Temp\T\0375D.WK3")
EDIT: In Win8 64bit the above doesn't work; just hangs.
The code below works in XP 32 bit as well as in Win8 64 bit. I checked with process monitor what is happening under the hood. CreateObject checks for the CLSID in the registry using the given object. Then it looks up the necessary info using the CLSID.
Public Shared Function GetLotusWB(ByVal sFile As String) As Object
'HKCU takes precedence if exists
'HKCU\Software\Classes\Lotus123.Workbook\CLSID
'HKCU\Software\Classes\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}
'normally this is used because Lotus123 doesn't create HKCU entries
'HKCR\Lotus123.Workbook\CLSID = {29130007-2EED-1069-BF5D-00DD011186B7}
'HKCR\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}\InprocHandler32 = ole32.dll
'HKCR\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}\LocalServer32 = C:\Lotus\123\123w.exe
'using object as that sometimes works better
Dim LotusObj As Object = CreateObject("Lotus123.Workbook")
'get application
'need a reference to Lotus 123 else declare as Object
Dim LotusApp As Lotus123.Application = LotusObj.Application
'FAILS: LotusApp.Visible = True
'open file; also works fine As Lotus123.Document
Dim ldoc As Object = LotusApp.OpenDocument(sFile)
'visible and activate (must declare as Object else gives exception)
Dim appObject As Object = ldoc.Application
appObject.Visible = True
ldoc.Activate()
Return ldoc
End Function
This works great because it creates the "Lotus123.Workbook" which is used to get the application object.
Load the file into an Excel workbook. It should be able to convert the lotus123 workbook on the fly.
First of all, check to make sure your inclusions (I think under Tools menu, includes or references or something like that) include the library that references Lotus123.Document. Chances are it's in the "Microsoft Excel 14.0 Object Library" or similar.
I've heard it said that VB is not VBA!

Advice on Debugging Machine-Specific Excel VBA Issue

I have an Excel workbook with dependencies on code in other other Excel workbooks (these dependent .xls's are VB-level references, i.e. via the Tools->References dialog box in the VBA editor), and some dependencies on dll's such as:
Microsoft Scripting Runtime
Microsoft Forms 2.0 Object Library
This sheet has worked for about 2 years on around 20 machines running Windows XP and Office XP. Recently we have taken delivery of 3 new machines (same OS, same office version) which refuse to run this sheet. When the sheet opens, it throws a 'Compile Error', and the session hangs.
If I open the sheet on a 'bad' machine, hold down the left shift key to stop macro's from running, and then go to VBA Editor->Debug->Complie VBAProject, it compiles fine. I am then able to save the sheet and open it normally on a 'bad' machine. However this new version of the sheet refuses to run on a 'good' machine!!
I think there must be some sort of version mismatch between certain dll's on the 'good' and 'bad' machines. How do I establish what is causing the issue? Are there any tools available for comparing versions of com components?
Two suggestions
1) First open the file with macros disabled. And then check VBA Editor | Tools | References. Check for any missing references and then let us know what are they. We will take it from there.
2) For references like "Microsoft Scripting Runtime Object Library" I never use Early Binding. Early Binding is the major cause for these kind of errors. Just FYI: Early Binding is creating references beforehand via VBA Editor | Tools | References. I would recommend changing your code to Late Binding. Here are 2 examples of the same code using "Microsoft Scripting Runtime Object Library" with Early Binding and Late Binding
EARLY BINDING EXAMPLE
'~~> Set Reference to "Microsoft Scripting Runtime Object Library"
Sub EBExample()
Dim FSO As Scripting.FileSystemObject
Dim SourceFolder As Scripting.Folder
Dim FileItem As Scripting.File
Set FSO = New Scripting.FileSystemObject
Set SourceFolder = FSO.GetFolder(SourceFolderName)
For Each FileItem In SourceFolder.Files
'~~> You code
Next FileItem
End Sub
LATE BINDING EXAMPLE
'~~> This doesn't need a reference
Sub LBExample()
Dim FSO As Object, SourceFolder As Object, FileItem As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Set SourceFolder = FSO.GetFolder(SourceFolderName)
For Each FileItem In SourceFolder.Files
'~~> You code
Next FileItem
End Sub
As for me I use early binding to take advantage of Intellisense but then convert it to late binding to avoid version-specific code before distributing the code. That ways the code always works. :)
IMP NOTE: Late Binding fails in scenarios where the destination machine doesn't have the relevant dll registered.
RECOMMENDED LINK:
Topic: Using early binding and late binding in Automation
Link: http://support.microsoft.com/kb/245115
Hope this helps
Sid
The easiest way I've found to compare references on two different machines is to run a little macro on each pc to show me all the details.
Make sure Excel is set to trust access to the VBA project object model and run the below code on the two versions of your macro.
Sub GetReferences()
Dim r As Object
For Each r In ActiveWorkbook.VBProject.References
Debug.Print r.Name, r.Description, r.FullPath
Next r
End Sub

Adjusting a VB Script to Programmatically Create a Folder triggered by an E-mail

This is my first time asking a question to y'all. I'm a SQL Developer by trade, and am very green when it comes to VB.
I manage a on-line database for my department, Quickbase, and with this website we manage report requisitions. I create a ticket for each one, and that ticket creates an e-mail notifying the dev. responsible for that assignment. We have folders set up for each request that comes in, and it is very laborious and frustrating to manually create said folders.
So I asked and looked around, coming across a script that was able to do what I needed, or so I am told. However, I'm not sure how to customize it to my needs, nor implement it correctly. This is where I need your assistance, fair programming gods of SO, please help me slay this dragon, and all the riches of the realm will be yours*!
Outlook VBA
Sub MakeFile(MyMail As MailItem)
myMailEntryID = MyMail.EntryID
Set outlookNameSpace = Application.GetNamespace(“MAPI”)
Set outlookMail = outlookNameSpace.GetItemFromID(myMailEntryID)
MyArgument = OutlookMail.Subject
Dim sMyCommand = “c:\makefile.bet ” & MyArgument
Shell “cmd /c ” & sMyCommand, vbHide
End Sub
Makefile.bat
#echo off
cls
mkdir %1
The webtsite URL is: www.quickbase.com
The root folder path: h:///ntsp/data/reports - criteria/quickbase docs/[Folder to be created]
*Riches are not monetary, but the feeling of goodness, and completeness only gained by helping a fellow nerd out, oh and it makes the e-peen grow might and strong!
being a fellow nerd I am going to get you started in the right direction. I think we can achieve what you want with VBA alone and do not need to use shell.
First we need to hook an event of when this all should happen. I imagine that is when your inbox gets an email. If I am right here is the start of this.
Please understand 2 important things.
This only works on items coming into your inbox. Thus if you already have a rule moving items to another folder it will not work.
You need to "Test" the email coming in - my example shows a test of the subject. It will only call you special routine IF and ONLY IF the subject has in it "My Test"
To enter the code in the Visual Basic Editor:
On the Tools menu, point to Macro, and then click Visual Basic Editor.
In the Project pane, click to expand the folders, and then double-click the ThisOutlookSession icon.
Type or paste the following code into the Code window.
Dim WithEvents objInboxItems As Outlook.Items
' Run this code to start your rule.
Sub StartRule()
Dim objNameSpace As Outlook.NameSpace
Dim objInboxFolder As Outlook.MAPIFolder
Set objNameSpace = Application.Session
Set objInboxFolder = objNameSpace.GetDefaultFolder(olFolderInbox)
Set objInboxItems = objInboxFolder.Items
End Sub
' Run this code to stop your rule.
Sub StopRule()
Set objInboxItems = Nothing
End Sub
' This code is the actual rule.
Private Sub objInboxItems_ItemAdd(ByVal Item As Object)
If Item.Subject = "My Test" Then
Call checkForFolder
End If
End Sub
Private Sub checkForFolder()
End Sub
On the File menu, click Save VbaProject.OTM.
You can now run the StartRule and StopRule macros to turn the rule on and off.
Quit the Visual Basic Editor.
(You might need to start and stop Outlook to get the variables to "Hook".
Once you get this working and understood, then you can remove the on off switches.
Then you have to decide your test for making a new folder so that we can then test the email and compare it to existing folders and then make a new one etc.

How do I programatically add a reference to a VBA project?

I'm deploying an early bound styled VBA module that needs Scripting.Dictionary and RegExp.
The script, predictably, fails when it runs on another computer.
The user has to go to Tools->Reference in the VBA IDE and add a reference to those two libraries manually to make it work.
Hence lies the problem. Asking the non-technical end user to go to the IDE and manually add references is asking way too much of them.
The other alternative is to rewrite the whole (very long script written by someone else) to use late binding. I rather not take this path if there are other methods.
As an altervative, some people suggest adding a reference programatically like so:
Application.VBE.ActiveVBProject.References.AddFromFile [Path to library]
Is this the correct solution and if so are there any downsides of this strategy?
If not, are there other methods that will to enable the code to remain early bound yet does not require references to be added manually by the user.
Suggestions involving direct calls to the Win32/64 API are also welcome.
Thanks.
In my own limited environment (small # of other people using spreadsheets I develop, relatively standard machine setups), if I create the file and add the references, and then give a copy to someone else, they can open it with no problems and not have to do anything, so keep that in mind with this answer. (I'm wondering why that doesn't work for you.) Also, this was with Excel.
Rather than adding a reference from a file path, you might consider using the GUID property instead.
Here is some code I once used to automatically create references in a newly created workbook. (It's part of a script that would export code, references, and unit tests on worksheets to text for use with Subversion and then later reconstitute the workbook from the text files.) You might find it useful to your situation. (EH and cleanup removed to keep it short...)
'Export refs in existing workbook to text file
Private Sub exportRefs_(srcWbk As Workbook)
Dim fs As FileSystemObject
Set fs = New FileSystemObject
Dim tsout As TextStream
Set tsout = fs.CreateTextFile(fs.BuildPath(getTargetPath_(srcWbk), "refs.refs"))
Dim ref As Reference
For Each ref In Application.ThisWorkbook.VBProject.References
Call tsout.WriteLine(ref.GUID)
Next ref
'<EH + cleanup...>
End Sub
'Add refs to newly created workbook based on previously exported text file
Private Sub importRefs_(wbk As Workbook, path As String)
Dim fs As FileSystemObject
Set fs = New FileSystemObject
Dim tsin As TextStream
Set tsin = fs.OpenTextFile(path)
Dim line As String
Dim ref As Reference
While Not tsin.AtEndOfStream
line = tsin.ReadLine()
Set ref = Nothing
On Error Resume Next
Set ref = wbk.VBProject.References.AddFromGuid(line, 0, 0)
On Error GoTo 0
If ref Is Nothing Then
Debug.Print "add failed: " & line
End If
Wend
'<EH + cleanup...>
End Sub
Like, I said, limited environment, but hopefully it helps.