c# running-object-table

c# - Problemas para acceder a la tabla de objetos en ejecución



running-object-table (3)

Desde este sitio parece que podría deberse a una configuración de registro o a la configuración de seguridad en un objeto registrado en la tabla:

Check "HKLM/Software/Network OLE/Enabled". Fail the request if zero. Check "HKCU/Software/Network OLE/Enabled". Fail the request if zero. Before performing any operation against a ROT entry (i.e., IRunningObjectTable::Revoke, IRunningObjectTable::IsRunning, IRunningObjectTable::GetObject, IRunningObjectTable::NoteTimeChange, IRunningObjectTable::GetTimeOfLastChange, or when including an entry in an IEnumMoniker::Next of an IEnumMoniker returned from IRunningObjectTable::EnumRunning), check the call against the SECURITY_DESCRIPTOR available from IRunningObjectTable::Register. This will be either the value returned by the object''s IActivationSecurity::GetSecurityDescriptor at the time of IRunningObjectTable::Register or will have been taken from "HKCU/Software/Network OLE/DefaultROTSecurity" or "HKLM/Software/Network OLE/DefaultROTSecurity" at the time of IRunningObjectTable::Register if the object did not support IActivationSecurity.

En mi programa, uso la tabla de objetos en ejecución (ROT) para garantizar que solo se ejecute una instancia de mi programa. Desde que "heredé" ese código de un desarrollador que, lamentablemente, dejó la empresa, soy el pobre para resolver los problemas. El código funciona bien, pero tenemos 3 clientes (de un total de 39,000) que obtendrán una AccessDeniedException . Cada cliente ejecuta el software en modo de usuario.

¿Alguna sugerencia que podría estar mal?

