.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
值
当调用返回 HRESULT
的 PreserveSig
方法时,如果 HRESULT
表示失败,则可以使用 ThrowExceptionForHR 方法引发相应异常。 类似地,在实现 PreserveSig
方法时,可以使用 GetHRForException 方法返回 HRESULT
,其指示异常的相应值。
将 HRESULT 封送为结构
使用 PreserveSig
方法时,int
应为 HRESULT
的托管类型。 但是,使用自定义 4 字节结构作为返回类型允许定义可以简化 HRESULT
操作的辅助方法和属性。 在内置封送中,这是自动工作的。 要在源生成的封送中使用结构代替 int
作为 HRESULT
的托管表示,请添加以 Error 为参数的 MarshalAsAttribute 属性。 此属性的存在将 HRESULT
位重新解释为结构。