#c# #com #interop #binary-compatibility
#c# #com #взаимодействие #двоичная совместимость
Вопрос:
Мы разработали сборку .NET, в которой хранится информация о переводе языка, и она должна использоваться приложением VB6.
Мы хотели бы иметь возможность изменять информацию о переводе без необходимости перекомпиляции приложения.
Перевод обеспечивается двухфайловым частичным классом, называемым LanguageServices.
Один файл представляет собой неизменяемые методы библиотеки, другой — все автоматически сгенерированные свойства из файла resx, а regx генерируется из базы данных информации о переводе языка.
Все это возникло из-за необходимости иметь центральную базу данных переводов, которые можно было бы программно «сгладить» до формата, который может использоваться каждым из наших разрозненных приложений.
Теперь я могу решить эту проблему, обойдя ее и выполнив по-другому. На самом деле я мог бы просто избавиться от автоматически сгенерированного списка свойств, и проблема исчезла бы.
Меня интересует, как я могу решить эту проблему, которая, таким образом:
Если мы добавим новые метки перевода в базу данных (ЭТО СЛОВО в ЭТОМ СЛОВЕ становится ЭТИМ СЛОВОМ), это добавит новые свойства классу, который, в свою очередь, добавит новые открытые свойства в COM-интерфейс.
Свойства добавляются в середине COM-интерфейса, что нарушает двоичную совместимость. Они добавляются в середине, потому что компилятор C # суффиксирует динамическую часть частичного класса со статической частью частичного класса. Что мне нужно, так это либо объединить их наоборот, либо явно указать порядок в самих файлах C #. Я думал, что явная установка идентификаторов в статической части класса сделает это, но этого не произошло.
Вот пара файлов 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);
И я думаю, что в этом-то и проблема, это изменение порядка методов. Я думал, что порядок может быть переопределен путем явного определения идентификатора (вы можете видеть, что все, начиная с 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. [DispId] работает только в том случае, если клиентский код использует позднюю привязку. Очевидно, что это не так. Это ахиллесова пята COM, порядок v-таблиц имеет решающее значение. Лучше всего изменить [Guid] .
Ответ №1:
Из раздела 10.2.6 спецификации языка C # версии 4
Порядок элементов внутри типа редко имеет значение для кода C #, но может иметь значение при взаимодействии с другими языками и средами. В этих случаях порядок элементов внутри типа, объявленного в нескольких частях, не определен.
Таким образом, в c # нет условий для управления порядком элементов типа, отличным от порядка их объявления. В типе, который объявлен частично, порядок полностью не определен.
Итак, вывод здесь таков: не используйте частичные объявления для интерфейсов, которые вы собираетесь предоставить COM. Невозможно управлять порядком элементов интерфейса, и поскольку он не определен в языке, результирующий порядок элементов может измениться в любое время.
Комментарии:
1. Да, я понял это в конце концов и просто перестал использовать частичные классы. После этого все просто сработало. Однако, поскольку моя DLL фактически компилируется с использованием csc.exe компилятор командной строки и не VS.NET , я обнаружил, что изменение порядка файлов в командной строке повлияло на порядок, в котором был создан интерфейс для частичного класса. Но просто не использовать частичные классы было более простым решением.