Правильное использование IntPtr в C # - PullRequest
9 голосов
/ 23 мая 2011

Я думаю Я понимаю использование IntPtr, хотя я действительно не уверен.

Я скопировал шаблон IDisposable из MSDN, просто чтобы посмотреть, что я могу из него получить, и, хотя я понимаю его по большей части, я не знаю, как правильно реализовать 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()
        { 

        }
    }
}

Кроме того, кто-то может сказать мне, в чем смысл компонента здесь, точно?У меня также возникают проблемы с обдумыванием этой концепции.

Ответы [ 4 ]

12 голосов
/ 23 мая 2011

Вы можете использовать объекты 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.

Так что слова о вашем примере кода - не очень хорошая идея избавиться от внешнего выделенного неуправляемого объекта. Вы должны располагать только объект, который вы разместили в конструкторе класса. Это не строгое правило, а какая-то хорошая практика.

4 голосов
/ 23 мая 2011

IntPtr (эта ссылка на самом деле говорит о многом из того, что я делаю) - это специальная целочисленная форма, которая является размером указателя для текущей битности process - размер составляет 4 байта в 32-битной x86, 8 байтов в 64-битной x86, поскольку это соответствует размеру указателя.

Хотя это может относиться к месту в памяти, это не нужно. Как и в опубликованном коде, он может просто ссылаться на дескриптор или другой непрозрачный номер. Это важно из-за изменений размера в системных вызовах P / Invoked (системные вызовы используют целое число постоянного размера, в то время как некоторые зависят от архитектуры, а указатели всегда зависят от архитектуры). IntPtr не обязательно должен быть удален сам по себе, но непрозрачное число, содержащееся в нем, может относиться к ресурсу, который действительно должен быть освобожден; это все часть контракта API с тем, где было получено значение.

См. new IntPtr(long) и IntPtr.ToInt32/ToInt64 для преобразования в / из стандартного числового типа (в 64-битной среде возможно, что ToInt32 вызовет исключение переполнения).

И нет, хотя для получения значения для IntPtr (например, для вызова функции P / Invoked) могут потребоваться соответствующие разрешения безопасности, код unsafe не требуется (см. Ссылку или что unsafe разрешает) - но, возможно, все, что говорит с нативным кодом, «небезопасно», так как может вызвать приятный сбой процесса; -)

Счастливого кодирования.

3 голосов
/ 23 мая 2011

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 голосов
/ 23 мая 2011

IntPtr - это целое число размером с указатель, 32 бита в 32-битных системах и 64 бита в 64-битных системах. Как правило, он используется для переноса указателя или дескриптора для передачи неуправляемой функции, как вы это сделали. «небезопасный» означает, что вы используете указатели в своем коде C #, поэтому IntPtrs вне небезопасных блоков или без возможности компилирования небезопасного кода.

Я также не могу сказать, в чем смысл этого компонента, но он действительно, действительно не должен существовать. Дескрипторы должны принадлежать объекту, который предоставляет функциональность, которую представляет дескриптор, и должен отвечать за управление жизненным циклом этого дескриптора. Класс, который просто произвольно закрывает дескрипторы, которые он не выделил, пугающе плохой дизайн.

...