COM Interop & Outlook - Make Outlook Visible? - com

I'm automating Outlook from a VB.NET program, transferring items into the calendar and contacts at the user's request. It's all working, that isn't the problem; the problem is that automating Outlook like this when it wasn't already open creates a hidden instance. I can perhaps understand how this could be useful, to stop the user closing it down while you're still working on it, but since Outlook appears to force one instance only, if the user tries to inspect the changes made while my program is still hooked into Outlook, nothing happens - the one instance is that hidden instance and the user can't see anything.
In the old days of COM automation I used to be able to make Word or Excel visible, but I seem unable to do that with Outlook. I've tried:
OutlookApp.Visible = True
OutlookApp.Application.Visible = True
OutlookApp.ActiveWindow.Visible = True
OutlookApp.ActiveExplorer.Display()
but none of them work.
It's not critical, but does anyone know if I can get Outlook to show its main window? Bonus points if I can get it to disallow the user to close down the instance, but I'll settle for just showing the window :)

You can show your create mail like this :
mailItem.Display();
It is c# code, but I think, this but be close to your vb.

I normaly test to see if the "Outlook" process is running first, if not then shell up Outlook.exe then attach. This way you should never get a hidden process.
There really is no way to cancel the shut down outlook, you can hook the application quit event to disconnect and dispose in your app though.

I came across this question just now because I had the same challenge. I wasn't entirely happy with the accepted answer since this meant I would have to determine the full path of Outlook.exe. "Shelling up Outlook.exe" does not work. Therefore, I looked for and found another solution. But before I present that, let's look at how you can determine the full path of Outlook.exe if you want to do that.
To determine the full path of Outlook.exe, you need to fetch the Path value from the
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\16.0\Outlook\InstallRoot
registry key (assuming you have Office 16, a.k.a. Office 2016 or 2019) and build the full path of Outlook.exe. On my machine, the Path value is
C:\Program Files\Microsoft Office\root\Office16
so the full path is
C:\Program Files\Microsoft Office\root\Office16\Outlook.exe
However, you need to take into account that the user might have an older (e.g., Office 15, a.k.a. Office 2013) or newer Office version installed and pick the appropriate registry key. You can also get the Office version by retrieving the default value of the
HKEY_CLASSES_ROOT\Outlook.Application\CurVer
key (e.g., Outlook.Application.16). From this, you can infer the version number (e.g., 16) and build the segment of your registry key (e.g., 16.0). Or you can try to find the key with a subkey Outlook\InstallRoot having a Path value. Anyhow, you might see why I wanted to avoid this.
So let's look at an easier solution, noting that I am working with multiple Office applications and, therefore, have the following using directive:
using Outlook = Microsoft.Office.Interop.Outlook;
To make Outlook show its main window if no window is currently visible, I wrote the following utility method:
private static void EnsureOutlookIsVisible(Outlook.Application outlook)
{
object window = null;
NameSpace ns = null;
MAPIFolder folder = null;
try
{
// Check whether Outlook has an active window. If so, Outlook is visible
// and we don't have to do anything.
window = outlook.ActiveWindow();
if (window != null) return;
// No active window is shown, so Outlook is not visible and we need to
// have Outlook display a window.
ns = outlook.GetNamespace("MAPI");
folder = ns.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
folder.Display();
}
finally
{
folder.ReleaseComObject();
ns.ReleaseComObject();
window.ReleaseComObject();
}
}
The above method uses the following extension method to release COM objects:
public static void ReleaseComObject<T>(this T resource) where T : class
{
if (resource != null && Marshal.IsComObject(resource))
{
Marshal.ReleaseComObject(resource);
}
}
With the above methods, to attach to a new or existing Outlook process and make sure Outlook shows its main window, all you need are the following two lines of code:
var outlook = new Outlook.Application();
EnsureOutlookIsVisible(outlook);

Related

PropertyAccessor error

I have an Outlook 2007 add-in made in Visual Basic with Visual Studio 2010. I developed this quite a long time ago and it has always worked fine. I detect embedded images using the attachment PropertyAccessor. I now have to go back and make some changes and am running into the following problem. Attempting get the property now gives me the following error (it never did before)
The property "http://schemas.microsoft.com/mapi/proptag/0x37140003" is unknown or cannot be found. Other properties I can get fine. I have seen other posts with the same error which mention that the PR_ATTACHMENT_HIDDEN property is not set. I don't understand this as the attachment is an ordinary jpg sent by me from another account. If I build the add-in and install in Outlook 2016 it works just fine. What I find strange is that it used to work with no errors. I don't know if it makes any difference but it is running in VirtualBox.
Any Ideas would be welcome.
Keep in mind that PropertyAccessor.GetProperty behavior changed (in Outlook 2010?) when it comes to properties that are not present - GetProperty will now raise an exception instead of returning null.
You need to expect and handle that exception - no MAPI property should be expected to be present. In your particular case, missing PR_ATTACHMENT_HIDDEN is the same as PR_ATTACHMENT_HIDDEN == false.
Take a look at the available MAPI properties on the attachment in OutlookSpy (I am its author) - select the message, click IMessage button on the OutlookSpy ribbon, go to the GetAttachmentTable tab, double click on the attachment.

