none
Outlook 2013 initial setup ADAL (Browser) prompt doesn't respond to COM automation via IHTMLDocument2 or IHTMLDocument RRS feed

  • Question

  • Any suggestions on configuration of Outlook/Internet Explorer to enable automation, or pointer to what is wrong with getting the document/window?

    Outlook configured for ADAL prompting from federation source for authentication

    During regular usage (post initial configuration) addition of a new account, federated prompt for auth can be automated manipulating the HTMLDocument2 interface. 

    During initial configuration "Welcome to outlook 2013" , add an email account, provide email address that will call on federation and displays embedded browser window "Internet Explorer_Server" window.  Attempting to automate this window getting HTMLDocument2 using WM_HTML_GETOBJECT which initially seems successful.  However calls to the COM API then mostly fail and getting response 

    (-2147418111) 0x80010001 (RPC_E_CALL_REJECTED) Call was rejected by callee

    Added MessageFilter and saw that it was prompting SERVERCALL_RETRYLATER   however even with 3 second wait time doesn't become responsive, and results in reject.

    I've put in delays and in testing run well after the window was ready and idle, still blocked with retry later.

    For testing I can partly automate the exact same using .Net UI Automation framework, however it is troublesome as the federation login page keeps changing shape, and we then have to support 2 methods because of existing usage.

    automate (HWND IEServerWnd ) {
    	static UINT nMsg = RegisterWindowMessage( L"WM_HTML_GETOBJECT");
    	
    	CComPtr<IHTMLDocument2> doc;
    	::SendMessageTimeout(IEServerWnd, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&lRes);
    	ObjectFromLresult(lRes, IID_IHTMLDocument2, 0, (LPVOID*)&doc);
    	if(doc){
    		CComObject<IMessageFilterImpl> *filterCOM;
    		if (!FAILED(CComObject<IMessageFilterImpl>::CreateInstance(&filterCOM))){
    			filterCOM->RegisterFilter();
    		}
    		
    		BSTR url = 0;
    		OutputDebugString( L"getting document url ");
    		HRESULT hr = doc->get_URL(&url);
    // Rejected result here
    		if (SUCCEEDED(hr) && (url)) {
    			OutputDebugString( L"Got document url ");
    		}
    
    // This doesn't reject
    		CComPtr<IServiceProvider> provider;
    		hr = doc->QueryInterface(IID_IServiceProvider,(LPVOID *)&provider);
    
    // But this does
    		CComPtr<IWebBrowser2> spWeb;
    		hr = provider->QueryService(IID_IWebBrowserApp,IID_IWebBrowser2,(LPVOID *)&spWeb);
    	}
    }
    
    
    class ATL_NO_VTABLE IMessageFilterImpl :
    	public CComObjectRootEx<CComSingleThreadModel>, 
    	public IMessageFilter
    {
    	BEGIN_COM_MAP(IMessageFilterImpl)
    		COM_INTERFACE_ENTRY(IMessageFilter)
    	END_COM_MAP()
    private:
    	IMessageFilter *m_old;
    
    public: 
    	IMessageFilterImpl()     {}
    	~IMessageFilterImpl()    { RevokeFilter(); }
    
    public:
    
    	HRESULT RegisterFilter()
    	{
    		return ::CoRegisterMessageFilter(this, &m_old);
    	}
    	HRESULT RevokeFilter()
    	{
    		IMessageFilter *old;
    		return ::CoRegisterMessageFilter(m_old, &old);
    	}
    
    
    public: //IMessageFilter 
    	void FinalRelease(){}
    
    	// Default as we don't have incoming
    	STDMETHODIMP_(DWORD) HandleInComingCall(
    		DWORD dwCallType,
    		HTASK threadIDCaller,
    		DWORD dwTickCount,
    		LPINTERFACEINFO lpInterfaceInfo)
    	{
    		return SERVERCALL_ISHANDLED;
    	}
    
    	STDMETHODIMP_(DWORD) RetryRejectedCall(
    		HTASK threadIDCallee,
    		DWORD dwTickCount,
    		DWORD dwRejectType)
    		//the ret val from HandleInComingCall() 
    	{
    		OutputDebugString(L"RetryRejectedCall");
    		//indicates that the call should be canceled 
    		if (dwRejectType == SERVERCALL_REJECTED
    			|| dwTickCount > 3e3) // 3 second timeout
    		{
    			return -1;
    		}
    
    		//we must've got SERVERCALL_RETRYLATER 
    		// Value >= 0 and <100  - The call is to be retried immediately.
    		// Value >= 100	        - COM will wait for this many milliseconds and then retry the call.
    		return 150;
    	}
    
    	STDMETHODIMP_(DWORD) MessagePending(
    		HTASK threadIDCallee,
    		DWORD dwTickCount,
    		DWORD dwPendingType)
    		//the ret val from RetryRejectedCall() 
    	{
    		return PENDINGMSG_WAITDEFPROCESS;
    	}
    };


    Thursday, March 2, 2017 10:27 AM

