lundi 3 août 2015

Why does constructor Invoke on COM object fail if run as Service?

I want to create a C# .net app that will be run as a service. That app will make calls to a COM component. From what I understand (based on SO posts here, here, and here), in order for my code to work I need to start a new thread to run my code on with apartment state == STA.

Consider the following:

public class MessagePumpManager
        {
            private readonly Thread messagePump;
            private AutoResetEvent messagePumpRunning = new AutoResetEvent(false);
            SimconnectToJson eventsToJson = null;

            public MessagePumpManager()
            {
                // start message pump in its own thread
                messagePump = new Thread(RunMessagePump) { Name = "ManualMessagePump" };

                messagePump.SetApartmentState(ApartmentState.STA); 
                messagePump.Start();
                messagePumpRunning.WaitOne();
            }

            // Message Pump Thread
            private void RunMessagePump()
            {

                Console.WriteLine("Message Pump Thread apt state " + messagePump.GetApartmentState().ToString() 
                    + " started with handle " + messageHandler.Handle.ToString());

                // Create control to handle windows messages
                MessageHandler messageHandler = new MessageHandler();

                // Attempt to initialize COM object...
                 Type myComObjectType = typeof(Microsoft.FlightSimulator.SimConnect.SimConnect);

                System.Reflection.ConstructorInfo ctorInfo = myComObjectType.GetConstructor(new Type[] { typeof(string), typeof(IntPtr), typeof(uint), typeof(System.Threading.WaitHandle), typeof(uint) });

                try
                {
                    Microsoft.FlightSimulator.SimConnect.SimConnect simconnect 
                    = (Microsoft.FlightSimulator.SimConnect.SimConnect)ctorInfo.Invoke(new object[] { "Service Connection", messageHandler.Handle, (uint)0x0402, null, (uint)0 });

                    Console.WriteLine("Successful invocation to create new COM object");

            }
            catch (Exception ex) 
            {
                Console.WriteLine("Error attempting to create COM object");
                Console.WriteLine("");
                Console.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
            }

                messagePumpRunning.Set();
                Application.Run();
            }

        }

If the code's executable is run from the UI, it works. If it's run as a service, it fails with an error:

Exception has been thrown by the target of an invocation.
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)

According to the log output, the ApartmentState of my thread is STA, and I know that when run from a UI my constructor invoke is correct. Yet I'm clearly doing something wrong.





Aucun commentaire:

Enregistrer un commentaire