Cannot add DLL reference with AddFromFile - excel

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.

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

Excel VBA 7.1 comdlg

This is for VBA 7.1 excel's macro. I am trying to export a file to a location and it crashes at this part.
The error I am running into is "Can't find project or library". The reference not found is the "comDlg". I know this is a referencing error. However, I am unsure how to resolve it.
Private Sub cmdBrowse_Click()
comDlg.Filter = "XML Files"
comDlg.DialogTitle = "Save Export File As..."
comDlg.ShowSave
txtExportFile.Text = comDlg.Filename
I had the same issue with a UCase() function call, a simple VBA.UCase() solved it. If you need any more information please let me know. I believe this macro/excel sheet was made on an older version and carried over to a 64bit Windows 10.
You're trying to use VB6 controls that are unsupported in a 64-bit VBA host. Try a 32-bit Excel install, but consider using Application.GetSaveAsFileName/GetOpenFileName for this kind of thing:
Private Sub cmdBrowse_Click()
Dim promptResult As Variant
promptResult = Application.GetSaveAsFilename("file.xml", "XML Files (*.xml),*.xml", 1, "Save Export File As...")
If VarType(promptResult) = vbBoolean Then Exit Sub 'prompt was cancelled
txtExportFile.Text = CStr(promptResult)
End Sub
As for VBA failing to resolve the UCase function, ...wild things happen with broken project references: I'd expect these to just go away once the legacy 32-bit stuff is removed from the project.

VBA Dim Object error

If I Run
Sub test()
Dim Template_Excel_Instance As excel.application
Set Template_Excel_Instance = CreateObject("excel.application")
End Sub
my code breaks with an error "Automation Error, Library not registered"
If I run
Sub test()
Dim Template_Excel_Instance As object
Set Template_Excel_Instance = CreateObject("excel.application")
End Sub
It runs fine. Is there any way to fix this? Reason I ask is that this issue only affects one PC, despite having the same references as all other PCs. The first error is not coming up anywhere else
Does that PC have a different version of Excel?
The problem is with As Excel.Application. If you don't have the appropriate reference defined, then the VBA compiler will not recognise the type. Yes, VBA does have a compilation step. If you do have the reference defined, then this is sensitive to the application version (just the major part of the application version I think), so is therefore inherently non-portable.
In your latter example, you are using late binding, so only COM object registration is required, not any specific library to be added to your project. For portability, this is the way to go.
Firstly, have you tried repairing the install?
Secondly, is there a reason you are instantiating a second instance of Excel?
Thirdly, let's get a grip on what is happening, please run this code and report back.
Option Explicit
Sub TestWhatVersionDoesCreateObjectReturn()
Dim obj As Object
Set obj = CreateObject("Excel.Application")
Debug.Print obj.Version
End Sub
Sub TestWhatOtherVersionsAreCreatable()
Dim lLoop As Long
For lLoop = 7 To 16
Dim obj As Object
Set obj = Nothing
On Error Resume Next '///set break on unhadnled errors
Set obj = CreateObject("Excel.Application." & CStr(lLoop))
On Error GoTo 0
If Not obj Is Nothing Then Debug.Print obj.Version
Next
End Sub
Also, look for multiple installations of Excel. The code at this blog page will read your registry and write the results to an Excel worksheet. You say you have repaired the install but there might be other Excel installations interfering. The code in that blog will highlight multiple Excel installs.
Ok, from your feedback you say all the Excel libraries version return 14, implying no multi version installs. Hmmm, we ought to consider that actually the library that error is complaining about isn't Excel but a dependency.
Other possible leads
Social MSDN - automation error. Library not registered - solved by cleaning registry of legacy "Microsoft.Office.Interop.Excel" keys

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"

Change .xla File with MSBuild

