Saving Template after VBA references are added - vba

I have some code which adds the VBA references i require for using Access. (it checks if the user already has them included).
This all works fine. My problem is when i then go to close word I am prompted to save the Acronym Tools template (Where the macro that this code is stored). Is it possible to do this programtically so that is it done as soon as references are added and the user does not see it happening?
The code i use:
If Not isReferenceLoaded("Access") Then
MsgBox ("Access Object library not found, the script will now attempt to find the library for you.")
'Ensure access library is included so database actions can be done
ID.AddFromFile "C:\Program Files (x86)\Microsoft Office\Office14\MSACC.OLB"
MsgBox ("Access Object library added")
End If
If Not isReferenceLoaded("DAO") Then
MsgBox ("DAO library not found, the script will now attempt to find the library for you.")
'Ensure access library is included so database actions can be done
ID.AddFromFile "C:\Program Files (x86)\Common Files\microsoft shared\OFFICE14\ACEDAO.DLL"
MsgBox ("DAO library added")
End If
The function to see if reference is loaded:
Private Function isReferenceLoaded(referenceName As String) As Boolean
Dim xRef As Variant
For Each xRef In ThisDocument.VBProject.References
isReferenceLoaded = (xRef.Name = referenceName) Or isReferenceLoaded
Next xRef
End Function

If you don't want to save the document with the updated references (they'll be remade next time the doc is open, I guess) add to your Document object (supposedly named ThisDocument) the following subroutine:
Private Sub Document_Close()
ThisDocument.Saved = True
End Sub
This would inhibit the save-check only for ThisDocument. Add a Call ThisDocument.Save statement at the beginning, if you do want to save. However, I would count on the users to save their changes... but that's a matter of strategy, please do as you wish.
Nota Bene: Do not forget to save after adding the function: you'll lose the changes if you forget to; Word will ignore your changes because it was just instructed to do so by the function. :-)

Related

Disable Save As but not Save in Word 2010

I am looking to disable Save As in a Word 2010 file but still allow save. In other words I want users to be able to update the existing file but not create copies. I realize that this is impossible to truly do for people who know workarounds but for the general user I have successfully done this in Excel but am pretty new to word VBA.
When I add the following to a brand new document everything works as intended:
Sub FileSaveAs()
MsgBox "Copies of this file cannot be created. Please save changes in the original document." & _
, , "Copy Cannot be Created"
End Sub
My document has other macros for various command buttons but none of them involve saving the document (under original name or save as). There is also a macro running on open but that is 1 line going to a bookmark. When I try to "save as" in this document I get the message box as intended. When I try to "save" though things get strange: I get the save as dialogue (problem 1). Whether I try to save either under same name or other name the dialogue behaves as it normally would except it doesn't save and the dialogue box opens again automatically essentially creating an endless loop until I hit cancel (problem 2). I also intermittently get a "disk is full" warning pop-up after trying to save which I can dismiss but appears a few minutes later as long as he file is open (perhaps related to autosave?)
Since the macro works in the test file I assumed this strange behavior must be something elsewhere in my code but my document with the other macros saves normally as long as I don't include the save as code above so now I'm totally confused. Before I put up the rest of my code which is lengthy and for the reasons stated above I would not think impact things, I figured I'd ask this:
1. Is there any place other than my other command button macros that could be causing this behavior?
2. Is there a better method people recommend to achieve my ultimate goal of disabling save as but not save?
Thanks in advance for any advice you can provide.
The Word application has a DocumentBeforeSave event. To enable application events I suggest to create a class module by the name of ThisApplication and paste the following code into it.
Option Explicit
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Word.Application
End Sub
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, _
SaveAsUI As Boolean, _
Cancel As Boolean)
If SaveAsUI Then
MsgBox "Please always use the ""Save"" command" & vbCr & _
"to save this file.", _
vbExclamation, "SaveAs is not allowed"
Cancel = True
End If
End Sub
Add the following code to your ThisDocument module.
Dim WdApp As ThisApplication
Private Sub Document_Open()
Set WdApp = New ThisApplication
End Sub
You may add the Set App = ... line to your existing Document_Open procedure. After the WdApp variable has been initialised all application events will be received by the ThisApplication class where the DocumentBeforeSave event procedure is programmed not to allow SaveAs.
Of course, this is a blanket refusal for all documents. Therefore you may wish to add code to the procedure to limit the restriction to certain documents only. The proc receives the entire document object with all its properties, including Name, Path, FullName and built-in as well as custom properties. You can identify the files you wish to be affected by any of these.
Note that the WdApp variable will be erased in case of a program crash. If this happens the application events will no longer fire. It may be useful to know that application events occur before document events. This is if you wish to use the application's DocumentOpen event as well as or instead of the document's Document_Open event.

