本主題描述如何存取非受管理的函數,並介紹數個屬性欄位,以標註受管理的程式碼中的方法定義。 如需示範如何建構基於.NET的宣告以搭配平台調用使用的範例,請參閱 使用平台調用封送數據。
您需要知道函式的名稱和匯出它的 DLL 名稱,才能從管理代碼存取非受管 DLL 函式。 透過這項資訊,您可以開始為 DLL 中實作的 Unmanaged 函式撰寫 Managed 定義。 此外,您可以調整平臺叫用建立函式的方式,並從函式封送處理數據。
備註
Windows API 函式提供配置字串的功能,讓您可利用諸如 LocalFree
的方法來釋放字串。 平台調用會以不同的方式處理這類參數。 針對平台調用呼叫,請將參數設為 IntPtr
類型,而不是 String
類型。 使用 System.Runtime.InteropServices.Marshal 類別提供的方法,以便手動將型別轉換為字串並手動釋放。
宣告基本概念
針對非管理函式的管理定義是依語言而定,如下列範例所示。 如需更完整的程式代碼範例,請參閱 平台調用範例。
Friend Class NativeMethods
Friend Declare Auto Function MessageBox Lib "user32.dll" (
ByVal hWnd As IntPtr,
ByVal lpText As String,
ByVal lpCaption As String,
ByVal uType As UInteger) As Integer
End Class
若要將 DllImportAttribute.BestFitMapping、DllImportAttribute.CallingConvention、、DllImportAttribute.ExactSpellingDllImportAttribute.PreserveSig、 DllImportAttribute.SetLastError或 DllImportAttribute.ThrowOnUnmappableChar 字段套用至 Visual Basic 宣告,您必須使用 DllImportAttribute 屬性,而不是 Declare
語句。
Imports System.Runtime.InteropServices
Friend Class NativeMethods
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Friend Shared Function MessageBox(
ByVal hWnd As IntPtr,
ByVal lpText As String,
ByVal lpCaption As String,
ByVal uType As UInteger) As Integer
End Function
End Class
using System;
using System.Runtime.InteropServices;
internal static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern int MessageBox(
IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
using namespace System;
using namespace System::Runtime::InteropServices;
[DllImport("user32.dll")]
extern "C" int MessageBox(
IntPtr hWnd, String* lpText, String* lpCaption, unsigned int uType);
調整定義
無論您是否明確設定它們,屬性字段都在工作中定義 Managed 程式代碼的行為。 平台調用會根據元件中作為元數據存在之各種字段上設定的預設值運作。 您可以藉由調整一或多個字段的值來改變此預設行為。 在許多情況下,您可以使用 DllImportAttribute 來設定值。
下表列出與平台調用相關的一組完整屬性欄位。 針對每個欄位,數據表會包含預設值和連結,以瞭解如何使用這些欄位來定義 Unmanaged DLL 函式。
領域 | 說明 |
---|---|
BestFitMapping | 啟用或停用最佳匹配映射。 |
CallingConvention | 指定要在傳遞方法自變數中使用的呼叫慣例。 默認值為 WinAPI ,對應至 __stdcall 32 位 Intel 型平臺。 |
CharSet | 控制名稱修飾以及字串參數傳遞至函式的方式。 預設值為 CharSet.Ansi 。 |
EntryPoint | 指定要呼叫的 DLL 進入點。 |
ExactSpelling | 控制是否應該修改進入點以對應至字元集。 預設值會因程式設計語言而異。 |
PreserveSig | 控制受控方法簽章是否應該轉換成傳回 HRESULT 的非受控簽章,並包含作為傳回值的額外 [out, retval] 參數。 預設值為 true (不應該轉換簽章)。 |
SetLastError | 可讓呼叫端使用 Marshal.GetLastWin32Error API 函式來判斷執行 方法時是否發生錯誤。 在 Visual Basic 中,預設值為 true ;在 C# 和C++中,預設值為 false 。 |
ThrowOnUnmappableChar | 控制在將無法映射的 Unicode 字元轉換成 ANSI “?” 字元時擲出例外狀況。 |
如需詳細的參考資訊,請參閱 DllImportAttribute。
平台調用安全性考慮
Assert
、Deny
和PermitOnly
是SecurityAction列舉的成員,稱為堆疊步行修飾詞。 如果這些成員在平台調用宣告和 COM 介面定義語言 (IDL) 語句上做為宣告式屬性,則會予以忽略。
平台調用範例
本節中的平台呼叫範例說明如何使用 RegistryPermission
屬性搭配堆疊漫遊修飾詞。
在下列範例中, SecurityActionAssert
會忽略 、 Deny
和 PermitOnly
修飾詞。
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
[RegistryPermission(SecurityAction.Assert, Unrestricted = true)]
private static extern bool CallRegistryPermissionAssert();
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
[RegistryPermission(SecurityAction.Deny, Unrestricted = true)]
private static extern bool CallRegistryPermissionDeny();
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
[RegistryPermission(SecurityAction.PermitOnly, Unrestricted = true)]
private static extern bool CallRegistryPermissionDeny();
不過,下列範例中的 Demand
修飾符是可以接受的。
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]
private static extern bool CallRegistryPermissionDeny();
SecurityAction 如果修飾詞位於包含平台調用呼叫的類別上,修飾詞會正常運作。
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]
public ref class PInvokeWrapper
{
public:
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
private static extern bool CallRegistryPermissionDeny();
};
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]
class PInvokeWrapper
{
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
private static extern bool CallRegistryPermissionDeny();
}
SecurityAction 修飾詞在嵌套情境下也能正確運作,其中它們被放置於平台調用的呼叫者上。
{
public ref class PInvokeWrapper
public:
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
private static extern bool CallRegistryPermissionDeny();
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]
public static bool CallRegistryPermission()
{
return CallRegistryPermissionInternal();
}
};
class PInvokeScenario
{
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]
private static extern bool CallRegistryPermissionInternal();
[RegistryPermission(SecurityAction.Assert, Unrestricted = true)]
public static bool CallRegistryPermission()
{
return CallRegistryPermissionInternal();
}
}
COM Interop 範例
本節中的 COM Interop 範例說明如何使用屬性搭配堆疊行走修飾詞。
下列 COM Interop 介面宣告會忽略 Assert
、 Deny
和 PermitOnly
修飾詞,類似於上一節中的平台調用範例。
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]
interface IAssertStubsItf
{
[RegistryPermission(SecurityAction.Assert, Unrestricted = true)]
bool CallRegistryPermission();
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
bool CallFileIoPermission();
}
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]
interface IDenyStubsItf
{
[RegistryPermission(SecurityAction.Deny, Unrestricted = true)]
bool CallRegistryPermission();
[FileIOPermission(SecurityAction.Deny, Unrestricted = true)]
bool CallFileIoPermission();
}
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]
interface IAssertStubsItf
{
[RegistryPermission(SecurityAction.PermitOnly, Unrestricted = true)]
bool CallRegistryPermission();
[FileIOPermission(SecurityAction.PermitOnly, Unrestricted = true)]
bool CallFileIoPermission();
}
此外,在COM互操作介面宣告案例中,不接受Demand
修飾詞,如下列範例所示。
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]
interface IDemandStubsItf
{
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]
bool CallRegistryPermission();
[FileIOPermission(SecurityAction.Demand, Unrestricted = true)]
bool CallFileIoPermission();
}