Как сохранить двоичную совместимость для сборки.NET, которая представляет интерфейс COM?
Мы разработали сборку.NET, которая хранит информацию о переводе языка и должна использоваться приложением VB6.
Мы хотели бы иметь возможность изменять информацию о переводе без перекомпиляции приложения.
Перевод обеспечивается частичным классом из двух файлов, который называется LanguageServices.
Один файл не изменяет библиотечные методы, другой - все автоматически генерируемые свойства из файла resx, а regx генерируется из базы данных информации о переводе языка.
Все это возникло из-за необходимости иметь центральную базу данных переводов, которая могла бы быть программно "сведена" к формату, который может использоваться каждым из наших разрозненных приложений.
Теперь я могу решить эту проблему, обойдя ее и сделав это по-другому. На самом деле я мог бы просто избавиться от автоматически сгенерированного списка свойств, и проблема исчезла бы.
Что меня интересует, так это то, как я могу решить эту проблему:
Если мы добавим новые метки перевода в базу данных (ЭТО СЛОВО в ЭТОМ СЛОВЕ становится ЭТОМ СЛОВОМ), оно добавляет новые свойства в класс, что, в свою очередь, добавляет новые открытые свойства в интерфейс COM.
Свойства добавляются в середине интерфейса COM, что нарушает двоичную совместимость. Они добавляются в середине, потому что компилятор C# добавляет суффикс динамической части частичного класса к статической части частичного класса. Для этого мне нужно либо объединить их наоборот, либо явно указать порядок в самих файлах C#. Я думал, что установка DispID явно в статической части класса сделает это, но это не так.
Вот пара файлов IDL, сгенерированных процессом сборки:
Вот IDL, прежде чем я добавлю новое свойство.
А вот IDL после добавления нового свойства и нарушения совместимости:
Точная разница в том, что этот бит посередине:
[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? Строго упорядочивать методы кажется просто... блин.
Спасибо,
J1M.
1 ответ
Из спецификации языка C# версии 4, раздел 10.2.6
Порядок членов внутри типа редко имеет значение для кода C#, но может иметь значение при взаимодействии с другими языками и средами. В этих случаях порядок членов в типе, объявленном в нескольких частях, не определен.
Таким образом, в языке C# нет положений, управляющих упорядочением элементов типа, кроме порядка, в котором они объявлены. В типе, который объявлен частично, порядок полностью не определен.
Итак, вывод здесь - не используйте частичные объявления для интерфейсов, которые вы собираетесь выставить для COM. Нет никакого способа контролировать порядок элементов интерфейса, и, поскольку он не определен на языке, результирующий порядок элементов может измениться в любое время.