句柄(Handle)緣來
.NET提供了一個結構體System.IntPtr
專門用來代表句柄或指針。
句柄是對象的標識符,當調用這些API創建對象時,它們並不直接返回指向對象的指針,而是會返回一個32位或64位的整數值,這個在進程或系統範圍內唯一的整數值就是句柄(Handle
),隨後程序再次訪問對象,或者刪除對象,都將句柄作爲Windows API的參數來間接對這些對象進行操作。
句柄指向就是指向文件開頭,在windows系統所有的東西都是文件,對象也是文件。所以句柄和指針是一樣的意思。句柄是面向對象的指針的稱呼。
指針是對存儲區域的引用,該區域包含您感興趣的一些數據。指針是面向過程編程的稱呼。
IntPtr
類是IntPointer
的縮寫。
C#中用來取代指針,也可以說對指針進行封裝,指向託管內存。
它也不常用,因爲C#項目中指針都被棄用了,那指針的封裝—句柄自然也被棄用了。
但總有特殊的地方會用到指針,比如調用C++動態庫之類的;所以微軟貼心的爲我們做了個句柄,畢竟指針用起來太難受了。
句柄的定義
句柄是一個結構體,簡單的來說,它是指針的一個封裝,是C#中指針的替代者,下面我們看下句柄的定義。
namespace System
{
//
// 摘要:
// A platform-specific type that is used to represent a pointer or a handle.
public readonly struct IntPtr : IComparable, IComparable<nint>, IEquatable<nint>, ISpanFormattable, IFormattable, ISerializable
{
//
// 摘要:
// A read-only field that represents a pointer or handle that has been initialized
// to zero.
public static readonly IntPtr Zero;
//
// 摘要:
// Initializes a new instance of System.IntPtr using the specified 32-bit pointer
// or handle.
//
// 參數:
// value:
// A pointer or handle contained in a 32-bit signed integer.
public IntPtr(int value);
//
// 摘要:
// Initializes a new instance of System.IntPtr using the specified 64-bit pointer.
//
// 參數:
// value:
// A pointer or handle contained in a 64-bit signed integer.
//
// 異常:
// T:System.OverflowException:
// On a 32-bit platform, value is too large or too small to represent as an System.IntPtr.
public IntPtr(long value);
//
// 摘要:
// Initializes a new instance of System.IntPtr using the specified pointer to an
// unspecified type.
//
// 參數:
// value:
// A pointer to an unspecified type.
[CLSCompliant(false)]
public IntPtr(void* value);
//
// 摘要:
// Represents the largest possible value of System.IntPtr.
public static IntPtr MaxValue { get; }
//
// 摘要:
// Represents the smallest possible value of System.IntPtr.
public static IntPtr MinValue { get; }
//
// 摘要:
// Gets the size of this instance.
//
// 返回結果:
// The size of a pointer or handle in this process, measured in bytes. The value
// of this property is 4 in a 32-bit process, and 8 in a 64-bit process. You can
// define the process type by setting the /platform switch when you compile your
// code with the C# and Visual Basic compilers.
public static int Size { get; }
//
// 摘要:
// Adds an offset to the value of a pointer.
//
// 參數:
// pointer:
// The pointer to add the offset to.
//
// offset:
// The offset to add.
//
// 返回結果:
// A new pointer that reflects the addition of offset to pointer.
public static IntPtr Add(IntPtr pointer, int offset);
//
// 摘要:
// Converts the string representation of a number in a specified style and culture-specific
// format to its signed native integer equivalent.
//
// 參數:
// s:
// A string containing a number to convert.
//
// style:
// A bitwise combination of the enumeration values that indicates the style elements
// that can be present in s.
//
// provider:
// An object that supplies culture-specific formatting information about s.
//
// 返回結果:
// A signed native integer equivalent to the number contained in s.
//
// 異常:
// T:System.ArgumentNullException:
// s is null.
//
// T:System.ArgumentException:
// style is not a System.Globalization.NumberStyles value or style is not a combination
// of System.Globalization.NumberStyles.AllowHexSpecifier and System.Globalization.NumberStyles.HexNumber
// values.
//
// T:System.FormatException:
// s is not in the correct format.
//
// T:System.OverflowException:
// s represents a number less than System.IntPtr.MinValue or greater than System.IntPtr.MaxValue.
public static IntPtr Parse(string s, NumberStyles style, IFormatProvider? provider);
//
// 摘要:
// Converts the read-only span of characters representation of a number in a specified
// style and culture-specific format to its signed native integer equivalent.
//
// 參數:
// s:
// A read-only span of characters containing a number to convert.
//
// style:
// A bitwise combination of the enumeration values that indicates the style elements
// that can be present in s.
//
// provider:
// An object that supplies culture-specific formatting information about s.
//
// 返回結果:
// A signed native integer equivalent to the number contained in s.
//
// 異常:
// T:System.ArgumentException:
// style is not a System.Globalization.NumberStyles value or style is not a combination
// of System.Globalization.NumberStyles.AllowHexSpecifier and System.Globalization.NumberStyles.HexNumber
// values.
//
// T:System.FormatException:
// s is not in the correct format.
//
// T:System.OverflowException:
// s represents a number less than System.IntPtr.MinValue or greater than System.IntPtr.MaxValue.
public static IntPtr Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null);
//
// 摘要:
// Converts the string representation of a number in a specified culture-specific
// format to its signed native integer equivalent.
//
// 參數:
// s:
// A string containing a number to convert.
//
// provider:
// An object that supplies culture-specific formatting information about s.
//
// 返回結果:
// A signed native integer equivalent to the number contained in s.
//
// 異常:
// T:System.ArgumentNullException:
// s is null.
//
// T:System.FormatException:
// s is not in the correct format.
//
// T:System.OverflowException:
// s represents a number less than System.IntPtr.MinValue or greater than System.IntPtr.MaxValue.
public static IntPtr Parse(string s, IFormatProvider? provider);
//
// 摘要:
// Converts the string representation of a number in a specified style to its signed
// native integer equivalent.
//
// 參數:
// s:
// A string containing a number to convert.
//
// style:
// A bitwise combination of the enumeration values that indicates the style elements
// that can be present in s.
//
// 返回結果:
// A signed native integer equivalent to the number contained in s.
//
// 異常:
// T:System.ArgumentNullException:
// s is null.
//
// T:System.ArgumentException:
// style is not a System.Globalization.NumberStyles value or style is not a combination
// of System.Globalization.NumberStyles.AllowHexSpecifier and System.Globalization.NumberStyles.HexNumber
// values.
//
// T:System.FormatException:
// s is not in the correct format.
//
// T:System.OverflowException:
// s represents a number less than System.IntPtr.MinValue or greater than System.IntPtr.MaxValue.
public static IntPtr Parse(string s, NumberStyles style);
//
// 摘要:
// Converts the string representation of a number to its signed native integer equivalent.
//
// 參數:
// s:
// A string containing a number to convert.
//
// 返回結果:
// A signed native integer equivalent to the number contained in s.
//
// 異常:
// T:System.ArgumentNullException:
// s is null.
//
// T:System.FormatException:
// s is not in the correct format.
//
// T:System.OverflowException:
// s represents a number less than System.IntPtr.MinValue or greater than System.IntPtr.MaxValue.
public static IntPtr Parse(string s);
//
// 摘要:
// Subtracts an offset from the value of a pointer.
//
// 參數:
// pointer:
// The pointer to subtract the offset from.
//
// offset:
// The offset to subtract.
//
// 返回結果:
// A new pointer that reflects the subtraction of offset from pointer.
public static IntPtr Subtract(IntPtr pointer, int offset);
//
// 摘要:
// Converts the read-only span of characters representation of a number in a specified
// style and culture-specific format to its signed native integer equivalent. A
// return value indicates whether the conversion succeeded.
//
// 參數:
// s:
// A read-only span of characters containing a number to convert. The string is
// interpreted using the style specified by style.
//
// style:
// A bitwise combination of enumeration values that indicates the style elements
// that can be present in s.
//
// provider:
// An object that supplies culture-specific formatting information about s.
//
// result:
// When this method returns, contains the signed native integer value equivalent
// of the number contained in s, if the conversion succeeded, or zero if the conversion
// failed. The conversion fails if the s parameter is empty, is not of the correct
// format, or represents a number less than System.IntPtr.MinValue or greater than
// System.IntPtr.MaxValue. This parameter is passed uninitialized; any value originally
// supplied in result will be overwritten.
//
// 返回結果:
// true if s was converted successfully; otherwise, false.
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out IntPtr result);
//
// 摘要:
// Converts the read-only span of characters representation of a number to its signed
// native integer equivalent. A return value indicates whether the conversion succeeded.
//
// 參數:
// s:
// A read-only span of characters containing a number to convert.
//
// result:
// When this method returns, contains the signed native integer equivalent of the
// number contained in s, if the conversion succeeded, or zero if the conversion
// failed. The conversion fails if the s parameter is empty, is not of the correct
// format, or represents a number less than System.IntPtr.MinValue or greater than
// System.IntPtr.MaxValue. This parameter is passed uninitialized; any value originally
// supplied in result will be overwritten.
//
// 返回結果:
// true if s was converted successfully; otherwise, false.
public static bool TryParse(ReadOnlySpan<char> s, out IntPtr result);
//
// 摘要:
// Converts the string representation of a number to its signed native integer equivalent.
// A return value indicates whether the conversion succeeded.
//
// 參數:
// s:
// A string containing a number to convert.
//
// result:
// When this method returns, contains the signed native integer value equivalent
// of the number contained in s, if the conversion succeeded, or zero if the conversion
// failed. The conversion fails if the s parameter is null or empty, is not of the
// correct format, or represents a number less than System.IntPtr.MinValue or greater
// than System.IntPtr.MaxValue. This parameter is passed uninitialized; any value
// originally supplied in result will be overwritten.
//
// 返回結果:
// true if s was converted successfully; otherwise, false.
public static bool TryParse([NotNullWhen(true)] string? s, out IntPtr result);
//
// 摘要:
// Converts the string representation of a number in a specified style and culture-specific
// format to its signed native integer equivalent. A return value indicates whether
// the conversion succeeded.
//
// 參數:
// s:
// A string containing a number to convert. The string is interpreted using the
// style specified by style.
//
// style:
// A bitwise combination of enumeration values that indicates the style elements
// that can be present in s.
//
// provider:
// An object that supplies culture-specific formatting information about s.
//
// result:
// When this method returns, contains the signed native integer value equivalent
// of the number contained in s, if the conversion succeeded, or zero if the conversion
// failed. The conversion fails if the s parameter is null or empty, is not of the
// correct format, or represents a number less than System.IntPtr.MinValue or greater
// than System.IntPtr.MaxValue. This parameter is passed uninitialized; any value
// originally supplied in result will be overwritten.
//
// 返回結果:
// true if s was converted successfully; otherwise, false.
public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out IntPtr result);
//
// 摘要:
// Compares the current instance with another object of the same type and returns
// an integer that indicates whether the current instance precedes, follows, or
// occurs in the same position in the sort order as the other object.
//
// 參數:
// value:
// An object to compare, or null.
//
// 返回結果:
// A value that indicates the relative order of the objects being compared. The
// return value has these meanings:
// Value – Meaning
// Less than zero – This instance precedes obj in the sort order.
// Zero – This instance occurs in the same position in the sort order as obj.
// Greater than zero – This instance follows obj in the sort order.
public int CompareTo(object? value);
//
// 摘要:
// Compares the current instance with another object of the same type and returns
// an integer that indicates whether the current instance precedes, follows, or
// occurs in the same position in the sort order as the other object.
//
// 參數:
// value:
// A signed native integer to compare.
//
// 返回結果:
// A value that indicates the relative order of the objects being compared. The
// return value has these meanings:
// Value – Meaning
// Less than zero – This instance precedes other in the sort order.
// Zero – This instance occurs in the same position in the sort order as other.
// Greater than zero – This instance follows other in the sort order.
public int CompareTo(IntPtr value);
//
// 摘要:
// Indicates whether the current object is equal to another object of the same type.
//
// 參數:
// other:
// An object to compare with this object.
//
// 返回結果:
// true if the current object is equal to other; otherwise, false.
public bool Equals(IntPtr other);
//
// 摘要:
// Returns a value indicating whether this instance is equal to a specified object.
//
// 參數:
// obj:
// An object to compare with this instance or null.
//
// 返回結果:
// true if obj is an instance of System.IntPtr and equals the value of this instance;
// otherwise, false.
public override bool Equals([NotNullWhen(true)] object? obj);
//
// 摘要:
// Returns the hash code for this instance.
//
// 返回結果:
// A 32-bit signed integer hash code.
public override int GetHashCode();
//
// 摘要:
// Converts the value of this instance to a 32-bit signed integer.
//
// 返回結果:
// A 32-bit signed integer equal to the value of this instance.
//
// 異常:
// T:System.OverflowException:
// On a 64-bit platform, the value of this instance is too large or too small to
// represent as a 32-bit signed integer.
public int ToInt32();
//
// 摘要:
// Converts the value of this instance to a 64-bit signed integer.
//
// 返回結果:
// A 64-bit signed integer equal to the value of this instance.
public long ToInt64();
//
// 摘要:
// Converts the value of this instance to a pointer to an unspecified type.
//
// 返回結果:
// A pointer to System.Void; that is, a pointer to memory containing data of an
// unspecified type.
[CLSCompliant(false)]
public void* ToPointer();
//
// 摘要:
// Converts the numeric value of the current System.IntPtr object to its equivalent
// string representation.
//
// 返回結果:
// The string representation of the value of this instance.
public override string ToString();
//
// 摘要:
// Formats the value of the current instance using the specified format.
//
// 參數:
// format:
// The format to use. -or- A null reference (Nothing in Visual Basic) to use the
// default format defined for the type of the System.IFormattable implementation.
//
// provider:
// An object that supplies culture-specific formatting information.
//
// 返回結果:
// The value of the current instance in the specified format.
public string ToString(string? format, IFormatProvider? provider);
//
// 摘要:
// Converts the numeric value of the current System.IntPtr object to its equivalent
// string representation.
//
// 參數:
// format:
// A format specification that governs how the current System.IntPtr object is converted.
//
// 返回結果:
// The string representation of the value of the current System.IntPtr object.
//
// 異常:
// T:System.FormatException:
// format is invalid or not supported.
public string ToString(string? format);
//
// 摘要:
// Converts the numeric value of this instance to its equivalent string representation
// using the specified format and culture-specific format information.
//
// 參數:
// provider:
// An object that supplies culture-specific formatting information.
//
// 返回結果:
// The string representation of the value of this instance as specified by provider.
public string ToString(IFormatProvider? provider);
//
// 摘要:
// Tries to format the value of the current instance into the provided span of characters.
//
// 參數:
// destination:
// The span where this instance's value formatted as a span of characters should
// be written.
//
// charsWritten:
// When this method returns, contains the number of characters that were written
// in destination.
//
// format:
// The characters that represent a standard or custom format string that defines
// the acceptable format for destination.
//
// provider:
// An optional object that supplies culture-specific formatting information for
// destination.
//
// 返回結果:
// true if the formatting was successful; otherwise, false.
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
//
// 摘要:
// Adds an offset to the value of a pointer.
//
// 參數:
// pointer:
// The pointer to add the offset to.
//
// offset:
// The offset to add.
//
// 返回結果:
// A new pointer that reflects the addition of offset to pointer.
public static IntPtr operator +(IntPtr pointer, int offset);
//
// 摘要:
// Subtracts an offset from the value of a pointer.
//
// 參數:
// pointer:
// The pointer to subtract the offset from.
//
// offset:
// The offset to subtract.
//
// 返回結果:
// A new pointer that reflects the subtraction of offset from pointer.
public static IntPtr operator -(IntPtr pointer, int offset);
//
// 摘要:
// Determines whether two specified instances of System.IntPtr are equal.
//
// 參數:
// value1:
// The first pointer or handle to compare.
//
// value2:
// The second pointer or handle to compare.
//
// 返回結果:
// true if value1 equals value2; otherwise, false.
public static bool operator ==(IntPtr value1, IntPtr value2);
//
// 摘要:
// Determines whether two specified instances of System.IntPtr are not equal.
//
// 參數:
// value1:
// The first pointer or handle to compare.
//
// value2:
// The second pointer or handle to compare.
//
// 返回結果:
// true if value1 does not equal value2; otherwise, false.
public static bool operator !=(IntPtr value1, IntPtr value2);
//
// 摘要:
// Converts the value of a 64-bit signed integer to an System.IntPtr.
//
// 參數:
// value:
// A 64-bit signed integer.
//
// 返回結果:
// A new instance of System.IntPtr initialized to value.
//
// 異常:
// T:System.OverflowException:
// On a 32-bit platform, value is too large to represent as an System.IntPtr.
public static explicit operator IntPtr(long value);
//
// 摘要:
// Converts the value of the specified System.IntPtr to a 32-bit signed integer.
//
// 參數:
// value:
// The pointer or handle to convert.
//
// 返回結果:
// The contents of value.
//
// 異常:
// T:System.OverflowException:
// On a 64-bit platform, the value of value is too large to represent as a 32-bit
// signed integer.
public static explicit operator int(IntPtr value);
//
// 摘要:
// Converts the value of the specified System.IntPtr to a 64-bit signed integer.
//
// 參數:
// value:
// The pointer or handle to convert.
//
// 返回結果:
// The contents of value.
public static explicit operator long(IntPtr value);
//
// 摘要:
// Converts the value of a 32-bit signed integer to an System.IntPtr.
//
// 參數:
// value:
// A 32-bit signed integer.
//
// 返回結果:
// A new instance of System.IntPtr initialized to value.
public static explicit operator IntPtr(int value);
//
// 摘要:
// Converts the value of the specified System.IntPtr to a pointer to an unspecified
// type. This API is not CLS-compliant.
//
// 參數:
// value:
// The pointer or handle to convert.
//
// 返回結果:
// The contents of value.
[CLSCompliant(false)]
public static explicit operator void*(IntPtr value);
//
// 摘要:
// Converts the specified pointer to an unspecified type to an System.IntPtr. This
// API is not CLS-compliant.
//
// 參數:
// value:
// A pointer to an unspecified type.
//
// 返回結果:
// A new instance of System.IntPtr initialized to value.
[CLSCompliant(false)]
public static explicit operator IntPtr(void* value);
}
}
我們可以看到,句柄IntPtr
裏包含創建指針,獲取指針長度,設置偏移量等等方法,並且爲了編碼方便還聲明瞭些強制轉換的方法。
看了句柄的結構體定義,相信稍微有點基礎的人已經明白了,在C#中,微軟是希望拋棄指針而改用更優秀的IntPtr
代替它的。
但我們還會發現,句柄裏還提供一個方法是ToPointer()
,它的返回類型是Void*
,也就是說,我們還是可以從句柄裏拿到C++中的指針,既然,微軟期望在C#中不要使用指針,那爲什麼還要提供這樣的方法呢?
這是因爲,在項目開發中總是會有極特殊的情況,比如,你有一段C++寫的非常複雜、完美的函數,而將這個函數轉換成C#又及其耗時,那麼最簡單省力的方法就是直接在C#裏啓用指針進行移植。
也就是說,C#支持指針,其實是爲了體現它的兼容性,並不是提倡大家去使用指針。
使用要點
- C#中的
IntPtr
類型被稱之爲"平臺特定的整數類型",用於本機資源,例如窗口句柄。 - 資源的大小取決於使用的硬件和操作系統,即此類型的實例在32位硬件和操作系統中將是32位,在64位硬件和操作系統中將是64位;但其大小總是足以包含系統的指針(因此也可以包含資源的名稱)。
- 在調用API函數時,類似含有窗口句柄參數(HANDLE)的原型函數,應顯示地聲明爲
IntPtr
類型。 IntPtr
類型對多線程操作是安全的。Int
和IntPtr
互轉。
示例模型
struct ChatDemo
{
public double X;
public double Y;
public ChatDemo(double x, double y)
{
X = x;
Y = y;
}
}
Struct與IntPtr互相轉換
public void ConvertStruct()
{
var chatDemo = new ChatDemo(1, 10);
// 通過使用指定的字節數,從進程的非託管內存中分配內存
IntPtr intPtr = Marshal.AllocHGlobal
(
// 內存中的所需字節數
cb: Marshal.SizeOf(typeof(ChatDemo))
);
// 將數據從託管對象封送到非託管內存塊
Marshal.StructureToPtr
(
// 包含要封送的數據的託管對象。該對象必須是格式化類的結構或實例
structure: chatDemo,
// 指向非託管內存塊的指針,必須在調用此方法之前分配該指針
ptr: intPtr,
// 如果在此方法複製該數據前在DestroyStructure(IntPtr,Type)參數上調用ptr,則爲true。該塊必須包含有效的數據。請注意,在內存塊已包含數據時傳遞false可能會導致內存泄漏。
fDeleteOld: true
);
// 將數據從非託管內存塊封送到託管對象
var chatDemoRes = (ChatDemo)Marshal.PtrToStructure
(
// 指向非託管內存塊的指針
ptr: intPtr,
// 要創建的對象的類型。此對象必須表示格式化類或結構
structureType: typeof(ChatDemo)
);
}
Struct對象數組與IntPtr互相轉換
public void ConvertStructs()
{
var chatDemos = new List<ChatDemo>() { new ChatDemo(1,3), new ChatDemo(2,10), new ChatDemo(4,200)};
// 將託管對象數組轉成指針
var intPtr = MarshalMangagedStruct2Array(chatDemos.ToArray());
// 將指針轉成託管對象數組
MarshalUnmananagedArray2Struct(intPtr, chatDemos.Count, out ChatDemo[] chatDemoRes);
}
/// <summary>
/// 將指針轉成託管對象數組
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="unmanagedArray">非託管指針</param>
/// <param name="length">數據長度</param>
/// <param name="mangagedArray">託管對象數組</param>
public void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray)
{
if (length <= 0) throw new ArgumentException(nameof(length));
// 獲取對象的空間大小
var size = Marshal.SizeOf(typeof(T));
mangagedArray = new T[length];
for (int i = 0; i < length; i++)
{
// 增加指針內存位置以便定位到下一個結構體
IntPtr nextStructureMemBlock = new IntPtr(unmanagedArray.ToInt64() + i * size);
mangagedArray[i] = Marshal.PtrToStructure<T>(nextStructureMemBlock);
}
}
/// <summary>
/// 將託管對象數組轉成指針
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="mangagedArray"></param>
/// <returns></returns>
public IntPtr MarshalMangagedStruct2Array<T>(T[] mangagedArray) where T : struct
{
if (mangagedArray == null || mangagedArray.Length <= 0) throw new ArgumentException(nameof(mangagedArray));
// 獲取對象的空間大小
int size = Marshal.SizeOf(typeof(T));
// 得到非託管內存的總長度
int allSize = size * mangagedArray.Length;
// 申請非託管內存空間
IntPtr ptr = Marshal.AllocHGlobal(allSize);
// 寫入長度屬性值
Marshal.WriteInt32(ptr, mangagedArray.Length);
// 寫入數據
for (int i = 0; i < mangagedArray.Length; i++)
{
// 根據指針+偏移量來寫入指定序號的對象數據
Marshal.StructureToPtr<T>(mangagedArray[i], ptr + (size * i), false);
}
return ptr;
}
C++與C#類型對照關係
C++ 數據類型 | C# 數據類型 |
---|---|
WORD |
ushort |
DWORD |
uint |
UCHAR |
int/byte |
UCHAR* |
string/InPtr |
unsigned char* |
[MarshalAs(UnmanagedType.LPArray)]byte[] / IntPtr |
char* |
string |
LPCTSTR |
string |
LPTSTR |
[MarshalAs(UnmanagedType.LPTStr)] string |
long |
int |
ulong |
uint |
Handle |
IntPtr |
HWND |
IntPtr |
void* |
IntPtr |
int |
int |
int* |
ref int |
*int |
IntPtr |
unsigned int |
uint |
COLORREF |
uint |