locked
Threading and MarshalingBehavior attributes ignored on C++/CX classes?

    Question

  • Very simple class:

    #include <objbase.h>
    
    using namespace Windows::Foundation::Metadata;
    
    namespace TestComponent
    {
    
    [Threading(ThreadingModel::MTA)]
    [MarshalingBehavior(MarshalingType::Standard)]
    public ref class TestClass sealed
    {
    public:
    	Platform::String^ TestMethod()
    	{
    		APTTYPE aptType;
    		APTTYPEQUALIFIER qualifier;
    		CoGetApartmentType(&aptType, &qualifier);
    
    		switch (aptType)
    		{
    		case APTTYPE::APTTYPE_MTA:
    			return "MTA";
    		case APTTYPE::APTTYPE_STA:
    			return "STA";
    		case APTTYPE::APTTYPE_NA:
    			return "NA";
    		case APTTYPE::APTTYPE_MAINSTA:
    			return "MAINSTA";
    		default:
    			return "Unknown";
    		}
    	}
    };
    
    }

    Unless I am missing something, the way the attributes are set up, when this class is created properly (i.e. ref new in C++/CX, new in C#/JS, or RoActivateInstance()), TestClass::TestMethod can only return "MTA".

    Yet it certainly returns something else. When used this way:

    document.body.textContent = (new TestComponent.TestClass).testMethod();

    Body gets assigned "STA".

    Is there any way to compel C++/CX to obey threading and marshaling attributes?

    Thanks!


    • Edited by Mythanar Sunday, May 19, 2013 4:03 AM typos
    Sunday, May 19, 2013 3:58 AM

All replies

  • Here is a link to repro project:

    http://sdrv.ms/12DBHZP

    Sunday, May 19, 2013 4:02 AM
  • Hi,

    I think CoGetApartmentType can get thread ApartmentType, not the attribute for class. If we create this class in different thread, the result will be different. Please these codes in C#.

                IAsyncAction ThreadPoolWorkItem = Windows.System.Threading.ThreadPool.RunAsync(
        (source) =>
        {
            TestClass tc = new TestClass();
            String res=tc.TestMethod();
    
            Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High,
             () =>
             {
                 TestClass tc2 = new TestClass();
                 String res2 = tc2.TestMethod();
                 int a = 9;
             });
        });

    For more information about threading and marshaling, please take a look of this
    http://msdn.microsoft.com/en-us/library/windows/apps/hh771042.aspx

    Best regards,
    Jesse


    Jesse Jiang
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, May 20, 2013 6:42 AM
  • Jesse,

    You are correct in that CoGetApartmentType() returns the thread ApartmentType, not the attribute. Please correct me if I am wrong, but according to this:

    http://msdn.microsoft.com/en-us/library/windows/apps/hh771042.aspx

    The ThreadingModel attribute specifies where the class is loaded when activated: only in a user-interface thread (STA) context, only in a background thread (MTA) context, or in the context of the thread that creates the object (Both).

    Unless I am misinterpreting this [Threading(ThreadingModel::MTA)] means that the class can only be activated in an MTA thread. Before, in good old COM days, trying to activate MTA class in STA thread caused COM to create MTA thread, activate the class there, and marshal the interface back to STA thread.

    Since I specifically put [MarshalingBehavior(MarshalingType::Standard)] there, the marshaling behavior is one of standard COM marshaler, which is well-known:

    http://msdn.microsoft.com/en-us/library/windows/apps/windows.foundation.metadata.marshalingtype.aspx

    Standard | standard 3
    The class does not implement IMarshal or forwards to CoGetStandardMarshal on all interfaces

    And the behavior of standard marshaler for inter-apartment communication is described in http://msdn.microsoft.com/en-us/library/ms809971.aspx, under "Mixed Model Development" section.

    In the case of STA->MTA call, system should have created MTA, created the object in that MTA, and marshaled the TestMethod() call. It is just very surprising it does not happen, despite the attributes propagating correctly to the manifest.

    Tuesday, May 21, 2013 3:51 AM
  • Hi Mythanar,

    The attribute will affect how the COM creates the control (e.g. via CoCreateInstanceFromApp or RoActivateInstance). In your code snippet you new the class instead of going through COM. This creates ThisClass as a normal C++ class, not as a COM object, and so no marshaling is done when calling TestMethod. It gets called on the current thread in whichever apartment the thread runs on.

    --Rob

    Tuesday, May 21, 2013 6:58 AM
    Owner
  • Hi Rob,

    Apologies for not being more clear. My second code example (the one that creates the class with "new") is in *JavaScript*. It cannot create the class as a "normal C++ class", it has to go through WinRT.

    Tuesday, May 21, 2013 5:50 PM