2012-02-16 11 views
8

Estoy intentando que COM inicie mi servidor .NET COM fuera de proceso. Funciona si el proceso del servidor se compila con x64, pero si utilizo AnyCPU (que es lo que quiero) se cuelga por un tiempo y finalmente falla con 0x80080005 (CO_E_SERVER_EXEC_FAILURE). ¿Cómo puedo hacer que esto funcione?COM no puede iniciar el servidor fuera de proceso .Net compilado como AnyCPU

  • Me estoy ejecutando en una máquina de 64 bits: Windows 7 con Visual Studio 2008 SP1.
  • Veo en el Administrador de tareas que inicia mi servidor. Así que supongo que el problema está en las comunicaciones entre COM y el servidor (registro de clase).
  • Mi aplicación cliente de prueba está escrita en C#, pero no importa si está compilada para x86 o x64. El problema también ocurre con algo escrito en C++ de 32 bits.
  • Si reconstruyo el servidor utilizando x64 y lo ejecuto, y luego reconstruyo de nuevo como AnyCPU, entonces COM puede iniciarlo. Un reinicio me llevará de vuelta a la situación original. Quizás COM no sabe de antemano qué bitness se va a usar, y una ejecución previa ayuda.
  • Encontré Andy McMullen's blog post y traté de pasar CLSCTX_ACTIVATE_64_BIT_SERVER a CoCreateInstance(), pero eso desencadena una falla anterior: 0x80040154 (REGDB_E_CLASSNOTREG). ¿Estoy haciendo algo mal en mi registro COM? Puedes ver a continuación que es muy simple. El registro ocurre cuando se ejecuta en 64 bits, y el problema ocurre cuando el cliente tiene 64 bits, por lo que Wow6432Node no debería estar involucrado.

Otro tipo ha tenido un similar problem, pero la respuesta de MSFT es confusa. Parece sugerir que solo puede funcionar a través de DCOM (ver enlace) o COM +. Sospecho que cualquiera de los dos será mucho trabajo y mucho peor que distribuir mi .exe creado como x64 y x86.

Quizás se esté preguntando por qué estoy implementando IPersistFile. Es porque mi verdadero problema es hacer que BindMoniker() trabaje desde un programa C++ de 32 bits a mi programa AnyCPU .Net. Reduje mi problema al ejemplo más simple presentado aquí.

Aquí es el código de cliente:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)] 
    [return: MarshalAs(UnmanagedType.Interface)] 
    static extern object CoCreateInstance(
     [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, 
     [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, 
     CLSCTX dwClsContext, 
     [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid); 

    [Flags] 
    enum CLSCTX : uint 
    { 
     CLSCTX_LOCAL_SERVER = 0x4, 
     CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000, 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     IPersistFile pf = (IPersistFile)CoCreateInstance(
      new Guid("1984D314-FC8D-44bc-9146-8A13500666A6"), 
      null, 
      CLSCTX.CLSCTX_LOCAL_SERVER, 
      new Guid("0000010b-0000-0000-C000-000000000046")); // IPersistFile 
     pf.Load("c:\\bozo", 0); 
    } 
} 

y aquí está el servidor:

static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     if (Environment.CommandLine.Contains("/reg")) { 
      RegistryKey cls = Registry.LocalMachine.CreateSubKey(String.Format(
       "SOFTWARE\\Classes\\CLSID\\{0}", PersistFile.ClassID.ToString("B"))); 
      cls.SetValue("InprocHandler32", "Ole32.dll"); 
      RegistryKey ls32 = cls.CreateSubKey("LocalServer32"); 
      ls32.SetValue(null, '"' + Application.ExecutablePath + '"'); 
      ls32.SetValue("ServerExecutable", Application.ExecutablePath); 
     } 

     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 

     RegistrationServices reg = new RegistrationServices(); 
     reg.RegisterTypeForComClients(
      typeof(PersistFile), 
      RegistrationClassContext.LocalServer, 
      RegistrationConnectionType.MultipleUse); 

     Application.Run(new Form1()); 
    } 
} 

[ComVisible(true), 
Guid("1984D314-FC8D-44bc-9146-8A13500666A6"), 
ClassInterface(ClassInterfaceType.None)] 
public class PersistFile : IPersistFile 
{ 
    public static Guid ClassID 
    { 
     get 
     { 
      GuidAttribute a = (GuidAttribute)typeof(PersistFile).GetCustomAttributes(typeof(GuidAttribute), false)[0]; 
      return new Guid(a.Value); 
     } 
    } 

    #region IPersistFile 
    public void GetClassID(out Guid pClassID) 
    { 
     MessageBox.Show("GetClassID"); 
     pClassID = ClassID; 
    } 

    public int IsDirty() 
    { 
     MessageBox.Show("IsDirty"); 
     return 1; 
    } 

    public void Load(string pszFileName, int dwMode) 
    { 
     MessageBox.Show(String.Format("Load {0}", pszFileName)); 
    } 

    public void Save(string pszFileName, bool fRemember) 
    { 
     MessageBox.Show("Save"); 
     throw new NotImplementedException(); 
    } 

    public void SaveCompleted(string pszFileName) 
    { 
     MessageBox.Show("SaveCompleted"); 
     throw new NotImplementedException(); 
    } 

    public void GetCurFile(out string ppszFileName) 
    { 
     MessageBox.Show("GetCurFile"); 
     throw new NotImplementedException(); 
    } 
    #endregion 
} 
+1

