Christoph Meißner
Christoph Meißner

Reputation: 1622

How do I put a void* into a COM VARIANT so that it gets marshalled as IntPtr by COM-Interop to .NET?

How do I put a void pointer (aka. void* or handle) into a COM VARIANT-Type. The wrapper class _variant_t converts it incorrectly to a boolean. But I need to put it in as pointer so that the COM-Marshaller for .NET recognizes it as IntPtr.

One of the threads I've read, was this one at MSDN Social. They suggested a solution like this:

VARIANTARG Arg; 
VariantInit(&Arg);
V_VT(&Arg) = VT_INT;
V_INT(&Arg) = (int)m_hWnd; 

The problem with this solution is, that V_INT (member intVal) is a 32 bit integer and forces the pointers to be 32 bit pointers. In other words: I can't transfer 64 bit pointers. Is there any solution for that issue? Any other way to put it in an transfer it as 64 bit integer?

I've tested this by some code. Therefore I use a .NET method that receives any System.Object and puts out its value and type.

public static void TakePtr(object ptr)
{
    Console.WriteLine("Received a " + ptr.GetType() + " with value " + ptr);
}

My VARIANT is filled for compatibility by v.llVal = 0; v.byref = myPointer; so it should get compiled always correctly regarding 32/64 bit pointers. Furthermore I need to set the variant type so that .NET mapps it to System.IntPtr without casting. (Because for the real Assembly I can't change the code and don't want to wrapp it. That would add major complexity.).

So what VARENUM flag would specify a System.IntPtr?

Upvotes: 4

Views: 2858

Answers (5)

Mauro H. Leggieri
Mauro H. Leggieri

Reputation: 1104

I used VT_VOID and byref param without almost any problem. Almost because in some situations, .NET raises an MDA error about a bad conversion from native to a managed object (InvalidVariant error)

I couldn't check yet if the problem is when the byref is 0 because it doesn't appears always. :)

Upvotes: 0

David R.
David R.

Reputation: 11

I had a similar problem. When a 64 bit pointer is put into a PVOID variant type, it gets truncated/marshalled as a 32 bit pointer! So I then used the VT_I8 variant type and put the pointer into its llVal member and everything worked great.

Upvotes: 0

Christoph Meißner
Christoph Meißner

Reputation: 1622

At the moment I found a temporary solution to solve the full problem: To explicitly define the argument types when invoking a method, I will use an alternative invoke mechanism. Therefore I use the following method, which almost wrapps the arguments of the type invoke method, but additionally checks the types and casts them accordingly, so that a System.IntPtr is recognized as such and not as System.Int32 or System.Int64.

public static object InvokeMember(this Type type, object obj, String name, System.Reflection.BindingFlags invokeAttr, Type[] argTypes, object[] argValues)
{
    if (argTypes.Length != argValues.Length) throw new InvalidOperationException("Mismatching count of types an parameters.");
    for (int i = 0; i < argTypes.Length; i++) if (argTypes[i] == typeof(IntPtr)) argValues[i] = new IntPtr(Convert.ToInt64(argValues[i]));
    return type.InvokeMember(name, invokeAttr, null, obj, argValues);
}

A call of this method could look like:

typeof(ExampleLib.HelloNET).InvokeMember(
    null,
    "TakePtr",
    BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod,
    new Type[] { typeof(IntPtr) },
    new object[] { 42 });

Finally this has to be used from COM to pass the pointer VARIANTs, which are filled as described: v.llVal = 0; v.byref = myPointer; v.vt = VT_INT;

/solved :-)

Upvotes: 2

Ben Voigt
Ben Voigt

Reputation: 283684

The PVOID byref; member of variant looks helpful. But I'm not sure what type should be set to accompany it.

You could just use LONGLONG llVal as Hans suggested, since even 32-bit platforms have the IntPtr(Int64) constructor. Should work until someone invents 128-bit pointers.

Upvotes: 1

Hans Passant
Hans Passant

Reputation: 941545

That's not possible, VARIANT is an automation type. Automation does not like to deal with pointers to an unknown type, there's no safe way to dereference them. The only pointer types supported are IUnknown* and IDispatch*, interface pointers.

At best you could store it as a VT_I8 which marshals to a long. Which you can then cast to IntPtr.

Upvotes: 5

Related Questions