Outlook ItemSend - ADXOlForm has null value

Add-in Express™ Support Service
That's what is more important than anything else

Outlook ItemSend - ADXOlForm has null value
 
peter.norman




Posts: 8
Joined: 2018-03-10
Hello Add-In-Express Team,

I'm working on an Outlook add-in that performs a number of tests of a user's email on send. Many of the tests require information from both the Outlook MailItem being sent and our ADXOlForm. There are a couple issues I'm concerned about. I'll describe them briefly and then provide some sample code below.

1. When I use GetCurrentForm() to retrieve the ADXOlForm, it occasionally returns null. This is a problem for performing the tests. Is there a better alternative?

2. In order to retrieve the MailItem, the add-in passes the ADXOlItemSendEventArgs e parameter from ItemSend to a test class, which then gets the Outlook MailItem from e.Item. My understanding is that we should not be releasing the MailItem since it is obtained through an ADX event parameter. However, because the tests are fairly extensive and because my users might have other add-ins operating at the same time, I'm afraid I might be violating your recommendation to use "short transactions" when dealing with the Outlook Object Model. Do you have a recommendation for the best practice in this regard?

Here are some relevant excerpts from the code. First we handle the ItemSend event and pass the ADXOlItemSendEventArgs e parameter to a SendTest class:

private void adxOutlookAppEvents_ItemSend(object sender, ADXOlItemSendEventArgs e)
{
SendTests mySendTests = new SendTests();
mySendTests.RunTests(e);
}


Next, the class retrieves an instance of the ADXOlForm form using GetCurrentForm() and then performs a series of tests using MailItem and form:

class SendTests
{

public void RunTests(ADXOlItemSendEventArgs e)
{
	// ComposeEmailsForm is an adxOlForm for compose scenario emails

ComposeEmailsForm storageForm = adxOlFormsCollectionItem1.GetCurrentForm() as ComposeEmailsForm;

	PerformFirstTest(e, storageForm);

	// perform some additional tests
}

private void PerformFirstTest(ADXOlItemSendEventArgs e, ComposeEmailsForm storageForm)
{
	if (e.Item is Outlook.MailItem)
	{
Outlook.MailItem mail = e.Item as Outlook.MailItem;
if (mail != null)
{
			// Need to perform some operations using form properties and mail properties
		}
	}
}
}


I note that in order to avoid getting a null value for the ADXOlForm, we're considering using the following to get the form:



	if (e.Item is Outlook.MailItem)
	{
Outlook.MailItem mail = e.Item as Outlook.MailItem;
if (mail != null)
{
Outlook.Inspector inspector = mail.GetInspector;
ComposeEmailsForm storageForm = adxOlFormsCollectionItem1.GetForm(inspector) as ComposeEmailsForm;
		}
	}


Do you recommend this approach? And I assume in this case we do not release the inspector?
Posted 09 Apr, 2018 18:23:55 Top
Andrei Smolin


Add-in Express team


Posts: 18830
Joined: 2006-05-11
Hello Peter,

1. We recommend using ADXOlFormsCollectionItem.GetForm({an object representing an Outlook window}) instead of GetCurrentForm().

You can use long transactions if required; using short transactions is a recommendation, not a requirement. Releasing COM objects is a requirement: to prevent issues, all COM object the add-in produces should be released. mail.GetInspector creates such a COM object.

Actually, any Office object model declares these types of methods/properties:

1) those returning void (in VB.NET these are called procedures) or a simple value (string, integer, etc.): these don't produce COM objects
2) those returning an object type from the same or other object model: *every* call to such method/property creates a COM object which you need to release
3) those returning a System.Object (in VBA, these return Variant or Object): you test the resulting value: a) you do nothing, if it is a simple value, b) you need to release it if it is a COM object. You can use System.Runtime.Marshal.IsComObject() to check whether an object is a COM object or not.

You are correct: you don't release mail because you get it via e.Item and this one is created by Add-in Express.

Feel free to ask more questions.


Andrei Smolin
Add-in Express Team Leader
Posted 10 Apr, 2018 05:14:00 Top
peter.norman




Posts: 8
Joined: 2018-03-10
Thank you very much,Andrei. So to confirm, even if mail is derived from an ADX event parameter, when you get an Inspector by using mail.GetInspector, you should release the inspector when done with it.

One final question: does it matter at all whether I derive mail from e.Item once at the beginning of the test class (i.e., in RunTests) and pass it as a parameter for each test method or derive instances of the MailItem separately in each test method (i.e., in PerformFirstTest and then again in each following test)?
Posted 10 Apr, 2018 20:11:45 Top
Andrei Smolin


Add-in Express team


Posts: 18830
Joined: 2006-05-11
Hello Peter,

peter.norman writes:
So to confirm, even if mail is derived from an ADX event parameter, when you get an Inspector by using mail.GetInspector, you should release the inspector when done with it.


Yes. This is so because you should release all COM objects created by *your* code: mail is another "name" for the COM object you retrieve via e.Item, mail.GetInspector is how your code creates a new COM object; you should release it.

peter.norman writes:
One final question: does it matter at all


It doesn't. e.Item returns a reference to an object that Add-in Express creates. You can use that reference as you like - clone it, cast a copy to another type, etc. You should understand that whatever you do, Add-in Express releases the associated COM object after the ItemSend method completes.

Below is a citation from https://www.add-in-express.com/creating-addins-blog/2011/11/04/why-doesnt-excel-quit/:

Because of the way .NET manages COM objects, oApp.Workbooks creates three objects:

#1 ?Â?Ð?ã the Workbooks collection, a COM object. You don?Â?Ð?ét access it in your code; instead, you manipulate it using #2 and #3 below.
#2 ?Â?Ð?ã an object called RCW which is an intermediate layer between a COM object and .NET; it deals with how the COM object is created/released. Although this is a .NET object, it isn?Â?Ð?ét accessible in your code.
#3 ?Â?Ð?ã a regular .NET object which holds a reference to the RCW (#2 above); this object represents the Workbooks collection in .NET.

That is: a .NET object (#3) holds a reference to the RCW (#2), which in its turn references the COM object (#1). When you call Marshal.ReleaseComObject(#3), #2 releases #1. Starting from this moment, invoking any property/method on #3 produces the exception "COM object that has been separated from its underlying RCW cannot be used".


e.Item stands for #3 in this scheme; creating a copy of it and/or casting it to a type (e.g. mail) doesn't influence #2 and #1.

An example:

Outlook._Inspector inspector = GetRequiredInspector();
object item = inspector.CurrentItem;
Outlook._MailItem mail = item as Outlook._MailItem;
if (mail != null) {
    // handle _MailItem here
}
Outlook._TaskItem task = item as Outlook._TaskItem;
if (task != null) {
    // handle _TaskItem here
}
Marshal.ReleaseComObject(item); item = null;


Here mail, task and item are several references to the same COM object; all of them stand for #3 in the scheme above. Because mail and task may be null, they may not refer to the COM object, so I release item.


Andrei Smolin
Add-in Express Team Leader
Posted 11 Apr, 2018 02:34:03 Top
peter.norman




Posts: 8
Joined: 2018-03-10
Thank you, Andrei! Very clear explanation.
Posted 11 Apr, 2018 07:28:02 Top
Andrei Smolin


Add-in Express team


Posts: 18830
Joined: 2006-05-11
You are welcome!


Andrei Smolin
Add-in Express Team Leader
Posted 11 Apr, 2018 07:34:50 Top