Excel-VBA Import Module from text file (without requiring trust center) - excel

I'm trying to create a custom library of subs and functions saved as .txt files in a network location where various users of the workbook I'm creating can import them based on which userform function they select. The users of the workbook are only going to be using the workbook through the userforms. I don't want to require them to modify their security trust center settings for this import libraries code to work, so I don't want to use the wb.VBproject.References.import command, or make them add-in.
I found this method which works great, but only as long as no other workbooks are open. If another workbook is open, this code ends up inserting a new module in that other workbook instead, and then the userform calls are meaningless.
I don't understand how this is possible, since I'm referencing all of this code within a "With ThisWorkbook" statement. What am I doing wrong?
Sub importLib(libName As String)
Application.DisplayAlerts = False
Path = "C:\Users\(username)\Desktop\excelLibraries\" 'Example only, my path is actually a network location
lib = Path & libName
Dim wb As Workbook
Set wb = ThisWorkbook
With wb
On Error Resume Next
Dim wbModules As Modules
Set wbModules = .Application.Modules 'wb=ThisWorkbook, but doesn't point to this workbook when other workbooks open???
'wb.VBProject.VBComponents.Import lib '---> library saved as .bas, but this requires the user to change their security settings
'wb.VBProject.References.AddFromFile lib '---> library saved as add-in reference, but this requires user to change security settings
'----This method works when no other workbooks are open
For Each a In wbModules 'Clear any previous Library Module
If a.Name = "Library" Then
a.Delete
Exit For
End If
Next
Set m = wbModules.Add 'Create a new Library Module
m.Name = "Library"
m.InsertFile lib 'Insert the code
On Error GoTo 0
End With
Application.DisplayAlerts = True
End Sub
Sub callsub()
importLib "library1.txt"
End Sub

Making the following changes seemed to work for me.
Change Dim wbModules As Modules to Dim wbModules. Otherwise I get a type-mismatch error.
Remove the .Application in Set wbModules = .Application.Modules.
EDIT:
As #Mathieu Guindon has pointed out in a comment, but worth preserving in the question body, Application refers to the Excel application instance, the host. The Modules collection contains all open modules, regardless of the workbook.

Try referencing the following through the VBA editor
Tools > References > Microsoft Visual Basic For Applications Extensibility 5.3
This will allow the code to remove "Library" then reimport the new code.

Related

Is it possible to automatically input VBA code when a new sheet is generated?

Is it possible to have a new Excel sheet created and then VB code automatically put in that sheet?
Here is what I have done so far. A sheet called Template has the input for all of the information that users need to input. I have various checks to make sure that all fields are filled out and are filled out correctly before anything else will execute. When they click on a certain cell to execute the script it will open a Word document and import all required information in it. Then a new sheet in Excel is created. A name is given to the new sheet, based on what was selected in the ComboBox (cboSites) from the Template sheet. I also have a check in place to make sure there already isn't a sheet with the same name. I have all of this working without any issues.
From here what I would like to do and can't think of how to do it, is when the new sheet is created I want VBA code automatically dumped in this new sheet. I know the code that I want to use, but I just have no idea how to get it so it will automatically put that code with that sheet.
Is this possible to do or can only a new sheet be created and formatted, without being able to import any code into it?
Here is a sample code which will insert
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
MsgBox "Hello World"
End Sub
in a new sheet in a new workbook.
Sub Sample()
Dim wb As Workbook, ws As Worksheet
Dim VBP As Object, VBC As Object, CM As Object
Dim strProcName As String
Set wb = Workbooks.Add
Set ws = wb.Sheets(1)
Set VBP = wb.VBProject
Set VBC = VBP.VBComponents(ws.Name)
Set CM = VBC.CodeModule
strProcName = "Worksheet_SelectionChange"
With wb.VBProject.VBComponents( _
wb.Worksheets(ws.Name).CodeName).CodeModule
.InsertLines Line:=.CreateEventProc("SelectionChange", "Worksheet") + 1, _
String:=vbCrLf & _
" Msgbox ""Hello World"""
End With
End Sub
Please amend it to suit your needs.
You need to ensure that Trust access to Visual Basic Project is selected. To select it, follow the steps mentioned in THIS LINK
I am not aware of an easy way to put code in an Excel file.
Someone might think about changing the XML structure directly (xlsx files are basically a zipped directory of xml and code files).
But did you consider using XLAM (Excel addin) files, that can contain code and be imported for all users, who ever need to use it. And would open up with Excel, when the users start it. Depending on your setup, this could help you?
Look into the VBProject property of the workbook you are generating. You should be able to manipulate it, adding new VBComponents items containing the code you want.
http://msdn.microsoft.com/en-us/library/office/ff194737.aspx
This will require the appropriate security settings to do.
Here's a thread on another site related to this topic:
http://www.mrexcel.com/forum/excel-questions/663848-access-vbulletin-project-not-trusted.html
I haven't tried it myself, so I can't give much more detail.

