2012-02-16 7 views
8

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 
} 
+1

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

+1

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? –

+2

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! –

risposta

1

tenta di utilizzare la classe di RegistrationServices per registrare il montaggio com. Sceglierà anche i percorsi di registro corretti e farà alcune altre cose.

Esempio:

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

Inoltre credo, che le assemblee client .NET hanno qualche problema in base al server NET com, ma non riesco a trovare qualsiasi risorsa per esso ...

Speranza , aiuterà ...

+0

Sto già utilizzando RegistrationServices per registrare la classe che implementa IPersistFile (vedi domanda). Mentre lo leggo, RegisterAssembly() registra * ogni * classe abilitata al COM nell'assembly, ma non vedo come tale registrazione sia diversa (solo nomi di classi). Tuttavia, lo proverò e pubblicherò di nuovo. –

+0

Purtroppo non è stato d'aiuto. –

+0

Hmm, scusa per questo. –

1

Immagino che il problema sia in fase di esecuzione. Ho creato un server COM che registra utilizzando una libreria C++ (la registrazione viene eseguita in modo impeccabile). Ho avuto problemi con il passaggio a AnyCPU da .NET (CS).

L'architettura:

  • libreria C++ interfaccia COM (costruita su entrambe le piattaforme x64 e x86)
  • .NET libreria wrapper (CS) (istanzia correttamente la richiesta x64/x86 libreria C++)
  • Applicazione .NET (CS) - COM client o server COM

Le cose brutte si verificano durante la registrazione dell'applicazione .NET creata come "AnyCPU". Una volta che il client COM richiama il server COM tramite DCOM, l'applicazione server viene avviata ma il client riceve un errore che non ha potuto avviare il server COM.

sono andato alcuni passi avanti, ha analizzato i dati di registrazione con ProcMon e altri strumenti e ho raggiunto la stessa conclusione:

  • x86 registra le classi nelle classi \ Wow6432Node
  • x64 e AnyCPU registrare le classi nelle classi (su una macchina Windows x64, esattamente le stesse chiavi; scommetto che x86 e AnyCPU avrebbero registrare lo stesso su una macchina x86)

Ora, ho fatto alcuni altri esperimenti: x86/x64/AnyCPU COM cliente può connettersi senza problemi a nessuno x86/x64 server COM, ma non è possibile connettersi in ogni caso a un server AnyCPU COM ...

ho poi svolto le seguenti casi di test:

  1. si registrino x86 server COM, sostituire l'eseguibile con il AnyCPU COM Server: COM Client stava avviando il server COM x86, ma nessuna comunicazione ... stava avviando il server più e più volte ..
  2. Avere il registro server COM x64, sostituire l'eseguibile con il server COM AnyCPU: COM Client era avvio del server COM x64, ma nessuna comunicazione ... stava avviando il server più e più volte.
  3. Hav e il registro Server COM AnyCPU, sostituire l'eseguibile con il server COM x86: COM Client è stato in grado di avviare e connettersi correttamente al server COM x86.
  4. Fare in modo che il server COM AnyCPU registri, sostituire l'eseguibile con il server COM x64: COM Client è stato in grado di avviare e connettersi correttamente al server COM x64.
  5. Fare in modo che il registro del server COM x86 sostituisca l'eseguibile con il server COM x64: COM Client è stato in grado di avviare e connettersi correttamente al server COM x64.
  6. Registrare il server COM x64, sostituire l'eseguibile con il server COM x86: COM Client è stato in grado di avviare e connettersi correttamente al server COM x86.

Dove diavolo è il problema di comunicazione? Questo è molto strano ... Nessuna delle soluzioni presentate (CLSCTX_ACTIVATE_64_BIT_SERVER, PreferredServerBitness o corflag) ha aiutato.

Qualcun altro ha fatto qualche progresso in merito? Dovremmo contattare Microsoft?

+0

Ho concluso che l'unico modo per il mio scenario .Net è quello di distribuire due file binari (x86, x64), se voglio risolvere il problema. Sarebbe interessante chiedere a Microsoft. –

Problemi correlati