VBA - Relative vs Absolute References to XLAM files - vba

my colleague developed an application contained in an xslm file, which has to be accessible to me as well. The file connects to a lot of xlam libraries which are saved locally on his machine due to speed reasons (loading 30 libraries from a shared drive takes ages).
In order for me to access this application a copy was created on a shared drive, where the libraries were copied as well. When my colleague opens another applications (which references the his local xlam libraries) and then the shared xlsm file in one instance of Excel, the references in the shared file get diverted from the shared xlams to the local xlams, making the shared application dysfunctional to me - I cannot access his local xlams. Can this automatic change of xlam references be prohibited? Is there a way to hard-code paths to xlam references just as one would reference a DLL in VBA?
Any help is much appreciated,
Daniel

These xlam files are addIns.
You should be able to reference them at runtime using the path. Here is an example I found for you on MSDN Blog: Accelerating Things (Loading Excel Add-Ins at Runtime) (you could find this with a Google search)
Private Sub Workbook_Open()
call LoadExternalAddIns
End Sub
Private sub LoadExternalAddIns(optional byval sAddinName as string="")
Dim success As Boolean
Dim myAddIn As AddIn
dim sPath2AddIn as string
'dim sAddinName as string
sPath2AddIn = "c:\myaddins\"
if sAddinName="" then sAddinName="myxlam.xlam"
' Load XLL
'success = Application.RegisterXLL(sPath2AddIn & sAddinName)
' Load and install new XLAM
Set myAddIn = Application.AddIns.Add(sPath2AddIn & sAddinName)
myAddIn.Installed = True
' Load known XLAM
For Each myAddIn In AddIns
If myAddIn.Name = "myknownaddin.xlam" Then
myAddIn.Installed = False
myAddIn.Installed = True
End If
Next
End Sub
Of course, this is just a sample to get you started, in fact as you have lot's of AddIns, you might want to list them on a hidden worksheet then call the above procedure LoadExternalAddIns maybe passing the name for each one in a loop such as call LoadExternalAddIns (sAddInName).
More Resources:
Chip Pearson - Installing An Add-In

Related

Need code help on calling a macro from a new VTSO addin for Word

