Нетривиальный вопрос про SafeHandle
От: syomin  
Дата: 14.04.10 11:15
Оценка:
А что делать в случае, когда нужна обёртка для неправляемого ресурса, который имеет тип, отличный от IntPtr? К примеру, ulong?
Re: MarshalAs
От: Qbit86 Кипр
Дата: 14.04.10 11:17
Оценка:
Здравствуйте, syomin, Вы писали:

S>А что делать в случае, когда нужна обёртка для неправляемого ресурса, который имеет тип, отличный от IntPtr? К примеру, ulong?


Указывать MarshalAs?
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: MarshalAs
От: syomin  
Дата: 14.04.10 11:21
Оценка:
Q>Указывать MarshalAs?
А можно подробнее?
Re[3]: MarshalAs
От: Jolly Roger  
Дата: 14.04.10 11:35
Оценка:
Здравствуйте, syomin, Вы писали:

Q>>Указывать MarshalAs?

S>А можно подробнее?

Для "подробней", я думаю, сначала Вам стоит более детально описать ситуацию.
"Нормальные герои всегда идут в обход!"
Re[4]: MarshalAs
От: syomin  
Дата: 14.04.10 11:41
Оценка:
JR>Для "подробней", я думаю, сначала Вам стоит более детально описать ситуацию.

Да запросто. Есть DLL'ка, в ней есть функция вида:
unsigned long create_foo();

Внутри функция создаёт некий ресурс и возвращает его дескриптор. Как только ресурс перестанет быть нужен, он должен быть освобожден с помощью функции:
void destroy_foo(unsigned long foo);


Для хранения дескриптора неуправляемого ресурса в .Net есть класс SafeHandle, который гарантирует корректное освобождение во всяких специфических ситуациях, но он работает только с IntPtr, размерность которого в 2 раза меньше. Можно ли сделать то же самое для ulong и какой ценой?
Re[5]: MarshalAs
От: Jolly Roger  
Дата: 14.04.10 11:53
Оценка: 3 (1) +2
Здравствуйте, syomin, Вы писали:

S>Для хранения дескриптора неуправляемого ресурса в .Net есть класс SafeHandle, который гарантирует корректное освобождение во всяких специфических ситуациях, но он работает только с IntPtr, размерность которого в 2 раза меньше. Можно ли сделать то же самое для ulong и какой ценой?


Да запросто Написать своего наследника от CriticalFinalizerObject и реализовать в нём вызов destroy_foo, делов-то
"Нормальные герои всегда идут в обход!"
Re[6]: MarshalAs
От: syomin  
Дата: 14.04.10 13:32
Оценка:
JR>Да запросто Написать своего наследника от CriticalFinalizerObject и реализовать в нём вызов destroy_foo, делов-то

А как организовать маршалинг и подсчёт ссылок наподобие того, как это сделано в SafeHandle?
Re[7]: MarshalAs
От: Jolly Roger  
Дата: 14.04.10 13:42
Оценка:
Здравствуйте, syomin, Вы писали:

S>А как организовать маршалинг


Маршалинг куда? long и так можно использовать без проблем и в нативном, и в управляемом коде.

S>и подсчёт ссылок наподобие того, как это сделано в SafeHandle?


А какие ссылки Вы собираетесь считать? Ваш ресурс поддерживает подсчет ссылок? Тогда ему и переадресуйте. Нет? Тогда Вам зачем? Впрочем, при желании можно и целиком в управляемом коде сделать, только есть ли смысл?
"Нормальные герои всегда идут в обход!"
Re[5]: MarshalAs
От: _FRED_ Черногория
Дата: 14.04.10 13:45
Оценка: 21 (2) +1
Здравствуйте, syomin, Вы писали:

JR>>Для "подробней", я думаю, сначала Вам стоит более детально описать ситуацию.

S>Да запросто. Есть DLL'ка, в ней есть функция вида:
S>unsigned long create_foo();

S>Внутри функция создаёт некий ресурс и возвращает его дескриптор. Как только ресурс перестанет быть нужен, он должен быть освобожден с помощью функции:
S>void destroy_foo(unsigned long foo);

