Directory picker for Visual Basic macro in MS Outlook 2007 - vba

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.

Related

How to force a fresh and independent instance of Word

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.

How to add an AutoCorrect entry for a specific language with VBA in Outlook

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.

VBA add-in: how to "embed" the required reference libraries? Getting "Compile error in hidden module" when sending functional add-in to other users

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

Control already created Outlook message from excel VBA

longtime reader, first time messenger (not my first time making a bad joke though),
I would like to know if it is possible to gain control of an Outlook email message that has already been created. At work we have to download new work orders from a secure website, thanks mostly to this site, I have been able to set up a macro that logs in, finds the new work orders, and clicks the button to open the work order. Once this button is clicked, a new IE window is opened with a pdf file, and the "Send page by email" command is used to create a new outlook message. I have the outlook 12 reference (using Office 2007), and am able to take control of an existing outlook session to create a new email using:
Dim SendOrder As Outlook.Application
Set SendOrder = GetObject(, "Outlook.Application")
But I cannot figure out how to make it control the email message that was opened by IE. I tried using GetObject(, "Outlook.Application.MailItem), and a few other failed ideas, but 3 ideas were all I had, so I'm hoping someone on here can help me out with this, otherwise I'll probably have to save the file in IE and create a new email message, which seems like adding an extra step.
You're on the right path, I think. Something like this works with Outlook mailItems opened from Outlook. I have not tested it on mailItems opened from IE, though.
Sub GetAMailItem()
'## Requires reference to MS Outlook object library ##
Dim oApp As Outlook.Application
Dim mItem As MailItem
Set oApp = GetObject(, "Outlook.Application")
If TypeName(oApp.ActiveWindow) = "Inspector" Then
Set mItem = oApp.ActiveWindow.CurrentItem
End If
Set oApp = Nothing
End Sub
Found the guts of that code here, just made a modification or two to give you a structured example that might suit your needs.

Word VBA and Multiple Word Instances

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