ApartmentState para tontos

120

Acabo de corregir un error usando esto:

_Thread.SetApartmentState(ApartmentState.STA);

¡Ahora me gustaría entender qué significa y por qué funciona!

Benjol
fuente
1
Esta publicación puede ayudarlo
Arsen Mkrtchyan

Respuestas:

236

COM es el abuelo de .NET. Tenían objetivos bastante elevados con él, una de las cosas que hace COM pero que .NET omite por completo es proporcionar garantías de subprocesamiento para una clase. Una clase COM puede publicar qué tipo de requisitos de subprocesamiento tiene. Y la infraestructura COM asegura que se cumplan esos requisitos.

Esto está completamente ausente en .NET. Puede usar un objeto Queue <>, por ejemplo, en varios subprocesos, pero si no lo bloquea correctamente, tendrá un error desagradable en su código que es muy difícil de diagnosticar.

Los detalles exactos del subproceso COM son demasiado grandes para caber en una publicación. Me centraré en los detalles de tu pregunta. Un hilo que crea objetos COM tiene que decirle a COM qué tipo de soporte quiere dar a las clases COM que tienen opciones de subprocesamiento restringidas. La gran mayoría de esas clases solo admiten los llamados subprocesos de apartamento, sus métodos de interfaz solo se pueden llamar de forma segura desde el mismo subproceso que creó la instancia. En otras palabras, anuncian "No apoyo el subproceso en absoluto, por favor tenga cuidado de no llamarme nunca desde el hilo equivocado". Incluso si el código del cliente realmente lo llama desde otro hilo.

Hay dos tipos, STA (apartamento de un solo hilo) y MTA. Se especifica en la llamada CoInitializeEx (), una función que debe ser llamada por cualquier hilo que haga algo con COM. El CLR realiza esa llamada automáticamente cada vez que inicia un hilo. Para el hilo de inicio principal de su programa, obtiene el valor para pasar del atributo [STAThread] o [MTAThread] en su método Main (). El valor predeterminado es MTA. Para los subprocesos que crea usted mismo, lo determina su llamada a SetApartmentState (). El valor predeterminado es MTA. Los subprocesos de Threadpool son siempre MTA, que no se pueden cambiar.

Hay una gran cantidad de código en Windows que requiere una STA. Ejemplos notables que usaría usted mismo son el Portapapeles, Arrastrar + Soltar y los diálogos de shell (como OpenFileDialog). Y mucho código que no puede ver, como programas de automatización de la interfaz de usuario y enlaces para observar mensajes. Ninguno de ese código tiene que ser seguro para subprocesos, su autor tendría muchas dificultades para hacerlo seguro sin saber en qué programa se utiliza. En consecuencia, el subproceso de la interfaz de usuario de un proyecto de WPF o Windows Forms siempre debe ser STA para admitir dicho código, al igual que cualquier subproceso que crea una ventana.

Sin embargo , la promesa que le hace a COM de que su hilo es STA requiere que siga el contrato de apartamento de un solo hilo. Son bastante rígidos y puede resultar complicado diagnosticar problemas cuando se rompe el contrato. Los requisitos son que nunca bloquees el hilo durante un período de tiempo y que bombees un bucle de mensaje. El último requisito lo cumple un subproceso de interfaz de usuario de WPF o Winforms, pero deberá encargarse de él usted mismo si crea su propio subproceso de STA. El diagnóstico común para romper el contrato es un punto muerto.

Por cierto, hay bastante soporte integrado en el CLR para cumplir con estos requisitos, lo que lo ayuda a evitar problemas. La declaración de bloqueo y los métodos WaitOne () bombean un bucle de mensaje cuando se bloquea en un hilo de STA. Sin embargo, esto solo se ocupa del requisito de no bloquear nunca, aún necesita crear su propio bucle de mensajes. Application.Run () tanto en WPF como en Winforms.

Anteriormente, contribuí con una respuesta que contiene más detalles sobre la importancia de tener un bucle de mensajes para mantener contento a COM. Encontrarás la publicación aquí .

Hans Passant
fuente
4
¡Excelente respuesta! El error que resolví fue para un hilo que creé para un generador de informes de larga duración que usaba controles WPF para crear partes del informe, por lo que tiene sentido, aunque no sé que ese hilo tiene un bucle de mensaje. .
Benjol
4
En serio, tuve que leer la publicación de MSDN varias veces para entenderla. Tu respuesta es muy clara y está bien escrita. ¡Gracias!
ak3nat0n