S>Для хранения дескриптора неуправляемого ресурса в .Net есть класс SafeHandle, который гарантирует корректное освобождение во всяких специфических ситуациях, но он работает только с IntPtr, размерность которого в 2 раза меньше. Можно ли сделать то же самое для ulong и какой ценой?

А размер этого "unsigned long" точно 64 бита? А не прячется ли там на самом деле указёр? Просто не совсем понятно, зачем понадобилось дестроить обычное число
Help will always be given at Hogwarts to those who ask for it.
Re[6]: MarshalAs
От: syomin  
Дата: 14.04.10 17:24
Оценка:
_FR>А размер этого "unsigned long" точно 64 бита? А не прячется ли там на самом деле указёр? Просто не совсем понятно, зачем понадобилось дестроить обычное число

Сие великая тайна есть. Библиотека сторонняя и посмотреть, что там происходит внутри нет никакой возможности...
Чисто ради расширения кругозора предположим, что действительно не передаются числа бОльшие, чем IntPtr для данной архитектуры. Тогда в управляемом коде можно использовать наследник от SafeHandle, но как правильно сделать маршалинг? С помощью MarshalAs?
Re[7]: MarshalAs
От: _FRED_ Черногория
Дата: 15.04.10 06:48
Оценка: 4 (2)
Здравствуйте, syomin, Вы писали:

_FR>>А размер этого "unsigned long" точно 64 бита? А не прячется ли там на самом деле указёр? Просто не совсем понятно, зачем понадобилось дестроить обычное число


S>Сие великая тайна есть. Библиотека сторонняя и посмотреть, что там происходит внутри нет никакой возможности...


На счёт указателя понятно, но на счёт размера-то вы обязаны знать! Почему вы говорите, что "он работает только с IntPtr, размерность которого в 2 раза меньше"? Поскольку "размерность" IntPtr на разных платформах разная, смысл этого высказывания совсем не ясен. А библиотека ваша строго 32х-битная или имеется и 64х-битная версия? Проблема имеет место быть только если ваша библиотека возвращает именно 64х-битное (что сомнительно, если судить про прототипу) значение вне зависимости от платформы. В других случаях можно пользоваться SafeHandle.

S>Чисто ради расширения кругозора предположим, что действительно не передаются числа бОльшие, чем IntPtr для данной архитектуры. Тогда в управляемом коде можно использовать наследник от SafeHandle, …


Тогда не нужно "использовать наследник от SafeHandle", …

S>…но как правильно сделать маршалинг? С помощью MarshalAs?


…а надо самому выписать всё необходимое. Вы уверены, что вам необходим подсчёт ссылок, реализованный в SafeHandle?
Help will always be given at Hogwarts to those who ask for it.
Re[8]: MarshalAs
От: syomin  
Дата: 15.04.10 07:25
Оценка:
_FR>На счёт указателя понятно, но на счёт размера-то вы обязаны знать! Почему вы говорите, что "он работает только с IntPtr, размерность которого в 2 раза меньше"? Поскольку "размерность" IntPtr на разных платформах разная, смысл этого высказывания совсем не ясен. А библиотека ваша строго 32х-битная или имеется и 64х-битная версия? Проблема имеет место быть только если ваша библиотека возвращает именно 64х-битное (что сомнительно, если судить про прототипу) значение вне зависимости от платформы. В других случаях можно пользоваться SafeHandle.

Сейчас платформа 32-битная. Дескриптор возвращается 64-битный, сейчас специально проверил: отличны от 0 как старшие 4 байта, так и младшие.

_FR>…а надо самому выписать всё необходимое. Вы уверены, что вам необходим подсчёт ссылок, реализованный в SafeHandle?

Не уверен. Точнее, мне-то он совсем не нужен, но книжка Рихтера утверждает, что это сделано из соображений безопасности. Правда каких — я так и не понял.

