I am trying to create a module that I can copy into various MS Office applications that will return the current project/application/workbook/document path based on what application is running the VBA code. My code (adapted from other code I found in searches) works, but when I try to compile it, I get a "Compile Error: Variable Not Defined" message because it doesn't recognize the property calls for the other Office Apps. Is there some way to trick the compiler into ignoring these other app property calls, or something else I can do to get my module to compile?
Private Function GetActivePath() As String
' function returns the local path of the current active MSO application
' ** MSOffice applications only
On Error GoTo errHandler
Dim errSub As String: errSub = "GetActivePath"
Dim AppName As String
AppName = Application.name
Select Case AppName
Case "Microsoft Access"
GetActivePath = AccessPath
Case "Microsoft Excel"
GetActivePath = ExcelPath
Case "Microsoft Outlook"
GetActivePath = "*** un-defined ***"
Case "Microsoft Powerpoint"
GetActivePath = PowerpointPath
Case "Microsoft Project"
GetActivePath = ProjectPath
Case "Microsoft Visio"
GetActivePath = VisioPath
Case "Microsoft Word"
GetActivePath = WordPath
Case Else
GetActivePath = "*** un-defined ***"
End Select
Exit Function
errHandler:
Debug.Print errSub & ": " & Err.Description
End Function
Private Function AccessPath() As String
' will not compile unless current app is Access
AccessPath = CurrentProject.Path
End Function
Private Function ExcelPath() As String
' will not compile unless current app is Excel
ExcelPath = ActiveWorkbook.Path
End Function
Private Function PowerpointPath() As String
' will not compile unless current app is Powerpoint
PowerpointPath = ActivePresentation.Path
End Function
Private Function ProjectPath() As String
' will not compile unless current app is Project
ProjectPath = ActiveProject.Path
End Function
Private Function VisioPath() As String
' will not compile unless current app is Visio
VisioPath = ActiveDocument.Path
End Function
Private Function WordPath() As String
' will not compile unless current app is Word
WordPath = ActiveDocument.Path
End Function
You can access PowerPoint's ActivePresentation variable (and presumably the corresponding ones for the other Office applications) as a property of the Application object.
Object properties can be accessed dynamically by name using the CallByName function.
So something like this (not tested) should work:
Private Function GetActivePath() As String
Dim DocPropertyName As String
Dim Document as Object
Select Case Application.Name
Case "Microsoft Access"
DocPropertyName = "CurrentProject"
Case "Microsoft Excel"
DocPropertyName = "ActiveWorkbook"
Case "Microsoft Powerpoint"
DocPropertyName = "ActivePresentation"
Case "Microsoft Project"
DocPropertyName = "ActiveProject"
Case "Microsoft Visio"
DocPropertyName = "ActiveDocument"
Case "Microsoft Word"
DocPropertyName = "ActiveDocument"
Case Else
Err.Raise 1000
End Select
Set Document = CallByName(Application, DocPropertyName, vbGet)
GetActivePath = Document.Path
End Function
Alternatively, since we already know what all the possible property names are, we can make use of late binding by declaring a reference of type Object to the Application object:
Private Function GetActivePath() As String
Dim App as Object
Set App = Application
Select Case Application.Name
Case "Microsoft Access"
GetActivePath = App.CurrentProject.Path
Case "Microsoft Excel"
GetActivePath = App.ActiveWorkbook.Path
Case "Microsoft Powerpoint"
GetActivePath = App.ActivePresentation.Path
Case "Microsoft Project"
GetActivePath = App.ActiveProject.Path
Case "Microsoft Visio"
GetActivePath = App.ActiveDocument.Path
Case "Microsoft Word"
GetActivePath = App.ActiveDocument.Path
Case Else
Err.Raise 1000
End Select
End Function
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/option-explicit-statement
Check if you have the command Option Explicitat the top
Use Option Explicit statement to force explicit declaration of all variables. Attempting to use an undeclared variable causes an error at compile time. The Option Explicit statement is used at the module level only.
Related
I am using an Excel 2013 file, which was updated by me to use PowerQuery for easier Data imports.
It already uses VBA Macros and i would like to include a Warning/MsgBox with a link to download PowerQuery, if it is not already installed.
How would i check for the existence of PowerQuery on the host System?
Adapting the code by Rory at the link i provided you would have something like the following. Note: You could use Rory's additional code to handle 2016 version or earlier ensuring if present is installed.
As you can't use a hyperlink direct i have adapted the Wiktor Stribiżew's code here that allows the user to click OK to go to the download site after getting msgbox saying not installed.
Option Explicit
Private Sub IsPowerQueryAvailable()
Dim downloadlink As String
downloadlink = "https://www.microsoft.com/en-gb/download/details.aspx?id=39379"
Dim bAvailable As Boolean
If Application.Version >= 16 Then
bAvailable = True
Else
On Error Resume Next
bAvailable = Application.COMAddIns("Microsoft.Mashup.Client.Excel").Connect
On Error GoTo 0
If Not bAvailable Then DownloadPowerQuery downloadlink
End If
End Sub
Private Sub DownloadPowerQuery(downloadlink As String)
Dim objShell As Object
Dim Message As String
Dim Wscript As Object
Set objShell = CreateObject("Wscript.Shell")
Message = MsgBox("Would you like to download PowerQuery?", vbYesNo, "Powerquery not available")
If Message = vbYes Then
objShell.Run (downloadlink)
Else
Wscript.Quit
End If
End Sub
I am looking to develop a basic com add-in for Office 2016 (perhaps globally for some of the other office apps - most likely, Excel, Word, PowerPoint, Publisher & OneNote) but in this instance for Outlook 2016 and specifically to add an 'Insert from Scanner" function to the "Microsoft.Outlook.Mail.Compose" Inspector Ribbon in a custom group ("Scanner's & Cameras") on it's "Insert" Tab.
This is my first VSTO com add-in project and I am new to code (but a willing learner!). My extensive research has gleaned little in step-by-step advice but I have identified the following code sample from microsoft
https://code.msdn.microsoft.com/office/VBOutlookRibbonXml-bc478854 which I had hoped to adapt (perhaps utilising the following 'scan' function vb code):
Private Declare PtrSafe Function GetTempPath Lib "kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Private Function TempPath() As String
Const MaxPathLen = 256 ' Max length of the path, just as big as possible
Dim FolderName As String ' Name of the folder
Dim ReturnVar As Long ' Return Value
FolderName = String(MaxPathLen, 0)
ReturnVar = GetTempPath(MaxPathLen, FolderName)
If ReturnVar <> 0 Then
TempPath = Left(FolderName, InStr(FolderName, Chr(0)) - 1)
Else
TempPath = vbNullString
End If
End Function
Sub Scan(control As IRibbonControl)
Const olEditorWord = 4
Dim objCommonDialog As WIA.CommonDialog
Dim objImage As WIA.ImageFile
Dim strDateiname As String
Dim ActiveObject As Object, ActiveTarget As Object
' instantiate Scan WIA objects
Set objCommonDialog = New WIA.CommonDialog
Set objImage = objCommonDialog.ShowAcquireImage
strDateiname = Environ$("TEMP") & "\Scan.jpg" ' set temporary file
If Not objImage Is Nothing Then
If Dir(strDateiname) <> "" Then Kill strDateiname
objImage.SaveFile strDateiname 'save into temp file
DoEvents
'Insert the picture into the office application:
Select Case Trim$(Replace$(Application.Name, "Microsoft", ""))
Case "Excel"
Set ActiveObject = CallByName(Application, "ActiveSheet", VbGet)
Set ActiveTarget = CallByName(Application, "ActiveCell", VbGet)
If ActiveTarget Is Nothing Then
'Insert into a chart, etc.
ActiveObject.Shapes.AddPicture _
strDateiname, False, True, 0, 0, -1, -1
Else
'Insert into a sheet at the active cell
ActiveObject.Shapes.AddPicture _
strDateiname, False, True, ActiveTarget.Left, ActiveTarget.Top, -1, -1
End If
Case "Outlook"
Set ActiveObject = CallByName(Application, "ActiveInspector", VbGet)
If ActiveObject Is Nothing Then
MsgBox "Create a new mail and try again"
Exit Sub
End If
With ActiveObject
If .IsWordMail And .EditorType = olEditorWord Then
.WordEditor.Application.Selection.InlineShapes.AddPicture strDateiname
End If
End With
Case "PowerPoint"
Set ActiveObject = CallByName(ActiveWindow, "View", VbGet)
ActiveObject.Slide.Shapes.AddPicture strDateiname, False, True, 0, 0, -1, -1
Case "Publisher"
Set ActiveObject = CallByName(Application, "ActiveDocument", VbGet)
ActiveObject.ActiveView.ActivePage.Shapes.AddPicture strDateiname, False, True, 0, 0, -1, -1
Case "Word"
Set ActiveObject = CallByName(Application, "Selection", VbGet)
ActiveObject.InlineShapes.AddPicture strDateiname
End Select
End If
End Sub
Unfortunately the above Microsoft Office Development Centre Sample code is for Office 2010 and VS 2010 and so can't be accessed.
How can I adapt the sample for use with Office (Outlook) 2016 and VS 2015?
Can the above VB code block be inserted (as written) to replace the code of one the test buttons on the sample, or will it require adapting further?
You can simply copy the classes in the sample project into your VS 2015 project. The sample project is using a project template for a version of Office that VS 2015 doesn't support, and the Interop references would be different as well.
If you're looking to add a custom Ribbon button, just add a Ribbon (Visual Designer) item (or Ribbon (XML) item) from the Office/SharePoint node in the Add New Item dialog.
I have created a VB add-in for excel with the below code. However, when I open a new excel workbook and attempt to add the reference to the newly created dll, I get an error: "Can't add a reference to the specified path." I am using Excel 2010 on a 32 bit machine. The add-in was created with Visual Studio 2010.
Public Class Utils
'!Deletes old reference and adds new reference. Must be used when switching to new machine.
Function addReference() As Boolean
If Dir("C:\refname.dll") <> "" Then
Dim ref
For Each ref In Application.ActiveWorkbook.VBProject.VBE.ActiveVBProject.References
If ref.name = "refname" Then
Application.ActiveWorkbook.VBProject.VBE.ActiveVBProject.References.Remove(ref)
End If
Next
Application.ActiveWorkbook.VBProject.VBE.ActiveVBProject.References.AddFromFile("C:\refname.dll")
addReference = True
Else
addReference = False
End If
End Function
End Class
You can try this:
Option Explicit
Sub AddReference()
Dim VBAEditor As VBIDE.VBE
Dim vbProj As VBIDE.VBProject
Dim chkRef As Reference
Dim BoolExists As Boolean
Set VBAEditor = Application.VBE
Set vbProj = ActiveWorkbook.VBProject
'~~> Check if reference is already added
For Each ref In vbProj.References
If ref.Name = "refname" Then
BoolExists = True
GoTo CleanUp
End If
Next
vbProj.References.AddFromFile "C:\WINDOWS\system32\refname.dll\3"
CleanUp:
If BoolExists = True Then
MsgBox "Reference already exists"
Else
MsgBox "Reference Added Successfully"
End If
Set vbProj = Nothing
Set VBAEditor = Nothing
End Sub
I got this code from: How to add a reference programmatically
I want to write a single VBA code module that works on the three main Office Apps (Excel, PowerPoint, Word).
Because the object models are different in each app, if I write code that's specific for PowerPoint while in the Excel VBE, the project won't compile. The way to go first appears to be to use conditional compiler constants. But this still causes the VBE to spit out errors depending on which MSO app the VBE is currently being hosted in.
In the simplified example below, I want to add a picture to a sheet, slide or document, depending on which app the VBA code is being run from. If I try to compile it in Excel, the PowerPoint code doesn't compile (even though it's within a conditional compiler If...Then statement!) and vice-versa. How does one get round this without adding references to the other MSO apps (as this causes compatibility issues when distributing to different MSO versions)?
The way the compiler continues to look at code that should be effectively "commented out" by the conditional compiler constants is very odd/annoying behaviour!
' Set the compiler constant depending on which MSO app is hosting the VBE
' before saving as the respective .ppam/.xlam/.dotm add-in
#Const APP = "EXL"
Option Explicit
Dim curSlide As Integer
Dim curSheet As Integer
Public Sub InsertPicture()
Dim oShp as Shape
#If APP = "PPT" Then
' Do PowerPoint stuff
' The next 2 lines will throw "Invalid qualifier" and
' "Variable not defined" errors respectively when compiling in Excel.
curSlide = ActiveWindow.View.Slide.SlideIndex
Set oShp = ActivePresentation.Slides(curSlide).Shapes.AddPicture & _
(filename, msoFalse, msoTrue, 0, 0)
#ElseIf APP = "EXL" Then
' Do Excel stuff
curSheet = ActiveWindow.ActiveSheet
Set oShp = ActiveSheet.AddPicture(filename, msoFalse, msoTrue, 0, 0)
#ElseIf APP = "WRD" Then
' Do Word stuff
#End If
End Sub
Since I'm unable to answer my own question:
Expanding on your idea KazJaw, I think something like this may work, replacing the CreateObject function with GetObject (because the instance will already exist since the procedure is being called from within an add-in):
' CONDITIONAL COMPILER CONSTANTS
' Set this value before saving to .ppam, .xlam or .dotm
#Const APP = "EXL" ' Allowed Values : PPT, EXL or WRD
Sub One_Sub_For_Word_Excel_PP(filename As String, Optional SlideIndex as Integer)
#If APP = "PPT" Then
Dim appPPP As Object
Set appPPT = GetObject(, "PowerPoint.Application")
appPPT.ActivePresentation.Slides(SlideIndex).Shapes.AddPicture & _
(filename,msoFalse,msoTrue,0,0)
#ElseIf APP = "EXL" Then
Dim appEXL As Object
Set appEXL = GetObject(, "Excel.Application")
appEXL.ActiveSheet.AddPicture(filename, msoFalse, msoTrue, 0, 0)
#ElseIf APP = "WRD" Then
Dim appWRD As Object
Set appWRD = GetObject(, "Word.Application")
appWRD.ActiveDocument.AddPicture(filename, msoFalse, msoTrue, 0, 0)
#End If
End Sub
You could try:
Public AppName as String
Public App as Object
Sub One_Sub_For_Word_Excel_PP(filename As String, Optional SlideIndex as Integer)
AppName = Application.Name
Set App = Application
Select Case AppName
Case "Microsoft PowerPoint"
App.ActivePresentation.Slides(SlideIndex).Shapes.AddPicture & _
(filename,msoFalse,msoTrue,0,0)
Case "Microsoft Excel"
App.ActiveSheet.AddPicture(filename, msoFalse, msoTrue, 0, 0)
Case "Microsoft Word"
App.ActiveDocument.AddPicture(filename, msoFalse, msoTrue, 0, 0)
End Select
End Sub
Alternatively, write a COM Add-in.
As I stated in my comment- I can't imagine the situation I would like to use the solution you are trying to prepare. However, there is one solution even you set lot's of limitations (including not setting references to other application libraries). Please keep in mind that such attempt will be not efficient and I would never recommend anything like this.
The following test subroutine works for all three applications: MS Word, MS PowerPoint, MS Excel. Additional information in comments inside the code.
Sub One_Sub_For_Word_Excel_PP()
Dim XLS As Object
Dim PP As Object
Dim WRD As Object
'this will open instances of all application- to avoid any errors
Set XLS = CreateObject("Excel.Application")
Set PP = CreateObject("PowerPoint.Application")
Set WRD = CreateObject("Word.Application")
'your code here
'remember- do not use vba constants like msoFalse but use _
their numeric values instead
'simple test
If Application.Name = "Microsoft Excel" Then
'do things only for excel
Debug.Print XLS.Name
ElseIf Application.Name = "Microsoft PowerPoint" Then
'do things only for PP
Debug.Print PP.Name
Else
'do things only for Word
Debug.Print WRD.Name
End If
Set XLS = Nothing
Set PP = Nothing
Set WRD = Nothing
End Sub
Isn't it
#Const APP = "EXL"
#If APP = "PPT" Then
etc.?
I'm assuming that you want the same code to be able to run from within any VBA-enabled app (but not necessarily to invoke other apps). So ...
Sub One_Sub_To_Rule_Them_All()
' Modified version of KazJaw's previous post
Dim oApp As Object
Set oApp = Application
Select Case oApp.Name
Case Is = "Microsoft Excel"
'do things only for excel
Case Is = "Microsoft PowerPoint"
'do things only for PP, eg
MsgBox oApp.ActivePresentation.Fullname
Case Is = "Microsoft Word"
' do wordthings
Case Is = "Visio or CorelDraw or Whatever"
' do whatever things
Case Else
MsgBox "Jumping up and down and waving hands and running around like headless chicken"
End Select
Set oApp = Nothing
End Sub
All the same, I wouldn't do it this way. Apart from the other objections, you need to treat the apps as objects in order for the code to compile, and when you do that, you toss out intellisense. Not a trivial loss. Sure, you can get around that by developing the Word part in Word, the PPT part in PPT ... but in that case, why not just make separate code modules?
I want to remove all vba-modules from an MS Word template using VBScript. I wrote the following script.
const wdDoNotSaveChanges = 0
WScript.Echo "starting Word..."
Dim oApplication, doc
Set oApplication = CreateObject("Word.Application")
WScript.Echo "opening template..."
oApplication.Documents.Open "path\to\test.dot"
Set doc = oApplication.ActiveDocument
Dim comp, components
Set components = oApplication.ActiveDocument.VBProject.VBComponents
For Each comp In components
components.Remove comp
Next
WScript.Echo "exiting..."
doc.close wdDoNotSaveChanges
oApplication.Quit wdDoNotSaveChanges
When running similar code in a VBA-module in Word, that works, but when I run this VBScript, I get this error: test.vbs(14, 2) Microsoft VBScript runtime error: Invalid procedure call or argument
It turns out that it is not possible to remove the VBComponent named "ThisDocument" (If you right click it in the IDE the remove option is not active). You can use something like:
For Each comp In components
If comp.Name <> "ThisDocument" Then
components.Remove comp
End If
Next