See the question and my original answer on StackOverflow

The list of Automation compatible type is documented here: 2.2.49.3 Automation-Compatible Types

As you see, there isn't any concept of a "pointer", handle, or anything that smells "native" (low level). This is because Automation was meant originally for VB (not the .NET one, VB/VBA/VBScript, etc.) that was a language and IDE designed for ease of use, not pointer handling fun, in a time when 64-bit Windows did not existed yet.

So, IntPtr, a raw and opaque pointer (not a handle) which has the particularity to be variable in storage size depending on the executing process bitness, is not a COM automation compatible type, so it can't be put as is, as a pointer, in a VARIANT, because in a VARIANT you want to use in interop code, you can only put automation compatible things.

There are many solutions/workarounds however, because VARIANT can transport 64 bits size things, if you ask nicely. So, you could define the method like this:

public static void Test(object input)
{
    // check for int (Int32) or long (Int64) here
}

And in C++ code do something like this:

variant_t vtIntArg;
if (64-bit mode)
{
    vtIntArg = (__int64)dc; // force VT_I8, this overload available only if _WIN32_WINNT >= 0x0501
}
else
{
    vtIntArg = (long)dc; // force VT_I4
}

Another solution is to define this in C#

public static void Test32(int ptr)
{
}

public static void Test64(long ptr)
{
}

And call the proper function, still using the __int64 overload for the Test64 method.