C# 什么时候有必要/合适使用InAttribute和OutAttribute进行COM互操作

C# 什么时候有必要/合适使用InAttribute和OutAttribute进行COM互操作,c#,com-interop,C#,Com Interop,我正在尝试整理我们分散在各个项目中的COM互操作定义,并将它们收集到一个已知的好位置,整个开发团队都可以从中受益。这项工作的一部分涉及清理多年来积累的定义 其中一些是从其他源代码中借用的,一些是从pinvoke.net中逐字复制的,还有一些看起来是直接从SDK头中翻译出来的。我注意到的一件事是,关于何时使用各种编组属性没有一致性(即使在pinvoke.net示例中,这也是非常成功的)。问题的一部分是,我认为这里的任何人(包括我自己)都不完全理解何时需要或不需要各种属性,或者它们实际做什么。到目前

我正在尝试整理我们分散在各个项目中的COM互操作定义,并将它们收集到一个已知的好位置,整个开发团队都可以从中受益。这项工作的一部分涉及清理多年来积累的定义

其中一些是从其他源代码中借用的,一些是从pinvoke.net中逐字复制的,还有一些看起来是直接从SDK头中翻译出来的。我注意到的一件事是,关于何时使用各种编组属性没有一致性(即使在pinvoke.net示例中,这也是非常成功的)。问题的一部分是,我认为这里的任何人(包括我自己)都不完全理解何时需要或不需要各种属性,或者它们实际做什么。到目前为止,要把这些翻译正确似乎是猜测和随机变化的结合,直到COMExceptions停止发生,但我更希望翻译是正确的,因为有人真的看了它们并宣称它们是正确的

所以,我从
[In]
[Out]
开始。我知道这两个属性在概念上的作用:它们通知封送员数据必须流向哪个方向。例如,我假设封送处理程序不会费心将
[In]
数据复制回调用者,或者知道
[Out]
数据可能需要在被调用者端释放,等等。我不知道的是:

  • 何时有必要使用这些属性?也就是说,当默认编组行为错误时,属性会使其正确吗
  • 何时使用这些属性是安全的?也就是说,指定默认行为是否会改变封送拆收器的工作方式
  • 什么时候使用这些属性是危险的?我假设显式地标记输出参数
    [In]
    是错误的,但是标记输入参数
    [In,Out]
    实际上会破坏任何东西吗
  • 因此,假设有一个COM接口方法,其IDL如下所示:

    HRESULT Foo(
        [in] ULONG a,
        [out] ULONG * b
        [in, out] ULONG * c);
    
    我可能会将其翻译为以下任何一种:

    void Foo(
      uint cb,
      out uint b,
      ref uint c);
    
    void Foo(
      uint cb,
      [Out] out uint b,
      [In, Out] ref uint c);
    
    void Foo(
      [In] uint cb,
      [Out] out uint b,
      [In, Out] ref uint c);
    

    这三者在功能上有什么不同吗?除了技术正确性之外,是否有任何一个被认为比其他属性“更好”呢?

    不,严格来说,没有必要在您自己的代码中应用这些属性中的任何一个。它们都是可选的,并根据参数的类型自动应用于代码

    本质上,它们具有以下C#关键字等价物:

    • 默认情况下,您将获得
      [In]
    • ref
      关键字可以获取
      [In,Out]
    • out
      关键字可以获取
      [out]
    所有这些都记录在一个漂亮的表格中

    您只需要在希望更改这些隐含语义或更改封送拆收器的默认行为时使用它们。例如,您可以使用
    [In]
    属性标记声明的参数
    ref
    ,以抑制封送处理的“out”部分


    也就是说,我发现自己在使用它们,因为它们是一种非常简单的代码自记录方法。

    我喜欢自记录的想法!只需补充一点:Bool和string使用MarshalAs更为相关,请检查这些……是的,我对MarshalAs属性更为熟悉,尽管其中有一些细节我还不清楚(主要是数组)。我开始很简单:)啊,这实际上解释了我在pinvoke.net上看到的很多东西!例如,如果一个方法采用refid参数,则通常为“ref Guid”。但是如果该参数在IDL中被指定为[in],我可以将其指定为“[in]ref-Guid-refid”,并且它不会费事将Guid结构封送给我,因为我不需要它,对吗?@Michael:是的,没错。您将无法看到对结构所做的任何修改,但如果您不需要这些修改,那么将其标记为
    [In]ref
    就可以了。这就是说,这绝对是一个微观优化,可能不值得为它的正确性付出太多努力。如果我理解正确,
    ref
    out
    关键字也增加了间接性。例如,如果您有一个类
    X
    ,那么在托管代码中说
    f(out X)
    就像在非托管代码中说
    f(**X)
    。因此,如果非托管签名是
    f(*x)
    ,其中x被修改,则必须使用
    [Out]
    属性(或使x成为结构)。资料来源:这里要小心,你在比较苹果和桔子。IDL文件中的[in]和[out]属性仅由midl.exe在生成代理/存根代码时使用。仅当需要在单元或进程之间封送COM调用时才使用。.NET[In]和[Out]属性仅由pinvoke marshaller使用。它运行在一个完全不同的运行时环境中,始终处于进程中,不考虑单元。他们的意图当然是一样的,优化通话。是的,我(想我)理解这一部分。出于我的目的,在将IDL转换为C#时,我只使用IDL属性作为方法“应该”如何运行的提示。注意,IDL中的[out]属性意味着接口编写器希望该参数从调用方返回,并在其中存储一个值,因此我需要确保C#代码以相同的方式运行。我的不足之处在于理解.NET封送处理程序的默认行为,以及何时我需要告诉它该做什么,而不是何时它自己做正确的事情。它们在IDL中是必要的,因为使用指针时完全不清楚数据流的方向。使用ref和out关键字在C#中不是问题。99%的好例子只是省略了C#中的属性。如果您实际使用C#替换COM代码,则为100%。