3

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?

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I have changed the signature to use ref UIntPtr phContext, it still seems to work and I guess it makes more sense than a negative number as a pointer. +1
@Stijn See my updated answer. I think actually you should use out rather than ref.
2

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.

3 Comments

So when writing native code, the developer is not exposed to the meaning of SCARDCONTEXT, but when writing managed code, the developer (writing the P/Invoke part) is exposed to the meaning. Correct?
@Stijn In both managed an unmanaged code, SCARDCONTEXT is just a value. It doesn't really have a 'meaning' other than identifying something. But in p/invoking windows api functions (which, unless they are com-based, are always C-style function-orientated) from .net )object-oriented) you are bridging across two different styles of code. So you have to be conscious of code 'idioms' used in the windows api. Thats what I was trying to explain. Others have done a good job of explaining why pointers are used (to return a value from the function) and what the p/invoke signature should be.
@Stijn By the way, and in case you're not aware of it, you can find a fairly complete set of p/invoke function signatures at pinvoke.net. Click the 'winscard' entry on the left-side navigation bar to see the functions that people have contributed signatures for.
1

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.

8 Comments

I don't think your final paragraph is right. SCARDCONTEXT feels to me as thought it is an opaque handle type. Then when a parameter with signature __out LPSCARDCONTEXT phContext is used that suggests using a pointer to pass by reference. I suspect that this is really C style code and that a pass by reference is not used because C only has pass by value.
Accepted this as the answer. I won't comment on the other part of the answer/discussion.
@Stijn The main problem with this is that the body of the answer is the final paragraph which uses the term marshal incorrectly. Use of ref (and anyway it should be out rather than ref but that's a side issue) does not result in marshalling. All it does it ensure that the value of the parameter, the IntPtr is passed back to the caller. There's no marshalling here.
Then should I undo the accept, and when @Miky removes that paragraph I can accept it again?
@Stijn, I'm glad my answer helped you. @David - I don't think I've used the terminology incorrectly. Although it may not be modifying the data, the Interop marshaller is involved when P/Invoke APIs are used. So for the sake of the argument I think we can call it marshalling. See this overview for the reasoning: msdn.microsoft.com/en-us/library/eaw10et3%28VS.71%29.aspx
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.