C#中IntPtr的正确使用方法

18

认为我理解了IntPtr的用法,但我并不确定。

我从MSDN上复制了IDisposable模式,只是想看看能得到什么东西,虽然大部分我都理解了,但我不知道如何正确地实现IntPtr,或者甚至理解它应该指向什么,或者引用什么。此外,我不知道如何将整数、字符串、字符、双精度等数据类型赋值或转换为IntPtr以创建指针。

另外,IntPtr是否需要使用不安全代码?

无论如何,这里有一些代码来说明我的问题:

namespace Utilities
{   
    class Disposer : IDisposable
    {

        private IntPtr handle;

        private Component component = new Component(); 

        private bool disposed = false;

        public Disposer(IntPtr handle)
        {
            this.handle = handle;

        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if(!this.disposed)
            {
                if (disposing)
                {
                    component.Dispose(); 
                }
                CloseHandle(handle);

                handle = IntPtr.Zero;

                disposed = true;

            }
        }

        [System.Runtime.InteropServices.DllImport("Kernal32")]
        private extern static Boolean CloseHandle(IntPtr handle);
    }



    public unsafe class ExecuteMain
    {
        Object nuller = new Object();

        byte boa = 0;

        byte *blargh = boa;

        public static void Main()
        { 

        }
    }
}

还有,能否有人告诉我这里组件的意义是什么?我也很难理解这个概念。


1
如果您只是想跟踪 Windows 内核对象的句柄,请使用“SafeHandle”:http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle.aspx - Gabe
4个回答

19

您可以这样使用IntPtr对象:

int test = 55;

// Allocating memory for int
IntPtr intPointer = Marshal.AllocHGlobal(sizeof(int));

Marshal.WriteInt32(intPointer,test);

// sending intPointer to unmanaged code here

//Test reading of IntPtr object
int test2 = Marshal.ReadInt32(intPointer); // test2 would be equal 55

// Free memory
Marshal.FreeHGlobal(intPointer);

您可以探索其他Marshal方法,以了解如何将字符串、双精度数等写入IntPtr。

关于您的示例代码,不建议释放外部分配的非托管对象。应该只释放在类构造函数中分配的对象。这不是严格的规则,但是一种良好实践。


2
十年过去了,现在是否有更简单的方法来做这件事呢? - Chef Gladiator

8

IntPtr(此链接实际上涵盖了我所说的大部分内容)是一种特殊形式的整数,它的大小与当前进程的位数相同 - 在32位x86中为4个字节,在64位x86中为8个字节,因为这与指针的大小相对应。

虽然它可以指向内存中的位置,但不一定需要。就像发布的代码中一样,它只能引用句柄或其他不透明数字。它之所以重要,是因为P/Invoked系统调用中的大小更改(系统调用使用恒定大小的整数,而某些调用取决于架构,指针始终依赖于架构)。IntPtr本身不需要被处理,但其中包含的不透明数字可能会引用需要释放的资源;这都是与获取值的API合同的一部分。

请参阅new IntPtr(long)IntPtr.ToInt32/ToInt64以进行标准数字类型的转换(在64位环境中,ToInt32可能会引发溢出异常)。

不,虽然获取IntPtr的值(例如调用P/Invoked函数)可能需要适当的安全权限,但不需要unsafe代码(请参阅链接或unsafe允许的内容)- 但可以说与本地代码交互的任何内容都是“不安全”的,因为它可能会导致进程崩溃;-)

愉快的编码。


5
一个 IntPtr 只是值类型,其大小与目标平台上指针的大小相匹配。您需要在处理未托管指针时主要使用它。一个 IntPtr 本身不能被释放,因为它只表示内存中的位置。您的清理需要特定于由 IntPtr 引用的对象。假设您有一个需要窗口句柄来执行工作的未托管函数。在这种情况下,您可以使用属性 Control.Handle 来获取控件窗口句柄的指针。为了正确清除控件及其底层窗口,您不必关心引用未托管句柄的 IntPtr,而是应该释放控件。
[DllImport("UnmanagedLibrary.dll")]
private static void DoSomethingWithWindowHandle(IntPtr windowHandle);

private void Foo()
{
    Form form = new Form();
    // ...
    DoSomethingWithWindowHandle(form.Handle);
    // ...
    form.Dispose();
}

2

IntPtr是一个整数,其大小与指针相同,在32位系统上为32位,在64位系统上为64位。它通常用于封装指针或句柄以交给未托管函数使用,就像您所做的那样。"unsafe"意味着您在C#代码中使用指针,因此必须在不安全块外使用IntPtr或允许编译不安全代码。

我也无法告诉您该组件的目的,但它绝对不应该存在。句柄应该由公开句柄所代表功能并负责管理该句柄生命周期的对象拥有。一个仅随意关闭其未分配的句柄的类是非常糟糕的设计。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接