Tôi muốn truyền a byte[]
cho một phương thức lấy IntPtr
Thông số trong C #, điều đó có thể không và bằng cách nào?
GCHandle
phương thức này hoạt động như một bùa mê ... cũng là fixed
phương pháp. : P :))
Tôi muốn truyền a byte[]
cho một phương thức lấy IntPtr
Thông số trong C #, điều đó có thể không và bằng cách nào?
GCHandle
phương thức này hoạt động như một bùa mê ... cũng là fixed
phương pháp. : P :))
Câu trả lời:
Không chắc chắn về việc nhận IntPtr vào một mảng, nhưng bạn có thể sao chép dữ liệu để sử dụng với mã không được quản lý bằng cách sử dụng Mashal.Copy:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);
Ngoài ra, bạn có thể khai báo một cấu trúc với một thuộc tính và sau đó sử dụng Marshal.PtrToStr struct, nhưng điều đó vẫn sẽ yêu cầu phân bổ bộ nhớ không được quản lý.
Chỉnh sửa: Ngoài ra, như Tyalis đã chỉ ra, bạn cũng có thể sử dụng cố định nếu mã không an toàn là một tùy chọn cho bạn
Marshal.Copy
với sự quá tải đó cần một chỉ số bắt đầu. Cuộc gọi nên làMarshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
Cách khác,
GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();
Điều này sẽ hoạt động nhưng phải được sử dụng trong bối cảnh không an toàn:
byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
// do you stuff here
}
hãy cẩn thận, bạn phải sử dụng con trỏ trong khối cố định! Các gc có thể di chuyển đối tượng một khi bạn không còn trong khối cố định.
Bạn có thể sử dụng Marshal.UnsafeAddrOfPinnedArrayElement(array, 0)
để có được một con trỏ bộ nhớ đến mảng.
Đây là một thay đổi trong câu trả lời của @ user65157 (+1 cho điều đó, BTW):
Tôi đã tạo một trình bao bọc IDis Dùng cho đối tượng được ghim:
class AutoPinner : IDisposable
{
GCHandle _pinnedArray;
public AutoPinner(Object obj)
{
_pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
}
public static implicit operator IntPtr(AutoPinner ap)
{
return ap._pinnedArray.AddrOfPinnedObject();
}
public void Dispose()
{
_pinnedArray.Free();
}
}
sau đó sử dụng nó như vậy:
using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
UnmanagedIntPtr = ap; // Use the operator to retrieve the IntPtr
//do your stuff
}
Tôi thấy đây là một cách hay để không quên gọi Free () :)
Marshal.Copy hoạt động nhưng khá chậm. Nhanh hơn là sao chép các byte trong một vòng lặp for. Thậm chí nhanh hơn là truyền mảng byte thành mảng ulong, sao chép càng nhiều ulong càng phù hợp với mảng byte, sau đó sao chép 7 byte còn lại có thể (đường dẫn không được căn chỉnh 8 byte). Nhanh nhất là ghim mảng byte trong một câu lệnh cố định như được đề xuất ở trên trong câu trả lời của Tyalis.
IntPtr GetIntPtr(Byte[] byteBuf)
{
IntPtr ptr = Marshal.AllocHGlobal(byteBuf.Length);
for (int i = 0; i < byteBuf.Length; i++)
{
Marshal.WriteByte(ptr, i, byteBuf[i]);
}
return ptr;
}
Trong một số trường hợp, bạn có thể sử dụng loại Int32 (hoặc Int64) trong trường hợp IntPtr. Nếu bạn có thể, một lớp hữu ích khác là BitConverter. Đối với những gì bạn muốn, bạn có thể sử dụng BitConverter.ToInt32 chẳng hạn.
Int32
như một con trỏ. Đây là một thực tế tồi tệ được thực hiện nhiều năm trước và nó dẫn đến tất cả các loại vấn đề chuyển. Thậm chí Int64
không an toàn vì đã có kiến trúc 128 bit và kích thước con trỏ sẽ tăng lên. Con trỏ chỉ nên được đại diện như con trỏ.
int
/ long
cho con trỏ là khi ngôn ngữ được sử dụng không có khái niệm về chúng (ví dụ VB6). C # hỗ trợ con trỏ và có IntPtr
- hoàn toàn không cần sử dụng int
thay cho con trỏ. Tôi sẽ xóa -1 nếu bạn thêm cảnh báo rõ ràng và giải thích về các vấn đề tiềm ẩn cho câu trả lời của bạn.