All replies

  • Hello,

    You should deal with the OOM on the main thread only. Or you will get an exceptions like that. Outlook uses a single threaded apartment model which doesn't allow dealing with OOM from secondary threads. The recent Outlook version detects such usage and fires an exception.

    See How to: Fix 'Application is Busy' and 'Call was Rejected By Callee' Errors for more information.

    Also be aware, Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.

    If you are building a solution that runs in a server-side context, you should try to use components that have been made safe for unattended execution. Or, you should try to find alternatives that allow at least part of the code to run client-side. If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution. Read more about that in the Considerations for server-side Automation of Office article.


    profile for Eugene Astafiev at Stack Overflow, Q&A for professional and enthusiast programmers


    Thursday, March 2, 2017 12:52 PM
  • Thankyou for the suggestions.

    This is a client side application that users will be present during usage, so server side limitations don't seem to be an issue, nor unattended.

    It is also not within the Outlook process, Inter process COM calls, but not DCOM.

    Unfortunately we are still suffering this issue even having ensured that the thread of execution working with Office/Outlook/MSHTML COM objects is STA.  This was an existing issue we had resolved - though I will look more closely at logs to validate the thread hasn't had some other action cause it to be MTA before we are intentionally initialising it to STA.

    // HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);  // The IE objects don't respond properly in a multithreaded apartment.
    HRESULT res = CoInitializeEx(0, COINIT_APARTMENTTHREADED);

    The referenced link is a little confusing to me in that it seems to be discussing how to automate Visual Studio itself, with only side reference to Office.

    Just to repeat seperate from the previous wall of text.
    Automation of the Office display of embedded browser works in all other situations identified except for initial user setup.

    Thursday, March 2, 2017 1:29 PM
  • Does your STA thread have a message pump?
    Thursday, March 2, 2017 2:12 PM
  • Not sure I really understand where that is heading.

    Yes, there is a message pump in the STA thread, the pump is busy calling COM.

    It's an outgoing COM call, the only way I can think of handling additional messages is to add 'pump' actions to the IMessageFilter while it waits...

    Friday, March 3, 2017 10:31 AM
  • Since threads in the MTA don't need a message pump and code had been switched over from using the MTA to STA I just wanted to verify that there was a message pump on the STA thread.
    Friday, March 3, 2017 11:53 AM
  • To review, still doesn't work on initial outlook configuration of ADAL popup embedded browser

    Running inside of Outlook process or Out of Process from our own app calls of MSHTML COM interfaces fail with busy response.

    While other COM interfaces supporting MSAA and .net UI Automation framework succeed.  Appears 'busy' response is a lie.

    Monday, March 27, 2017 2:00 AM
  • >>Automation of the Office display of embedded browser works in all other situations identified except for initial user setup.

    It seems this issue only exist while initial user setup. Would you mind moving ADAL before or after user setup for a work around?


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, March 28, 2017 3:18 AM
  • @Edward, might be an option..

    If the 'first time profile setup initial mailbox' on the host could be skipped and go into an empty - no mailbox.

    Then add the mailbox after then that subsequent mailbox setup has worked for us.

    is there some configuration to allow skipping new profile setup?

    Tuesday, March 28, 2017 5:00 AM
  • >>is there some configuration to allow skipping new profile setup?

    I am afraid we could not skip new profile setup. But, I suggest you try to check whether a new profile without email account will meet your requirement. You could add email account later.

    1.Open Outlook->Next

    2.Add an Email Account->Do you want to set up Outlook to connect to an email account?->No

    3.Don't Add an Email Account->Check Use Outlook without an email account

    4.Finish


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, March 29, 2017 6:06 AM