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
}
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!) –
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? –
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! –