P / Вызывать указатель на указатель? - PullRequest
1 голос
/ 17 мая 2011

При обновлении сторонней библиотеки для корректной работы на x64 (она использует int для указателей), я просматривал подписи P / Invoke.

Для одной из них требуется

__out  LPSCARDCONTEXT phContext

Это определено в WinSCard.h

typedef ULONG_PTR SCARDCONTEXT;
typedef SCARDCONTEXT *PSCARDCONTEXT, *LPSCARDCONTEXT;

Я не очень знаком с C ++, так что поправьте меня, если я ошибаюсь.Это означает, что LPSCARDCONTEXT - это указатель на ULONG_PTR, который тоже является указателем.Это также объясняет, почему IntPtr phContext не работает и ref IntPtr phContext работает, в сигнатуре P / Invoke.

Я смущен дизайном.Зачем нужен указатель на указатель?

Ответы [ 3 ]

2 голосов
/ 17 мая 2011

Некоторые мысли о понимании API.

Определение SCARDCONTEXT как ULONG_PTR является распространенным методом точного скрытия того, на что указывает указатель. На самом деле это не указатель на LONG, это «длинная без знака», содержащая указатель на некоторую структуру, определение которой у вас нет, потому что она вам не нужна ( непрозрачный тип ). Когда вы передаете SCARDCONTEXT одной из функций смарт-карты, она внутренне преобразуется в указатель на непрозрачную структуру. И когда он возвращает вам один из этих указателей, он сначала получает SCARDCONTEXT.

Это распространенный метод инкапсуляции в C-API, потому что операции, которые вы можете выполнять в SCARDCONTEXT, существуют отдельно от определения SCARDCONTEXT, а не в виде функций связанных элементов, как в объектно-ориентированном языке. , Как объяснил @ DavidHeffernan , это работает, потому что ULONG_PTR на самом деле unsigned long, который может содержать указатель.

Смысл всего этого в том, что вы можете игнорировать тот факт, что SCARDCONTEXT содержит указатель. Просто отнеситесь к этому как к непрозрачному значению. Тогда PSCARDCONTEXT и LPSCARDCONTEXT станет легче для понимания, потому что они просто указатели на непрозрачное значение. Нет необходимости возиться с указателями на указатели.

2 голосов
/ 17 мая 2011

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

Именование ULONG_PTR явно обмануло вас, заставив поверить, что представляет указатель, когдана самом деле намерение состоит в том, чтобы указать, что он имеет ширину в качестве указателя.

Вы можете думать о ULONG_PTR как о C ++, эквивалентном UIntPtr.

Я предполагаю, что ваша функция возвращается из нативного в управляемое значение SCARDCONTEXT.Вы бы вызвали P / Invoke следующим образом:

[DLLImport(...)]
void MyFunc(out IntPtr Context);

Я предпочитаю использовать IntPtr вместо UIntPtr, так как я думаю, что вам никогда не нужно ничего делать со значением, потому что это, вероятно, непрозрачныйсправиться.И IntPtr, как правило, предпочтительнее UIntPtr, поскольку оно совместимо с CLS.

1 голос
/ 17 мая 2011

Хотя я думаю, что @ Дэвид Хефферман абсолютно прав, есть вторая часть вопроса, который он не затронул.

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

Кроме того, причина, по которой вам нужно использовать ключевое слово ref, чтобы правильно упорядочить данные для полученияВаш код на C # таков, что если вы не используете ref, данные только маршалируются в нативный код.Если вам нужно получить измененное значение в вашем аргументе (на что указывает переменная, объявленная как указатель), вам нужно использовать ref или out, чтобы указать нужному API P / Invokeданные маршалировали из собственного кода после выполнения функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...