用于改进接口和方法设计的 IDL 技术

在开发处理一致数据和变体数据的 RPC 接口和方法时,请考虑使用以下 IDL 特定技术来提高安全性和性能。 本主题中引用的属性是在处理一致数据(例如 [size_is] 和 [max_is] 属性)或变体数据(例如 [length_is] 和 [string] 属性)的方法参数上设置的 IDL 属性。

使用具有一致数据参数的 [range] 属性

[range] 属性指示 RPC 运行时在数据解组过程中额外执行大小验证。 具体来说,它验证作为关联参数传递的数据的大小是否在指定范围内。

[range] 属性不会影响线路格式。

如果线路上的值超出允许的范围,RPC 将抛出 RPC_X_INVALID_BOUND 或 RPC_X_BAD_STUB_DATA 异常。 这提供了额外的数据验证级别,并有助于防止缓冲区溢出等常见安全错误。 同样,使用 [range] 可以提高应用程序性能,因为用它标记的一致数据具有明确定义的约束可供 RPC 服务考虑。

RPC 服务器存根内存管理规则

在为支持 RPC 的应用程序创建 IDL 文件时,了解 RPC 服务器存根内存管理规则非常重要。 应用程序可以通过将 [range] 与上述一致数据结合使用,并故意避免将可变长度数据 IDL 属性(如 [length_is])应用于一致数据来提高服务器资源利用率。

不建议将 [length_is] 应用于 IDL 文件中定义的数据结构字段。

可变长度数据参数的最佳做法

以下是为可变大小数据结构、方法参数和字段定义 IDL 属性时要考虑的几个最佳做法。

  • 使用早期关联。 通常,最好定义可变大小参数或字段,使其在控制整数类型之后立即出现。

    例如,

    earlyCorr
    (
    [in, range(MIN_COUNT, MAX_COUNT)] long size, 
    [in,size_is(size)] char *pv
    );
    

    优于

    lateCorr
    (
    [in,size_is(size)] char *pv, 
    [in, range(MIN_COUNT, MAX_COUNT)] long size)
    );
    

    其中,earlyCorr 紧邻可变长度数据参数之前声明大小参数,而 lateCorr 则紧接在其之后声明大小参数。 使用早期对应可提高整体性能,尤其是在频繁调用该方法的情况下。

  • 对于以 [out, size_is] 属性元组标记的参数,如果数据长度在客户端已知或客户端有合理的上限,则方法定义在参数归属和顺序方面应如下所示:

    outKnownSize
    (
    [in,range(MIN_COUNT, MAX_COUNT)] long lSize,
    [out,size_is(lSize)] UserDataType * pArr
    );
    

    在这种情况下,客户端为 pArr 提供固定大小的缓冲区,从而允许服务器端 RPC 服务以良好的保证程度分配合理大小的缓冲区。 请注意,在示例中,数据是从服务器接收的 ([out])。 传递到服务器的数据 ([in]) 的定义类似。

  • 对于 RPC 应用程序的服务器端组件决定数据长度的情况,方法定义应如下所示:

    typedef [range(MIN_COUNT,MAX_COUNT)] long RANGED_LONG;
    
    outUnknownSize
    (
    [out] RANGED_LONG *pSize,
    [out,size_is(,*pSize)] UserDataType **ppArr
    );
    

    RANGED_LONG 是为客户端和服务器存根定义的类型,并且具有客户端可以正确预期的指定大小。 在此示例中,客户端将 ppArr 作为 NULL 传递,RPC 服务器应用程序组件分配正确的内存量。 返回时,客户端上的 RPC 服务将为返回的数据分配内存。

  • 如果客户端想要将大型一致数组的子集发送到服务器,应用程序可以指定子集的大小,如以下示例所示:

    inConformantVaryingArray
    (
    [in,range(MIN_COUNT,MAX_COUNT)] long lSize,
    [in] long lLength, 
    [in,size_is(lSize), length_is(lLength)] UserDataType *pArr
    );
    

    这样,RPC 将只通过线路传输数组的 lLength 元素。 但是,此定义强制 RPC 服务在服务器端分配大小为 lSize 的内存。

  • 如果客户端应用程序组件确定服务器可以返回的数组的最大大小,但允许服务器传输该数组的子集,则应用程序可以通过定义类似于以下示例的 IDL 来指定此类行为:

    inMaxSizeOutLength
    (
    [in, range(MIN_COUNT, MAX_COUNT)] long lSize,
    [out] long *pLength,
    [out,size_is(lSize), length_is(*pLength)] UserDataType *pArr
    );
    

    客户端应用程序组件指定数组的最大大小,服务器指定传回客户端的元素数。

  • 如果服务器应用程序组件需要将字符串返回到客户端应用程序组件,并且客户端知道要从服务器返回的最大大小,则应用程序可以使用一致字符串类型,如以下示例所示:

    outStringKnownSize
    (
    [in,range(MIN_COUNT, MAX_STRING)] long lSize,
    [out,size_is(lSize),string] wchar_t *pString
    );
    
  • 如果客户端应用程序组件不应控制字符串的大小,则 RPC 服务可以专门分配内存,如以下示例所示:

    outStringUnknownSize
    (
    [out] LPWSTR *ppStr
    );
    

    在调用 RPC 方法时,客户端应用程序组件必须将 ppStr 设置为 NULL