How to simulate ThisPresentation in PowerPoint VBA - vba

I would like to be able to access the document properties of a PowerPoint add-in file (a presentation saved as "PowerPoint Add-in (*.ppa)", from some VBA code in the add-in itself.
If it helps to understand the problem, what I'm actually trying to do is read a custom document property that stores the version number of the add-in, so that I can display that in a dialog box.
With Word & Excel I can do this using ThisDocument & ThisWorkbook, both of which return a reference to the document containing the running code. However, there is no ThisPresentation equivalent in PowerPoint.
For a standard PowerPoint presentation or template, I could use ActivePresentation. However, this method won't work for an add-in.
Any ideas? Please, no suggestions about where else I should stick the version number :-)

Like everyone else I expected a ThisPresentation object in PowerPoint. I thought of another way to accomplish it, without a hardcoded filename. Obviously any piece of code would need to know how to distinguish between the projects. I chose to use the projectname for this (default name "VBAProject" in the Project Explorer): it is not used for anything else, no user will change it and if it is protected they can't.
Here is my code (change MyProject into your own projectname):
Function ThisPresentation() As Presentation
Dim p As Presentation
For Each p In Presentations
If p.VBProject.Name = "MyProject" Then
Set ThisPresentation = p
Exit Function
End If
Next
End Function

REVISED FEB 2, 2010: Cleaned up answer to only show the final solution
Here's the way to do what was asked, no DLLs. Really simple:
Sub ReturnPPAasPresentation()
Dim p As Presentation
Set p = Presentations("presentation1.ppa")
Dim title As String, version As String
version = p.CustomDocumentProperties("Version").Value
title = p.BuiltInDocumentProperties("Title").Value
MsgBox "Version: " & version & " of " & title, vbOKOnly, title
End Sub

Credit goes to macnerd nerd for the general idea, but added the AddIn functionality that was requested by the OP. Unfortunately, AddIns don't have VBProject names, so not quite as robust:
Function ThisPresentation(project_name As String) As Object
Dim p As Object
all_presentations = Array(Application.AddIns, Application.Presentations)
For Each pArray In all_presentations
For Each p In pArray
Debug.Print p.FullName
If InStr(p.FullName, project_name) > 0 Then
Set ThisPresentation = p
Exit Function
End If
Next
Next
End Function

Related

Run a VBA routine from VBA IDE?

I want to get a list of routines from a VBA project, then run the macros selected by the user.
The image below shows the native "Macros" box. I want to extend this functionality to multiple macros across multiple documents.
I found this link which solves the first part of the problem. Now that I have my list, how do I run a selected routine by name?
Hello and welcome to SO
Below is code sample how to execute VBA macro using code. You need to add some form to select documents and macros for execute. This depends on your implementation.
Sub RunMacroUsingCode()
Dim vbaProjectName As String
vbaProjectName = "InventorVBA"
Dim vbaModuleName As String
vbaModuleName = "m_Tests"
Dim vbaMacroName As String
vbaMacroName = "RunMultipleMacrosTestCall"
Dim vbaProject As InventorVBAProject
For Each vbaProject In ThisApplication.VBAProjects
If vbaProject.name = vbaProjectName Then Exit For
Next
Dim vbaModule As InventorVBAComponent
For Each vbaModule In vbaProject.InventorVBAComponents
If vbaModule.name = vbaModuleName Then Exit For
Next
'Using result is optional
Dim result As Variant
Call vbaModule.InventorVBAMembers(vbaMacroName).Execute(result)
End Sub
Function RunMultipleMacrosTestCall()
Call MsgBox("TEST")
RunMultipleMacrosTestCall = True
End Function

My shortcut key will not allow me to run a message box