Для простоты предположим, что я просто делаю класс, который является прямым наследником CriticalFinalizerObject. Как сделать так, чтобы внешний (extern) метод create_foo() вместо ulong возвращал экземпляр этого класса?
Re: Нетривиальный вопрос про SafeHandle
От: BluntBlind  
Дата: 15.04.10 10:02
Оценка:
Здравствуйте, syomin, Вы писали:
S>А что делать в случае, когда нужна обёртка для неправляемого ресурса, который имеет тип, отличный от IntPtr? К примеру, ulong?

SafeHandle Class
Represents a wrapper class for operating system handles.

Он использует IntPtr, который 32 бита для 32битной ос и 64 бита для 64битной. А ulong который возвращается из сторонней библиотеки не имеет прямого отношения к operating system handles (МОЕ ИМХО). Конечно возможно, что эта либа косвенно и мапит этот ulong на какой-то OS handle.

А мысль моя, что достаточно обернуть это дело обычным IDisposable объектом. Где в методе Dispose вызывать тот самый библиотечный метод, куда и передавать этот ulong для уничтожения ...
Re[9]: MarshalAs
От: Jolly Roger  
Дата: 15.04.10 10:41
Оценка:
Здравствуйте, syomin, Вы писали:

S>Для простоты предположим, что я просто делаю класс, который является прямым наследником CriticalFinalizerObject. Как сделать так, чтобы внешний (extern) метод create_foo() вместо ulong возвращал экземпляр этого класса?


Боюсь, для этого Вам придётся устроиться в Microsoft и переписать компилятор C# Буду рад ошибиться, но похоже на то, что для типов вроде SafeHandle, StringBuilder в компилятор "вшито" специальное поведение, compile magic.

Но ведь это и не обязательно, верно? Можно ведь в свой потомок CriticalFinalizerObject добавить статический метод-фабрику, который будет вызывать эту самую create_foo(), а её результат "упаковывать" в экземпляр этого класа, который и возвращать. То на то и выйдет.
"Нормальные герои всегда идут в обход!"
Re[10]: MarshalAs
От: syomin  
Дата: 15.04.10 10:56
Оценка:
JR>Но ведь это и не обязательно, верно? Можно ведь в свой потомок CriticalFinalizerObject добавить статический метод-фабрику, который будет вызывать эту самую create_foo(), а её результат "упаковывать" в экземпляр этого класа, который и возвращать. То на то и выйдет.

Не спортивно.
Я так понимаю, что можно сделать свой собственный marshal'ер, он почему-то именно для сreate_foo(), которая на самом деле принимает ещё несколько аргументов, он не работает
Re[11]: MarshalAs
От: _FRED_ Черногория
Дата: 15.04.10 11:01
Оценка: +1
Здравствуйте, syomin, Вы писали:

S>Я так понимаю, что можно сделать свой собственный marshal'ер, он почему-то именно для сreate_foo(), которая на самом деле принимает ещё несколько аргументов, он не работает


Покажи как "свой собственный marshal'ер" сделан и как используется.
Help will always be given at Hogwarts to those who ask for it.
Re[12]: MarshalAs
От: Jolly Roger  
Дата: 15.04.10 11:06
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, syomin, Вы писали:


S>>Я так понимаю, что можно сделать свой собственный marshal'ер, он почему-то именно для сreate_foo(), которая на самом деле принимает ещё несколько аргументов, он не работает


_FR>Покажи как "свой собственный marshal'ер" сделан и как используется.


Вот и мне тоже интересно
"Нормальные герои всегда идут в обход!"
Re[12]: MarshalAs
От: syomin  
Дата: 15.04.10 11:08
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, syomin, Вы писали:


S>>Я так понимаю, что можно сделать свой собственный marshal'ер, он почему-то именно для сreate_foo(), которая на самом деле принимает ещё несколько аргументов, он не работает


