Outlook - Task pane in explorer mail view as well as in mail inspector window

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

Outlook - Task pane in explorer mail view as well as in mail inspector window
 
Subscribe
Steve Faleiro




Posts: 15
Joined: 2024-09-12
Using: Delphi 10.2 Tokyo, Add-in Express for Office and VCL 10.1.1705, Outlook 2024

I need the task pane to appear in Explorer mail item view, as well as in the mail Inspector window, independent of each other ie. separate instances of the same class.

Is it possible?

Background:

I created an addin for Outlook using the wizard and chose to add a task pane. As a result, the project got a unit and designer file (pas and dfm files) for the task pane. Also, in the property inspector of the addin module designer, there is a property TaskPanes, which is a list, and in that list there is one task pane (Type = TadxCustomTaskPane), for which I have set the Title property. It also has a set property: OutlookWindows with options to select the values olExplorers and olInspectors.

I developed and tested the addin in a mail item inspector, and so i set olInspectors to True, and left olExplorers as False.

I added controls and code as needed to the task pane, and its working correctly in mail inspector windows.

Now I have a requirement to also show the task pane in Explorer view. My addin reads the email contacts (sender, recipients) and does some processing with those (eg. sending them to an external API). So far it is working correctly when the task pane loads in mail Inspector windows.

But I am facing difficulties if I show the task pane in Explorer mail views (by setting olExplorers = True).

These are:
1. Access violation when starting Outlook.

Access violation at address 0000000000406D92 in module 'myaddin.dll'. Read of address FFFFFFFFFFFFFFFF.

2. When Outlook does start, the task pane works in the mail Inspector windows, but it is static and does not run the code from the task pane unit in the Explorer mail views. That code is launched from 'ActiveFormCreate' event of the task pane, and it relates to getting the email contact list of the selected email.


This is my current code:

1. In Addin module

In the class definition:

  public
    FInspectorMailItem: TMailItem;
    FExplorerMailItem: TMailItem;


Event handlers:


procedure TAddInModule.adxCOMAddInModuleAddInBeginShutdown(Sender: TObject);
begin
  if Assigned(FInspectorMailItem) then
  begin
    FInspectorMailItem.Disconnect();
    FreeAndNil(FInspectorMailItem);
  end;

  if Assigned(FExplorerMailItem) then
  begin
    FExplorerMailItem.Disconnect();
    FreeAndNil(FExplorerMailItem);
  end;
end;

procedure TAddInModule.adxCOMAddInModuleOLExplorerSelectionChange(Sender: TObject);
var
  Sel: Selection;
  imail: _MailItem;
  mailitem: TMailItem;
  savedemail: Boolean;
begin
  if Assigned(FExplorerMailItem) then
  begin
    FExplorerMailItem.Disconnect();
    FreeAndNil(FExplorerMailItem);
  end;

  // Show task pane only when opening an existing email
  try
    Sel := OutlookApp.ActiveExplorer.Selection;
  except
    Sel := nil;
  end;

  if Assigned(Sel) then
  begin
    if Sel.Count >= 1 then
    begin
      Sel.Item(1).QueryInterface(IID__MailItem, imail);

      if (Assigned(imail)) then
      begin
        FExplorerMailItem := TMailItem.Create(Self);
        FExplorerMailItem.Tag := 1;
        FExplorerMailItem.ConnectTo(imail);
        savedemail := (FExplorerMailItem.EntryID <> EmptyStr);

        imail := nil;
      end;
    end;
  end;

  adxclsMyAddin.TaskPanes[0].Visible := savedemail;
end;

procedure TAddInModule.adxCOMAddInModuleOLNewInspector(ASender: TObject; const Inspector: _Inspector);
var
  imail: _MailItem;
  mailitem: TMailItem;
  savedemail: Boolean;
begin
  if Assigned(FInspectorMailItem) then
  begin
    FInspectorMailItem.Disconnect();
    FreeAndNil(FInspectorMailItem);
  end;

  // Show task pane only when opening an existing email
  Inspector.CurrentItem.QueryInterface(IID__MailItem, imail);
  if (Assigned(imail)) then
  begin
    FInspectorMailItem := TMailItem.Create(Self);
    FInspectorMailItem.Tag := 2;
    FInspectorMailItem.ConnectTo(imail);
    savedemail := (FInspectorMailItem.EntryID <> EmptyStr);

    imail := nil;
  end;

  adxclsMyAddin.TaskPanes[0].Visible := savedemail;
end;


2. In task pane module 'OnCreate' event (in the code it is named as 'ActiveFormCreate')


var
  mailitm: TMailItem;
  c: Integer;
begin
  if (Assigned(adxclsMyAddin.FInspectorMailItem)) then
    mailitm := adxclsMyAddin.FInspectorMailItem
  else if (Assigned(adxclsMyAddin.FExplorerMailItem)) then
    mailitm := adxclsMyAddin.FExplorerMailItem;
