Accessing some Outlook OLE object properties fails in a Win10/Exchange Online environment

2

I have some older code connecting to Outlook through OLE that no longer works running on Win10 where Outlook is set up to connect to Exchange online: It raises an exception with message (Dutch) "De bewerking is afgebroken", probably translated as "The operation was aborted" *, when I try to get properties in two different places indicated by !!!:

    const
      scxOutlookApp = 'outlook.application';
      scxNameSpace  = 'MAPI';

    type
      TDataModuleSyncOutlook = class(TTimeTellDataModule)
      private
        FOutlookApp,
        FNameSpace,
        FCalendarFolder: OleVariant;

    function TDataModuleSyncOutlook.ConnectToOutlook(AUserSMTP: String = ''): Boolean;
    var
       lRecipient,
       lVar      : OleVariant;
       lLog,
       lLoginSMTP: String;
    begin
       Result      := false;
       FWasCreated := False;  
       try
          FOutlookApp := GetActiveOleObject(scxOutlookApp);     
          Result := True;
       except
          try
             FOutlookApp := CreateOleObject(scxOutlookApp);     
             FWasCreated := True;
             Result := True;
          except
             on E:Exception do
                TSyncLogger.LogError('Inner exception 1: ' + E.Message);  // Just some logging
          end;
       end;
       if Result then   
       begin
          try
             FNameSpace := FOutlookApp.GetNamespace(scxNameSpace);
             // From https://stackoverflow.com/questions/18053110/retrieve-outlook-logged-in-user-smtp-address-after-connecting-through-ole/
             lLog := Format('Connected to Outlook; Application.DefaultProfilename: %s, Application.Name: %s, Application.Version: %s, NameSpace.CurrentProfileName: %s, NameSpace.ExchangeMailboxServerName: %s, NameSpace.Type: %s',
               [FOutlookApp.DefaultProfileName,
                FOutlookApp.Name,
                FOutlookApp.Version,
                FNameSpace.CurrentProfileName,
                FNameSpace.ExchangeMailboxServerName,
                FNameSpace.Type]);
             TSyncLogger.LogDebug(lLog);
             // Output here is:
             // Connected to Outlook; Application.DefaultProfilename: Office365, Application.Name: Outlook, Application.Version: 16.0.0.13801, NameSpace.CurrentProfileName: Office365, NameSpace.ExchangeMailboxServerName: https://outlook.office365.com/mapi/emsmdb/?MailboxId=23[snip]1de@timetell.nl, NameSpace.Type: Mapi 
             lVar := FOutlookApp.Session;                                   // NameSpace object for the current session
             // VarIsClear(lVar) is FALSE here, then this goes wrong:  !!!1
             if not VarIsClear(lVar) then lVar := lVar.CurrentUser;         // Recipient object for the currently logged-on user
             if not VarIsClear(lVar) then lVar := lVar.AddressEntry;        // AddressEntry object for the recipient
             if not VarIsClear(lVar) then lVar := lVar.GetExchangeUser;     // Returns an ExchangeUser object that represents the AddressEntry
             if not VarIsClear(lVar) then lVar := lVar.PrimarySmtpAddress;  // String representing the SMTP address for the ExchangeUser
             if not VarIsClear(lVar) then
             begin
                lLoginSMTP := FOutlookApp.Session.CurrentUser.AddressEntry.GetExchangeUser.PrimarySmtpAddress;
                TSyncLogger.LogDebug('Primary Exchange SMTP address detected as: ' + lLoginSMTP);
             end
             else
             begin
                TSyncLogger.LogError(sErrNoExchangeAccount);
                DisConnectFromOutlook;
                Exit;
             end;
          except
             on E:Exception do
             begin
                TSyncLogger.LogError('Inner exception 2: ' + E.Message);  // This is where we come after the CurrentUser error
                DisConnectFromOutlook;
                Exit;
             end;
          end;

