【发布时间】:2013-06-24 18:20:41
【问题描述】:
我想将一个对象从托管代码传递给一个 WinApi 函数作为IntPtr。它会将该对象作为IntPtr 传递回托管代码中的回调函数。它不是一个结构,它是一个类的一个实例。
如何将object 转换为IntPtr 并返回?
【问题讨论】:
-
使用 GCHandle 固定它
标签: c# winapi callback marshalling intptr
我想将一个对象从托管代码传递给一个 WinApi 函数作为IntPtr。它会将该对象作为IntPtr 传递回托管代码中的回调函数。它不是一个结构,它是一个类的一个实例。
如何将object 转换为IntPtr 并返回?
【问题讨论】:
标签: c# winapi callback marshalling intptr
所以如果我想通过 WinApi 将列表传递给我的回调函数,我会使用 GCHandle
// object to IntPtr (before calling WinApi):
List<string> list1 = new List<string>();
GCHandle handle1 = GCHandle.Alloc(list1);
IntPtr parameter = (IntPtr) handle1;
// call WinAPi and pass the parameter here
// then free the handle when not needed:
handle1.Free();
// back to object (in callback function):
GCHandle handle2 = (GCHandle) parameter;
List<string> list2 = (handle2.Target as List<string>);
list2.Add("hello world");
编辑: 如 cmets 所述,您需要在使用后松开手柄。我也使用铸造。使用静态方法GCHandle.ToIntPtr(handle1) 和GCHandle.FromIntPtr(parameter) 可能是明智之举,例如here。我还没有验证。
【讨论】:
虽然公认的答案是正确的,但我想补充一点。
我越来越喜欢为此创建扩展,因此它显示为:list1.ToIntPtr()。
public static class ObjectHandleExtensions
{
public static IntPtr ToIntPtr(this object target)
{
return GCHandle.Alloc(target).ToIntPtr();
}
public static GCHandle ToGcHandle(this object target)
{
return GCHandle.Alloc(target);
}
public static IntPtr ToIntPtr(this GCHandle target)
{
return GCHandle.ToIntPtr(target);
}
}
另外,根据您的工作量,最好将您的列表包含在IDisposable 中。
public class GCHandleProvider : IDisposable
{
public GCHandleProvider(object target)
{
Handle = target.ToGcHandle();
}
public IntPtr Pointer => Handle.ToIntPtr();
public GCHandle Handle { get; }
private void ReleaseUnmanagedResources()
{
if (Handle.IsAllocated) Handle.Free();
}
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~GCHandleProvider()
{
ReleaseUnmanagedResources();
}
}
然后你可以这样消费它:
using (var handleProvider = new GCHandleProvider(myList))
{
var b = EnumChildWindows(hwndParent, CallBack, handleProvider.Pointer);
}
【讨论】: