-2

I'm trying to do this helper function for P/Invoking, the purpose is help determining which should be the most appropriated managed type to declare for specific marshalings of a windows API definition.

I've taken the info from here: UnmanagedType Enumeration

In C# or Vb.Net, I ask for help to fill the emptyness Cases and to fix my current errors, if any.

( Also, as an optional and orientative question to answer or not: Do you think could be reliable to write the opposite?, a sort of ManagedTypeToUnmanagedTypeEnum function. )

Public Shared Function UnmanagedTypeToManagedType(ByVal [type] As UnmanagedType) As Type

    Select Case [type]

        Case UnmanagedType.AnsiBStr
            Return GetType(String)

        Case UnmanagedType.AsAny
            Return GetType(Object)

        Case UnmanagedType.BStr
            Return GetType(String)

        Case UnmanagedType.Bool
            Return GetType(Boolean)

        Case UnmanagedType.ByValArray

        Case UnmanagedType.ByValTStr

        Case UnmanagedType.Currency
            Return GetType(Decimal)

        Case UnmanagedType.CustomMarshaler
            Return GetType(Object)

        Case UnmanagedType.Error
            Return GetType(IntPtr)

        Case UnmanagedType.FunctionPtr
            Return GetType([Delegate])

        Case UnmanagedType.I1
            Return GetType(SByte)

        Case UnmanagedType.I2
            Return GetType(Short)

        Case UnmanagedType.I4
            Return GetType(Integer)

        Case UnmanagedType.I8
            Return GetType(Long)

        Case UnmanagedType.IDispatch
            Return GetType(IntPtr)

        Case UnmanagedType.Interface
            Return GetType(IntPtr)

        Case UnmanagedType.IUnknown
            Return GetType(IntPtr)

        Case UnmanagedType.LPArray
            Return GetType(IntPtr)

        Case UnmanagedType.LPStr
            Return GetType(StringBuilder)

        Case UnmanagedType.LPStruct
            Return GetType(IntPtr)

        Case UnmanagedType.LPTStr
            Return GetType(String)

        Case UnmanagedType.LPWStr
            Return GetType(String)

        Case UnmanagedType.R4

        Case UnmanagedType.R8

        Case UnmanagedType.SafeArray

        Case UnmanagedType.Struct

        Case UnmanagedType.SysInt
            Return GetType(IntPtr)

        Case UnmanagedType.SysUInt
            Return GetType(UIntPtr)

        Case UnmanagedType.TBStr
            Return GetType(String)

        Case UnmanagedType.U1
            Return GetType(Byte)

        Case UnmanagedType.U2
            Return GetType(UShort)

        Case UnmanagedType.U4
            Return GetType(UInteger)

        Case UnmanagedType.U8
            Return GetType(ULong)

        Case UnmanagedType.VariantBool
            Return GetType(Boolean)

        Case UnmanagedType.VBByRefStr
            Return GetType(String)

        Case Else
            Throw New InvalidEnumArgumentException(argumentName:="type", invalidValue:=[type],
                                                   enumClass:=GetType(UnmanagedType))
            Return Nothing

    End Select

End Function
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • This question is based on a flawed premise. A parameter's semantics are not specified by type. There is no one-to-one mapping between managed and unmanaged types. The functions that you are trying to construct are useless. – David Heffernan Jan 04 '16 at 10:02
  • 2
    @DavidHeffernan The OP knows that there's no one-to-one mapping between managed and unmanaged types because he/she asked for the **most appropriate type**. The question is that from the many-to-many mapping between managed and unmanaged types, construct a typical one-to-one mapping. Since OP didn't specify why he/she wants such function or what it will be used for, it's hard to know whether it's completely useless or not. – Hadi Brais Jan 05 '16 at 18:09
  • @Hadi Please define "most appropriate type". I'm amazed that you managed to do so with no knowledge of the application of this map. – David Heffernan Jan 05 '16 at 18:11
  • 2
    @DavidHeffernan Well, if you go through the documentation, you'll see that more than half of the unmanaged types correspond to only one managed type. The rest of them correspond to System.Object, System.Delegate or System.Array. Of course, the exact types depend on the function being called. But don't you think this mapping makes sense in general? OP can now try it out for whatever reason it's needed and see whether it works or not. – Hadi Brais Jan 05 '16 at 18:18
  • @Hadi No I don't think it makes sense at all in a number of places. – David Heffernan Jan 05 '16 at 18:24
  • @DavidHeffernan Would you provide a couple examples to elaborate, at least to guide the OP? I might agree with you and you can promote your comment to an answer. – Hadi Brais Jan 05 '16 at 18:29
  • @Hadi everywhere where there's not a one to one mapping – David Heffernan Jan 05 '16 at 18:30
  • @ElektroStudios Can you tell us why you need such mapping? – Hadi Brais Jan 05 '16 at 18:33
  • @Hadi Brais Sorry I read the comments too late. The only reason of this function is to provide some kind of help to determine an approximated/appropiated managed type when things still unclear even when read the MSDN docs, it could be useful in specific cases for example with undocumented APIs ...i think, it is for non-unmanaged-code gurus, like me. Thanks for the comments – ElektroStudios Jan 06 '16 at 05:22
  • I really think the downvotes are unnecesary, this is an original question and originality should be compensated with good reception, maybe the idea is a little absurd in much scenarios, but original, think about that!. – ElektroStudios Jan 06 '16 at 05:25
  • 1
    You should tell the user, when necessary, that the exact managed type depends on the unmanaged API being used. Places of ambiguity are highlighted in my answer. – Hadi Brais Jan 06 '16 at 05:41

