.NET 互操作中的隐式方法签名转换

为了保持编程语言的不可知性,Windows COM 系统和许多 Windows API 返回调用了 HRESULT 的 4 字节整数类型,以指示 API 是成功还是失败,以及有关失败的一些信息。 需要传递给调用者的其他值通过指针参数“返回”,指针参数充当“out”参数,通常是签名中的最后一个参数。 像 C# 和 Visual Basic 这样的语言传统上会将失败代码转换为异常,以匹配失败在语言中的传播方式,并期望互操作方法签名不包括 HRESULT。 要将方法签名转换为本机签名,运行时会将方法的返回值移动到额外的“out”参数,该参数具有间接级别(换句话说,使其成为指向托管签名的返回类型的指针),并假设 HRESULT 返回值。 如果托管方法返回 void,则不添加其他参数,返回值变为 HRESULT。 例如,请参阅以下两个转换为相同本机签名的 C# COM 方法:

int Add(int a, int b);

void Add(int a, int b, out int sum);
HRESULT Add(int a, int b, /* out */ int* sum);

COM 中的 PreserveSig

默认情况下,C# 中的所有 COM 方法都应使用转换后的签名。 若要在不进行签名转换和 HRESULT 值处理的情况下使用和导出方法,请将 PreserveSigAttribute 添加到 COM 接口方法中。 将属性应用于方法时,不会对签名进行转换,也不会因 HRESULT 值失败而引发异常。 这适用于内置 COM 和源生成的 COM。 例如,请参阅以下带有 PreserveSig 属性的 C# 方法签名及其相应的本机签名。

[PreserveSig]
int Add(int a, int b, out int sum);
HRESULT Add(int a, int b, int* sum);

如果方法可能返回不同 HRESULT 值,而这些值不是失败,但必须以不同的方式处理,那么这将非常有用。 例如,当某个方法没有失败但只返回部分结果时,某些方法可能会返回值 S_FALSE,而当它返回所有结果时,则返回值 S_OK

PreserveSig 和 P/Invokes

DllImportAttribute 属性还有与 PreserveSigAttribute 类似的 bool PreserveSig 字段,但默认为 true。 要指示运行时应转换托管签名并处理返回的 HRESULT,请在 DllImportAttribute 中将 PreserveSig 字段设置为 false。 例如,请参阅以下对同一本机方法的两个 P/Invokes 的签名,其中一个具有 PreserveSig,设置为 false,另一个保留为默认 true 值。

[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true, PreserveSig = false)]
public static extern void SHAutoComplete(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);

[DllImport("shlwapi.dll", EntryPoint = "SHAutoComplete", ExactSpelling = true)]
public static extern int SHAutoCompleteHRESULT(IntPtr hwndEdit, SHAutoCompleteFlags dwFlags);

注意

源生成的 P/Invokes 使用 LibraryImportAttribute,没有 PreserveSig 字段。 生成的代码总是假定本机和托管签名是相同的。 有关详细信息,请参阅源生成的 P/Invokes

手动处理 HRESULT

当调用返回 HRESULTPreserveSig 方法时,如果 HRESULT 表示失败,则可以使用 ThrowExceptionForHR 方法引发相应异常。 类似地,在实现 PreserveSig 方法时,可以使用 GetHRForException 方法返回 HRESULT,其指示异常的相应值。

将 HRESULT 封送为结构

使用 PreserveSig 方法时,int 应为 HRESULT 的托管类型。 但是,使用自定义 4 字节结构作为返回类型允许定义可以简化 HRESULT 操作的辅助方法和属性。 在内置封送中,这是自动工作的。 要在源生成的封送中使用结构代替 int 作为 HRESULT 的托管表示,请添加以 Error 为参数的 MarshalAsAttribute 属性。 此属性的存在将 HRESULT 位重新解释为结构。

另请参阅