Как сохранить двоичную совместимость COM для сборки .NET при добавлении свойств? - PullRequest
5 голосов
/ 27 октября 2011

Мы разработали сборку .NET, которая хранит информацию о переводе языка и должна использоваться приложением VB6.

Мы хотели бы иметь возможность изменять информацию о переводе без перекомпиляции приложения.

Перевод обеспечивается частичным классом из двух файлов под названием LanguageServices.

Один файл является неизменяемым библиотечным методом, другой - автоматически сгенерированными свойствами из файла resx, а regx генерируется из базы данных информации о переводе языка.

Все это возникло из-за необходимости иметь центральную базу данных переводов, которая могла бы программно «сглаживаться» до формата, который может использоваться каждым из наших разрозненных приложений.

Теперь я могу решить эту проблему, обойдя ее и сделав это по-другому. На самом деле я мог бы просто избавиться от автоматически сгенерированного списка свойств, и проблема исчезла бы.

Что меня интересует, так это то, как я могу решить эту проблему, а именно:

Если мы добавим новые метки перевода в базу данных (ЭТО СЛОВО в ЭТОМ СЛОВЕ становится ЭТОМ СЛОВО), оно добавляет новые свойства в класс, что, в свою очередь, добавляет новые открытые свойства в интерфейс COM.

Свойства добавляются в середине интерфейса COM, что нарушает двоичную совместимость. Они добавляются в середине, потому что компилятор C # добавляет суффикс динамической части частичного класса к статической части частичного класса. Для этого мне нужно либо объединить их наоборот, либо явно указать порядок в самих файлах C #. Я думал, что установка DispID явно для статической части класса сделает это, но это не так.

Вот пара файлов IDL, сгенерированных в процессе сборки:

Вот IDL, прежде чем я добавлю новое свойство.

http://pastebin.com/qPvcUV9z

А вот IDL после добавления нового свойства и нарушения совместимости:

http://pastebin.com/K2MuqtYV

Точная разница в том, что этот бит посередине:

[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);

А я думаю в этом проблема, это изменение порядка методов. Я думал, что порядок можно изменить, явно указав DispID (вы можете видеть, что все, начиная с HRESULT Culture([in] ICultureInfo* pRetVal);, имеет идентификатор, начинающийся с 0.

Вот код C #, который написан / сгенерирован: ILanguageServices.cs: автоматически сгенерированный интерфейс.

[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: неизменяемая часть интерфейса

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);
}

Думая об этом, я мог бы просто сделать это не частичным классом. Просто измените xslt, который сгенерировал автоматически созданную часть, чтобы включить статическую часть. Это было просто аккуратно, чтобы держать его отдельно.

Независимо от того, кто-нибудь может сказать мне, почему он не работает и как обеспечить более жесткий контроль над интерфейсом COM? Строго упорядочивание методов выглядит так ... bleugh.

Спасибо

J1M.

1 Ответ

3 голосов
/ 09 ноября 2011

Из спецификации языка C # Версия 4 Раздел 10.2.6

Порядок членов внутри типа редко имеет значение для кода C #, но может иметь значение при взаимодействии сдругие языки и среды.В этих случаях порядок элементов в типе, объявленном в нескольких частях, не определен.

Таким образом, в языке c # нет положений для управления порядком элементов типа, отличного от порядкаони объявлены.В типе, который объявлен частично, порядок полностью не определен.

Итак, вывод здесь - не используйте частичные объявления для интерфейсов, которые вы собираетесь выставить для COM.Нет способа контролировать порядок элементов интерфейса, и, поскольку он не определен на языке, результирующий порядок элементов может измениться в любое время.

...