Whatever way one opens Word (Excel etc.) I aim that the instance is 'safe' and independent. What I mean by that is that if the user currently has a Word window open that this doesn't interact with what my software is opening and that both windows can operate independent of one another.
The best way to describe this is the effect of right-clicking on Word or Excel in the taskbar and opening up a new Word (or Excel) window (as opposed to opening a file). Each time you do this, you seem to get a complete and independent instance.
It would seem that the following code:
Dim wdApp As Word.Application
Dim oDoc As Word.Document
wdApp = CreateObject("Word.Application")
... will open up within an existing window if Word is currently running on the PC. How do I force a fresh and independent instance of Word?
Using New Word.Application is preferred if the project has a reference to the Word APIs. Based on the sample code in the question, that would be the case in this instance.
Another possibility would be to create a new Process and hook up to that. In this case, a reference to the Word APIs would not be necessary (late-binding).
Dim wdApp As Word.Application = New Word.Application()
Dim oDoc As Word.Document = wdApp.Documents.Add() 'or wdApp.Documents.Open(filepath)
CreateObject should not be using an existing window, only GetObject would do that. Assuming the assertion in the question cannot be confirmed (I've never seen it happen, in over thirty years), then CreateObject is also possible for starting a new instance of an Office application. This would also not require a reference to the Word APIs.
As to the topic of maintaining an independent instance of an Office application: You may also want to refer to this SO answer.
Related
I'm writing e-mails in different languages in Outlook, and I frequently need to add AutoCorrect entries for one of these languages. The menu navigation to the AutoCorrect settings is quite cumbersome, so I'm looking for a way to script this with a VBA macro.
Having browsed through various Internet sources, I still faced two problems:
Unlike other Office applications (Word, Excel), Outlook doesn't seem to have an Application.AutoCorrect property.
Where AutoCorrect objects exist, they don't specify which language they apply to.
Is there still a way to configure Outlook's AutoCorrect settings for a specific language through a VBA macro?
Office applications share their AutoCorrect settings, so the trick is to configure them through the Word VBA Object Library settings:
Sub AddAutoCorrectEntry(typed As String, fixed As String, language As Word.WdLanguageID)
Dim wordApp As Word.Application
Set wordApp = New Word.Application
' Set language on a (non-empty) document
Dim wordDoc As Word.Document
Set wordDoc = wordApp.Documents.Add
Call wordDoc.Range.InsertAfter("foo")
Let wordDoc.Range.LanguageID = language
' Now AutoCorrect configures that language
Call wordApp.AutoCorrect.Entries.Add(typed, fixed)
Call wordApp.Quit(False)
End Sub
In the Outlook Object Model, assuming a message is being displayed in an inspector, Application.ActiveInspector.WordEditor.Application will give you the Word Application object which exposes the AutoCorrect property.
I wrote a powerpoint add-in that works well on my local computer. Yet when I email the file to others, all of a sudden half the functionalities no longer work and show the compile error as listed in the subject line.
Digging a bit deeper, it appears the issue is that the client computers do not have the correct reference libraries (e.g., Excel 14.0 Object Library, Outlook, Access, etc.). I have these libraries correctly referenced when writing the add-in as a pptm file, but imagine that when I saved the pptm into a ppam file, the reference libraries were "lost" somehow.
Does anyone have any ideas? Note that adding the reference libraries directly on the client computers does not appear to help. Instead, I'd need to send the client the foundational pptm file, add the reference libraries, then save that file as a ppam file on the client computer directly, which of course is not practical. So how does one "embed" reference libraries to the add-in?
Thank you!
So how does one "embed" reference libraries to the add-in?
You don't.
Alternative:
Instead of Early binding, use Late Binding.
Example code of Early Binding.
Here we set a reference to MS Word xx.xx Object Library.
Sub Sample()
Dim oWrd As Word.Application
Dim oDoc As Word.Document
Set oWrd = New Word.Application
Set oDoc = oWrd.Documents.Open("....")
'
'~~> Rest of the code
'
End Sub
Converting the above to Late Binding.
Here we do not set the reference but let the code bind to whatever version of MS Word is present in the destination pc.
Sub Sample()
Dim oWrd As Object
Dim oDoc As Object
Set oWrd = CreateObject("Word.Application")
Set oDoc = oWrd.Documents.Open("....")
'
'~~> Rest of the code
'
End Sub
Similarly you can change your code for MS-Excel as well.
For Further Reading
Good morning.
I am having a problem with getting my code to find other instances of word and have hit a brick wall after much google searching.
My Code below will find all open word documents and populate them into a combo box.
My problem is we have applications (I have no control over these) that will open word documents in a new instance and therefore my code will not find/control these documents.
Any ideas?
Dim objWordDocument As Word.Document
Dim objWordApplication As Word.Application
'//find all open word documents
Set objWordApplication = GetObject(, "Word.Application")
'//clear combobox
OpenDocs.Clear
'//add all open documents to combo box
For Each objWordDocument In objWordApplication.Documents
OpenDocs.AddItem objWordDocument.Name
Next objWordDocument
From what I have seen, and come to understand, the only sure fire way to do this is to iterate through the running instances of word and then kill each one in turn to be sure that you are getting the next instance.
Since word registers in the running object table the same exact way for every instance of itself, there is no way to get through them without first closing the one you were looking at.
One option to this approach, which is probably not desirable, is to get all the file names while you are killing the application instances and then load them all back in one instance that you create.
Alternately if you know the names of the open files, you can 'getObject' by open file name since Word will push its document names into the running object table, sadly this does not sound like the case for you.
Without writing an active x MFC service, you are not going to be able to do what you are looking to do.
I hope that is helpful.
EDIT:
there was an extensive discussion on subclassing and windows API's to get handles in order to change focus. http://www.xtremevbtalk.com/showthread.php?t=314637
if you dove into that head first and were able to enumerate the word instances by hwnd then you could potentially focus each one in turn and then list the file names. I do warn you though; that is some nasty subclassing which is dark magic that only some people who really want to accidentally break stuff play with.
In any event if you wanted to take the look at one instance, kill, repeat, reopen try this:
Adapted from this thread: http://www.xtremevbtalk.com/showthread.php?t=316776
Set objWordApplication = GetObject(, "Word.Application")
'//clear combobox
OpenDocs.Clear
'//add all open documents to combo box
Do While Not objWordDocument is nothing
For Each objWordDocument In objWordApplication.Documents
OpenDocs.AddItem objWordDocument.Name
Next objWordDocument
objWordApplication.Quit False
Set objWordApplication = Nothing
Set objWordApplication = GetObject(, "Word.Application")
loop
** use create object to open a new instance of word here and then go though
** your list of files until you have opened them all as documents in the new
** instance.
It's an old thread, but I too have the need to iterate over Word instances and bumped here.
Following the #Pow-Ian's advise, I tried to do the
if you dove into that head first and were able to enumerate the word
instances by hwnd then you could potentially focus each one in turn
and then list the file names.
Although I have managed to get all the handles, I have found an easier strategy with regard to office applications through AccessibleObjectFromWindow and our question is now solved.
Also, I believe the code showed #Pow-lan's has a mistype on
Do While Not objWordDocument is nothing
and should be:
Do While Not objWordApplication is nothing
I have a tried reading an embedded excel document in a word document. I followed the code specified at this blog article:
http://vbadud.blogspot.com/2010/08/how-to-read-excel-sheet-embedded-in.html
Dim oWB As Excel.Workbook
Dim oIShape As InlineShape
For Each oIShape In ActiveDocument.InlineShapes
If InStr(1, oIShape.OLEFormat.ProgID, "Excel") Then
oIShape.OLEFormat.Activate
Set oWB = oIShape.OLEFormat.Object
oWB.Sheets(1).Range("A1").Value = "ProdID"
End If
Next oIShape
It works fine but the Activate line causes the document to flicker on each excel document I read. I tried to remove the oIShape.OLEFormat.Activate code but it causes the next line to throw a "Runtime error '430' (class does not support Automation or does not support expect).
The question is there any other way to access embedded excel without calling the Activate method?
This is tricky! The short answer is, no. Not with an embedded Excel.
I did some experimentation and some research. Since I could not find any sources that specifically explained the behavior. this is somewhat a guess on my part. It appears that when you embed the Excel spreadsheet into your word document essentially Word stores a link of spreadsheet, which displays only the appearance because it needs to be interpreted with the Excel program. Until you actually active the shape, you cannot interact with it because that cannot be done with Word directly. This article alludes to the behavior, but doesn't explain it. Here's a quote:
If you edit the object in Word, click anywhere outside the object to return
to the destination file.
If you edit the object in the source program in a separate window,
click Exit on the File menu of the source program to return to the
destination file.
You may have noticed that even if you use. Application.ScreenUpdating = false it still does the flickering you mention. This is because you are using a different application when you access the shapes! Every time you active the shape, the object specific menus etc load.
A possible work around:
If instead of embedding Excel Spreadsheets via the insert menu, you can instead add a control. On my machine using Office 2003 the comparible one is: Microsoft Office Spreadsheet 11.0 This is technically a web control, but the methods and behavior are very comparable to an Excel workbook.
Using the control instead of the handy inserted object, with a slight variation of your code I was able to comment out your activate command and the code ran as expected. Specifically, I had to change these lines:
Dim oWB As Spreadsheet instead of Excel.Workbook.
If InStr(1, oIShape.OLEFormat.ProgID, "OWC11.Spreadsheet.11") Then instead of "Excel"
Basically you can decide... Activate your embedded object that requires Excel to interpret, or use a different control that doesn't require activation.
I wrote a Visual Basic macro for archiving attachments for Outlook 2007, but did not find a totally satisfactory way for showing a directory picker from the Outlook macro. Now, I don't know much about either Windows APIs or VB(A) programming, but the "standard" Windows file dialog I see most often in Microsoft applications would seem like an obvious choice, but it does not seem to be easily available from Outlook's macros.
Ideally, the directory picker should at least allow to manually paste a file path/URI as a starting point for navigation, since I sometimes already have an Explorer window open for the same directory.
What are the best choices for directory pickers in Outlook macros?
Two things I already tried and did not find totally satisfactory are (the code is simplified and w/o error handling and probably also runs in older Outlook versions):
1) Using Shell.Application which does not allow me to actually paste a starting point via the clipboard or do other operations like renaming folders:
Set objShell = CreateObject("Shell.Application")
sMsg = "Select a Folder"
cBits = 1
xRoot = 17
Set objBFF = objShell.BrowseForFolder(0, sMsg, cBits, xRoot)
path = objBFF.self.Path
2) Using the Office.FileDialog from Microsoft Word 12.0 Object Library (via tools/references) and then using Word's file dialog, which somehow takes forever on my Vista system to appear and does not always actually bring Word to the foreground. Instead, sometimes Outlook is blocked and the file dialog is left lingering somewhere in the background:
Dim objWord As Word.Application
Dim dlg As Office.FileDialog
Set objWord = GetObject(, "Word.Application")
If objWord Is Nothing Then
Set objWord = CreateObject("Word.Application")
End If
objWord.Activate
Set dlg = objWord.FileDialog(msoFileDialogFolderPicker)
path = dlg.SelectedItems(1)
Any other ideas?
Your best bet will probably be to use the Windows32 API for this. See this MSDN article for sample VBA code on how to interact with the API.
The article outlines a few different techniques, but I'd suggest searching the article for "COMDLG32.dll" and following the steps outlined in that section.