Remove a VBA Project Reference

In VBA I can see three different references for PDFCreator. One of them (see the second image) is a version of the software stored locally, and it works. I'd like to use this reference.
The other two are references to versions stored on a server, and they're broken (at this stage, I don't have permission to reinstall or delete them).
My problem is that after selecting the desired reference (see the second image) and clicking 'Ok', it resets to an incorrect reference, as shown in the third image.
How can I either override whatever's going on and select the desired reference or remove the incorrect references? While I'm not able to uninstall these versions from the server, I see no reason that my Excel would need to reference them. Can they just be removed from the list?
Image 1: Default state of the VBA Project References (PDFCreator not selected)
Image 2: Selecting the correct PDFCreator version
Image 3: Re-opening the menu shows that the incorrect PDFCreator version is selected
You might be able to something like the following...
To Remove broken references:
Private Sub RemoveBrokenReferences(ByVal Book As Workbook)
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Dim oRefS As Object, oRef As Object
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Set oRefS = Book.VBProject.References
For Each oRef In oRefS
If oRef.IsBroken Then
Call oRefS.Remove(oRef)
End If
Next
End Sub
To Remove a specific reference:
Use something like:
Call ActiveWorkbook.VBProject.References.Remove(oReference)
and you can get the oReference from:
Private Function GetReferenceFromPath(ByVal FilePathName As String) As Object
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Dim oFs As Object, oReferenceS As Object, oReference As Object
Dim sFileName As String, sRefFileName As String
'////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Set oFs = Interaction.CreateObject("Scripting.FileSystemObject")
sFileName = oFs.GetFileName(FilePathName)
Set oReferenceS = ActiveWorkbook.VBProject.References
For Each oReference In oReferenceS
sRefFileName = oFs.GetFileName(oReference.FullPath)
If StrComp(sFileName, sRefFileName, vbTextCompare) = 0 Then
Set GetReferenceFromPath = oReference
End If
Next
End Function
Public Sub RemoveReference()
On Error GoTo EH
Dim RefName As String
Dim ref As Reference
RefName = "Selenium"
Set ref = ThisWorkbook.VBProject.References(RefName)
ThisWorkbook.VBProject.References.Remove ref
Exit Sub
EH:
'If an error was encountered, inform the user
Select Case Err.Number
Case Is = 9
MsgBox "The reference is already removed"
Exit Sub
Case Is = 1004
MsgBox "You probably do not have to have Trust Access To Visual Basic Project checked or macros enabled"
Exit Sub
Case Else
'An unknown error was encountered
MsgBox "Error in 'RemoveReference'" & vbCrLf & vbCrLf & Err.Description
End Select
End Sub
P.S It is not possible to remove A MISSING/ broken references programmatically after MISSING occurs, only before it happens or manually after it happens. Most cases of MISSING/ broken references are caused because the type library has never before been registered on that system.
See How to Remove Reference programmatically?
I had a broken reference problem with a large number of Excel spreadsheets when I uninstalled Flash (which for some unknown reason I had included as a reference).
I got round the problem as follows:
BE CAREFUL BECAUSE THIS INVOLVES A REGISTRY HACK AN IS COMPLICATED.
MAKE A BACKUP OF REGISTRY BEFORE HACKING.
I wrote VBA code to find the Guid of the broken reference.
I used Regedit to insert a DUMMY TypeLib entry as follows:
D27CDB6B-AE6D-11CF-96B8-444553540000 was the Guid of the Broken Reference.
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0 Adobe Acrobat 7.0 Browser Control Type Library 1.0
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\0
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\0\win32 C:\Program Files (x86)\Common Files\Adobe\Acrobat\ActiveX\AcroPDF.dll
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\FLAGS 0
HKEY_CLASSES_ROOT\TypeLib{D27CDB6B-AE6D-11CF-96B8-444553540000}\1.0\HELPDIR C:\Program Files (x86)\Common Files\Adobe\Acrobat\ActiveX\
I based the above on another TypeLib entry.
Then I wrote VBA code to read each Reference.Guid in turn and if the Guid matched {D27CDB6B-AE6D-11CF-96B8-444553540000} to remove the reference using References.Remove Reference.
Code for doing this is available all over the forums so I won't repeat here.
After modifying all the affected Workbooks I reinstated the saved registry.
Hope this works for you.
BE CAREFUL BECAUSE THIS INVOLVES A REGISTRY HACK AN IS COMPLICATED.
MAKE A BACKUP OF REGISTRY BEFORE HACKING.