Outlook MailItem cache issue when saving in vb.net

I have a small WinForms program that allows my users to create email blasts for our clients. The app has two options: one is an HTML editor to design the email (works great) and the second is to import .msg or .oft template.
Once the email is complete it is moved to a shared outlook mailing folder for a nightly macro send job.
Pretty simple stuff!
The problem: Once the template is open in the application outlook seems cache that version. If the user decided to get out make a change in the template Outlook doesn't pick up the update.
Note: If the users clicks on the "Preview" button they received the correct UPDATED version in their inbox. But when they submit the MailItem it picks up the old version.
Dim newItem as Outlook.MailItem = gobjOutlook.CreateItemFromTemplate(fileEmailTemplate.FileName)
The send command works fine newItem.Send()
But when I move it to the shared folder it gets the original version from somewhere.
Dim addFldr As Outlook.MAPIFolder
addFldr = StoreFLDR.Folders.Add(gobjNamespace.CurrentUser.Name & ": " & DateTime.Now.ToString())
newItem.Save()
newItem.Move(addFldr )
I have tried forcing the GC and SaveAs to another location and reload the template, no luck.
I'd suggest starting from releasing underlying COM objects instantly. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object.
You may find the How To: Create a new Outlook message based on a template article helpful. Anyway, it would be great to see your full source code related to Outlook.

Word opens for real when trying to fill-in the blanks dynamically?

I'm currently using Word documents as templates where blanks have to be filled dynamically/programmatically in PwoerBuilder.
This has always worked fine until the company moves on Windows 7.
In short, the Word application is opened and made invisible.
Word.Application.Visible = false
Except that sometimes, and I don't know why, once the template is accessed, Word opens itself just as if I had double-clicked the template byself through the Explorer - but I didn't.
So, it asks whether I want to open it in read-only mode, since the application already has a handle on the file. And even if I click [Cancel] not to open the file, Word opens with no document, then the application crashes.
It reports PowerBuilder System Error 35.
Error Number 35.
Error text = Error calling external object function open at line 24 in function of_fusion of object n_cst_9999.
The external object that the application is trying to call a function against is Word.
oleobject lole_word
lole_word = create oleobject
lole_word = ConnectToNewObject("Word.Application")
lole_word.Documents.Open("templatefile.docx")
It may work for a few documents, and after a few, the problem comes up. This is the first time ever I meet with this issue.
I'll be glad to answer anyone's question who's trying to help.
Will, you may try setting DisplayAlerts and FeatureInstall properties on Word Application object.
That hid most of word alerts for us. (The code is from C# project and may not be exactly what you need)
Word.Application.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
Word.Application.FeatureInstall = 0;
You may also try making a copy of the file before opening it to avoid accessing same .docx from different threads - if that may be the case.

How to determine if 'SaveChanges:=wdDoNotSaveChanges' was passed to Interop.Word.Document.Close()

I have an issue with an Office addin I'm working on, which is implemented for Office 2003 & 2007. The addin is written in VB.NET 3.5 using VSTO.
The problem comes from some external code which automates a mail merge, opening the mail merge template, merging and then closing the template document. The close is done with this code:
objWord.Documents(sDoco).Close SaveChanges:=wdDoNotSaveChanges, OriginalFormat:=wdPromptUser
Because of some logic in my addin, instigated from the Interop.Word.Application.DocumentBeforeClose event, a message box is opened which prevents the Office document from closing, which breaks the automation.
Is there a way for me to determine the SaveChanges parameter (if any) on a Close within an Office.Interop.Word.Application event, such as DocumentBeforeClose? I'm trying to capture this parameter and determine if it's set to wdDoNotSaveChanges so that I can work around this problem.
I'm pretty sure you get the DocumentSave event BEFORE the DocumentBeforeClose, so set a flag in it, and if that flag is set at close, you know the doc has been saved, but if not, it was not. I've had to do similar things to know whether a Document was SAVED-AS vs just SAVED.
I'm not aware of any way to interrogate the state of that parameter from DocumentBeforeClose.

Visual studio automation: Enumerate opened windows upon solution loading

How to enumerate opened code windows (i.e. those windows where you edit documents) upon solution loading using macros?
As you probably know, MSVS remembers opened documents, i.e. when you load solution, IDE will load previously opened files. What I want to do is to perform some actions with those windows upon solution loading.
I tried to access these windows in SolutionEvents_Opened handler. But have no luck - it seems that mentioned windows are not available at the moment SolutionEvents_Opened invoked. DTE.Documents is empty and DTE.Windows.Items doesn't contain them.
I need some code like:
Private Sub SolutionEvents_Opened() Handles SolutionEvents.Opened
Dim window As Window = DTE.Documents.Item(?).Windows // one of the opened windows
...
End Sub
One way I've found to enumerate the window is on DocumentEvents.DocumentOpened event, but it fires it always and not only during the loading of a solution. It does not seem that the SolutionEvents.Opened gets fired at all in my experience otherwise a static variable could be changed in it.
This might help explain it though.