If I comment out the above section playing with the lVar to avoid the exception, the following code gets executed, but it then shows the same problem when retrieving lRecipient.Address:

          if LowerCase(AUserSMTP) <> Lowercase(lLoginSMTP) then
          begin   // Open shared calendar if it's a different user
             try
                lRecipient := FNameSpace.CreateRecipient(AUserSMTP);
                if not VarIsClear(lRecipient) then  // Also FALSE at this moment   !!! 2
                begin
                   lLog := Format('Logging in as different user (%s), created recipient for %s, calling GetSharedDefaultFolder',[AUserSMTP,lRecipient.Address]);
                   TSyncLogger.LogDebug(lLog);
                end
                else
                   OutputDebugString('lRecipient unassigned');
                FCalendarFolder := FNameSpace.GetSharedDefaultFolder(lRecipient, olFolderCalendar);
                if not VarIsClear(FCalendarFolder) then
                begin
                   lLog := Format('GetSharedDefaultFolder folder path = %s',[FCalendarFolder.FolderPath]);
                   TSyncLogger.LogDebug(lLog);
                end
                else
                   OutputDebugString('FCalendarFolder unassigned');
             except
                on E:Exception do
                begin
                   Result := false;    // This is where we come after the lRecipient.Address error
                   TSyncLogger.LogError(Format(sErrOpenGedeeldeAgenda,[AUserSMTP]));
                end;
             end;
          end
          else   // ... otherwise open default calendar folder 
          begin
             FCalendarFolder := FNameSpace.GetDefaultFolder(olFolderCalendar);
             TSyncLogger.LogDebug('Opened default calendar folder, folder path = ' + FCalendarFolder.FolderPath);
          end;
       end;  // if Result
       if Result then TSyncLogger.LogDebug('Connected to Outlook') else TSyncLogger.LogAlways('Connection to Outlook failed');
    end;
  • OLE is already properly initialized at this stage
  • I have verified that the NameSpace.CurrentUser and Recipient.Address properties still exist
  • It makes no difference if I have Outlook running or not at these moments
  • AUserSMTP is a valid Exchange user who has shared his calendar with me on this machine, it is the primary SMTP address of the user which we use in FNameSpace.CreateRecipient.
    (FWIW We also do calendar synchronisation with the same users and data through EWS, and that works fine, so rights don't seem to be the issue).
  • Registry keys for MAPI access are correct
  • This is a 32 bit Delphi 10.4.1 desktop app running on Win 10; the code is running in the main thread
  • FNameSpace.ExchangeConnectionMode is 700=olCachedConnectedFull
  • CurrentUser is defined: In Outloik, when I use Alt+F11 to open the VBA project, select View, Immediate Window, type ? Application.Session.CurrentUser.Name I get my name
  • Trust Center settings are greyed out (but I would have expected an error earlier if they were interfering):
    enter image description here

What can be going on? Why does accessing these two properties NameSpace.CurrentUser and Recipient.Address cause an exception or what can I try to resolve this (e.g. get more error info than "The operation was aborted")?

* It's probably the 0x80004004 Operation aborted error

delphi
outlook
ole
delphi-10.4-sydney
asked on Stack Overflow Mar 23, 2021 by Jan Doggen • edited Mar 23, 2021 by Jan Doggen

2 Answers

3

Looks like you are faced with an Outlook security issue. It can also be a prompt issued by Outlook if you try to access any protected property or method. But in your case that is an exception. You get the security prompts/exceptions because Outlook is configured on the client computer in one of the following ways:

  • Uses the default Outlook security settings (that is, no Group Policy set up)
  • Uses security settings defined by Group Policy but does not have programmatic access policy applied
  • Uses security settings defined by Group Policy which is set to warn when the antivirus software is inactive or out of date

You can create a group policy to prevent security prompts from displaying if any up-to-date antivirus software is installed on the system or just turn these warning off (which is not really recommended).

Read more about that in the Security Behavior of the Outlook Object Model article.

Also you may consider using a low-level code on which Outlook is built and which doesn't give security issues - Extended MAPI. Consider using any third-party wrappers around that API such as Redemption.

Another option would be the Outlook Security Manager which allows suppressing Outlook security issues at runtime on the fly.

answered on Stack Overflow Mar 23, 2021 by Eugene Astafiev • edited Mar 23, 2021 by Dmitry Streblechenko
0

Eugene's answer pointed me in the right direction: it is indeed a 'security issue' in the sense that the 3 disabled 'Programmatic access' radio buttons caused this.

After our sysadmin had removed me from the Office 2016 group policy, the first button was suddenly checked (although I still could not change them):

enter image description here

Now the code ran without issues.

The admin also tried to tweak all kinds of Office 2016 settings under Microsoft Outlook 2016/Security/Security Form Settings/Programmatic Security, but those did did not resolve the issue. Either this specific setting cannot be influenced or there is a bug somewhere when applying the policy.

Relevant links may be:
How to disable Programmatic Access in Group Policy for a user
Change Outlook's Programmatic Access Options
Outlook 2010 programmatic security settings for Simple MAPI cannot be configured by using the Group Policy Object

answered on Stack Overflow Apr 1, 2021 by Jan Doggen

User contributions licensed under CC BY-SA 3.0