Я предполагаю, что ответы будут короткими, например «да» или «нет», поэтому у меня сразу два вопроса.

Вопрос №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?

0
Anonymous 13 Окт 2014 в 01:59
В каждой строчке вашего кода «Я могу сделать» есть семантическая ошибка; у большинства два.
 – 
Ben Voigt
13 Окт 2014 в 21:43

2 ответа

Лучший ответ

Кажется, я нашел объяснение, которое помогло мне понять, как мне нужно использовать этот IntPtr.

Следующий абзац содержит неправильные предложения.

  • Я спрашивал, что такое 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);
    }
}
0
Community 23 Май 2017 в 14:57
Приветствуются любые примечания, исправления и рекомендации к предоставленному коду.
 – 
Anonymous
16 Окт 2014 в 04:27
1
Основное исправление заключается в том, что IntPtr не является указателем. Это хранилище размером с указатель. Он может содержать указатель на закрепленные данные и позволяет позже вернуть исходное значение указателя. Или удерживайте GCHandle для незакрепленных данных. Или сохраните дескриптор ОС размером с указатель (дескриптор ядра = HANDLE, дескриптор окна = HWND и т. д.). Поэтому неправильно говорить, что «все IntPtr являются void**» или что-то в этом роде. Значение любого конкретного IntPtr зависит от того, что вы в нем сохранили.
 – 
Ben Voigt
17 Окт 2014 в 17:19

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, созданные для этой цели. Они уводят вас от таких ошибок.

5
Ben Voigt 13 Окт 2014 в 19:34
Что касается незакрепленного GCHandle - я опустил его в этом конкретном примере, я знаю, что второй параметр позволяет мне выбрать, какой дескриптор использовать: Нормальный, Слабый, Закрепленный и т.д.
 – 
Anonymous
13 Окт 2014 в 20:57
Что касается просочившегося GCHandle - это потому, что он не был сохранен в локальной переменной? Или где именно произошла эта утечка?
 – 
Anonymous
13 Окт 2014 в 20:58
Основная причина, по которой я снова задал этот вопрос, заключается в том, что мне нужно сохранить этот дескриптор в неуправляемом коде и, возможно, даже каким-то образом изменить объект оттуда. В этом случае мне нужно понять, что именно было сохранено в этом IntPtr. Согласно первому предложению в вашем ответе, это переменная, в которой хранится указатель на указатель на данные, вид CData **, и когда я делаю IntPtr(...).ToPointer(), я получаю точный указатель на данные, что-то как CData * или void *, верно?
 – 
Anonymous
13 Окт 2014 в 21:07
Когда я вызываю AddrOfPinnedObject(), я предполагаю, что он должен вернуть мне точный адрес в памяти, где начинается мой CData, чтобы я мог получить к нему доступ из неуправляемого кода, или он вернет мне адрес указателя на CData? Есть ли возможность увидеть графическое представление того, что такое IntPtr?
 – 
Anonymous
13 Окт 2014 в 21:20
@Art: Вы совершенно сбиты с толку, и я понятия не имею, почему вы упорно пытаетесь выяснить, как использовать IntPtr, не беспокоясь сначала о том, когда использовать Это. Что почти никогда. Вместо этого используйте pin_ptr. pin_ptr отвечает за закрепление и открепление и может указывать на поля внутри объекта.
 – 
Ben Voigt
13 Окт 2014 в 21:42