字串的預設封送處理
更新:2007 年 11 月
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();
}
}