How to access a Word public variable in Excel VBA

I'm trying to automate some report generation where Excel VBA is doing all the work. My employer has a standardized set of templates of which all documents are supposed to be generated from. I need to populate one of these templates from Excel VBA. The Word templates utilize VBA extensively.
This is (some of) my Excel VBA code:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
WordApp.Documents.Add Template:="Brev.dot"
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
' more code
End sub
In the Word VBA Autoexec module, a public variable named user is defined and declared. The Userinfo sub from the Utilities module populates user. Both these routines are run without any complaints from VBA. I would then like to be able to access the user variable in my Excel VBA, but I get the following error
Compile Error: Variable not yet created in this context.
How can I access the Word VBA variable in Excel VBA? I thought it more or less was the same?
EDIT: the user variable is a user defined Type with only String attributes. Copying the Word VBA functions that populate the user variable is absolutely doable, just more work than I though was necessary...
In a Word module:
Public Function GetUserVariable() As String '// or whatever data type
GetUserVariable = user
End Function
In an Excel module:
myUser = WordApp.Run("GetUserVariable")
Alternatively, you could be able to replicate the variables value - as it's called user I suspect it is returning some information about a user, or author, of a document. In which case one of the following might be what you're after:
'// Username assigned to the application
MsgBox WordApp.UserName
'// Username defined by the system
MsgBox Environ$("USERNAME")
'// Name of the author of the file specified
MsgBox CreateObject("Shell.Application").Namespace("C:\Users\Documents").GetDetailsOf("MyDocument.doc", 9)
Another option - if you could only add a line of code to the Utilities.UserInfo sub (after setting your public variable):
ActiveDocument.Variables("var_user") = user
Then you could access it easily afterwards in Excel:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
'I am assuming your WordApp object is public, as you don't declare it.
'Capture the new document object
Dim newdoc as Object
set newdoc = WordApp.Documents.Add(Template:="Brev.dot")
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
'Get and show the value of "user"
Dim user as String
user = newdoc.Variables("var_user")
msgbox, user
End Sub
This is assuming that useris a string.
EDIT: As it is a requirement to work only on the Excel VBA, I would definely try the approach suggested by Scott and MacroMan - replicating the same functionality of the Word macros in Excel - if possible.
I assume that you've already ruled out the possibility of using an edited copy of the original template, set in a public folder...
For the sake of completness, there is another possibility: actually it is possible to inject VBA code in a Word document without the VBProject Object Model, by "brute force". If you rename a Word document as a .zip file and open it, you will notice a \word\vbaProject.bin file in it. This file contains the VBA project for the document and, in principle, one could add or change VBA code by modifying or replacing it.
I did some tests transplanting code from one document to another by simply copying the vbaProject.bin file, and the concept works. If you are interested in learning more about this file, this topic could be of use.
Notice, however, that to do what you want with such a technique would be somewhat complex (it would involve, for starters, updating zip files from your Excel VBA), and would require a lot of experimentation to mitigate the risk of accidentally corrupting your files. Definetly not recommended if you are looking for an easy and simple solution - but it is possible.