How to properly rename CodeModule of ThisWorksheet

I'm trying to rename the ThisWorkbook code module of an Excel worksheet using VBA Extensibility.
I accomplish this with the line
ThisWorkbook.VBProject.VBComponents("ThisWorkbook").Name = "wb"
I can see the change in the VB editor and also access the workbook's properties, e.g. Debug.? wb.Name.
However: If I save and close the file and then reopen it, I get strange behavior. If the code module was empty before renaming it, it reverts back to the old empty ThisWorkbook name.
If it was not empty or was populate before the saving, I now have both, an empty ThisWorkbook module (that would fire events if there were any) - and the filled wb module - which does not fire workbook events:
Has anyone seen this behavior - and knows a fix/workaround?
Quick answer: ThisWorkbook.[_CodeName] = "newName"
Detailed answer
When I add references to the Microsoft Visual Basic For Applications Extensibility 5.3 and run your line
ThisWorkbook.VBProject.VBComponents("ThisWorkbook").Name = "wb"
The ThisWorkbook Name property isn't actually being modified
Saving and re-opening the file causes a duplication of the ThisWorkbook object
Which pretty much means now I have two Workbook objects within one workbook and both are named ThisWorkbook
The workaround is to rename the ThisWorkbook to wb using the Properties window or
ThisWorkbook.[_CodeName] = "newName" because ThisWorkbook.CodeName is read-only.

Excel's VBA ActiveWorkbook is "Nothing" when "Enable Editing" from ProtectedView

I have a VBA macro which is called from a spreadsheet function (user defined function, UDF). When the spreadsheet is downloaded from the internet and the user has set "Trust Center" settings accordingly, the spreadsheet will open in so the called "Protected View". The function will not be called. A button "Enable Editing" is shown.
If the button is pressed, the spreadsheet is "trusted" and reopened normally, starting calculation, and hence calling the user defined function.
However, in that VBA function the value of Application.ActiveWorkbook is Nothing. This can be verified in the debugger.
Since I just need to read some properties (like path name) of the spreadsheet, I could alternatively inspect the availability of Application.ActiveProtectedViewWindow which should reference to the protected version of the workbook. In the debugger, this object can be inspected. However, running in release (without debug) the value of Application.ActiveProtectedViewWindow is also Nothing.
Both behaviors - especially the first one - appears to be a bug present in Excel 2010 and 2013 (see also a post at the MSDN forum ).
Question: Is there a way to get hold of properties of the active workbook after it has been enabled for editing?
PS: As a follow up to the nice observation of Siddharth Rout, that "ThisWorkbook" might work: In my case, the macro is not part of the Workbook being openend. The UDF is defined in an XLA. Hence, ThisWorkbook would reference the XLA. I do need to get the ActiveWorkbook (= the workbook calling the UDF) instead of ThisWorkbook (= the workbook running the UDF).
IMPORTANT REQUIREMENT:
My function is called as a user defined function, i.e., execution order is determined by Excel updating the cell.
The function is not part of the workbook being opened. It is part of an XLA.
I cannot add any code to the workbook which is opened.
Summary: The problem can be replicated and there are some possible workarounds. The most promising one - resulting from a chat - is to use ActiveWindow.Parent instead of ActiveWorkbook.
I was able to replicate the problem.
I tried
Private Sub Workbook_Open()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ActiveWorkbook Is Nothing)
End Sub
And I got True
However, then I tried this and it gave me False
Private Sub Workbook_Open()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ThisWorkbook Is Nothing)
End Sub
Now answering your question...
Question: Is there a way to get hold of properties of the workbook after it has been enabled for editing?
Yes. Use ThisWorkbook instead of ActiveWorkbook
Followup From Comments
Once the workbook completely loads after you exit the Protected Mode, you would be able to access the ActiveWorkbook object. To test this, put this code in the protected file.
Private Sub Workbook_Activate()
MsgBox "Application.ActiveWorkbook Is Nothing = " & _
CStr(Application.ActiveWorkbook Is Nothing)
End Sub
You will notice that you get a False
So once your workbook loads, your add-in can use ActiveWorkbook to interact with the opened file.
Here is another test
Private Sub Workbook_Activate()
MsgBox ActiveWorkbook.Path
End Sub
This is what I got the moment, I exit the Protected Mode
FOLLOWUP FROM CHAT
Using ActiveWindow.Parent.Path instead of ActiveWorkbook.Path would solve the problem.
I had this same issue today, and neither the accepted answer nor any other answer that I could find on this page or through searching the Google-verse worked for me. I'm using the version of Excel within Office 365, and I figured that was at the root of the problem.
I eventually came to a solution after finding a Microsoft Excel 2010 resource and hitting the old try-fail cycle for a few hours. Here's what I got:
Option Explicit
Public WithEvents oApp As Application
Private bDeferredOpen As Boolean
Private Sub Workbook_Open()
Set oApp = Application
End Sub
Private Sub oApp_WorkbookActivate(ByVal Wb As Workbook)
If bDeferredOpen Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
End If
End Sub
Private Sub oApp_WorkbookOpen(ByVal Wb As Workbook)
Dim oProtectedViewWindow As ProtectedViewWindow
On Error Resume Next
'The below line will throw an error (Subscript out of range) if the workbook is not opened in protected view.
Set oProtectedViewWindow = oApp.ProtectedViewWindows.Item(Wb.Name)
On Error GoTo 0
'Reset error handling
If oProtectedViewWindow Is Nothing Then
bDeferredOpen = False
Call WorkbookOpenHandler(Wb)
Else
'Delay open actions till the workbook gets activated.
bDeferredOpen = True
End If
End Sub
Private Sub WorkbookOpenHandler(ByVal Wb As Workbook)
'The actual workbook open event handler code goes here...
End Sub
The difference between the 2010 solution and mine is that I had to call Workbook_Open and explicitly set the oApp variable there, because without that assignment neither the oApp_WorkbookActivate nor oApp_WorkbookOpen functions would fire when I opened the file.
Figured that someone else might be able to benefit from this, so I posted it, despite the fact that the most recent update to this thread is better than 2 years old.
Best.
Try using Application.Caller.Parent.Parent instead of Application.Activeworkbook
This is not a complete answer to the original question, but a (dirty) workaround for a problem related to this.
I needed ActiveWorkbook to infer the workbooks path, that is ActiveWorkbook.Path.
An alternative to using ActiveWorkbook.Path is to check for Application.RecentFiles(1).Path which is the path of the most recently opened file. In many cases this will be the workbook for which the user just has "Enabled Editing". However, of course, this method may fail: In the case the used opened another sheet, then enabling the previously opened sheet.
(Note: ActiveWorkbook.Path give the path of the folder, while Application.RecentFiles(1).Path gives the complete path of the file, so there has to be some post-processing).
I know it's old thread, but i came across the same issue and i found solution ;)
The only way to go around it, is to use variable type Workbook
Dim wbk as Workbook
Set wbk = Application.ProtectedViewWindows(index).Workbook
Warning:
ActiveSheet returns Nothing when active window is protected too.
Dim wsh As Worksheet
Set wsh = wbk.Worksheets(index)
Try this code it works.
If (UCase(ActiveWorkbook.Name) = ucase("<YOUR XLA NAME WITH EXTENSION>")) Then
End
End If
Set wbObj = ActiveWorkbook
First time when you run the macro, it just ends without doing anything. Second time it picks up the proper file.