Suena como que algo en el mundo AnyCPU está predeterminado en x86, y solo se necesitaría un pícaro para echar por tierra todo el asunto. Imagino que pasar CLSCTX_ACTIVATE_64_BIT_SERVER en su llamada de activación no funcionará a menos que también lo haya pasado en su llamada de registro a RegisterTypeForComClients.Y sospecho que construir como x64 y ejecutar, luego reconstruir para x86, funciona porque usted registra el objeto de clase pero no lo anula, por lo que el registro persiste en la tabla de objetos de clase global. He encontrado que AnyCPU es un problema, y ​​solo lo uso en proyectos 100% .NET (¡casi nunca!) –

+1

Gracias @Ciaran, tal vez se deba a que COM no es lo suficientemente inteligente como para interpretar el indicador AnyCPU en el CLR .exe encabezado inteligentemente. Intenté pasar CLSCTX_ACTIVATE_64_BIT_SERVER a RegisterTypeForComClients, pero arrojó E_INVALIDARG. No estoy sorprendido porque cuando hago la llamada ya estoy corriendo como 64bits, que la función puede determinar fácilmente, entonces, ¿cuál sería el punto? –

+2

He revisado sus referencias con más detalle hoy y, de hecho, parece que hay algo roto en la infraestructura. De mala gana, llegué a la conclusión de que esto no va a funcionar para ti, y tendrás que renunciar a AnyCPU. ¡La buena noticia es que una vez que hayas renunciado a esa herejía, te sentirás mucho mejor! –

Respuesta

1

intenta utilizar la clase RegistrationServices para registrar su montaje com. También elegirá las correcciones de registro correctas y hará otras cosas.

Ejemplo:

Assembly currentAssembly = Assembly.GetExecutingAssembly(); 
System.Runtime.InteropServices.RegistrationServices regAsm = new System.Runtime.InteropServices.RegistrationServices(); 
bool isRegistered = regAsm.RegisterAssembly(currentAssembly, System.Runtime.InteropServices.AssemblyRegistrationFlags.SetCodeBase); 

También pienso, que los conjuntos de cliente .NET tienen algunos problemas de acuerdo a los servidores COM de .NET, pero no puedo encontrar ningún recurso para que ...

Esperanza , ayudará ...

+0

Ya estoy usando RegistrationServices para registrar la clase que implementa IPersistFile (ver pregunta). Mientras lo leo, RegisterAssembly() registra * cada * clase habilitada para COM en el ensamblado, pero no veo cómo ese registro sería diferente (solo nombres de clase). Sin embargo, lo intentaré y publicaré nuevamente. –

+0

Desafortunadamente no ayudó. –

+0

Hmm, lo siento por esto. –

1

Supongo que el problema está en el tiempo de ejecución. Creé un servidor COM que se registra utilizando una biblioteca C++ (el registro se realiza sin problemas). Me he encontrado con problemas al cambiar a AnyCPU desde .NET (CS).

La arquitectura:

  • biblioteca de C++ COM interfaz (construido en ambas plataformas x64 y x86)
  • .NET envoltorio biblioteca (CS) (crea una instancia correctamente el requerido x64/x86 biblioteca de C++)
  • Aplicación .NET (CS): cliente COM o servidor COM

Ocurren cosas feas al registrar la aplicación .NET creada como "AnyCPU". Una vez que el Cliente COM invoca al Servidor COM a través de DCOM, la aplicación de servidor se inicia pero el cliente recibe el error de que no se pudo iniciar el Servidor COM.

Fui algunos pasos más, se analizaron los datos de registro con procmon y otras herramientas y llegó a la misma conclusión:

  • x 86 registra las clases en las clases \ Wow6432Node
  • x64 y Cualquier CPU registrar las clases en las clases (en una máquina de 64 bits de windows, exactamente las mismas claves; apuesto a que x86 y Cualquier CPU registrarían el mismo en una máquina x86)

Ahora, hice algunos más experimentos: la x86/x64/Cualquier CPU COM cliente puede conectarse sin problemas a cualquier x86/x64 servidor COM pero no se puede conectar en cualquier caso a un servidor COM ... Cualquier CPU

que a continuación realiza los siguientes casos de prueba:

  1. tener el registro del servidor x86 COM, reemplazar el ejecutable con el COM Cualquier CPU Servidor: COM Client estaba iniciando el servidor COM x86, pero no había comunicación ... estaba iniciando el servidor una y otra vez.
  2. Haga que el servidor COM x64 se registre, reemplace el ejecutable con el servidor COM AnyCPU: COM Client era iniciando el servidor COM x64, pero sin comunicación ... estaba iniciando el servidor una y otra vez ..
  3. Hav e el registro AnyCPU COM Server, reemplace el ejecutable con el servidor COM x86: COM Client pudo iniciarlo y conectarse exitosamente al servidor COM x86.
  4. Haga que el servidor AnyCPU COM se registre, reemplace el ejecutable con el servidor COM x64: COM Client pudo iniciarlo exitosamente y conectarse al servidor COM x64.
  5. Haga que el servidor COM x86 se registre, reemplace el ejecutable con el servidor COM x64: COM Client fue capaz de iniciarlo exitosamente y conectarse al servidor COM x64.
  6. Haga que el servidor COM x64 se registre, reemplace el ejecutable con el servidor COM x86: COM Client fue capaz de iniciarlo exitosamente y conectarse al servidor COM x86.

¿Dónde diablos está el problema de comunicación? Esto es muy extraño ... Ninguna de las soluciones presentadas (CLSCTX_ACTIVATE_64_BIT_SERVER, PreferredServerBitness o corflags) ayudó.

¿Alguien más hizo algún progreso en este asunto? ¿Deberíamos contactar a Microsoft?

+0

He llegado a la conclusión de que el único camino a seguir para mi escenario .Net es distribuir dos binarios (x86, x64), si deseo resolver el problema. Sería interesante preguntarle a Microsoft. –

Cuestiones relacionadas