字串的預設封送處理
System.String 和 System.Text.StringBuilder 這兩個類別擁有類似的封送處理行為。
字串會當做 COM-Style BSTR 型別,或當做 Null 參考中的字元陣列結尾 (在 Visual Basic 中為 Nothing) 封送處理。 字串內的字元可以封送處理為 Unicode 或 ANSI,或者以平台依存方式 (Unicode 於 Microsoft Windows NT、Windows 2000 和 Windows XP;ANSI 於 Windows 98 和 Windows Millennium Edition (Windows Me)) 封送處理字元。
這個主題提供以下有關封送處理字串型別的資訊:
在介面中使用的字串
在平台叫用中使用的字串
在結構中使用的字串
固定長度字串緩衝區
在介面中使用的字串
下表顯示當封送處理為 Unmanaged 程式碼的方法引數時,字串資料型別的封送處理選項。 MarshalAsAttribute 屬性提供多個 UnmanagedType 列舉值將字串封送處理為 COM 介面。
列舉型別 |
Unmanaged 格式的說明 |
---|---|
UnmanagedType.BStr (預設值) |
具有前置長度和 Unicode 字元的 COM-Style BSTR。 |
UnmanagedType.LPStr |
ANSI 字元之 Null 終端陣列的指標 |
UnmanagedType.LPWStr |
Unicode 字元之 Null 終端陣列的指標 |
本表會套用至字串。 不過,對於 StringBuilder 來說,唯一允許的選項是 UnmanagedType.LPStr 和 UnmanagedType.LPWStr。
下列範例顯示在 IStringWorker 介面中宣告的字串。
public interface IStringWorker {
void PassString1(String s);
void PassString2([MarshalAs(UnmanagedType.BStr)]String s);
void PassString3([MarshalAs(UnmanagedType.LPStr)]String s);
void PassString4([MarshalAs(UnmanagedType.LPWStr)]String s);
void PassStringRef1(ref String s);
void PassStringRef2([MarshalAs(UnmanagedType.BStr)]ref String s);
void PassStringRef3([MarshalAs(UnmanagedType.LPStr)]ref String s);
void PassStringRef4([MarshalAs(UnmanagedType.LPWStr)]ref String s);
);
下列範例顯示在型別程式庫中描述的對應介面。
[…]
interface IStringWorker : IDispatch {
HRESULT PassString1([in] BSTR s);
HRESULT PassString2([in] BSTR s);
HRESULT PassString3([in] LPStr s);
HRESULT PassString4([in] LPWStr s);
HRESULT PassStringRef1([in, out] BSTR *s);
HRESULT PassStringRef2([in, out] BSTR *s);
HRESULT PassStringRef3([in, out] LPStr *s);
HRESULT PassStringRef4([in, out] LPWStr *s);
);
在平台叫用中使用的字串
平台叫用會以從 .NET Framework 格式 (Unicode) 轉換為平台 Unmanaged 格式的方式複製字串引數。 當呼叫傳回時,字串是不變的,而且不會從 Unmanaged 記憶體複製回 Managed 記憶體。
下表列出當封送處理為平台叫用呼叫的方法引數時,字串的封送處理選項。 MarshalAsAttribute 屬性提供多個 UnmanagedType 列舉值封送處理字串。
列舉型別 |
Unmanaged 格式的說明 |
---|---|
UnmanagedType.AnsiBStr |
具有前置長度和 ANSI 字元的 COM-Style BSTR |
UnmanagedType.BStr |
具有前置長度和 Unicode 字元的 COM-Style BSTR。 |
UnmanagedType.LPStr |
ANSI 字元之 Null 終端陣列的指標 |
UnmanagedType.LPTStr (預設值) |
平台依存字元之 Null 終端陣列的指標 |
UnmanagedType.LPWStr |
Unicode 字元之 Null 終端陣列的指標 |
UnmanagedType.TBStr |
具有前置長度和平台依存字元的 COM-Style BSTR |
VBByRefStr |
一個值,可讓 Visual Basic .NET 變更 Unmanaged 程式碼中的字串,並且讓結果反映在 Managed 程式碼中。 只有平台叫用支援這個值。 |
本表會套用至字串。 不過,對於 StringBuilder 來說,唯一允許的選項是 LPStr、LPTStr 和 LPWStr。
下列型別定義顯示平台叫用呼叫的 MarshalAsAttribute 之正確用法。
Class StringLibAPI
Public Declare Auto Sub PassLPStr Lib "StringLib.Dll" _
(<MarshalAs(UnmanagedType.LPStr)> s As String)
Public Declare Auto Sub PassLPWStr Lib "StringLib.Dll" _
(<MarshalAs(UnmanagedType.LPWStr)> s As String)
Public Declare Auto Sub PassLPTStr Lib "StringLib.Dll" _
(<MarshalAs(UnmanagedType.LPTStr)> s As String)
Public Declare Auto Sub PassBStr Lib "StringLib.Dll" _
(<MarshalAs(UnmanagedType.BStr)> s As String)
Public Declare Auto Sub PassAnsiBStr Lib "StringLib.Dll" _
(<MarshalAs(UnmanagedType.AnsiBStr)> s As String)
Public Declare Auto Sub PassTBStr Lib "StringLib.Dll" _
(<MarshalAs(UnmanagedType.TBStr)> s As String)
End Class
class StringLibAPI {
[DllImport("StringLib.Dll")]
public static extern void PassLPStr([MarshalAs(UnmanagedType.LPStr)]
String s);
[DllImport("StringLib.Dll")]
public static extern void
PassLPWStr([MarshalAs(UnmanagedType.LPWStr)]String s);
[DllImport("StringLib.Dll")]
public static extern void
PassLPTStr([MarshalAs(UnmanagedType.LPTStr)]String s);
[DllImport("StringLib.Dll")]
public static extern void PassBStr([MarshalAs(UnmanagedType.BStr)]
String s);
[DllImport("StringLib.Dll")]
public static extern void
PassAnsiBStr([MarshalAs(UnmanagedType.AnsiBStr)]String s);
[DllImport("StringLib.Dll")]
public static extern void PassTBStr([MarshalAs(UnmanagedType.TBStr)]
String s);
}
在結構中使用的字串
字串是結構的有效成員,但是 StringBuilder 緩衝區在結構中是無效的。 下表顯示當型別是封送處理為欄位時,字串資料型別的封送處理選項。 MarshalAsAttribute 屬性提供多個 UnmanagedType 列舉值將字串封送處理為欄位。
列舉型別 |
Unmanaged 格式的說明 |
---|---|
UnmanagedType.BStr |
具有前置長度和 Unicode 字元的 COM-Style BSTR。 |
UnmanagedType.LPStr |
ANSI 字元之 Null 終端陣列的指標 |
UnmanagedType.LPTStr |
平台依存字元之 Null 終端陣列的指標 |
UnmanagedType.LPWStr |
Unicode 字元之 Null 終端陣列的指標 |
UnmanagedType.ByValTStr |
字元的固定長度陣列;陣列的型別是由內含結構的字元集所決定的 |
ByValTStr 型別是用於出現在結構內的內嵌且固定長度的字元陣列。 其他型別會套用到字串參考,其包含在含有字串指標的結構之內。
套用到包含結構的 StructLayoutAttribute 屬性之 CharSet 引數會決定結構中字串的字元格式。 下列範例結構包含字串參考與內嵌字串,以及 ANSI、Unicode 和平台依存字元。
形別程式庫表示
struct StringInfoA {
char * f1;
char f2[256];
};
struct StringInfoW {
WCHAR * f1;
WCHAR f2[256];
BSTR f3;
};
struct StringInfoT {
TCHAR * f1;
TCHAR f2[256];
};
下列程式碼範例會示範如何使用 MarshalAsAttribute 屬性,以不同格式定義相同結構。
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Ansi)> _
Structure StringInfoA
<MarshalAs(UnmanagedType.LPStr)> Public f1 As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
Public f2 As String
End Structure
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)> _
Structure StringInfoW
<MarshalAs(UnmanagedType.LPWStr)> Public f1 As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
Public f2 As String
<MarshalAs(UnmanagedType.BStr)> Public f3 As String
End Structure
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Auto)> _
Structure StringInfoT
<MarshalAs(UnmanagedType.LPTStr)> Public f1 As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
Public f2 As String
End Structure
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct StringInfoA {
[MarshalAs(UnmanagedType.LPStr)] public String f1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public String f2;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct StringInfoW {
[MarshalAs(UnmanagedType.LPWStr)] public String f1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public String f2;
[MarshalAs(UnmanagedType.BStr)] public String f3;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct StringInfoT {
[MarshalAs(UnmanagedType.LPTStr)] public String f1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public String f2;
}
固定長度字串緩衝區
在某些情況下,必須將固定長度字串緩衝區傳遞到要被操作的 Unmanaged 程式碼中。 在這種情況下只傳遞字串並不可行,因為被呼叫端無法修改傳遞的緩衝區內容。 即使字串是以傳址方式傳遞,還是無法將緩衝區初始化為指定的大小。
解決方式是將 StringBuilder 緩衝區做為引數而非字串傳遞。 只要不超過 StringBuilder 的容量,被呼叫端可以將 StringBuilder 解除參考並加以修改。 它也可以初始化為固定長度。 例如,如果您將 StringBuilder 緩衝區初始化為 N 的容量,封送處理器會提供大小為 (N+1) 字元的緩衝區。 +1 說明 Unmanaged 字串有 null 結束字元而 StringBuilder 卻沒有的事實。
例如,Microsoft Win32 API GetWindowText 函式 (定義於 Windows.h) 是固定長度的字元緩衝區,必須傳遞至要管理的 Unmanaged 程式碼。 LpString 指向大小為 nMaxCount 的呼叫端配置的緩衝區。 呼叫端必須配置緩衝區,並且將 nMaxCount 引數設為配置緩衝區的大小。 以下程式碼顯示 Windows.h 中定義的 GetWindowText 函式宣告。
int GetWindowText(
HWND hWnd, // Handle to window or control.
LPTStr lpString, // Text buffer.
int nMaxCount // Maximum number of characters to copy.
);
只要不超過 StringBuilder 的容量,被呼叫端可以將 StringBuilder 解除參考並加以修改。 下列程式碼範例示範如何將 StringBuilder 初始化為固定長度。
Public Class Win32API
Public Declare Auto Sub GetWindowText Lib "User32.Dll" _
(h As Integer, s As StringBuilder, nMaxCount As Integer)
End Class
Public Class Window
Friend h As Integer ' Friend handle to Window.
Public Function GetText() As String
Dim sb As New StringBuilder(256)
Win32API.GetWindowText(h, sb, sb.Capacity + 1)
Return sb.ToString()
End Function
End Class
public class Win32API {
[DllImport("User32.Dll")]
public static extern void GetWindowText(int h, StringBuilder s,
int nMaxCount);
}
public class Window {
internal int h; // Internal handle to Window.
public String GetText() {
StringBuilder sb = new StringBuilder(256);
Win32API.GetWindowText(h, sb, sb.Capacity + 1);
return sb.ToString();
}
}