2 Answers2

2
Case UnmanagedType.Error
    Return GetType(IntPtr)

No. IntPtr represents a virtual address which is 32-bit on 32-bit platforms and 64-bit on 64-bit platforms. But UnmanagedType.Error is of fixed size. It's a 32-bit signed integer that is mostly used to represent an encoded result of an operation. It's a misnomer because it does not always indicate an error.

Case UnmanagedType.IDispatch
    Return GetType(IntPtr)

Case UnmanagedType.Interface
    Return GetType(IntPtr)

Case UnmanagedType.IUnknown
    Return GetType(IntPtr)

You should probably use System.Object. This is also the case with UnmanagedType.Struct.

Case UnmanagedType.LPStr
    Return GetType(StringBuilder)

Well, you could use StringBuilder or String, it depends on the function being called. If you are doing general conversion, you should probably use String.

Case UnmanagedType.R4

Case UnmanagedType.R8

These correspond to System.Single and System.Double, respectively.

Case UnmanagedType.VariantBool
    Return GetType(Boolean)

VariantBool is a 2-byte Boolean type but the VB Boolean type is 4-byte. you need to use a signed or unsigned 2-byte integral type.

Case UnmanagedType.VBByRefStr
    Return GetType(String)

No. VBByRefStr means passing a string by reference. That is, the address of the reference of the string. Therefore, you should return Object or IntPtr. That webpage also tells that UnmanagedType.ByValTStr is a String.

UnmanagedType.ByValArray, UnmanagedType.LPArray and UnmanagedType.SafeArray all Correspond to System.Array. I strongly suggest reading this article to better understand these types. Also, if possible, you should create more specialized array types using this technique.

Finally, I think you or forgot or omitted UnmanagedType.HString and UnmanagedType.IInspectable, which correspond to String and Object, respectively.

I suggest grouping the switch cases as follows: array types, string types, primitive types, other pointer types and other integer types.

Hadi Brais
  • 22,259
  • 3
  • 54
  • 95
1

For such direct function-like translation/mapping (that is, each unique X has exactly one value of Y), I would recommend to use Dictionary instead of switch case

VB.Net/C#

Dim unToManagedDict As New Dictionary(Of UnmanagedType, Type) 'VB.Net

Dictionary<UnmanagedType, Type> unToManagedDict = new Dictionary<UnmanagedType, Type>(); //C#

Then list your UnmanagedType enum in your application's first loading like this:

VB.Net/C#

unToManagedDict.Add(UnmanagedType.AnsiBStr, GetType(String)) 'VB.Net
unToManagedDict.Add(UnmanagedType.AsAny, GetType(Object)) 'VB.Net
'and so on... or,

unToManagedDict.Add(UnmanagedType.AnsiBStr, typeof(string)); //C# unToManagedDict.Add(UnmanagedType.AsAny, typeof(object)); //C# //and so on...

So to check its managed-counterpart existence you could simply use the dictionary:

VB.Net/C#

Dim type As Type = unToManagedDict(UnmanagedType.AnsiBStr) 'VB.Net

Type type = unToManagedDict[UnmanagedType.AnsiBStr]; //C#

And that will throw exception error if the unmanaged input type is not found, similar to what you did in

Case Else
        Throw New InvalidEnumArgumentException(argumentName:="type", invalidValue:=[type],
                                               enumClass:=GetType(UnmanagedType))

This way, you do not need to use a new function to handle your mapping. Also, you do not need to put new Case everytime.

Now, apart from the style, your request to "fill the emptiness" is somewhat harder as not all the unmanaged has direct equivalent in the managed. (the use of the contrast words unmanaged vs managed themselves imply something!)

However, given the empty cases, I would probably do something like this:

Case UnmanagedType.ByValArray -> Array (no best equivalent, but Array is the closest)
Case UnmanagedType.ByValTStr -> String (no best equivalent, but String could be used)
Case UnmanagedType.R4 -> Single
Case UnmanagedType.R8 -> Double
Case UnmanagedType.SafeArray -> Array (no best equivalent, but Array is the closest)
Case UnmanagedType.Struct -> some Structure (no equivalent, each struct is unique, best is to use Structure to wrap Struct)

Your optional question asks if it is safe to do the reverse. To do the reverse and be perfectly equivalent, there must be one-to-one relation.

That is, borrowing from mathematics, f(x) can only have f'(x) if the relationship between x and f(x) is bijection. Since the current mapping from UnmanagedType to Managed is non-bijection, where the member of UnmanagedType is more than the Managed -> Conclusion: the inverse is not as safe.

Ian
  • 30,182
  • 19
  • 69
  • 107