Sto cercando di ottenere COM per avviare il mio server COM .NET out-of-process. Funziona se il processo del server è compilato con x64, ma se uso AnyCPU (che è quello che voglio) allora si blocca per un po 'e alla fine fallisce con 0x80080005 (CO_E_SERVER_EXEC_FAILURE). Come posso farlo funzionare?COM non può avviare fuori processo. Server Net compilato come AnyCPU
- Sono in esecuzione su una macchina a 64 bit: Windows 7 con Visual Studio 2008 SP1.
- Vedo in Task Manager che avvia il mio server. Quindi immagino che il problema sia nelle comunicazioni tra COM e il server (registrazione della classe).
- La mia applicazione client di prova è scritta in C#, ma non importa se è compilata per x86 o x64. Il problema si verifica anche con qualcosa scritto in C++ a 32 bit.
- Se ricostruisco il server utilizzando x64 ed eseguo, quindi ricompilare nuovamente come AnyCPU, COM può avviarlo. Un riavvio mi riporterà alla situazione originale. Forse COM non sa in anticipo quale Bitness sarà usato, e una esecuzione precedente aiuta.
- Ho trovato Andy McMullen's blog post e ho provato a passare CLSCTX_ACTIVATE_64_BIT_SERVER a CoCreateInstance(), ma questo causa un errore in precedenza: 0x80040154 (REGDB_E_CLASSNOTREG). Sto facendo qualcosa di sbagliato nella mia registrazione COM? Puoi vedere sotto che è molto semplice. La registrazione si verifica quando si esegue a 64 bit e il problema si verifica quando il client è a 64 bit, quindi Wow6432Node non dovrebbe essere coinvolto.
Un altro tizio ha avuto un similar problem, ma la risposta MSFT è confusa. Sembra suggerire che può funzionare solo tramite DCOM (vedi link) o COM +. Ho il sospetto che sia un sacco di lavoro, e sostanzialmente peggio di distribuire il mio. Exe costruito come x64 e x86.
Ci si potrebbe chiedere perché sto implementando IPersistFile. È perché il mio vero problema è far funzionare BindMoniker() da un programma C++ a 32 bit al mio programma AnyCPU .Net. Ho ridotto il mio problema all'esempio più semplice presentato qui.
Ecco il codice del client:
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);
}
}
e qui è il server:
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
}
Sembra che qualcosa nel mondo AnyCPU sia impostato per default su x86 e ci vorrebbe solo un ladro per far naufragare tutto. Immagino che il passaggio di CLSCTX_ACTIVATE_64_BIT_SERVER alla chiamata di attivazione non funzionerà, a meno che tu non lo abbia anche passato nella tua registrazione a RegisterTypeForComClients.E sospetto che la costruzione come x64 e in esecuzione, quindi la ricostruzione per x86, funzioni perché si registra l'oggetto classe ma non si annulla la registrazione, e quindi la registrazione persiste nella tabella degli oggetti di classe globale. Ho trovato AnyCPU un dolore, e lo uso solo in progetti .NET al 100% (cioè quasi mai!) –
Grazie a @Ciaran, forse si tratta di COM non abbastanza intelligente da interpretare il flag AnyCPU nel CLR .exe header in modo intelligente. Ho provato a passare CLSCTX_ACTIVATE_64_BIT_SERVER a RegisterTypeForComClients, ma ha lanciato E_INVALIDARG. Non sono sorpreso perché nel momento in cui faccio la chiamata sono già in esecuzione come 64 bit, che la funzione può facilmente determinare, quindi quale sarebbe il punto? –
Ho esaminato le tue referenze in modo più dettagliato oggi e sembra davvero che ci sia qualcosa di rotto nell'infrastruttura. Con una certa riluttanza concludo che questo non funzionerà per te, e dovrai rinunciare a AnyCPU. La buona notizia è che una volta che hai rinunciato a quell'eresia ti sentirai molto meglio! –