GCHandle для получения адреса (указателя) объекта.net
Мне удалось получить адрес объекта.net путем
GCHandle objHandle = GCHandle.Alloc(obj,GCHandleType.WeakTrackResurrection);
int address = GCHandle.ToIntPtr(objHandle).ToInt32();
и я могу вспомнить объект по
Object obj = GCHandle.FromIntPtr(IntPtr(address)).Target;
Ну, цель состоит в том, чтобы хранить адрес в собственном классе и иметь информацию о том, какой нативный объект передан какому объекту.net.
AFAIK адрес не меняется из-за выделения, это правда или у кого-то есть идея лучше, чтобы служить моей цели?
Спасибо
4 ответа
Как указал Тим и thecoop, GCHandle.Alloc может запретить сборку мусора, но реальный адрес объекта может измениться, так как GC может перемещать объект, если вы не закрепите объект. Кроме того, ваш код использует GCHandleType.WeakTrackResurrection
и это не может предотвратить сборку мусора. GCHandle.ToIntPtr
даст адрес дескриптора, который может быть обработан в обход неуправляемого вызова. Фактический адрес объекта будет дан AddrOfPinnedObject
метод.
Сказав все это, IMO, ваш код может служить для связи объекта.NET с неуправляемым объектом. Это потому, что GCHandle.To/FromIntPtr вернет вам правильный GCHandle, и вы сможете через него получить доступ к объекту.NET (при условии, что он не собран мусором). ИМО, должно быть несущественно, изменился фактический адрес объекта или нет.
Вы захотите закрепить GCHandle, чтобы остановить движение объекта, поскольку GC перемещает объекты вокруг, так что закрепленный указатель может стать недействительным. Закрепление объекта останавливает его движение:
GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
Вам также придется освободить ручку, когда вы закончите:
handle.Free();
То, что вы получаете, на самом деле не является адресом.
Как вы заметили, в большинстве случаев он действует как адрес, и вы можете вызвать объект, используя GCHandle.FromIntPtr. Однако интересная проблема заключается в том, что вы используете GCHandleType.WeakTrackResurrection.
Если ваш объект со слабой ссылкой получен (вероятно, может, так как GCHandle на него слабо ссылается), у вас все еще есть IntPtr, и вы можете передать его в GCHandle.FromIntPtr (). Если вы сделаете это, вы получите нулевое значение, при условии, что IntPtr не был переработан.
(Если по какой-либо причине CLR переработал IntPtr, у вас возникли проблемы. Я не уверен, что это может произойти.)
Вам лучше использовать либо GCHandleType.Normal, либо GCHandleType.Pinned (если вам нужно взять адрес объекта в неуправляемом коде), если вы хотите сильную ссылку на объект.
(Чтобы использовать GCHandleType.Pinned, ваш объект должен быть, например, примитивным, иметь атрибут [StructLayout] или быть массивом таких объектов.)
AFAIK адрес не меняется из-за выделения, правда ли это
Адрес управляемого объекта действительно меняется. Сборщик мусора может свободно перемещать объекты в памяти. При запуске сборщика мусора он собирает неиспользуемые объекты, а затем переставляет оставшиеся объекты, чтобы минимизировать общий размер управляемой кучи.
у кого-нибудь есть лучшая идея, чтобы служить моей цели?
Я не уверен, что вы найдете хороший способ держать указатель на управляемом объекте в течение длительного периода времени. Можно закрепить объекты в памяти и получить их адрес таким образом, но в C# можно закрепить объекты только в одном методе.
Было бы полезно, если бы вы объяснили более подробно, что вы собираетесь делать с указателем, как только он у вас будет.