none
Is it possible to marshal IUIAutomationElement to a different process? RRS feed

  • Question

  • I am trying to marshal IUIAutomationElement to send it to a different process. However the call to CoMarshalInterface fails with error 0x80040155 "Interface not registered". Does anyone know what does it mean?

    Here is the relevant part of my code:

    bool marshalElemPtr(IUIAutomationElement* elem)
    {
     ULONG ulSize;
     HRESULT hr = ::CoGetMarshalSizeMax (&ulSize,
      IID_IUIAutomationElement, elem, MSHCTX_LOCAL,
      NULL, MSHLFLAGS_NORMAL);
    
     if (FAILED(hr))
     {
      return false;
     }
    
     // ulSize is 390 here
    
     HGLOBAL hGlobal = ::GlobalAlloc (GMEM_MOVEABLE, 
                     (DWORD) ulSize);
    
     CComPtr<IStream> pStream;
     hr = ::CreateStreamOnHGlobal (hGlobal, TRUE, &pStream);
     if (FAILED(hr))
     {
      return false;
     }
    
    
     hr = ::CoMarshalInterface (pStream, 
      IID_IUIAutomationElement,
      elem,
      MSHCTX_LOCAL, NULL, 
      MSHLFLAGS_NORMAL);
     if (FAILED(hr))
     {
      // hr is 0x80040155 here
      return false;
     }
    
    
    • Edited by najaryan Friday, November 5, 2010 2:18 PM edited formatting
    Friday, November 5, 2010 2:17 PM

Answers

  • Hi, Najaryan,

    This is a limitation in the Windows 7 implementation - these interfaces are not registered for marshalling between processes.

    Keep in mind that if they were registered, calling a marshalled interface will block on the home process of that interface.  In other words, if you marshal an interface from process A to process B, and call it in B, process B will wait while process A carries out the action.  If you are trying to do this to avoid lockups, you will need to be thoughtful about how to avoid just re-creating your problem by having B lock up waiting for A.  I think that's why this isn't a common request.

    (A theoretical alternative is to create your own interface from B to A that is designed to be asynchronous, and then have A do the work synchronously while B waits unblocked.)

    If you do want to try this, you can actually work around this limitation by updating your registry.  If you look in HKEY_CLASSES_ROOT\Interface for IUIAutomationEventHandler, you can see an eventing interface that is registered for cross-process marshalling.  Just clone that registry key for each interface you want to marshal, replacing the interface name and IID appropriately with the interface name and IID of the interface you want to marshal (e.g. IUIAutomationElement and {D22108AA-8AC5-49A5-837B-37BBB3D7591E}, respectively.)

    Thanks,
    Michael


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Friday, November 5, 2010 4:05 PM

All replies

  • Hi, Najaryan,

    This is a limitation in the Windows 7 implementation - these interfaces are not registered for marshalling between processes.

    Keep in mind that if they were registered, calling a marshalled interface will block on the home process of that interface.  In other words, if you marshal an interface from process A to process B, and call it in B, process B will wait while process A carries out the action.  If you are trying to do this to avoid lockups, you will need to be thoughtful about how to avoid just re-creating your problem by having B lock up waiting for A.  I think that's why this isn't a common request.

    (A theoretical alternative is to create your own interface from B to A that is designed to be asynchronous, and then have A do the work synchronously while B waits unblocked.)

    If you do want to try this, you can actually work around this limitation by updating your registry.  If you look in HKEY_CLASSES_ROOT\Interface for IUIAutomationEventHandler, you can see an eventing interface that is registered for cross-process marshalling.  Just clone that registry key for each interface you want to marshal, replacing the interface name and IID appropriately with the interface name and IID of the interface you want to marshal (e.g. IUIAutomationElement and {D22108AA-8AC5-49A5-837B-37BBB3D7591E}, respectively.)

    Thanks,
    Michael


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Friday, November 5, 2010 4:05 PM
  • Thanks for warning. I intend to do most of my UI Automation calls in the helper process, however I still need to make some calls in the main process, hence I needed to marshal the elements from helper to main process.

    I will try to register IUIAutomationElement IID in the registry manually and see how it works. However I have an immediate question: should I expect any problems if I do it? Is there any reason why it is not done by UI Automation itself? Or it is just something that was not implemented simply due to lack of deveopment time, etc. but not because of technical problems expected?

    Thanks again.

    Friday, November 5, 2010 4:51 PM
  • I don't know of any problems.  There was a lack of demand for it at the time we were working on Windows 7.

    Thanks,
    Michael


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Saturday, November 6, 2010 5:29 PM
  • Michael,

    I tried your suggestion and after manually registering the interface in registry I can now marshal the interface.

    However the attempt to unmarshal it in a different process fails. CoUnmarshalInterface returns E_FAIL which unfortunately is not useful for diagnosis.

    It looks like the data that I send across the process is correct (it is 388 bytes long and starts with "MEOW" which is I believe the signature for standard marshalled data). I am not sure what could be wrong in my code. Here is unmarshaling part:

    	std::auto_ptr<char> elemPtrBuf(new char[dataLen+1]);
    	// I get data into elemPtrBuf here (I receive it from sockets)
    
    	HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, dataLen);
    
    	if (hGlobal == NULL) {
    	  return 0;
    	}
    
    	PBYTE pData = (PBYTE)::GlobalLock(hGlobal);
     
    	::CopyMemory (pData, elemPtrBuf.get(), dataLen);
    	::GlobalUnlock (hGlobal);
    
    	//
    	// Unmarshal the interface pointer.
    	//
    	CComPtr<IStream> pStream;
    	HRESULT hr = CreateStreamOnHGlobal (hGlobal, TRUE, &pStream);
    
    	if (FAILED (hr)) 
    	{
    		::GlobalFree (hGlobal);
    		return 0;
    	}
    
    	IUIAutomationElement* elem;
    	hr = CoUnmarshalInterface (pStream, IID_IUIAutomationElement,
    		(void**) &elem);
    
    	if (FAILED (hr)) {
    		// I get hr==E_FAIL here
    		::GlobalFree (hGlobal);
    		return 0;
    	}
    
     return elem;
    
    
    Tuesday, November 9, 2010 2:27 PM
  • Sorry for the delayed response - it's been quite a week.

    Three suggestions, based on the (working) implementation I have:

    1. Are you marshalling with the MSHCTX_LOCAL flag?  You need that to get a marshalled interface that can go across process.

    2. If you unmarshal in the same process, does it work?  (I know that sounds funny; it's just a sanity check.)  That was the first sanity check I did to make sure it was working.

    3. You might also want to register the related interface IUIAutomation for marshalling.  I did all of the UIA COM interfaces at once, so I haven't tried doing just IUIAutomationElement.

    Thanks,
    Michael


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Tuesday, November 16, 2010 9:32 PM
  • Michael,

    Yes, I use MSHCTX_LOCAL flag. I did not try unmarshalling in the same process. I have currently worked around the problem by not doing any marshaling at all (I found it easier to change my program to not require it). If I find that I need to do it anyway I will try your suggestions and will post back here.

    Thanks again for your help.

    Wednesday, November 17, 2010 7:07 AM