I have created a new addin with a ribbon in MVS. On click of button1 I want to run a macro that is stored in a .dotm file in the Startup folder in Word. The .dotm file is called MyMacros and the macro is titled "TableMacro".
The module name in Word is titled NewMacros and the top rows of the macro in Word are:
Sub TableMacro()
`
` TableMacro
I am sure the macro is started with the code below but even this is guess:
Private Sub Button1_Click_1(sender As Obeject, e As RibbonControlEventArgs) Handles Button1.Click
`code to call TableMacro'
End Sub
I know how to write macros but I have no idea the code needed to trigger the macro stored in the MyMacros.dotm file.
To search all global templates, including the Building Block template, from a VSTO add-in, you can use this:
Dim wApp = Globals.ThisAddIn.Application
Dim i As Integer, Tmplt As Word.Template = Nothing
For i = 1 To wApp.Templates.Count
If wApp.Templates(i).Name = "MyMacros.dotm" Then
Tmplt = wApp.Templates(i)
wApp.Run(Tmplt.Name & "!TableMacro")
End If
Next
The value of performing it this way is you now have an object variable set to a specific global template and you can then get at AutoText, Styles, etc. and of course macros that are stored in that specific global template.
Your VSTO code has a Microsoft.Office.Interop.Word.Application object. Say you're storing that reference in a variable named hostApp, you could do this:
hostApp.Run("TableMacro")
That requires the .dotm file to be the "active" document. If the document isn't active and you have a reference to it (say, theDocument), I think this might work (untested):
hostApp.Run(theDocument.Name & "!TableMacro")
The object VB.NET uses is the same one VBA uses, so if Application.Run "MyMacros!TableMacro" works in VBA, it will work in VB.NET. I'd try to fiddle in VBA first to get the syntax right - you get instant feedback, vs needing to build and launch the host, load the add-in and test the thing with VSTO.
The following Run syntax worked for me from within a VSTO Add-in to run VBA code in a Template loaded as an add-in. It uses the module name plus the macro name.
Keep in mind that Run can only work with public subs...
Globals.ThisAddIn.Application.Run("Module1.TestPublicVarx")

Writing VBA procedure/function to an .xlam addin

I have created a VBA project in Excel and within said project is a module that writes code to another module. I eventually password protected the project so as to keep the code hidden (not realizing right away that this would pose some pretty obvious issues).
When the user interacts with the file, the module attempts to run the code but, encounters an error (a password protected project can't modify itself! Hey we all have our duh moments :p).
So, as a way around this, I figured 'why not make the portion that is edited unprotected', whilst keeping the remainder of the file locked. So I figured I would create an unprotected .xlam add-in, then write said code to this portion. I am having some difficulty figuring how to do this though however. Below is the code I have written that writes code to another module:
Public Sub errorWrite()
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim s As String
Dim d As String
Dim lineNumb As Long
With ThisWorkbook.VBProject.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "Public sub newModule()"
j = j + 1
With ThisWorkbook.VBProject.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "(my code etc..)"
j = j + 1
With ThisWorkbook.VBProject.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "end sub"
end sub
While I have found some info on Stack and elsewhere regarding .xlam add-ins (Updating an xlam add-in using VBA) for example, I'd rather just keep it as simple as possible. Suggestions and hints are much appreciated. Thanks!
Using an unprotected Addin seems like a reasonable approach. But the code you have posted writes to another Module within the Protected addin. You need to reference the other AddIn as a VBProject
Change the name of your Unprotected AddIn to a unique Name (it defaults to VBAProject and does not have to be unique)
Then create a reference to it like this
Dim VBP As VBProject
Set VBP = ThisWorkbook.VBProject.VBE.VBProjects("NameOfYourUnprotectedAddin")
Then all your code uses this reference instead of ThisWorkbook.VBProject
Eg
With VBP.VBComponents.item("NewModule")
.CodeModule.InsertLines j, "Public sub newModule()"
' etc
End With
so I was able to find a way that seems to work, I wrote the code using FSO, then save the file as a .bas module. From there I used the below to have the module imported directly into the .xlam addin as a module:
Public Sub importBas()
Dim VBP As VBProject
Set VBP = Workbooks("example.xlam").VBProject
VBP.VBComponents.Import ("C:\Users\...example.bas")
VBP.VBComponents.Remove
End Sub
Then, in the original module, I deleted procedure within the xlam after execution was finished. Therefore no code can be seen (assuming I remember to use error-handling and disable break mode :p)
I am still curious how to write it 'directly', so I will play around with it more and see if I get it, although this way works
Thank you :)

Saving Template after VBA references are added

I have some code which adds the VBA references i require for using Access. (it checks if the user already has them included).
This all works fine. My problem is when i then go to close word I am prompted to save the Acronym Tools template (Where the macro that this code is stored). Is it possible to do this programtically so that is it done as soon as references are added and the user does not see it happening?
The code i use:
If Not isReferenceLoaded("Access") Then
MsgBox ("Access Object library not found, the script will now attempt to find the library for you.")
'Ensure access library is included so database actions can be done
ID.AddFromFile "C:\Program Files (x86)\Microsoft Office\Office14\MSACC.OLB"
MsgBox ("Access Object library added")
End If
If Not isReferenceLoaded("DAO") Then
MsgBox ("DAO library not found, the script will now attempt to find the library for you.")
'Ensure access library is included so database actions can be done
ID.AddFromFile "C:\Program Files (x86)\Common Files\microsoft shared\OFFICE14\ACEDAO.DLL"
MsgBox ("DAO library added")
End If
The function to see if reference is loaded:
Private Function isReferenceLoaded(referenceName As String) As Boolean
Dim xRef As Variant
For Each xRef In ThisDocument.VBProject.References
isReferenceLoaded = (xRef.Name = referenceName) Or isReferenceLoaded
Next xRef
End Function
If you don't want to save the document with the updated references (they'll be remade next time the doc is open, I guess) add to your Document object (supposedly named ThisDocument) the following subroutine:
Private Sub Document_Close()
ThisDocument.Saved = True
End Sub
This would inhibit the save-check only for ThisDocument. Add a Call ThisDocument.Save statement at the beginning, if you do want to save. However, I would count on the users to save their changes... but that's a matter of strategy, please do as you wish.
Nota Bene: Do not forget to save after adding the function: you'll lose the changes if you forget to; Word will ignore your changes because it was just instructed to do so by the function. :-)

How do I extract from a password protected zip file using VBA?

I have been struggling with this problem for quite some time now, and have trawled through various forums trying to find an answer.
I have a mailbox which received 5 emails each morning which have zipped, password-protected .csv files which require opening and processing in Excel. I am semi-competent in VBA so the Excel side of things is no issue, but it would save me a lot of time if I could automatically unzip these files and save them to a folder.
These emails have the same subjects, attachments and passwords each day, and are from the same sender to the same mailbox. I have code which can automatically process and save the .zip files to a location, but I am still stuck with the problem of having to go into each one, enter the password, open the file, save it, etc.
I have refrained from attaching this code because I would like to see if there are better solutions to my one, and it is the unzipping and password entering that I really need help with. This being said if you would like me to attach my code I will be happy to do so :)
To expand my comment, no input is required - the password is obtained via an event so can be provided in code.
Download the demo project and DLL
In the outlook VBA editor, right click the Project in the tree, Import File and add
cUnzip.cls
mUnzip.bas
Put the DLL in \System32 (or \SysWoW64 on 64bit)
As a naive example, add a new class cWhatever:
Private WithEvents Unzip As cUnzip
Private Sub Class_Initialize()
Set Unzip = New cUnzip
End Sub
Sub RunUnzip()
With Unzip
.ZipFile = "c:\blah\qqq.zip"
.UnzipFolder = "c:\temp"
.Unzip
End With
End Sub
Private Sub Unzip_PasswordRequest(sPassword As String, bCancel As Boolean)
sPassword = "password123"
End Sub
Then to run from elsewhere:
Dim x As New cWhatever
x.RunUnzip
First time you run there is an error highlighting App.EXEName, just delete the App.EXEName &

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!