Outside code for Excel 2003 macros

Does someone know how to write VBA code in outside file, which is "imported" into Excel 2003 VBA macro every time you run this macro in Excel - like self updating code?
The idea is I write code in outside editor (VIm), and every time I start Excel macro, macro checks for newer data, import that code inside if necessary and then run it (may be one or more functions/subs).
The steps are: open Excel, open XLS file, click on the button, the macro starts, checks for newer outside data (code I have written in outside editor), if data is new, delete old code, import outside code and then actually run macro itself.
Anyone?
Assuming your using this VIM (a text editor) to write .bas files, you can then use the many, many functions for overwriting modules or procedures from this site Programming the VBE.
So, your structure would like something like this:
Sub MyMacro()
Overwrite MyModule in this Workbook with MyModule in .bas file (you could do line count checks or something if you need to, or just overwrite each time to ensure latest code
Call MyModule
End Sub
There are obvious checks you want to incorporate, I would think, to ensure that your latest code always works with the workbook...
checks for newer outside data (code I have written in outside editor), if data is new, delete old code, import outside code and then actually run macro itself.
Found solution (partly from http://www.cpearson.com/excel/vbe.aspx) 10x Scott:
Sub AddOutsideCode()
Dim VBProjekt As VBIDE.VBProject
Dim VBKomponenta As VBIDE.VBComponent
Set VBProjekt = ThisWorkbook.VBProject
Call DeleteAllButThis(ThisWorkbook, "Fixed")
Set VBKomponenta = VBProjekt.VBComponents.Import(Filename:="C:\bat\bat.bas")
VBKomponenta.Name = "OutsideCode"
End Sub
'Delete all modules but named
Private Sub DeleteAllButThis(ByVal wb As Workbook, ByVal CompName As String)
Application.DisplayAlerts = False
On Error Resume Next
For Each komponenta In ThisWorkbook.VBProject.VBComponents
If komponenta.Name <> CompName Then
If komponenta.Type = vbext_ct_StdModule Then
ThisWorkbook.VBProject.VBComponents.Remove komponenta
End If
End If
Next komponenta
On Error GoTo 0
Application.DisplayAlerts = True
End Sub
But how to check if the outside code changed or not?

Is it possible to deploy a VB application made in Excel as a stand alone app?

Basically wondering if there's a way for me to create a VB application in Excel and have it run without a full version of MS Office. The VB application would load an Excel sheet that would import a CSV onload, then print a PDF of the sheet and close.
If you have any questions, let me know.
No. Not without converting to a standalone application.
If you had were familiar with VB6 (and had access to it; it's no longer for sale), you could create a VB6 app. that references the excel COM components (still need to be installed on each target PC).
Otherwise, build an app. using VB.NET and use Office VSTO 2010 (need to reference the Office PIAs)
How to: Target Office Applications Through Primary Interop Assemblies
Just a little conflict. In office, you code with VBA, which is different than VB. What you would need to do is create a VB app that uses excel libraries or something to do some meaningful work.
The short answer is no.
You could write an external visual basic script that calls in to office and opens excel using some excel libraries, if memory serves me correctly however - you'd still require office installed on this machine. (Unfortunately I can't find a link at the moment to back this up)
Your best bet is to parse the CSV data yourself and generate a PDF from that.
There is some information here: http://www.rlmueller.net/ReadCSV.htm on how to Read CSV data using VBS (to get the examples to run, you should simply have to rename the .txt to .vbs and double click it.)
I'll leave you to find out how you'd then generate the PDF.
I don't think however this is the best solution to your problem - a full .NET application or perhaps some Python would likely serve you better.
Code will go in several different places, "ThisWorkbook" object and the "UserForm" code.
"ThisWorkbook" contains code that will determine if the UserForm is the only Excel thing (workbook) open, and if it is it will hide the Excel application and hide the workbook itself. and if other workbooks are open it simply hides the workbook. I have it set to hide the application and the workbook in both cases so that a new instance of Excel can be opened after the UserForm is running without pulling up the workbook associated with the UserForm. The code for this is below (goes into the "ThisWorkbook" object):
Private Sub WorkBook_Open()
Dim wb As Workbook
Set wb = Workbooks("MyBook.xlsm")
If Workbooks.Count > 1 Then
wb.Windows(1).Visible = False
Else
wb.Windows(1).Visible = False
Application.Visible = False
End If
UserForm1.Show vbModeless
'Put defaults and populations here
End Sub
The UserForm1.Show vbModelessallows for Excel to be used while the UserForm is active.
A couple of notes on this section:
"UserForm1" is the name of my UserForm, change this to the name of yours
Where I Set wb = Workbooks("") change inside the quotes to the name of the
workbook the UserForm is in
The IfElse statement could be eliminated and moved to the If, if you don't need any other action on the opening with no other workbooks open
The next section of code goes in the UserForm Code. I have a button set up to show the Excel workbook in order to edit it and whatnot, you could have a region you click if you don't want a button to show up. When you want to activate the Excel sheet and the application will need to be activated. I unload (deactivate) the active thing (the UserForm). This bit of code isn't necessary if the user doesn't need access to the spreadsheet:
Private Sub See_Excel_Click()
Dim wb As Workbook
Set wb = Workbooks("MyBook.xlsm")
wb.Windows(1).Visible = True
Application.Visible = True
wb.Sheets("Sheet1").Activate
Unload Me
End Sub
Within the userform there should be a way to handle what happens when the userform is closed, as the excel application and workbook will stay open hidden in the background. I have the action close the workbook and the application. A quick note, if you set the Cancel = True then the red x button won't close the userform. The code I use for this is:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = 0 Then
Cancel = False
Dim wb As Workbook
Set wb = Workbooks("MyBook.xlsm")
wb.Windows(1).Visible = True
Application.Visible = True
ThisWorkbook.Saved = True
ThisWorkbook.Activate
If Workbooks.Count > 1 Then
ActiveWorkbook.Close
Else
Application.Quit
End If
End If
End Sub
That is it for the code that goes inside the UserForm. And the code that is necessary to have the UserForm in VBA act as it's own application while allowing for Excel to operate normally at the same time as the UserForm.
To summarize what happens:
When the Workbook is launched the workbook is hidden, and if no other workbook is open the the Excel application is hidden.
The UserForm is initiated to allow for Excel to be used at the same time
When the spreadsheet is activated again excel is re-enabled and the application and un-hide the worksheet
When the user form is closed, the workbook is closed, and if there are no other workbooks the excel application is closed
If you set defaults or populate ComboBoxes put them in the "WorkBook" object code.

Resources