Reputation: 24212
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
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 SCARDCONTEXT
first.
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
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
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