That leaves us with: ptr, ref, and unique.
- ptr - aka "full pointer," is supposed to be most C-like. It can be NULL (like _opt). It can be used for double pointers, where the size is unknown. It is good for aliased pointers, like using one allocation for more than one pointer.
The problem with with [ptr] is that it causes a lot more work for RPC to figure out what pointers own which buffers.
- ref - acts more like const in paramters. It may not be NULL. The buffers may not change, or be changed during the RPC call. Cannot be aliased. Ref is the default.
It may be a good choice for in parameters that should not change or cannot be NULL.
- unique - is like ptr, but the allocations for the pointers must be unique. This make it so that RPC can make a lot of safe assumptions about the buffers, and skip a lot of buffer checking.
In general, I would recommend using ref for const type in parameters, and unique for opt type in parameters. You should probably stay away from ptr pointers due to the RPC runtime overhead unless you have a good reason to use them. Also you may experiment with the MIDL compiler and see what SAL annotations are generated with which IDL attributes. There is a lot more nuance, so dive into the MSDN articles if you need more depth on the topic.