I have a macros that starts running when a document opens. It looks like this:
Private Sub Document_Open()
....
Dim strInput As String
strInput = ActiveDocument.Content
....
In office 2010 this macros works nicely, however, in office 2013 I get this error message (translation to English):
This command is not available, because there are no documents opened
And when I hit on Debug button, I see that this line of code is highligthed:
strInput = ActiveDocument.Content
I think it's a bug of office 2013, because for some insane reason it invokes this macros before the document is opened, even though I clearly force it to do right after opening - Private Sub Document_Open(). So, what is wrong with that and how can I fix that?
If you're seeing the yellow message bar then this isn't a bug. It's because your document isn't "trusted" - it's "sandboxed". You need to check whether it's a ProtectedViewWindow - introduced in Office 2010 - and take appropriate action.
The Protected View is explained in this article: https://support.office.com/en-us/article/What-is-Protected-View-d6f09ac7-e6b9-4495-8e43-2bbcdbcb6653. Basically, it prevents code from running in any document that comes from an origin / author that isn't trusted. The user needs to make an explicit decision to trust the document in order to go into Edit mode.
Obviously, this is a major impediment to VBA code, so you have the ProtectedViewWindow object in the object model. There are application-level events that can be used to manage this state in the entire Word environment, such as ProtectedViewWindowOpen that triggers when a document is opened "sandboxed". After determining whether the document is trustworthy, the code can make the document editable using the ProtectedViewWindow.Edit method.
For example, to trust all documents that come from a specific file path:
Private Sub app_ProtectedViewWindowOpen(ByVal PvWindow _
As ProtectedViewWindow)
If PvWindow.SourcePath = "C:\Test" Then
PvWindow.Edit
End If
End Sub
The above needs to be in an application-level class module and events for this class need to be active on the application. If you're not familiar with using application-level events, see https://msdn.microsoft.com/en-us/library/office/ff821218.aspx
Related
Each time I open my Libre Office document to add updates, I have to press CTRL+END to jump to the bottom of the document. I thought of adding a button to jump-to-the-bottom, but the Basic it uses is not VBA, so I'm in a pickle. Can anyone give me a sub?
As an after-thought; can I add an instruction to the document-startup?? to cursor-jump-to-the-bottom of the file? Please forgive my pants naming conventions, I'm still green!
I tried the offline help to no avail. Next I tried Google but could not find an exact sub to match. I then subscribed to the TheDocumentFoundation and was presented with an SO-like Q&A forum. The discobot did not help!
I also tried recording a macro which does work ok. The sub code is:
sub Main
rem define variables
dim document as object
dim dispatcher as object
rem get access to the document
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
dispatcher.executeDispatch(document, ".uno:GoToEndOfDoc", "", 0, Array())
end sub
I cannot however add the commands to the document.init or startup.
In fact, the macro for moving the cursor to the end of the document is a little easier to write. Each document has a controller. The text document controller has a "view cursor". You can control this cursor using its methods, in this case you need the .gotoEnd() method.
So the code can be like this: "if this document is a text document, then get its controller, take the cursor from the controller, make the cursor jump to the end of the text"
Sub jumpToEnd(Optional oEvent As Variant)
If ThisComponent.SupportsService("com.sun.star.text.TextDocument") Then
ThisComponent.getCurrentController().getViewCursor().gotoEnd(False)
EndIf
End Sub
The best place for this macro is some module in the Standard library under "My Macros" - this library will be loaded immediately after starting the office and macros will be available for execution at any time. (If you want to run this macro only for one document, then you can place it in the Standard library of this document)
To run the macro automatically, use the Tools - Customize - Events tab
This is where you specify whether the macro should be executed when you open one specific document or when you open any document.
Now that you know how to do this, you will probably agree that pressing Ctrl+End is much easier.
There are several instances of this problem, but this one is predominant. This is in relation to updates (our most notable problem child being KB2726958). We have a Leave Spreadsheet that looks like this:
Leave Spreadsheet example
By pressing the grey Leave button, you end up here:
Leave Word doc
All the programming for these is written in VBA (i've never worked with VBA before, I can understand it to a degree).
Now, the issue is that using the ActiveX button in the 'Leave Spreadsheet example' causes the 2 buttons 'Send by Email' and 'Save' to switch functions; Send by email attempts to save and save opens up Outlook and creates the email message.
Both functions have completely retained functionality, just on the wrong buttons.
The thing I find weird is that a hyperlink to the very same file works; the buttons aren't switched and have full functionality. The only hint that I have towards resolution is that when using a hyperlink, it's directly opening the file. When using the ActiveX button, it seems to be creating a new file based off the file it's linking to. For example, the hyperlink directly opens C:\Report.dotm but the ActiveX button opens Document1.doc with a template based on Report.dotm.
I'm considering that maybe the activeX button is opening up Word with an incorrect extension? But i'm not sure how to figure this out (code below shows that the linked file on the activeX control is a .dotm).
What further throws a spanner into the mix is that it only affects some computers... Considering on-site we all use the same type of PC with the same image... :(
My question is, does anyone know why they may be swapping? They're located on the same network drive albeit different directories. They require the same permissions to access. The code for the buttons is as follows:
Excel Button:
Private Sub CommandButton1_Click()
' This button links the excel spreadsheet to the word doc
Dim wrdApp As Object
Dim wrdDoc As Object
Dim i As Integer
Set wrdApp = CreateObject("Word.Application")
wrdApp.Visible = True
Set wrdDoc = wrdApp.Documents.Add("\\networkdrive\directories\Request for Leave.dotm")
End Sub
Word buttons 1 and 2:
Private Sub cmdSend_Click()
' This is the code for the button 'Send by Email'
MsgBox "Send the following email to your Team Leader/Line Manager", vbInformation
SendDocumentAsAttachment "", "IPL Request for Leave"
End Sub
Private Sub cmdSave_Click()
' This is the code for 'Save'
modSend.SaveLeaveForm
End Sub
Please Note: The comments above are not in the code in VBA, i've written them in myself in this question to provide clarity.
Troubleshooting that i've done:
Removing all .exd files
Running the MS Hotfix (removes all .exd files in a GUI)
The next step would be to try running all 6 patches related to fixing ActiveX controls with the particular patches we've done to see if that fixes the problem. The reason I haven't done this yet is because of ITIL (Change management) although I may try testing this later today.
What is the outcome i'm after?
Ideally, I want to understand what is causing these buttons to, from what it looks like, swap their functions. I have different scenarios of button swaps, some of which are remedied by removing the .exd files, and some that aren't.
By understanding what is happening, I hope that I can apply the knowledge to the other scenarios (same problem, different coding).
Then, I'll be able to document my findings so that when we perform the next round of patching that is known to break ActiveX controls, my organization will know how to deal with it.
So the patch mentioned below has fixed this issue. There's still some other issues that I need to test this patch against, but I definitely should have started there. Lesson learnt.
From my work email:
I’ve just tried using the patch related to the ActiveX controls breaking, KB2920754. I’ve used it on two PC’s here in the training room; both had different issues:
- The first one had buttons that had switched around (save attempted to email, email attempted to save)
- The second one couldn’t use the buttons at all.
This patch cured both w/o requiring a restart or logging out and back in. I didn’t remove any .exd files, either.
It does state, however:
“Important For this fix to be fully effective, you also have to apply the other patches for Office 2013 that are listed in the "Resolution" section of the following Microsoft Knowledge Base article”
There are 6 in total.
Patches:
1. KB2920754 – (the one I’ve used successfully)
2. KB2956145
3. KB2956163
4. KB2965206
5. KB2956176
6. KB2956155
I have a Normal.dotm file that contains an AutoNew macro.
This macro is automatically executed each time a new document is created using any other template.
Is there any way I can prevent this automatic behavior for a specific template?
I have a Word VSTO add-in running, so I can hook into Word's events, but so far I havn't found a way to prevent this.
I do know that I can prevent macro execution when using templates programmatically, for example like this:
' Disable auto-macros before opening document
wordApplication.WordBasic.DisableAutoMacros(1)
' Open document
newWordDocument = wordApplication.Documents.Open(template.FullName, ConfirmConversions:=False, [ReadOnly]:=True, AddToRecentFiles:=False, Revert:=True)
' Re-enable auto-macros
wordApplication.WordBasic.DisableAutoMacros(0)
But this solution doesn't work when the user uses a Word template from Windows explorer or the Open-dialog in Word, since in those cases I can't execute code before it's too late already.
Or can I?
I hope someone has a trick for me :-)
-
Edit: While trying different solutions, I discovered something that might help others in similar situations, though unfortunately it doesn't help me.
It seems that if a template contains a module containing an AutoNew (or AutoOpen for that matter), that local macro is executed instead of the one in Normal.dotm.
Example:
Normal.dotm contains the following macro:
Sub AutoNew()
MsgBox "Normal.dotm"
End Sub
Test.dotm contains the following macro:
Sub AutoNew()
MsgBox "Test.dotm"
End Sub
When executing Test.dotm the message "Test.dotm" is displayed, while the message "Normal.dotm" is not displayed.
If the AutoNew macro is removed from the Test.dotm template, the message "Normal.dotm" is indeed displayed.
So it is possible to easily override the auto-macros.
The local versions of AutoNew and AutoOpen can even be empty subs that do nothing. It still works.
This is not possible in my case though, since the template I use is generated by code, and cannot contain macros (because adding macros to templates programmatically requires the user to manually activate the option "Trust access to the VBA project object model", and that's something I cannot ask my customers to do for all users. It's also a security risk.)
Based on the workaround described in the "Edit" part of the question - providing a template with "empty" Auto-macros - it's possible to use the Open XML SDK to create a template and add the VBA project to it in order to provide this functionality. This approach avoids the user needing to allow access to the VBA project on his installation. The only "macro security" that could be triggered is that for not allowing macros to run. But since the client uses macros, anyway, this should not be a major obstacle.
The simplest method is to create as much of the basic template as possible in the Word UI and use this as a starting point.
Since you're unfamiliar with the Open XML SDK, the next step would be to create one (or more) templates in the Word UI using the basic template as the starting point, saving under a different file name.
You can then use Open XML SDK Productivity Tool to see the code required to generate any one of these files, as well as, using the Compare tool, the code for converting the basic template to the derived version. This should give you a decent start with the SDK and it's object model. Once you get a feel for how the Open XML SDK works, if you're familiar with Word's object model, using that provided by the SDK is relatively straight-forward as an effort was made to make it correspond as closely as possible to the "COM" object model.
The VBA project can be added later, but you can also include it in the basic template. That would be the simplest approach.
Include this "starting point" basic template as part of your solution, installing it as part of the solution.
Within the AutoNew macro you can check the AttachedTemplate property. Only if it is a template where you want to apply the cleaning you can execute the respective macros.
Sub AutoNew()
If ActiveDocument.AttachedTemplate <> "Normal.dotm" Then
Exit Sub
End If
' rest of the macro
End Sub
If you don't control the Normal.dotm you can put an empty AutoNew macro in your own templates. As Word only executes the auto macro in the closest context, the macro in the Normal.dotm file would not be executed.
If you don't control the other templates either, you can tell your users to hold down the SHIFT key while creating a document. This prevents the execution of the auto macro.
Probably it is best, however, if you ask the owner of the other system to find another solution that does not rely on polluting the Normal.dotm file.
I have written a form based document generation macro (in VBA) for distribution to a sales team.
For their ease of use, I want to provide a self-contained file which will display the form as soon as the document is opened.
Using AutoOpen I can get the form to display as intended if word is already open and the dotm file is opened within. However, if I double click the file from within explorer, nothing happens and I have to launch the macro manually. I thought AutoExec might allow this but no luck there. I've spent considerable time trying to get this to work through googling etc. but I'm not getting anywhere.
How can I make the form display even when the file is opened with a double click? Is it possible to do this without having to change normal.dotm for each user?
For further background, I am using Word 2013 with macros fully enabled during testing. The dotm file is stored in a trusted location.
I am using a macro to launch the form like this...
Public Sub AutoOpen()
StartPage.Show
End Sub
I have tried using AutoExec as well to no avail.
In the "generator.dotm" file got to Visual Basic and go in to the "ThisDocument" Microsoft Word Object.
At the top of the Visual Basic Editor select "Document" in the left hand side and then click on "New" on the right hand side. Private Sub Document_New() method will appear for you to be able to edit. Then you can call your userform in there. Similar to:
Private Sub Document_New()
Dim myForm As UserForm1
Set myForm = New UserForm1
myForm.Show
End Sub
Save your Generator.dotm and double click it through Windows explorer and you should get the results that you would like.
Apologies in advance if this ends up being generic. I have done some research on this and drawn a complete blank.
Excel is great, I love Excel. So much so that the "Recent Documents" section is of almost no use to me as I use that many spreadsheets in an insane amount of locations.
I have been researching a way to log (using VBA as an AddIn) documents when they are opened. Even if it is into something as simple as a text file with the date, however I cannot figure out how to have the VBA code "know" when a file is opened (which, along with outputting to a Text file, is all I want it to do at this stage).
Is there a way to have the VBA look for this action from within an excel instance?
The following steps (adapted from the excellent post at http://www.cpearson.com/excel/AppEvent.aspx ) is the "minimally viable" way to do what you need.
open a new workbook
open the VB editor
Insert a class module; in the properties window, set class name to CExcelEvents
Add the following code in the class module:
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Application
MsgBox "initialized the class!"
End Sub
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
MsgBox "New Workbook was opened: " & Wb.Name
End Sub
5. Right-click on the "ThisWorkbook" element in the project explorer, and select "View Code"
6. Add the following code:
Private XLApp As CExcelEvents
Private Sub Workbook_Open()
Set XLApp = New CExcelEvents
End Sub
This creates an instance of the CExcelEvents class, and "turns on event handling" when the addIn is loaded.
Finally, save the file as myEvents.xlam in the location where addIns are stored - this varies depending on your machine...
If you now close the file, and add the addIn (again, depends on your environment whether that is from the Developer ribbon or the Tools menu), you should see a dialog box that says "initialized the class!". This shows the addIn is properly installed and working.
Now, when you open a workbook, another message box will appear: "New Workbook was opened: " with the name.
Obviously you will want to get rid of the message boxes, and put in some "useful" code that does whatever you want to do (for example, log the name of the workbook to a file). It sounds to me like you don't need help with the latter - if I am wrong then please let me know.