...
...



then I use mailitm to get the Sender and Recipients names and email addresses.

I guess that the code in 'OnCreate' is incorrect in the way that it gets the mailitm, because a mail Inspector can be open, and the user can also select mail items in the explorer view. I'm not sure how to handle that.

What I need is this:

The task pane should work as it is currently working in the mail Inspector windows, and it should also work in the Explorer view when the user selects different emails, it should refresh the task pane that is displayed on the right side, by running code in the task panes unit (it can be OnCreate or any other public method), which will read the contacts from the selected email and populate a list control in the task pane.


Please tell me what changes I need to make so that it works correctly.

Thanks in advance.
Posted 10 Oct, 2024 21:07:29 Top
Andrei Smolin


Add-in Express team


Posts: 19041
Joined: 2006-05-11
Hello Steve,

Steve Faleiro writes:
I need the task pane to appear in Explorer mail item view, as well as in the mail Inspector window, independent of each other ie. separate instances of the same class.


You can have different panes showing instances of the same (or different) UserControl; the UserControl instances can reflect the state of a class (a single instance or multiple instances).

Regards from Poland (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 14 Oct, 2024 08:48:35 Top
Steve Faleiro




Posts: 15
Joined: 2024-09-12
Hi Andrei,

You can have different panes showing instances of the same (or different) UserControl; the UserControl instances can reflect the state of a class (a single instance or multiple instances).


Thanks for this. I've started over by creating a new add-in, and added 2 task panes - one for Explorer window, and one for Inspector windows. Then I created a frame control with my required user interface controls and added it to both of the task panes. Next, I have code in the add-in module, that selectively shows or hides the task pane depending on whether the active mail item is an existing email or not (it new email in compose or reply mode). Its the next bit that I'm not able to figure out - I'm unable to access the selected mail item from the task pane. It gives an access violation.

Here is my current code:


  public
    FInspectorMailItem: TMailItem;
    FExplorerMailItem: TMailItem;
    FInspectorMailItemFlag: Boolean;
    FExplorerMailItemFlag: Boolean;

..

uses
  coTaskPane_Inspector, coTaskPane_Explorer;

..


procedure TAddInModule.adxCOMAddInModuleAddInBeginShutdown(Sender: TObject);
begin
  if Assigned(FInspectorMailItem) then
  begin
    FInspectorMailItem.Disconnect();
    FreeAndNil(FInspectorMailItem);
  end;

  if Assigned(FExplorerMailItem) then
  begin
    FExplorerMailItem.Disconnect();
    FreeAndNil(FExplorerMailItem);
  end;
end;


procedure TAddInModule.adxCOMAddInModuleOLExplorerSelectionChange(Sender: TObject);
var
  Sel: Selection;
  imail: _MailItem;
  mailitem: TMailItem;
  savedemail: Boolean;
begin
  if Assigned(FExplorerMailItem) then
  begin
    FExplorerMailItem.Disconnect();
    FreeAndNil(FExplorerMailItem);
  end;

  // Show task pane only when opening an existing email
  try
    Sel := OutlookApp.ActiveExplorer.Selection;
  except
    Sel := nil;
  end;

  if Assigned(Sel) then
  begin
    if Sel.Count >= 1 then
    begin
      Sel.Item(1).QueryInterface(IID__MailItem, imail);

      if (Assigned(imail)) then
      begin
        FExplorerMailItem := TMailItem.Create(Self);
        FExplorerMailItem.Tag := 1;
        FExplorerMailItem.ConnectTo(imail);
        savedemail := (FExplorerMailItem.EntryID <> EmptyStr);

        imail := nil;

        if (savedemail) then
        begin
          adxcoOutlookMailChimpAddin.TaskPanes[1].Visible := True;
          TcoTaskPane_Explorer(adxcoOutlookMailChimpAddin.TaskPanes[1]).LoadContacts(FExplorerMailItem);
        end;
      end;
    end;
  end;
end;

..

  TAPIContact = record
    ContactName: String;
    ContactEmailAddr: String;
  end;

  TAPIContactArray = array of TAPIContact;

..


procedure TcoTaskPane_Explorer.LoadContacts(AMailItem: TMailItem);
var
  FContactArray: TAPIContactArray;
  c: Integer;
begin
  if (AMailItem = nil) then
    Exit;

  SetLength(AAPIContactArray, 0);
  SetLength(AAPIContactArray, AMailItem.Recipients.Count + 1);

  // Sender
  // AAPIContactArray[0].ContactEmailAddr := (OleVariant(adxclsOutlookMailChimpAddin.FInspectorMailItem.DefaultInterface)).SenderEmailAddress;
  AAPIContactArray[0].ContactEmailAddr := (OleVariant(AMailItem.DefaultInterface)).SenderEmailAddress;
  AAPIContactArray[0].ContactName := AMailItem.SenderName;

  // Recipient
  for c := 1 to AMailItem.Recipients.Count do
  begin
    AAPIContactArray[c].ContactEmailAddr := AMailItem.Recipients.Item(c).Address;
    AAPIContactArray[c].ContactName := AMailItem.Recipients.Item(c).Name;
  end;





LoadContacts(FExplorerMailItem); causes an access violation when trying to access AMailItem.


How can I access AMailItem in the task pane unit? Or is there some other way to do this?


Thanks in advance.
Posted 18 Oct, 2024 01:14:23 Top
Steve Faleiro




Posts: 15
Joined: 2024-09-12
**Please ignore duplicate message**
Posted 18 Oct, 2024 01:16:11 Top
Andrei Smolin


Add-in Express team


Posts: 19041
Joined: 2006-05-11
Hello Steve,

At https://www.add-in-express.com/forum/read.php?FID=1&TID=7232 we posted a source code example demonstrating how to access a task pane instance shown in a specified Outlook window.

Regards from Poland (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 18 Oct, 2024 12:58:05 Top
Steve Faleiro




Posts: 15
Joined: 2024-09-12
Hi Andrei,

I updated my code as per your example, but I get an access violation. Please see the code below:


procedure TAddInModule.adxCOMAddInModuleOLExplorerSelectionChange(Sender: TObject);
var
  Sel: Selection;
  imail: _MailItem;
  mailitem: TMailItem;
  savedemail: Boolean;

  CTP: TadxCustomTaskPane;
  Instance: TadxCustomTaskPaneInstance;
  Form: IcoTaskPane_Explorer;
begin
  if Assigned(FExplorerMailItem) then
  begin
    FExplorerMailItem.Disconnect();
    FreeAndNil(FExplorerMailItem);
  end;

  // Show task pane only when opening an existing email
  try
    Sel := OutlookApp.ActiveExplorer.Selection;
  except
    Sel := nil;
  end;

  if Assigned(Sel) then
  begin
    if Sel.Count >= 1 then
    begin
      Sel.Item(1).QueryInterface(IID__MailItem, imail);

      if (Assigned(imail)) then
      begin
        FExplorerMailItem := TMailItem.Create(Self);
        FExplorerMailItem.Tag := 1;
        FExplorerMailItem.ConnectTo(imail);
        savedemail := (FExplorerMailItem.EntryID <> EmptyStr);

        imail := nil;

        if (savedemail) then
        begin
          adxcoOutlookMailChimpAddin.TaskPanes[1].Visible := True;
          // TcoTaskPane_Explorer(adxcoOutlookMailChimpAddin.TaskPanes[1]).LoadContacts(FExplorerMailItem);

          CTP := Self.TaskPanes[1];
          Instance := CTP.Instances.Items[OutlookApp.ActiveWindow()];
          Form := Instance.Control as IcoTaskPane_Explorer;   // <---------- AV on this line
          TcoTaskPane_Explorer(Form).LoadContacts(FExplorerMailItem);

        end;
      end;
    end;
  end;
end;
Posted 18 Oct, 2024 14:00:54 Top
Andrei Smolin


Add-in Express team


Posts: 19041
Joined: 2006-05-11
Hello Steve,

Steve Faleiro writes:
Instance := CTP.Instances.Items[OutlookApp.ActiveWindow()];


Is Instance nil?

Regards from Poland (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 18 Oct, 2024 15:35:11 Top
Steve Faleiro




Posts: 15
Joined: 2024-09-12
Yes, Instance is nil.

       if (savedemail) then
        begin
          adxcoOutlookMailChimpAddin.TaskPanes[1].Visible := True;
          // TcoTaskPane_Explorer(adxcoOutlookMailChimpAddin.TaskPanes[1]).LoadContacts(FExplorerMailItem);

          CTP := Self.TaskPanes[1];
          Instance := CTP.Instances.Items[OutlookApp.ActiveWindow()];

          if(Instance = nil) then
           Showmessage('Instance is nil');

          Form := Instance.Control as IcoTaskPane_Explorer; // <---------- AV on this line
          TcoTaskPane_Explorer(Form).LoadContacts(FExplorerMailItem);

        end;
Posted 18 Oct, 2024 16:01:28 Top
Andrei Smolin


Add-in Express team


Posts: 19041
Joined: 2006-05-11
Does this occur at startup? Or, maybe, after you switch to a folder?

If so, create a test button to check if the task pane instance exists and to invoke LoadContacts(). Start Outlook and click the button. If this works, introduce a small delay - e.g. 100-300 ms (but do not suspend the main thread!) - before invoking LoadContacts().

Regards from Poland (GMT+2),

Andrei Smolin
Add-in Express Team Leader
Posted 18 Oct, 2024 16:17:01 Top
Steve Faleiro




Posts: 15
Joined: 2024-09-12
Hi Andrei,

Yes, this occurs at startup. Adding a TTimer to introduce a delay (500ms) solves the problem.

Thank you.
Posted 18 Oct, 2024 17:20:00 Top