bool retVal = false; IMoniker[] arrMoniker = new IMoniker[1]; IBindCtx bindCtx = null; string displayName; int hResult; int mkSys; Guid clsidRot; bool guidCompare = false; IntPtr number = IntPtr.Zero; moreObjectsListed = false; objectFromRot = null; try { // check the objects in the running object table for fitting the specified class id while ((retVal == false) && (0 == enumMoniker.Next(1, arrMoniker, number))) { hResult = CreateBindCtx(0, out bindCtx); if (hResult == 0) { arrMoniker[0].IsSystemMoniker(out mkSys); if (mkSys == 4) { try { // the display name is the class id of the object in the table // --> AccessDeniedException raises here <-- arrMoniker[0].GetDisplayName(bindCtx, null, out displayName); clsidRot = new Guid(displayName.Substring(1)); guidCompare = clsidRot.Equals(clsid); } catch(Exception) {} // an object with fitting class id was found if (guidCompare == true) { rot.IsRunning(arrMoniker[0]); rot.GetObject(arrMoniker[0], out objectFromRot); retVal = true; } } } } } finally { if (arrMoniker[0] != null) { moreObjectsListed = true; Marshal.ReleaseComObject(arrMoniker[0]); } if (bindCtx != null) { Marshal.ReleaseComObject(bindCtx); } }

Edición: Aquí está el código solicitado para el registro de un objeto en el ROT:

internal static extern uint RegisterActiveObject([MarshalAs(UnmanagedType.IUnknown)]object pIUnknown, ref Guid refclsid, uint flags, out uint pdwRegister); internal const uint ActiveObjectStrong = 0; ... NativeMethods.RegisterActiveObject(this, ref guid, NativeMethods.ActiveObjectStrong, out this.runningObjectTableRegisteredId);

Edición 2:

En primer lugar, una gran EXCUSA para todos los investigadores, no obtenemos una AccessDeniedException, es una System.UnauthorizedAccessException (HRESULT: 0x80070005 (E_ACCESSDENIED)).

En segundo lugar, las respuestas a las preguntas del "investigador" Ken Brittain: - SharePoint no está en la combinación - no puedo solicitar el objeto correcto a ROT - Otra sugerencia es que 1 de los 3 problemas (además de 39,000 que funcionan correctamente) se está ejecutando las aplicaciones en un WTS (Windows Terminal Server)

Edición 3:

Aquí hay un seguimiento de pila de una de esas excepciones: (He traducido el seguimiento de pila, porque estaba en una máquina alemana)

System.UnauthorizedAccessException: Access denied (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) at System.Runtime.InteropServices.ComTypes.IRunningObjectTable.EnumRunning(IEnumMoniker& ppenumMoniker) at Datev.Framework.DirectStart.RunningObjectTableClientManager..ctor()

El resto del seguimiento de pila está en nuestro código. En este caso, se puede marcar que la excepción se genera en el constructor de nuestro RunningObjectTableClientManager. Aquí está el código de ese constructor:

private IRunningObjectTable rot; private IEnumMoniker enumMoniker; public RunningObjectTableClientManager() { int retVal = GetRunningObjectTable(0, out this.rot); if (retVal == 0) { rot.EnumRunning(out this.enumMoniker); } }


En mi experiencia, la probabilidad de una colisión GUID, aunque es posible, parece poco probable, por lo que no se investigó. La primera pista que tomé estaba buscando lo que podría causar la AccessDeniedException . Trabajando hacia atrás desde allí, puede ver que GetDisplayName no lanza explícitamente esta excepción (o devuelve algo similar).

Entonces, ¿qué hace? Tu código parece estar en C #. A menos que me equivoque al usar COM de C #, pasaré por una interoperabilidad primaria. Solo hay dos (2) interopses que exponen una interfaz IMoniker que pude encontrar:

  • System.Runtime.InteropServices.ComTypes contiene IMoniker
  • Microsoft.VisualStudio.OLE.Interop contiene uno IMoniker

Estás hablando de una aplicación, así que mi instinto me dice que estás usando la versión en tiempo de ejecución. Mirando las llamadas, no pude encontrar una llamada que devolviera ninguna forma de HRESULT acceso denegado o similar. La interoperabilidad de VisualStudio menciona lo siguiente sobre el acceso y la confianza: Uso de bibliotecas desde un código parcialmente confiable . Esto sonaba como una ruta a seguir y se aplicaría si está utilizando los interops de Visual Studio.

Si está utilizando el espacio de nombres de los servicios de tiempo de ejecución que está contenido en el ensamblado mscorlib.dll (que de acuerdo con esta página .NET Framework Assemblies Callable by Parcialmente Código de confianza está marcado como un código de confianza parcialmente invocable) la explicación no parece aplicarse.

¿Y ahora que? Hice una búsqueda de AccessDeniedException y no encontré ninguna implementación compatible que no fuera una clase Microsoft.Office.Server.ApplicationRegistry.Infrastructure.AccessDeniedException que está marcada como obsoleta en MSDN. La clase se archiva en la biblioteca de clases de SharePoint 2010 .

Así que aquí están mis preguntas: ¿Qué interoperabilidad estás usando? ¿SharePoint está en la mezcla en absoluto? Anteriormente dije que la colisión con GUID no era sospechosa, pero ahora estoy cuestionando esa suposición. ¿Está solicitando el objeto adecuado de la ROT? ¿Se está ejecutando este objeto bajo otro proceso (es decir, no el tuyo)?


Quizás esta no sea la respuesta que está buscando, pero después de haber heredado este código, ¿se ha preguntado si esta era la técnica adecuada para su caso de uso? Esta es la primera vez que veo que una aplicación C # usa Com Interop para algo como prevenir múltiples instancias de aplicaciones. Nunca he tenido buenas experiencias con Com y he encontrado excepciones similares inexplicables o no documentadas.

¿Por qué no echar un vistazo a una técnica alternativa para prevenir múltiples instancias de aplicaciones? He usado un Mutex en soluciones anteriores mías y nunca tuve un problema. Si bien no tengo a mano mi código anterior, este problema se ha cubierto varias veces antes en con algunas respuestas bastante buenas que han sido revisadas por pares y editadas por la comunidad.

Por ejemplo, '' ¿Cuál es un buen patrón para usar un Global Mutex en C #? ''tiene una buena respuesta editada por la comunidad que parece tener en cuenta todo tipo de condiciones de carrera de bola impar y terminaciones de subprocesos / procesos, así como los posibles problemas de seguridad.

Así que mis recomendaciones serían alejarse de Com Interop y, en su lugar, tomar una implementación Mutex.