2011-10-27 11 views
5

Hemos desarrollado un .NET Assembly que almacena información de traducción de idiomas y necesita ser consumido por una aplicación VB6.¿Cómo preservo la compatibilidad binaria para un ensamblado .NET que presenta una interfaz COM?

Nos gustaría poder cambiar la información de traducción sin tener que volver a compilar la aplicación.

La traducción es proporcionada por una clase parcial de dos archivos llamada LanguageServices.

Un archivo es métodos de biblioteca no cambiantes, el otro son todas las propiedades autogeneradas de un archivo resx y el regx se genera a partir de una base de datos de información de traducción de idiomas.

Todo esto surgió de la necesidad de tener una base de datos central de traducciones que podría ser programáticamente "aplanada" a un formato que pueda ser consumido por cada una de nuestras dispares aplicaciones.

Ahora, puedo resolver este problema evitándolo y haciéndolo de una manera diferente. De hecho, podría deshacerme de la lista de propiedades generadas automáticamente y el problema desaparecería.

Lo que me interesa es cómo puedo solucionar este problema, que es por lo tanto:

Si añadimos nuevas etiquetas de traducción a la base de datos (esta palabra en esta palabra se convierte en la palabra) que añade nuevas propiedades a la clase, que a su vez agrega nuevas propiedades expuestas a la interfaz COM.

Las propiedades se agregan en el medio de la interfaz COM, lo que rompe la compatibilidad binaria. Se agregan en el medio porque el compilador de C# tiene el sufijo de la parte dinámica de la clase parcial con la parte estática de la clase parcial. Lo que necesito hacer es concatenarlos al revés o indicar explícitamente el orden en los archivos C#. Pensé que establecer los DispIDs explícitamente en la parte estática de la clase lo haría, pero no fue así.

Aquí son el par de archivos IDL generados por el proceso de construcción:

Aquí es el IDL antes de agregar una nueva propiedad.

http://pastebin.com/qPvcUV9z

Y aquí es el IDL después de una nueva propiedad ha sido añadido y la compatibilidad está roto:

http://pastebin.com/K2MuqtYV

La diferencia exacta es este bit se metió en el medio:

[id(0x60020039), propget] 
HRESULT Jn_ExactCaseMatch([out, retval] VARIANT_BOOL* pRetVal); 
[id(0x6002003a), propget] 
HRESULT Jn_Regex([out, retval] VARIANT_BOOL* pRetVal); 
[id(0x6002003b), propget] 
HRESULT Jn([out, retval] BSTR* pRetVal); 

Y yo creo ese es el problema m, es cambiar el orden de los métodos. Pensé que el orden podría ser anulado definiendo explícitamente el DISPID (se puede ver que todo, desde HRESULT Culture([in] ICultureInfo* pRetVal); en adelante tiene un ID empezando por 0.

Aquí está el código C# que está escrito/generada: ILanguageServices.cs: Auto interfaz genera.

[Guid("547a7f6e-eeda-4f77-94d0-2dd24f38ba58")] 
public partial interface ILanguageServices 
{ 
    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Offence_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Offence_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string Offence { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Colour_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Colour_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string Colour { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DebtManagementSystem_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DebtManagementSystem_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string DebtManagementSystem { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DateOfContravention_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DateOfContravention_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string DateOfContravention { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean ContraventionDetails_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean ContraventionDetails_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string ContraventionDetails { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Income_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Income_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string Income { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Hold_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Hold_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string Hold { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean CivilEnforcementOfficer_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean CivilEnforcementOfficer_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string CivilEnforcementOfficer { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean PCNDebt_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean PCNDebt_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string PCNDebt { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean OnHold_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean OnHold_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string OnHold { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DatePutOnHold_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DatePutOnHold_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string DatePutOnHold { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean HoldCode_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean HoldCode_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string HoldCode { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DateHoldExpires_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean DateHoldExpires_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string DateHoldExpires { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean PutOnHoldByUserName_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean PutOnHoldByUserName_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string PutOnHoldByUserName { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean CurrentState_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean CurrentState_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string CurrentState { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Vrm_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean Vrm_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string Vrm { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean State_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean State_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string State { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean CurrentStatechangedd2d2d4_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean CurrentStatechangedd2d2d4_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string CurrentStatechangedd2d2d4 { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean SimonTest_ExactCaseMatch { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    System.Boolean SimonTest_Regex { get; } 

    /// <summary> 
    /// 
    /// </summary> 
    string SimonTest { get; } 
} 

ILanguageServices_Static.CS: La parte no cambiante de la interfaz

public partial interface ILanguageServices 
{ 
    [DispId(0)] 
    ICultureInfo Culture { get; set; } 
    [DispId(1)] 
    IResourceManager ResourceManager { get; } 
    [DispId(2)] 
    ICultureInfo[] GetCultures(System.Globalization.CultureTypes enCultureTypes); 
    [DispId(3)] 
    ICultureInfo GetCultureInfo(int LCID); 
    [DispId(4)] 
    ICultureInfo CurrentCulture { get; } 
    [DispId(5)] 
    string TranslateString(string rawString, bool searchInsideString); 
    [DispId(6)] 
    string TranslateString(string rawString); 
} 

Pensando en ello, probablemente podría simplemente hacer que no una clase parcial. Simplemente cambie el xslt que generó la parte generada automáticamente para incluir la parte estática. Fue genial para mantenerlo separado.

De todos modos, ¿alguien puede decirme por qué no funciona y cómo mantener un control más estricto sobre la interfaz COM? Estrictamente ordenar los métodos simplemente parece tan ... bleugh.

Gracias,

J1M.

+2

El [DispId] solo funciona si el código del cliente utiliza el enlace tardío. Claramente no lo es. Este es el talón de Aquiles de COM, el orden de las tablas v es crítico. Cambiar el [Guid] es lo mejor. –

Respuesta

2

Desde el C# Langauge Specification Version 4 Sección 10.2.6

El orden de los miembros dentro de un tipo rara vez es significativo para el código C#, pero puede ser significativo, cuando interactúe con otros lenguajes y entornos. En estos casos, el orden de los miembros dentro de un tipo declarado en varias partes no está definido.

así que no hay disposiciones en el C# laguage para controlar el orden de los miembros de un tipo, que no sea el orden en que se declaran. En un tipo que se declara parcialmente, el orden está completamente indefinido.

Así que la conclusión aquí es que no use declaraciones parciales para las interfaces que va a exponer a COM. No hay forma de controlar el orden de los miembros de la interfaz, y dado que no está definido en el idioma, el orden de los miembros resultante podría cambiar en cualquier momento.

+0

Sí, pensé que al final y simplemente dejé de usar clases parciales. Simplemente funcionó después de eso. Sin embargo, como mi DLL se compila utilizando el compilador de línea de comandos csc.exe y no VS.NET, descubrí que cambiar el orden de los archivos en la línea de comandos afectaba el orden en que se creó la interfaz para la clase parcial. Pero el solo hecho de no usar clases parciales fue la solución más simple. – RoboJ1M

Cuestiones relacionadas