Я предполагаю, что ответы будут короткими, например «да» или «нет», поэтому у меня сразу два вопроса.
Вопрос №1 : я читал теоретические объяснения IntPtr, но было бы легче понять его по сравнению с собственными указателями C ++. Допустим, у меня есть структура.
ref struct CData
{
double Key;
};
С указателями в собственном коде я могу:
1) int address = & CData; // if i need its address
2) double value = * CData; // if i need value of its field
3) void * pointer1 = CData; // if i want create pointer to struct
4) CData * pointer2 = CData; // if i want create pointer to struct
Если CData является управляемым объектом, то операции, указанные выше, могут быть выполнены следующим образом:
1) IntPtr address = GCHandle::Alloc(CData).AddrOfPinnedObject() // if i need address
2) cannot get one field within pointer, only direct access within CData.Key
3) IntPtr pointer = GCHandle::ToIntPtr(GCHandle::Alloc(CData)) // if i need pointer
4) the same as # 3
Прав ли я в своих предположениях о сходстве указателей в собственном и управляемом коде?
Вопрос №2 : в моем родном приложении у меня нет такого типа, как void * , могу ли я просто использовать вместо него int ?
Полный список доступных типов данных представлен здесь http://www.mql5.com/en/docs / base / types - какой из них может быть действительной заменой void *, чтобы я мог использовать его вместе с IntPtr?
2 ответа
Кажется, я нашел объяснение, которое помогло мне понять, как мне нужно использовать этот IntPtr.
- Закрепление пустого массива
- http://blogs.msdn.com/b/jmstall/archive/2006/10/09/gchandle_5f00_intptr.aspx
Следующий абзац содержит неправильные предложения.
- Я спрашивал, что такое IntPtr, потому что: мне нужно было переместить все мои полезные функции в библиотеку C # (DLL) и в то же время иметь возможность вызывать их из неуправляемых программ, таких как Metatrader. К сожалению, Metatrader вообще не поддерживает указатели, поэтому мне нужно было выяснить, что именно IntPtr возвращает мне, потому что, если это обычный указатель, я не могу использовать его в MT. Также я подумал, что из-за циклов сборки мусора я не могу использовать внутренний указатель, поэтому в любом случае мне нужно закрепить управляемый объект и вернуть точный адрес памяти, с которой он начинается. Я ОШИБАЛСЯ.
Это то, к чему я пришел, уже протестировано и работает, поэтому я считаю, что это правильная реализация.
я не должен думать о закрепленном указателе, потому что я собирался работать с настраиваемым управляемым классом, который не кажется непреобразуемым типом и наверное не может быть приколота. Я мог бы использовать какой-нибудь общий тип, например байт array, закрепите его и получите фактический адрес с помощью AddrOfPinnedObject. В этом случай, когда я мог манипулировать данными как из управляемого, так и из неуправляемого программ, но я решил остаться на управляемой стороне только ради простоты.
Я понял, что IntPtr - это указатель на указатель на данные, своего рода void **, но для управляемой кучи, что означает, что есть два указателя, первый указывает на «дескриптор», а «дескриптор» указывает на реальные данные. Второй указатель («дескриптор») может измениться после цикла GC, если фактические данные были перемещены, но адрес этого дескриптора остается прежним, что означает, что первый указатель постоянен в любом случае. Я собирался манипулировать данными только из управляемого кода, поэтому достаточно было сохранить только первый указатель, а затем, используя этот указатель, я всегда могу получить «дескриптор», независимо от того, было ли его значение изменено GC или нет. Дан хэндл - может сам получить данные.
для меня шаблоны int_ptr <> и interrior_prt <> кажутся черным ящиком, поэтому, если бы я их использовал, вероятно, было бы сложнее понять, как именно работают эти дескрипторы, возможно, я скоро передумаю, но пока я реализовал это с помощью GCHandle
Пример:
Скажем, у меня есть класс в C # DLL, который я хотел бы использовать в качестве общей памяти через файлы с отображением памяти, я назвал его подключением:
http://www.codeproject.com/Articles/138290/Programming-Memory-Mapped-Files-with-the-NET-Frame
Затем в C ++ / CLI у меня есть:
#define ECV extern "C" __declspec(dllexport) void __cdecl
ECV MemoryOpen(char * name, int size, int &pointer)
{
try
{
Connection ^ memory = gcnew Connection(); // instantiate managed class
GCHandle gc = GCHandle::Alloc(memory); // get handle
pointer = (int) GCHandle::ToIntPtr(gc); // get pointer to handle and pass it to unmanaged code by reference
memory->Open(gcnew String(name), size); // do something from managed code
}
catch (Exception ^ e)
{
MessageBox::Show(e->Message + " " + e->StackTrace);
}
}
ECV MemoryClose(int pointer)
{
try
{
GCHandle gc = GCHandle::FromIntPtr(IntPtr(pointer)); // get pointer to handle
Connection ^ memory = (Connection ^) gc.Target; // get data using handle
memory->Close();
gc.Free(); // free handle
}
catch (Exception ^ e)
{
MessageBox::Show(e->Message + " " + e->StackTrace);
}
}
IntPtr
не является указателем. Это хранилище размером с указатель. Он может содержать указатель на закрепленные данные и позволяет позже вернуть исходное значение указателя. Или удерживайте GCHandle для незакрепленных данных. Или сохраните дескриптор ОС размером с указатель (дескриптор ядра = HANDLE
, дескриптор окна = HWND
и т. д.). Поэтому неправильно говорить, что «все IntPtr
являются void**
» или что-то в этом роде. Значение любого конкретного IntPtr
зависит от того, что вы в нем сохранили.
IntPtr
не является указателем. Это целое число, достаточно большое для хранения указателя, как uintptr_t
в стандартных C и C ++.
В C ++ / CLI типом «указателя на управляемый объект» является псевдошаблон interior_ptr<T>
и pin_ptr<T>
. Подобно фиксированному блоку C #, pin_ptr<T>
закрепляет объект в управляемой куче, позволяя вам передавать указатель как обычный собственный указатель на существующий код, не перемещая объект из-под этого кода.
Очень редко есть причина использовать GCHandle
непосредственно в C ++ / CLI. Вместо этого используйте шаблоны pin_ptr<T>
и gcroot<T>
. И есть веская причина избегать этого, вы допустили две серьезные ошибки всего в одной строчке кода в своем вопросе:
GCHandle::Alloc(CData).AddrOfPinnedObject()
Вы слили GCHandle. И вы вызвали AddrOfPinnedObject
на несвязанном GCHandle.
Используйте инструменты C ++ / CLI, созданные для этой цели. Они уводят вас от таких ошибок.
IntPtr
, не беспокоясь сначала о том, когда использовать Это. Что почти никогда. Вместо этого используйте pin_ptr
. pin_ptr
отвечает за закрепление и открепление и может указывать на поля внутри объекта.
Похожие вопросы
Связанные вопросы
Новые вопросы
pointers
Типы данных для «указания» на другие значения: значение указателя — это адрес памяти, где хранится указываемое значение. Этот тег следует использовать для вопросов, связанных с использованием указателей, а не ссылок. Распространенными языками программирования, использующими указатели, являются C, C++, Go, а также языки ассемблера и промежуточного представления; использовать определенный языковой тег. Другие полезные теги должны описывать, на что указывает указатель (например, функция, структура и т. д.).