Необходимо для pin_ptr в типах значений C++ CLR, почему?

Поскольку типы .NET Value (управляемые структуры C++) хранятся в стеке, почему необходимо (или, наоборот, действительно) необходимо их pin_ptr, чтобы передать указатель на неуправляемую функцию?

Например. БАЙТ b[100];

Если я передам &b неуправляемой функции без ее предварительного закрепления, может ли стек повредиться?

Может ли стек CLR изменяться так же, как и куча сборщика мусора? Меня заставили поверить, что стек CLR использует необычные оптимизации, такие как использование регистров процессора, что делает его непригодным для использования в качестве буфера для неуправляемых функций. Правила, касающиеся закрепления типов значений в стеке, кажутся неясными.

Я заметил, что при отправке буферных массивов таким образом в функцию ядра NTDLL NtfsControlFile происходит некоторое повреждение. Закрепление типа значения решает проблему. Но никогда к вызову API.

Не является ли поэтому принципиально небезопасным передавать любые указатели на любые типы значений в стеке любым неуправляемым функциям без их предварительного закрепления?


person Community    schedule 09.07.2009    source источник


Ответы (4)


Вы правы в том, что BYTE b[100]; создается в собственном стеке и, следовательно, не подлежит перемещению управляемой кучи и т. д. Однако я считаю, что ваша проблема - простая ошибка C++.

Ты говоришь,

Если я передам &b неуправляемой функции без ее предварительного закрепления, может ли стек повредиться?

Вы не должны использовать оператор адреса (&) для имени массива (b), так как имя массива само по себе уже является адресом начала массива. Использование &b не сработает, а результирующее поведение будет зависеть от множества факторов (например, от компилятора и его настроек).

Проще говоря, вы должны просто передавать имя массива (b) вместо использования оператора адреса для имени массива (&b) при вызове функции.

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

person dapi    schedule 13.12.2009

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

person Dave Archer    schedule 09.07.2009

Не является ли поэтому принципиально небезопасным передавать любые указатели на любые типы значений в стеке любым неуправляемым функциям без их предварительного закрепления?

да.

Это связано с тем, что GC удаляет/перемещает их асинхронно с вашим методом.

См. перемещение GC, чтобы узнать, как работает CLR GC.

person Christopher    schedule 09.07.2009
comment
Если они действительно находятся в стеке, сборщик мусора не может их переместить. Но типы значений также могут существовать как члены объектов в куче, которые будут перемещаться. - person Ben Voigt; 03.09.2010

Насколько я могу судить, все это относится к объектам в куче сборщика мусора.

Дело в том, что я имею в виду именно память STACK.

Я опубликовал пример, который, кажется, предполагает, что память стека повреждается во время вызова API, передающего память стека в качестве буфера: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/3779c1ee-90b8-4a6a-9b14-f48d709cb27c

Если память стека должна быть закреплена, то это, кажется, ломает идею «Это просто работает». В неуправляемом C++ мы можем объявить буфер стека, а затем передать указатель на него функции API. Однако, если переход к управляемому коду требует его закрепления, это, по-видимому, в корне подрывает принцип «Это просто работает».

Сбивает с толку то, что документы MSDN для pin_ptr говорят, что это только для предотвращения перемещения объектов, однако также возможно закрепить типы значений, которые, казалось бы, находятся в стеке и в любом случае не должны перемещаться.

Я специально поднимаю вопрос о том, одинаково ли обрабатывается стековая память в управляемом или неуправляемом коде. При отладке MSIL я обнаружил, что невозможно просмотреть стек, и для этого нет инструмента просмотра стека. Я слышал, но не уверен, что в MSIL нет «настоящего» стека, и вместо этого CLR виртуальной машины можно оптимизировать, например, используя свободные регистры процессора вместо фактической памяти. Неясно, так ли это и применимо ли это к стеку, как при передаче параметров, или стеку, как к памяти локальных переменных.

Странный эффект в приведенном выше примере проекта заключается в том, что pin_ptr на поврежденном объекте, кажется, решает проблему. однако объект находится в стеке и не требует закрепления. Может ли быть так, что /CLR интерпретирует pin_ptr не только как «не перемещать этот объект», но также как «оставлять эту область как настоящую память и не пытаться оптимизировать ее», что заставит ее оставаться чистой на время вывода? ?

Я особенно хотел бы знать, достаточно ли умен /CLR, чтобы сказать, что он избегает оптимизации своей памяти стека в методе во время вызова API, но, возможно, не дал бы мне такой же возможности в приведенном выше примере из-за прямой загрузки NTDLL и способ объявления функции как typedef.

Я рассматривал возможность добавления атрибутов Marshalling в typedef функции, но, похоже, не смог этого сделать. Я отмечаю, что в определениях WinAPI нет атрибутов MarshallAs.

Мне удалось проникнуть в проект выше, используя __debugbreak() непосредственно перед вызовом NTDLL, однако это дает мне только управляемый режим отладки, который, похоже, не может перейти в собственный код. Я не могу написать «asm int 3», потому что x64 его не поддерживает. Однако я вижу, что ошибочное значение NumberOfPairs передается в ячейку памяти, на которую указывает регистр, а не как сам регистр.

person Community    schedule 13.07.2009
comment
Первое предложение вашего вопроса неверно и действительно сбивает с толку всю дискуссию. Типы никогда не хранятся в стеке (System::Type — это ссылочный тип). Переменные типа значения хранятся в стеке тогда и только тогда, когда они являются обычными локальными переменными или параметрами функций (члены ссылочных типов и захваченные локальные переменные хранятся в куче, как и содержимое массива). Таким образом, pin_ptr полезен в случае, когда экземпляр типа значения находится внутри объекта кучи. - person Ben Voigt; 30.08.2010