添加属性后,如何保留.NET程序集的COM二进制兼容性? [英] How do I preserve COM binary compatibility for a .NET Assembly when properties are added?

查看:85
本文介绍了添加属性后,如何保留.NET程序集的COM二进制兼容性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We have developed a .NET Assembly that stores language translation information and it needs to be consumed by a VB6 application.

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

We would like to be able to change the translation information without having to recompile the application.

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

The translation is provided by a two-file partial class called LanguageServices.

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

One file is non-changing library methods, the other is all auto generated properties from a resx file and the regx is generated from a database of language translation information.

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

This all arose from a need to have a central database of translations that could be programatically "flattened" to a format that can be consumed by each of our disparate applications.

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

Now, I can solve this problem by bypassing it and doing it in a different manner. In fact I could just get rid of the auto-generated list of properties and the problem would go away.

我感兴趣的是我如何解决此问题的方法如下:

What I'm interested is how I can solve this problem, which is thus:

如果我们向数据库添加新的翻译标签(此单词中的此单词变为该单词),它将为该类添加新属性,

If we add new translation labels to the database (THIS WORD in THIS WORD becomes THAT WORD) it adds new properties to the class, which in turn adds new exposed properties to the COM interface.

这些属性被添加到COM接口的中间,从而破坏了二进制兼容性。之所以在中间添加它们,是因为C#编译器在部分类的动态部分和部分类的静态部分之间添加了后缀。我需要做的是以其他方式将它们串联起来,或者在C#文件本身中明确说明顺序。我以为在类的静态部分上显式设置DispIDs可以做到,但事实并非如此。

The properties get added in the middle of the COM interface, thus breaking binary compatibility. They get added in the middle because the C# compiler suffixes the dynamic part of the partial class with the static part of the partial class. What I need it to do is either concatenate them the other way around or explicitly state the order in the C# files themselves. I thought setting the DispIDs explicitly on the static part of the class would do it, but it has not.

这是构建过程生成的一对IDL文件:

Here are the pair of IDL files generated by the build process:

在添加新属性之前,这是IDL。

Here is the IDL before I add a new property.

http://pastebin.com/qPvcUV9z

这是新属性之后的IDL已添加并破坏了兼容性:

And here is the IDL after a new property has been added and compatibility is broken:

http:// pastebin .com / K2MuqtYV

确切的区别是,此位在中间被挤了。

The exact difference is this bit gets shoved in the middle:

[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)中的所有内容; 之后的ID均从0开始

And I think that's the problem, it's change the order of the methods. I thought the order could be overriden by explicitly defining the DispID (you can see that everything from HRESULT Culture([in] ICultureInfo* pRetVal); onwards has an id starting from 0.

以下是编写/生成的C#代码:
ILanguageServices.cs:自动生成的界面。

Here's the C# code that's written/generated: ILanguageServices.cs: Auto generated interface.

[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:界面的不变部分

ILanguageServices_Static.cs: The non-changing part of the interface

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更改为包括静态部分即可。
保持分开很整洁。

Thinking about it, I could probably just make it not a partial class. Just change the xslt that generated the auto-generated part to include the static part. It was just neat to keep it seperate.

无论如何,有人可以告诉我为什么它不起作用以及如何对COM接口进行更严格的控制吗?
严格排序方法似乎是如此……
令人毛骨悚然。

Regardless, can anybody tell me why it's not working and how to keep tighter control over the COM interface? Strictly ordering the methods just seems so... bleugh.

谢谢

J1M。

推荐答案

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


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

The ordering of members within a type is rarely significant to C# code, but may be significant when interfacing with other languages and environments. In these cases, the ordering of members within a type declared in multiple parts is undefined.

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

So there are no provisions in the c# laguage to control the ordering of members of a type, other than the order they are declared. In a type that is declared partially, then the order is completley undefined.

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

So the conclusion here is don't use partial declarations for interfaces that you are going to expose to COM. There is no way to control the interface member order, and since its undefined in the language the resulting member order could change at any time.

这篇关于添加属性后,如何保留.NET程序集的COM二进制兼容性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