_FR>Покажи как "свой собственный marshal'ер" сделан и как используется.

        public class HDSchedulerHandle : CriticalFinalizerObject
        {
            ~HDSchedulerHandle()
            {
                hdUnschedule(_handle);
            }

            private ulong _handle;
        }

        public class HDSchedulerHandleMarshaler : ICustomMarshaler
        {
            public void CleanUpManagedData(object ManagedObj) { }
            public void CleanUpNativeData(IntPtr pNativeData) { }

            public int GetNativeDataSize()
            {
                return sizeof(ulong);
            }

            public IntPtr MarshalManagedToNative(object ManagedObj)
            {
                return IntPtr.Zero;
            }

            public object MarshalNativeToManaged(IntPtr pNativeData)
            {
                return new HDSchedulerHandle();
            }

            public static ICustomMarshaler GetInstance(string cookie)
            {
                return _instance;
            }

            private static HDSchedulerHandleMarshaler _instance =
                new HDSchedulerHandleMarshaler(); 
        }

        [DllImport("hd.dll")]
        [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(HDSchedulerHandleMarshaler))]
        public static extern HDSchedulerHandleMarshaler
            hdScheduleAsynchronous(uint callback,
                                   uint data,
                                   ushort priority);


Я поставил точки останова на всех методах в классе HDSchedulerHandleMarshaler. Метод GetInstance() вызывается, а потом управление передается сразу коду, вызвавшему hdScheduleAsynchronous(), который возвращает null.
Re[13]: MarshalAs
От: Jolly Roger  
Дата: 15.04.10 11:08
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

А заодно уж сразу и сишное объявление функций, или что там у Вас — ну чтоб два раза не ходить
"Нормальные герои всегда идут в обход!"
Re[13]: MarshalAs
От: syomin  
Дата: 15.04.10 11:22
Оценка:
Ну а если быть максимально точным, то вот так выглядит C-шная функция:
/* Scheduler operation handle. */
typedef unsigned long HDSchedulerHandle;

/* Callback to scheduler operations. */       
typedef HDCallbackCode (HDCALLBACK *HDSchedulerCallback)(void *pUserData); 

HDAPI HDSchedulerHandle HDAPIENTRY hdScheduleAsynchronous(
                                            HDSchedulerCallback pCallback, 
                                            void *pUserData, 
                                            HDushort nPriority);


А вот часть обвязки для нёё на шарпе:

    internal static class NativeMethods
    {
        /// <summary>
        /// Scheduler's callback handle.
        /// </summary>
        public class HDSchedulerHandle : CriticalFinalizerObject
        {
            ~HDSchedulerHandle()
            {
                hdUnschedule(_handle);
            }

            private ulong _handle;
        }

        public class HDSchedulerHandleMarshaler : ICustomMarshaler
        {
            public void CleanUpManagedData(object ManagedObj) { }
            public void CleanUpNativeData(IntPtr pNativeData) { }

            public int GetNativeDataSize()
            {
                return sizeof(ulong);
            }

            public IntPtr MarshalManagedToNative(object ManagedObj)
            {
                return IntPtr.Zero;
            }

            public object MarshalNativeToManaged(IntPtr pNativeData)
            {
                return new HDSchedulerHandle();
            }

            public static ICustomMarshaler GetInstance(string cookie)
            {
                return _instance;
            }

            private static HDSchedulerHandleMarshaler _instance =
                new HDSchedulerHandleMarshaler(); 
        }

        public delegate uint HDSchedulerCallback(IntPtr data);

        [DllImport("hd.dll")]
        [return: MarshalAs(UnmanagedType.CustomMarshaler,
            MarshalTypeRef = typeof(HDSchedulerHandleMarshaler))]
        public static extern object
            hdScheduleAsynchronous(HDSchedulerCallback callback,
                                   IntPtr data,
                                   ushort priority);
    }


А вот так происходит вызов:
            var callbackHandle =
                NativeMethods.hdScheduleAsynchronous(
                DoAll, // Некий метод.
                (IntPtr)GCHandle.Alloc(this),
                NativeMethods.HD_DEFAULT_SCHEDULER_PRIORITY);


В результате callbackHandle равен 0. Отладчиком установил точки останова на всех методах класса HDSchedulerHandleMarshaler, так отрабатывает только метод GetInstance, а MarshalNativeToManaged() даже не вызывается.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.