Previous versions of this code had no message box, which sometimes resulted in the wrong workbook being closed. I added an okcancel message box to keep this from happening, but the message box doesn't show up when I use a shortcut key to open it. What am I missing?
Sub openerQuick()
Dim myfile As String
Dim clientID As String
Dim PDSfilename As String
Dim myopener As Variant
clientID = ActiveCell
PDSfilename = ActiveCell.Offset(0, 1)
myfile = "N:\DOWNLOAD\FILEDIR\" & clientID & "\original\" & PDSfilename
Set wbOpener = Workbooks.Open(myfile)
If MsgBox("Okay to close?", vbOKCancel) = vbOK Then
ActiveWorkbook.Close
End If
End Sub
I doubt the MsgBox itself has anything to do with losing the macro shortcut key.
Shortcut keys are defined by a hidden member attribute value, and the VBE has a tendency to lose member attributes when you rewrite a method's signature, or rewrite a module*; it's possible that modifying the code caused the previously existing attribute to somehow get lost.
Remove the module from the project, pick "Yes" when prompted whether to export or not
Open the exported file in Notepad++ your favorite text editor
Locate the procedure
Add the attribute if it's not there
Save the file if it was changed, re-import into the project
The member attribute should look something like this:
Public Sub OpenerQuick()
Attribute OpenerQuick.VB_ProcData.VB_Invoke_Func = "A\n14"
'...code....
End Sub
That exact attribute associates Ctrl+Shift+A to the macro; change the A for whichever letter rocks your boat to change the shortcut.
When you record a macro in Excel and specify A for a shortcut key, the macro recorder automatically adds this hidden attribute for you.
* Rubberduck's module rewriters have that very problem and it's driving me nuts.
In a module write the following 2 subs:
Public Sub OpenerQuick()
If MsgBox("Okay to close?", vbOKCancel) = vbOK Then ActiveWorkbook.Close
End Sub
Public Sub InitiateMe()
Application.OnKey "{a}", "OpenerQuick"
End Sub
Run only InitiateMe. Now, when you press a, InitiateMe would be triggered.

Adding Macro Code to Word Documents using VB.Net

Ok so let me explain in detail.
Suppose there is a word file called "Word.doc"
What I want to do is basically use VB.NET for doing the following things :
Open the word document
Add a macro code
For e.g
Add the following macro code to the Word Document
Sub AutoOpen()
Msgbox
End Sub
And then save this document.
Just remember that I want to insert macro code to a word document not retreive an already present macro code from a document
Here is a simple VBA macro that shows how to use the Word object model to add a macro to a document (VB.NET code will be almost identical).
Sub NewDocWithCode()
Dim doc As Document
Set doc = Application.Documents.Add
doc.VBProject.VBComponents("ThisDocument").CodeModule.AddFromString _
"Sub AutoOpen()" & vbLf & _
" MsgBox ""It works""" & vbLf & _
"End Sub"
End Sub
Note that running this code requires that access to the VBA project object model is trusted (this needs to be enabled in the trust center in the the Word options).
Working with objects in the VBA Editor through the object model requires a reference to the Microsoft Office VBA Extensibility 5.3 object model which you can find in the COM tab of Project/Add References in Visual Studio.
I like to add an Imports statement to the top of the code so that I don't have to always write out the full namespace qualification:
Imports VBE = Microsoft.Vbe.Interop
An AutoOpen macro needs to be a "public" Sub in a normal code module. Assuming you want to add a new code module to the document, use the VBComponents.Add method and specify the enumeration type vbext_ct_StdModule.
By default, the VBE will name the new module "Module#" - an incrementing number. If you ever need to programmatically address this module again (to see if it exists, for example) it would probably be better to assign a name to it.
Code as a string is added using the AddFromString method.
If you're adding code to a document the possibility exists that the document is of type docx, meaning it cannot contain macro code. A document must have the extension docm in order to contain macro code. So you may need to use the SaveAs method on the document to change the file type and the name!
Private Sub InsVbaCode_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InsVbaCode.Click
'Helper method to get current Word instance or, if none, start one
GetWordProcess()
Dim doc As Word.Document = WordApp.ActiveDocument
Dim vbModule As VBE.VBComponent = doc.VBProject.VBComponents.Add(VBE.vbext_ComponentType.vbext_ct_StdModule)
vbModule.Name = "basAddedCode"
vbModule.CodeModule.AddFromString( _
"Sub AutoOpen()" & vbLf & _
" MsgBox ""Document "" & ActiveDocument.FullName & "" has been opened successfully!""" & vbLf & _
"End Sub")
'doc.Save() or doc.SaveAs to change file type and/or name
End Sub

How do I resolve a false "spreadsheet locked for editing" issue?

