5

我们开发了一个存储语言翻译信息的 .NET 程序集,它需要由 VB6 应用程序使用。

我们希望能够更改翻译信息而无需重新编译应用程序。

翻译由名为 LanguageServices 的两文件分部类提供。

一个文件是不变的库方法,另一个是所有从 resx 文件自动生成的属性,而 regx 是从语言翻译信息数据库生成的。

这一切都源于需要有一个中央翻译数据库,该数据库可以通过编程方式“扁平化”为我们每个不同的应用程序可以使用的格式。

现在,我可以通过绕过它并以不同的方式来解决这个问题。事实上,我可以摆脱自动生成的属性列表,问题就会消失。

我感兴趣的是如何解决这个问题,即:

如果我们向数据库添加新的翻译标签(THIS WORD 中的 THIS WORD 变为 THAT WORD),它会向类添加新属性,进而向 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 开始的 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。

4

1 回答 1

3

来自C# 语言规范版本 4第 10.2.6 节

一个类型中成员的排序对于 C# 代码很少重要,但在与其他语言和环境交互时可能很重要。在这些情况下,在多个部分中声明的类型中成员的顺序是未定义的。

因此,除了声明它们的顺序外,c# 语言中没有任何规定来控制类型成员的顺序。在部分声明的类型中,顺序是完全未定义的。

所以这里的结论是不要对要公开给 COM 的接口使用部分声明。无法控制接口成员顺序,并且由于它在语言中未定义,因此生成的成员顺序可能随时更改。

于 2011-11-08T23:14:25.587 回答