2009-12-07 16 views
12

dire che ho un BaseClass classe che implementa IBaseClassC# esporre a COM - Interfaccia eredità

Poi ho un IClass interfaccia che eredita IBaseClass.

Quindi ho una classe denominata classe che implementa IClass.

Ad esempio:

[ComVisible(true), InterfaceType(ComInterfaceType.IsDual), Guid("XXXXXXX")] 
public interface IBaseClass 
{ 
    [PreserveSig] 
    string GetA() 
} 

[ComVisible(true), InterfaceType(ComInterfaceType.IsDual), Guid("XXXXXXX")] 
public interface IClass : IBaseClass 
{ 
    [PreserveSig] 
    string GetB() 
} 

[ComVisible(true), ClassInterface(ClassInterfaceType.None), Guid("XXXXXXX")] 
public class BaseClass : IBaseClass 
{ 
    public string GetA() { return "A"; } 
} 

[ComVisible(true), ClassInterface(ClassInterfaceType.None), Guid("XXXXXXX")] 
public class Class : BaseClass, IClass 
{ 
    public string GetB() { return "B"; } 
} 

Quando si espone al COM, se faccio un esempio di "Class" non permette che chiami Geta().

Quando si cerca il mio IDL nel file TLB, la mia interfaccia IClass assomiglia:

[ 
    odl, 
    uuid(XXXXXXXXXXXXXXXXXXXXX), 
    version(1.0), 
    dual, 
    oleautomation, 

] 
interface IClass : IDispatch { 
    [id(0x60020000)] 
    BSTR GetB(); 
} 

Non sembra nemmeno IClass deriva da IBaseClass!

Se tengo fuori da dove IClass deriva da IBaseClass e basta aggiungere i metodi all'interfaccia, funziona.

Come posso rendere C# abilitare questa ereditarietà in COM? Preferisco non ri-implementare le interfacce quando posso ereditarle.

CRAP: controllare questo link .Net COM Limitation

Se qualcuno ha una risposta a questo è il motivo per cui, o una soluzione migliore di copia-incolla per la mia interfaccia "derivata", me lo faccia sapere. Se no, segnerò una risposta tra un paio di giorni.

+0

È necessario eseguire una query per le altre interfacce per accedere ai metodi al loro interno. A seconda della lingua da cui si utilizza l'oggetto, potrebbe essere sconveniente. Per aggirare il problema è necessario creare un'interfaccia "piatta" per ogni classe o lasciare che sia il compilatore a farlo per te con "ClassInterface" (che ha altri problemi) – adrianm

+1

Se vedi il mio link, il rispondente accenna che .Net fa non applicare le interfacce di base alle interfacce COM quando esportate. Quindi, in altre parole, è necessario copiare e incollare la definizione dell'interfaccia di base per simulare un'interfaccia di base. – jonathanpeppers

+1

Penso che dovresti pubblicare il tuo link di limitazione COM .Net come risposta - è la soluzione corretta, e si consiglia di pubblicare la propria risposta se l'hai capito da solo. – bacar

risposta

11

Questo non è un problema .NET, è una conseguenza del modo in cui la COM funziona. Non supporta l'ereditarietà. Si dovrebbe risolvere questo dal lato client, è necessario chiamare QueryInterface() con l'IID per IBaseClass per ottenere un puntatore all'interfaccia all'interfaccia IBaseClass in modo che possa chiamare GetA(). .NET Interop fornisce automaticamente un'implementazione QI che rende questo lavoro. Tuttavia, non è molto facile da usare in questo caso, progetta il tuo codice C# per rendere più semplice per il cliente utilizzare la tua classe invece del contrario. In genere, è necessario un metodo di sovrascrittura one-liner che deleghi all'implementazione di classe C# di base.

Si noti che c'è un problema con le firme dei metodi e l'uso dell'attributo [PreserveSig]. Non sono richiamabili tramite IDispatch né possono essere auto-marshalling. Ciò richiede un metodo con un HRESULT come tipo di valore di ritorno. È automatico quando si rimuove l'attributo.

+0

Inizialmente ho impostato [PreserveSig] per facilità d'uso con i miei test in VBScript (che vuole pasticciare i parametri), ma funziona allo stesso modo senza (e ha una definizione IDL corretta). Ho intenzione di replicare i metodi nella classe base per evitare che il client debba chiamare QueryInterface. – jonathanpeppers

+1

IMHO questo ** è ** un problema .net, COM ** totalmente ** supporta l'ereditarietà * interface * (non di classe) e il regasm potrebbe aver generato un TLB che aveva IClass derivato dal ** ComVisible ** IBaseClass. Prendo atto che quando ciò accade, l'implementazione IDispatch di Class non è a conoscenza dei metodi IBaseClass - molto deludente. Il tuo consiglio (QI) è buono nel caso in cui una coclea implementi più interfacce; secondo l'MVP nel link di Jonathan, quella tecnica non funzionerà con un'ereditarietà _hierarchy_ perché semplicemente non è supportata. – bacar

+0

@bacar: l'aggregazione è probabilmente il termine migliore. Anyhoo, ci vuole una classe concreta per implementare effettivamente le interfacce. E per far funzionare le interfacce COM ereditate, il compilatore deve generare più v-tables, uno per ogni interfaccia. Questo è supportato solo dagli ambienti di runtime che supportano l'ereditarietà multipla. .NET non è uno di questi, una limitazione piuttosto fondamentale. Sì, potresti chiamarlo un problema .NET, non ti porterà da nessuna parte. –

6

Questo sembra essere molto simile a questa domanda: Interface inheritance in ComVisible classes in C#

come avrete notato nella vostra modifica, questa è una limitazione su .NET a COM hop, che l'interfaccia COM non include interfacce ereditate.

In alcune lingue è possibile risolvere questo problema inserendo un blocco di codice in tutte le interfacce rilevanti, ma per quanto posso vedere questo non è possibile in C#.

+0

Si scopre che questo sarà solo un fastidioso lavoro extra per rendere visibile il nostro assembly. – jonathanpeppers

0

Ecco un approccio diverso che riduce al minimo il lavoro e lascia l'interfaccia pulita.
Basta fornire un ulteriore proprietà che restituisce il BaseInterface:

public IBaseClass BaseProperties { 
    get { return this; } 
} 

Naturalmente si deve aggiungere thise parte nel vostro IBaseClass Interfaccia:

public ISingleShot BaseProperties { get; } 



Ecco il corrispondente codice VB.Net :

ReadOnly Property BaseProperties As ISingleShot 
Public ReadOnly Property BaseProperties As IBaseClass Implements IClass.BaseProperties 
    Get 
    Return Me 
    End Get 
End Property 
0

utilizzare [InterfaceType (ComInterfaceType.Interf aceIsIUnknown)] come attributo per l'interfaccia che estende gli altri. è una soluzione alternativa.