Compile error during workbook open - excel

I have a workbook which uses a certain type, let's call it T, that's in a module (DLL) defined in my References -- all good.
I created some code that's called in Workbook_Open() that will add a reference to the DLL if it's not already in the list of references. This is so I can give the workbook to someone and they won't have to deal with creating the reference by hand.
My problem is that when I open the workbook (double-click), before Workbook_Open() gets executed (and the Refence can be set) I get thrown into the debugger which points to and complains that type T, defined in the not-yet-referenced DLL, is not defined. Well no kidding it's not.
This seems a little chick and egg. Anyone seen this before? How did you fix it?

Like other people have said, if you are not going to add the reference to the DLL until Workbook_Open, you have to use late binding to connect to the stuff in the DLL.
However, I think you are going about this the wrong way. Instead of adding a reference to the DLL in Workbook_Open, have it already referenced and check .IsBroken instead. Then you can have code to do what ever is needed to fix the broken reference.
This keeps the benefit of early binding and you won't get the same kind of compile errors if the reference to the DLL is broken.
NOTE: You can still get compile errors, but they won't be for the missing DLL. While you have a broken reference, any use of functions from the VBA Strings module will trigger a compile error if the function isn't prefaced with Strings. For example, the following code will cause a compiler error before Workbook_Open runs if you have any broken references.
Private Sub Workbook_Open()
Dim r As Reference
For Each r In ThisWorkbook.VBProject.References
If r.IsBroken Then
MsgBox "Found broken reference." & vbCrLf _
& Mid(r.FullPath, InStrRev(r.FullPath, "\") + 1)
End If
Next r
End Sub
To fix, prefix Mid and InStrRev with Strings..
MsgBox "Found broken reference." & vbCrLf _
& Strings.Mid(r.FullPath, Strings.InStrRev(r.FullPath, "\") + 1)

Related

Changing the location of a referenced library

I have a C# Library which I use in an Excel VBA project. I wish now wish to restructure my project and want to move the library to a different folder. However after removing the reference to the library and deleting all instances of the library from my computer, the References Available box for the project still shows the reference to the old library location even though it no longer exists on the computer and nothing I do seems to be able to remove that reference.
I don't know if this makes any difference, but the project is an Excel AddIn.
I had this problem back in 2019, and received a response to a question with the same title in April 2019. On that occasion the the issue was resolved by following the advise given. However this time the system stubbornly refuses to play ball.
The procedure that I was advised to follow in 2019 was:
Remove the reference
Save the File and close it
Delete the dll (in my case the .tbl) file from your computer. Do not save it anywhere.
ReOpen the file
Check if there is any reference still there. If not copy the dll to a new folder and then set a reference again. Save and close the file.
ReOpen to check if everything is OK
I was going to try code to remove the reference, but the following code did not find the reference
Sub delRef()
deleteReference ("FiskDLLlib")
End Sub
Sub deleteReference(s As String)
Dim oFs As Object, oReferences As Object, oReference As Object
Dim sFileName As String, sRefName As String, sRefFileName As String
Dim toBeDeletedRef As String
Set oReferences = Application.Workbooks("fiskAIWkBook.xlam").VBProject.References
For Each oReference In oReferences
sRefFileName = oReference.FullPath
sRefName = oReference.name
If sRefName = s Then
toBeDeletedRef = s
Exit For
End If
Next
If toBeDeletedRef <> "" Then
Debug.Print oReference.FullPath
Else
Debug.Print "No Reference found for " & s
End If
End Sub
Similarly the Watches panel didn't show the library.
I have subsequently discovered a registry key
\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib{B22F6C9D-53E0-4D1B-9596-56AA1EA4BDBA}\1.0\0\win32
the value of which was set to the location of the reference I am trying to remove. I changed this to a reference to the new location in which I want to store my dll (.tlb) file. This at least looked like it would allow me to create a new reference in the References box but on clicking OK I got a message saying "Error in loading DLL"
I resolved my problem in the end by removing all references to the library in the Registry with a registry Cleanup tool (ReImage) before copying the library to its new directory and then reregistering it with regASM

Clas variable declaration not working to with dynamic class loading in VBA,

Any help with dynamically loading of classes /variables in VBA?
I have an excel sheet and other few dependent sheets, depending on the requirement, on click of a button I am loading other sheets.
Say Main workbook -> Loads sheet1.xlam and related classes ( cls_one.cls) on click of button.
I am using below code to load sheet1.xlam and cls_one.cls
Step 1: Load Main.xlsm
Step 2 load - sheet1.xlsm
Set addedWs = ActiveWorkbook.Sheets.Add(Type:=path, after:=ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.count))
Step 3: Load Cls_one.cls dynamically using the below method.
Public Function InsertClass(ByRef oWB As Workbook, ByVal className As String, ByVal filePath As String, Optional bUAT As Boolean = False) As Boolean
On Error GoTo Errhandler
oWB.VBProject.VBComponents.Import filePath
InsertClass = True
Exit Function
Errhandler:
If err <> 0 Then
If Not bUAT Then
MsgBox (err.Description)
End If
err.Clear
End If
InsertClass = False
End Function
Above code works fine, however, I have a reference to cls_one in sheet1.xlsm which never works, on loading or give error Undefined object error.
Public Myclassone As cls_one
this variable declaration is in sheet1.xlsm.
I tried flipping steps loading class first and sheet1.xlam next, but still getting the same error with Excel 2013, this piece of code works fine with 2010.
Trying to understand what is the best way to reference dynamically loaded classes in forms or other classes?
Also, I tried changing error preferences in tools -> Options -> Break on Unhandledexception.
You can't early-bind to something that's only going to exist at run-time, by definition - there's no way that could have worked:
Public Myclassone As cls_one
If cls_one doesn't exist at compile-time, then the module can't be compiled.
this piece of code works fine with 2010
No. This piece of code works fine if it's not in any execution path that involves the module declaring a public variable of a type that doesn't exist... regardless of what version the host application is (this behavior is purely VBA, nothing to do with Excel).
That's kind of a hack though: a VBA project will not compile (through Debug -> Compile), but will happily run anyway if the entry point doesn't involve loading the module: that's because of the somewhat-interpreted nature of VBA.
Proof:
Module1
Option Explicit
Public foo As Something '<~ undefined, won't compile
Module2
Option Explicit
Public Sub Test()
Debug.Print "I can run even if the project doesn't compile!"
End Sub
You can run Module2.Test regardless of whether the project compiles, because Module1 isn't in the picture at all. Now change Module2.Test to this:
Public Sub Test()
foo.DoStuff '<~ expect fireworks
End Sub
When I ran this, Excel just outright crashed.
So the bottom line is this: you can reference a non-existing class in a module. The project won't be compilable, but if no code references the non-existing class then the project will be executable anyway, and the non-compilable module can then be executed in another execution context (i.e. from a separate entry point), after the class is added.
I would not recommend using non-compilable code for anything remotely important though, since doing that takes away the only compile-time validation you have in the VBE for your code - consider reviewing Rubberduck inspections in that case (several inspections flag run-time error situations statically).
A better, more viable solution would be to reconsider the dependency chain and the overall approach.

How to find if any compile errors exists due to missing DLL reference?

We have developed a program in Excel VBA.
Say for instance we are having a compiler problem in one of the functions (Debug->Compile VBAProject gives some type mismatch error).
Without clicking that option, is there any way after the program load, we can intimate the user by showing a message like "You have compiler error in your test program, please click Debug->Compile VBAProject and fix the compiler errors"?
Actual problem is, a program (Excel VBA) is having compiler error, but the user doesn't know it. He just started running the program which results in failure.
So after the program load, without manually clicking Debug->Compile, after the excel is launched, it should throw a pop-up saying "you are having compiler errors, Do Debug->Compile VBAProject and fix it".
The actual error is "Compiler error: User-defined type not defined".
As part of our software installation, we are installing certain DLLs in the customer machine. The customer can add those DLLs as reference and edit their program.
As the Excel program is having a compiler error, "OnProgramLoaded interpose" function is not called. So the code inside that function is not executed. The user has no clue that this code is not called.
Additional information: Earlier we have the "Compile On Demand" checkbox as checked. So even though there is a compiler error in the program, the "OnProgramLoaded interpose" function is getting called. Now for different reason, we have removed the check box from "Compile On Demand", so what happens now is, since there is compiler error, it is not executing the other functions too.
So what we are trying to do is, as soon as the program is launched, we need to give the message to user that the program is having compiler errors. Fix it and restart the program.
You can check, if your desired VBA references are already enabled:
Private Sub CheckVBAReferences()
Dim dict As Object
Dim oRef As Object
Dim i As Long
Set dict = CreateObject("Scripting.Dictionary")
For Each oRef In ThisWorkbook.vbProject.References
dict.Add oRef.Description & ", " & oRef.fullpath, oRef.GUID
Debug.Print oRef.Description & ", " & oRef.fullpath
Next oRef
If Not dict.Exists("Microsoft Excel 16.0 Object Library, " & _
"C:\Program Files\Microsoft Office\Root\Office16\EXCEL.EXE") Then
MsgBox "Please add a VBA reference to ..."
' or set it according to following link
End If
dict.RemoveAll
Set dict = Nothing
End Sub
And then you may set a missing reference according to this answer.

Injecting module into excel and run it

In VBScript I'm injecting a module into excel files and then I want to run them. The injection goes fine but when I run it says it can't find it. I've put the location in the trust center so it should trust it just fine. The module has a public sub named Run as well.
Dim XL
Set XL = CreateObject("Excel.Application")
Dim book
Set book = XL.Workbooks.Open(wkpath + "\" + wkname, 0, false)
book.VBProject.VBComponents.Import "C:\MyModule.bas"
XL.Application.Run("'" + wkname + "'!Run")
The paths and names all work out. Am I doing something wrong with this? What are my next debugging steps here.
[EDIT]
Actually it looks like some references aren't being selected now so it's getting an error about user-defined type not defined, but that's not the error that I get from VBScript. I had to do what is happening manually and then I saw that error.
So I was missing a reference but the error was pretty bogus. I figured this out by stopping my process once the excel file was open and adding in the module manually and trying to run the function. So I added the following in vbscript and it worked:
book.VBProject.References.AddFromFile "path to my xlam that has the type I use"

Cannot add DLL reference with AddFromFile

I am trying to use a COM visible .NET DLL from Excel VBA. I have been successful when registering the DLL using regasm and then manually adding a reference to it via the Tools -> References menu item in the VBA Developer window.
However, I am now trying to register the DLL without using the regasm command so that the Excel file can be used on any computer without registering the DLL. So far this is what I've tried:
Dim JART_Instance As Object
Sub Initialize()
Dim RefPath As String, X As Byte
Const RefName = "JART xxx"
RefPath = Application.ActiveWorkbook.Path & "\JART\JART.dll"
With ActiveWorkbook.VBProject.References
For X = 1 To .Count
If .Item(X).Description Like RefName Then
.Remove .Item(X)
End If
Next
.AddFromFile (RefPath)
End With
End Sub
Sub PostInitialize()
Set JART_Instance = New JART.MainJobControl
End Sub
I have added a reference to "Microsoft Visual Basic for Applications Extensibility 5.3". When I run the above code I get "Run-time error '48': Error in loading DLL". I have loaded this DLL a couple times using regasm. Do I need to do something like change the GUID's used in the project and retry. I've seen code examples where this is supposed to work.
If I reference the tlb file instead of the .dll I do not get the DLL loading error. Instead I get an error whenever I try to use the JART_Instance variable saying that the reference has not been set. Even though PostInitialize gets called directly after Initialize and there is no evidence that any of the code threw an error or failed to run. If I try to put a "Stop" command in the PostInitialize function it tells me that it "Cannot enter break-mode at this time".
Any ideas, thanks.
Excel-DNA has a helper function that does this for Com Addins written on that platform.
It appears to:
load the addin
register it with CoRegisterClassObject
add it's progid to the registry
add it to the registry key HKCU\Software\Microsoft\Office\Excel\Addins
call Application.ComAddins.Update in Excel
remove all the previous registry entries
Unregister the object with CoRevokeClassObject
It would appear that once Excel has loaded the addon, it doesn't unload when the registry entries are removed and CoRevokeClassObject is called. It stays loaded until Excel closes and releases it.
So, it's doable but not easy.
Okay so I've resorted to doing a shell command to register the DLL with regasm. Here is my code:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim strWinCmd As String
Dim retVal As Double
strWinCmd = "cmd.exe %SystemRoot%\Microsoft.NET\Framework\v2.0.50727\regasm.exe /u /codebase /tlb .\JART\JART.dll"
retVal = Shell(strWinCmd, vbHide)
End Sub
Private Sub Workbook_Open()
Dim strWinCmd As String
Dim retVal As Double
strWinCmd = "cmd.exe /c %SystemRoot%\Microsoft.NET\Framework\v2.0.50727\regasm.exe /codebase /tlb """ & Application.ActiveWorkbook.Path & "\JART\JART.dll"""
Call Shell(strWinCmd, vbNormalFocus)
Call Button_Handlers.Sleep(1500)
Call Button_Handlers.Initialize
End Sub
For reference the Button_Handlers.Sleep just calls the system sleep method and Button_Handlers.Initialize does this:
Sub Initialize()
'This JART.MainJobControl is the main class in the JART DLL
Set JART_Instance = New JART.MainJobControl
'Use JART_Instance
End Sub
So basically I'm trying to register the DLL at start-up and un-register it on close. My problem is that when I open this file on a new PC I get an error in Button_Handlers.Initialize. It tells me that I'm trying to use an undefined class (JART.MainJobControl), as if the DLL wasn't referenced. If I try to reopen the file everything works fine???
The way I'm doing this is manually adding the reference to the DLL on a machine that already has it registered with regasm. I then save this excel file and transport it to a machine that hasn't had the DLL registered and try to open it and run it. I think that since the reference is already added to the excel file, alls the code has to do is register it with regasm. Does anyone know why this wouldn't work? Am I not sleeping long enough. I may post this as a separate question.

Resources