Using the Office 2010 suite, I have a PowerPoint presentation where all the charts are linked to an Excel workbook. In order to move the presentation and/or workbook to another directory, all the links must be updated to point to the new workbook location. To do so, I've written the following code which resides in a standard code module in PowerPoint:
Private Sub RedirectLinks()
Dim Source As String
Dim Dest As String
Dim Action As Integer
If InStr(1, ActivePresentation.Path, "Dev\") > 1 Then
Action = MsgBox("Changing pointers to PRODUCTION", vbOKCancel)
Source = "Dev\"
Dest = vbNull
Else
Action = MsgBox("Changing pointers to DEVELOPMENT", vbOKCancel)
Source = "Templates\"
Dest = "Dev\Templates\"
End If
If Action = vbOK Then
Dim SL As Slide
Dim SH As Shape
Dim Top As Double
Dim Left As Double
Dim Width As Double
Dim Height As Double
For Each SL In ActivePresentation.Slides
SL.Select
For Each SH In SL.Shapes
SH.Select
If SH.Type = msoLinkedOLEObject Then 'when we find a linked one
Top = SH.Top
Left = SH.Left
Width = SH.Width
Height = SH.Height
SH.LinkFormat.SourceFullName = Replace(SH.LinkFormat.SourceFullName, Source, Dest)
SH.Top = Top
SH.Left = Left
SH.Height = Height
SH.Width = Width
End If
Next
Next
End If
If InStr(1, Dest, "dev") > 0 Then
Action = MsgBox("About to OVER WRITE the Dev copy with this one." & vbCrLf & "Click 'Cancel' to prevent this and save manually", vbOKCancel, "OVER WRITE WARNING!!")
Else
Action = MsgBox("About to OVER WRITE the PRODUCTION copy with this one." & vbCrLf & "Click 'Cancel' to prevent this and save manually", vbOKCancel, "OVER WRITE WARNING!!")
End If
If Action = vbOK Then
ActivePresentation.SaveAs Replace(ActivePresentation.Path, Source, Dest) & ActivePresentation.Name
End If
End Sub
The code executes just fine, however, I frequently get this message box popping up from Excel when it is executing the SH.LinkFormat.SourceFullName = Replace(SH.LinkFormat.SourceFullName, Source, Dest) line.
Items of note:
The workbook in question is actually closed - I know that it's not open by anyone else (I'm the only one who usually uses it, and the other person who's in there isn't in the office this morning).
It claims the file is locked by 'another user' which is actually me. I can often get this warning by closing the workbook, then immediately reopening it. I don't know if it's a network latency issue (file resides on a server, not locally), or what, but after a few moments of using the workbook, I'll get the workbook is now available for read-write message.
I don't get this warning every time it tries to execute the line that sets the .SourceFullName. Sometimes I'll get it most times, sometimes I won't get it at all, sometimes I'll get it on occasion.
Despite my thoughts of network lag, it doesn't matter how quickly or slowly I debug through the code, I'll get this message at random times.
Flagging either new or old workbooks as Read-only at the OS level does not seem to improve the situation.
However, flagging both seems to get me 2 warnings for each replacement line execution.
Does anyone have any suggestions on how to resolve this?
I've run into odd behaviors when code in PPT opens a PPTM and my Macro security settings are anything tighter than "Open any fool thing". Try dialing your macros security in PPT and Excel as low as they'll go, just as a test, and see if that eliminates the problem.
If anyone knows of a way to set the security options on the fly and reset them after, that'd be even better. It might be possible to do that via the registry prior to doing anything that'd invoke XL.

Access VBA Userform Controls without Form instance

I have a friend with a VBA project in Excel. This project has a lot of Forms that pop up and perform various functionality while the spreadsheet is being used. Some of them have complex Form_Initialize methods that rely on other things already existing (this is not a problem when the project is used as expected).
We are trying to print out the names of every control on every form within the application. Our problem is that the VBA.UserForms collection only contains forms that have already been instantiated, and we can't instantiate all the forms without their Form_Initialize methods executing.
For example:
For Each f In VBA.UserForms
Debug.Print f.Name
Debug.Print "----------------------"
For Each c In f.Controls
Debug.Print c.Name
Next c
Next f
does nothing if no forms have been used/loaded. This code:
For Each c in frmConfig.Controls
Debug.Print c.Name
Next c
First executes frmConfig.Form_Initialize(), then loops through the controls on the form printing their names. This crashes, as things that need to happen before this form is available have not happened.
Is it possible to get the names of the controls on a form WITHOUT instantiating the form (avoiding execution of frmConfig.Form_Initialize())?
Any help much appreciated!
Is this what you are trying?
Option Explicit
Sub FindObjects()
Dim vbc As VBIDE.VBComponent
Dim frm As Object
Dim Ctrl As MSForms.Control
For Each vbc In ThisWorkbook.VBProject.VBComponents
If vbc.Type = vbext_ct_MSForm Then
With VBA.UserForms
On Error Resume Next
Set frm = .Add(vbc.Name)
Debug.Print "Found userform :" & vbc.Name
If Err.Number = 0 Then
For Each Ctrl In frm.Controls
Debug.Print "Controls in Userform " & vbc.Name & _
" - " & Ctrl.Name
Next Ctrl
End If
On Error Go To 0
End With
End If
Next vbc
End Sub
IMP:
Set reference to Microsoft Visual Basic For Applications Extensibility
In Excel options, set "Trust Access To the VBA project Object Model"
Screen Shot
FOLLOWUP
Since this is a one time thing, do this
Open VBA Project
Press CTRL + F
Do as shown in the screenshot below and then run the code.
Close the file without saving once you have got what you need