分享方式:


.NET Interop 中的隱含方法簽章轉譯

為了保持與程式設計語言不相關,Windows COM 系統和許多 Windows API 都會傳回稱為 HRESULT 的 4 位元組整數型別,以指出 API 成功或失敗,以及失敗的一些相關資訊。 需要傳遞至呼叫者的其他值會透過做為「out」參數的指標參數「傳回」,而且通常是簽章中的最後一個參數。 C# 和 Visual Basic 等程式設計語言傳統上會將失敗程式碼轉譯為例外狀況,以符合失敗在該語言中通常的傳播方式,並預期 Interop 方法簽章不會包含 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/Invoke

DllImportAttribute 屬性也有類似 PreserveSigAttributebool PreserveSig 欄位,但預設為 true。 若要指出執行階段應該轉譯受控簽章並處理傳回的 HRESULT,請將 DllImportAttribute 中的 PreserveSig 欄位設定為 false。 例如,請參閱下列相同原生方法的兩個 P/Invoke 簽章,其中一個將 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/Invoke,其使用的 LibraryImportAttribute 沒有 PreserveSig 欄位。 產生的程式碼一律會假設原生和受控簽章相同。 如需詳細資訊,請參閱來源產生的 P/Invoke

手動處理 HRESULT

當呼叫 PreserveSig 方法傳回 HRESULT 時,如果 HRESULT 表示失敗,您可以使用 ThrowExceptionForHR 方法來擲回對應的例外狀況。 同樣地,在實作 PreserveSig 方法時,您可以使用 GetHRForException 方法傳回 HRESULT,指出例外狀況的對應值。

將 HRESULT 封送處理為結構

使用 PreserveSig 方法時,int 必須是 HRESULT 的受控型別。 不過,使用自訂 4 位元組結構做為傳回型別,可讓您定義協助程式方法和屬性,以簡化 HRESULT 的使用。 在內建封送處理中,這會自動運作。 若要在來源產生封送處理中使用結構取代 int 做為 HRESULT 的受控表示法,請新增具有 Error 作為引數的 MarshalAsAttribute 屬性。 這個屬性的存在會將 HRESULT 的位元重新解譯為結構。

另請參閱