I'm trying to create a build script for my current project, which includes an Excel Add-in. The Add-in contains a VBProject with a file modGlobal with a variable version_Number. This number needs to be changed for every build. The exact steps:
Open XLA document with Excel.
Switch to VBEditor mode. (Alt+F11)
Open VBProject, entering a password.
Open modGlobal file.
Change variable's default value to the current date.
Close & save the project.
I'm at a loss for how to automate the process. The best I can come up with is an excel macro or Auto-IT script. I could also write a custom MSBuild task, but that might get... tricky. Does anyone else have any other suggestions?
An alternative way of handling versioning of an XLA file is to use a custom property in Document Properties. You can access and manipulate using COM as described here: http://support.microsoft.com/?kbid=224351.
Advantages of this are:
You can examine the version number without opening the XLA file
You don't need Excel on your build machine - only the DsoFile.dll component
Another alternative would be to store the version number (possibly other configuration data too) on a worksheet in the XLA file. The worksheet would not be visible to users of the XLA. One technique I have used in the past is to store the add-in as an XLS file in source control, then as part of the build process (e.g. in a Post-Build event) run the script below to convert it to an XLA in the output directory. This script could be easily extended to update a version number in a worksheet before saving. In my case I did this because my Excel Add-in used VSTO, and Visual Studio doesn't support XLA files directly.
'
' ConvertToXla.vbs
'
' VBScript to convert an Excel spreadsheet (.xls) into an Excel Add-In (.xla)
'
' The script takes two arguments:
'
' - the name of the input XLS file.
'
' - the name of the output XLA file.
'
Option Explicit
Dim nResult
On Error Resume Next
nResult = DoAction
If Err.Number <> 0 Then
Wscript.Echo Err.Description
Wscript.Quit 1
End If
Wscript.Quit nResult
Private Function DoAction()
Dim sInputFile, sOutputFile
Dim argNum, argCount: argCount = Wscript.Arguments.Count
If argCount < 2 Then
Err.Raise 1, "ConvertToXla.vbs", "Missing argument"
End If
sInputFile = WScript.Arguments(0)
sOutputFile = WScript.Arguments(1)
Dim xlApplication
Set xlApplication = WScript.CreateObject("Excel.Application")
On Error Resume Next
ConvertFileToXla xlApplication, sInputFile, sOutputFile
If Err.Number <> 0 Then
Dim nErrNumber
Dim sErrSource
Dim sErrDescription
nErrNumber = Err.Number
sErrSource = Err.Source
sErrDescription = Err.Description
xlApplication.Quit
Err.Raise nErrNumber, sErrSource, sErrDescription
Else
xlApplication.Quit
End If
End Function
Public Sub ConvertFileToXla(xlApplication, sInputFile, sOutputFile)
Dim xlAddIn
xlAddIn = 18 ' XlFileFormat.xlAddIn
Dim w
Set w = xlApplication.Workbooks.Open(sInputFile,,,,,,,,,True)
w.IsAddIn = True
w.SaveAs sOutputFile, xlAddIn
w.Close False
End Sub
I'm not 100% sure how to do exactly what you have requested. But guessing the goal you have in mind there are a few possibilities.
1) Make part (or all) of your Globals a separate text file that is distributed with the .XLA I would use this for external references such as the version of the rest of your app. Write this at build time and distribute, and read on the load of the XLA.
2) I'm guessing your writing the version of the main component (ie: the non XLA part) of your application. If this is tru why store this in your XLA? Why not have the main part of the app allow certain version of the XLA to work. Version 1.1 of the main app could accept calls from Version 7.1 - 8.9 of the XLA.
3) If you are just looking to update the XLA so it gets included in your version control system or similar (i'm guessing here) maybe just touch the file so it looks like it changed.
If it's the version of the rest of the app that you are controlling i'd just stick it in a text file and distribute that along with the XLA.
You can modify the code in the xla programmatically from within Excel. You will need a reference to the 'Microsoft Visual Basic for Applications Extensibility..' component.
The examples on Chip Pearson's excellent site should get you started.

Resources