我们开发了一个存储语言翻译信息的 .NET 程序集,它需要由 VB6 应用程序使用。
我们希望能够更改翻译信息而无需重新编译应用程序。
翻译由名为 LanguageServices 的两文件分部类提供。
一个文件是不变的库方法,另一个是所有从 resx 文件自动生成的属性,而 regx 是从语言翻译信息数据库生成的。
这一切都源于需要有一个中央翻译数据库,该数据库可以通过编程方式“扁平化”为我们每个不同的应用程序可以使用的格式。
现在,我可以通过绕过它并以不同的方式来解决这个问题。事实上,我可以摆脱自动生成的属性列表,问题就会消失。
我感兴趣的是如何解决这个问题,即:
如果我们向数据库添加新的翻译标签(THIS WORD 中的 THIS WORD 变为 THAT WORD),它会向类添加新属性,进而向 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 开始的 id。
这是编写/生成的 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。