Access autocad object properties without opening it by VBA

I have been using folder browser for VBA, I could paste the code of it, but bottom line is that I get returned file name as a string.
Is there any way to access drawing properties (i.e number of layouts) without open?
Public Sub TestFileDialog()
dwgname = FileBrowseOpen("C:", "*", ".dwg", 1) 'dwgname is typeof string
End Sub
Its only the first step (use of FileBrowseOpen function is shown, but also i can use FolderBrowse and collect all .dwg inside of folder),actually i had in mind to batch export all layouts of selected .dwgs to currenty open one. Is there any chance for that?
To effectively read a .dwg file you'll need to open AutoCAD, otherwise the information is not accessible. Some properties may be, such as author, but not number of layouts...
But you can use AutoCAD Console (accoreconsole.exe) to run a headless AutoCAD and use APIs to read any information you need. This is really fast for reading lot's of files and the user will not see it running (but it needs to be installed anyway).
http://aucache.autodesk.com/au2012/sessionsFiles/3338/3323/handout_3338_CP3338-Handout.pdf
you could stay in VBA and use ObjectDBX
it leads to a very similar approach as accoreconsole.exe on in .NET does, i.e you won't see any drawing open in UI since it works on the database itself
It requires adding library reference (Tools->References) to "AutoCAD/ObjectDBX Common XX.Y Type Library", where "XX.Y" is "19.0" for AutoCAD 2014
a minimal functioning code is
Sub main()
Dim myAxDbDoc As AxDbDocument
Dim FullFileName As String
FullFileName = "C:\..\mydrawing.dwg" '<== put here the full name of the file to be opened
Set myAxDbDoc = AxDb_SetDrawing(FullFileName)
MsgBox myAxDbDoc.Layers.Count
End Sub
Function AxDb_SetDrawing(FullFileName As String) As AxDbDocument
Dim DBXDoc As AxDbDocument
Set DBXDoc = Application.GetInterfaceObject("ObjectDBX.AxDbDocument.19") '<== place correct AutoCAD version numeber ("19" works for AutoCAD 2014)
On Error Resume Next
DBXDoc.Open FullFileName
If Err <> 0 Then
MsgBox "Couldn't open" & vbCrLf & vbCrLf & FullFileName, vbOKOnly + vbCritical, "AxDB_SetDrawing"
Else
Set AxDb_SetDrawing = DBXDoc
End If
On Error GoTo 0
End Function
Still, you must have one AutoCAD session running from which make this sub run! But you should have it since talked about "currently open" drawing

Leaving a Project file open after retrieving it with GetObject

What is the right way to leave an MS Project file opened with GetObject() open and visible to the user after the end of a macro in a different app?
The information I found online suggests that setting the Application.UserControl property to True before the objects go out of scope should allow the user to continue using the opened file. However, for MS Project at least, the Application.UserControl property appears to be read-only. Is there a way to work around this?
A simplified example showing the problem:
Sub AddTasks()
Dim proj As Object
' Already have the file path from another part of the workflow
Set proj = GetObject("C:\projtest.mpp")
' perform some calculations and add new tasks to project
proj.Tasks.Add "additional task"
' Leave Project open and visible for the user
proj.Application.Visible = True
proj.Application.UserControl = True ' Gives "Type Mismatch" error
' without the UserControl line, runs ok, but Project closes after the end of the macro
End Sub
Instead of using GetObject, could you create an instance of the application and open the project file in the instance?
Sub AddTasks()
Dim msProj as Object
Set msProj = CreateObject("Project.Application")
msProj.FileOpen "C:\projtest.mpp"
'do stuff to project file here
msProj.Visible = True
End Sub
Something like the above (I can't test the above code because I don't have MSProject, but similar code works for MSWord)
For Project UserControl just indicates if the user started the application or not; it appears to be read-only because it is. I've not done what you're asking for with Project, although here is a similar example for Word trying to see and find running instances of Excel. Perhaps this helps a little:
can-vba-reach-across-instances-of-excel