user247702
user247702

Reputation: 24212

P/Invoke pointer to pointer?

While updating a third party library to work correctly on x64 (it's using int for pointers), I was looking at the P/Invoke signatures.

One of them requires

__out  LPSCARDCONTEXT phContext

This is defined in WinSCard.h

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

I'm not really familiar with C++, so correct me if I'm wrong. This means LPSCARDCONTEXT is a pointer to a ULONG_PTR, which is a pointer too. This also explains why IntPtr phContext doesn't work and ref IntPtr phContext does work, in the P/Invoke signature.

I'm confused by the design. Why is a pointer to a pointer needed/used?

Upvotes: 2

Views: 2675

Answers (3)

Andy Johnson
Andy Johnson

Reputation: 8149

Some thoughts on understanding the API.

Defining SCARDCONTEXT as a ULONG_PTR is a common technique for concealing precisely what the pointer is pointing to. Its not really a pointer to a LONG, its an 'unsigned long' containing a pointer to some structure that you don't have the definition of because you don't need it (an opaque type). When you pass a SCARDCONTEXT to one of the smartcard functions it internally casts to to a pointer to the opaque structure. And when it returns one of these pointers to you then it gets cast to SCARDCONTEXTfirst.

This is a common encapsulation technique in C-based APIs because the operations that you can perform on a SCARDCONTEXT exist separately from the definition of SCARDCONTEXT, not as bound member functions as they would in an object-based language. As @DavidHeffernan explained, this works because ULONG_PTR is really an unsigned long, which can contain a pointer.

The point of all this is that you can ignore the fact that SCARDCONTEXT contains a pointer. Just treat it as an opaque value. Then PSCARDCONTEXT and LPSCARDCONTEXT become easier to understand because they're just pointers to an opaque value. No need to bother with pointers to pointers.

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 612864

Your understanding is not quite right. ULONG_PTR is an unsigned integer type that is at least as wide as a pointer. This means that you can cast from any pointer to ULONG_PTR and back again without losing information.

The naming of ULONG_PTR has clearly tricked you into believing that it represents a pointer when in fact the intention is to indicate that it is as wide as a pointer.

You can think of ULONG_PTR as the C++ equivalent to UIntPtr.

My guess is that your function is returning from native to managed a SCARDCONTEXT value. You would P/Invoke it like this:

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

I'm electing to use IntPtr rather than UIntPtr since I guess you never need to do anything with the value because this is probably an opaque handle. And IntPtr is generally to be preferred to UIntPtr since it is CLS-compliant.

Upvotes: 3

Mike Dinescu
Mike Dinescu

Reputation: 55720

While I think @David Hefferman is absolutely right there's a second part to the question which he did not address.

The part which he did not talk about is that pointers to pointers do exist as they are sometimes used to return pointers from function as arguments. Here's a more detailed discussion about the benefits of pointers-to-pointers.

Also, the reason you need to use the ref keyword in order to correctly marshal the data to-from your C# code is that unless you use ref, the data is only marshaled into the native code. If you need to receive the modified value into your argument (which is indicated by the fact that the variable is declared as a pointer) then you need to use ref or out to indicate to the P/Invoke API that you need the data marshaled out of the native code after the function is done executing.

Upvotes: 1

Related Questions