将C ++的VARIANT数据类型转换为C#

时间:2013-04-04 08:59:54

标签: c# c++ type-conversion variant

C#中C ++的VARIANT数据类型是什么?

我在C ++中使用VARIANT数据类型的代码。如何在C#中转换该代码?

4 个答案:

答案 0 :(得分:8)

嗯,C ++中实际上有两个变体:boost :: variant和COM变体。解决方案或多或少地遵循相同的想法,但前者更复杂。我希望你的意思是使用后者。

首先我要说的是,如果可能的话,这是你不应该使用的东西。那就是说,你就是这样做的: - )

变种和互操作

如果需要字节表示相同,变量有时用于互操作。

如果您正在处理互操作,请务必查看MSDN上的VariantWrapper课程并使其正常工作。

变体和移植注意事项

变体主要用于API,通常是这样的:

void Foo(SomeEnum operation, Variant data);

在C ++中这样做的原因是因为没有基类object类,因此你需要这样的东西。移植它的最简单方法是将签名更改为:

void Foo(SomeEnum operation, object data);

但是,如果您正在移植,那么您也非常想要考虑这两个,因为它们在编译时得到解决,并且可以为您节省方法Foo中通常遵循的大“切换”:< / p>

void SomeOperation(int data);
void SomeOperation(float data);
// etc

变体和字节一致性

在极少数情况下,您需要自己操纵字节。

本质上,变体只是一个包含在单个值类型(struct)中的值类型的大型联合。在C ++中,您可以在堆上分配值类型,因为结构与类相同(排序很好)。如何使用值类型只是有点重要,但稍后会更多。

Union简单地意味着您将重叠内存中的所有数据。请注意我如何明确指出上面的值类型;对于变体而言,这基本上就是它的全部内容。这也为我们提供了一种测试方法 - 即通过检查结构中的另一个值。

在C#中执行此操作的方法是在值类型中使用StructLayout属性,该属性基本上如下:

[StructLayout(LayoutKind.Explicit)]
public struct Variant
{
    [FieldOffset(0)]
    public int Integer;
    [FieldOffset(0)]
    public float Float;
    [FieldOffset(0)]
    public double Double;
    [FieldOffset(0)]
    public byte Byte;
    // etc
}

// Check if it works - shouldn't print 0.
public class VariantTest
{
    static void Main(string[] args)
    {
        Variant v = new Variant() { Integer = 2 };
        Console.WriteLine("{0}", v.Float);

        Console.ReadLine();
    }
}
如前所述,

C ++变体也可以存储在堆上。如果这样做,您可能仍希望内存签名相同。这样做的方法是将我们之前构建的Variant结构框封装到object

答案 1 :(得分:1)

这是一个棘手的问题。

从C#4开始,您可以使用 dynamic 来表明该类型在运行时已知。

但是,根据我个人的理解,c ++需要在编译时知道类型。因此,您可以考虑使用object,但C#中的object是现有类型。

对于VARIANT的多类型,单值(AKA多态)的概念,您不需要在C#中找到相应的类型,只需定义类和接口即可。您始终可以引用一个对象作为该类实现的接口。

如果要移植代码,并找出可以在LHS中使用的语法,并且在编译时已知类型的考虑,则使用 var

答案 2 :(得分:1)

.NET实现COM接口时,只需使用VARIANT *

然后使用 IntPtr 类型接收指针,在.NET接收端绕过编组

public class ComVariant
{
    [StructLayout(LayoutKind.Sequential)]
    public struct Variant
    {
        public ushort vt;
        public ushort wReserved1;
        public ushort wReserved2;
        public ushort wReserved3;
        public Int32 data01;
        public Int32 data02;
    }

    private Variant _variant;

    private IntPtr _variantPtr;

    public ComVariant(int variantPtr) : this(new IntPtr(variantPtr))
    {
    }

    public ComVariant(IntPtr variantPtr)
    {
        _variant = (Variant)Marshal.PtrToStructure(variantPtr, typeof(Variant));
        _variantPtr = variantPtr;
    }

    public VarEnum Vt
    {
        get
        {
            return (VarEnum)_variant.vt;
        }
        set
        {
            _variant.vt = (ushort)value;
        }
    }

    public object Object
    {
        get
        {
            return Marshal.GetObjectForNativeVariant(_variantPtr);
        }
    }
}

然后,如果您正在访问指向COM接口对象实例的VT_UNKNOWN,则只需

var variant = new ComVariant(variantPtr);
var stream = variant.Object as IStream; // will not be null if type is correct
var obj = variant.Object as IObj; // in general...

会做的伎俩,但要注意不要使用新分配的VARIANT并将其所有权授予.NET实现,而无需在某处解除分配...

对于更复杂的代码,您可以阅读 this article,其中还讨论了内存管理。

答案 3 :(得分:0)

让我们后退一步。迟早,我们想要VARIANT中的实际数据。 VARIANT只是有意义数据的持有者。假设我们将VARIANT转换为C#中的某种Object,它具有变体类型和.NET引擎下的一些原始缓冲区(例如.NET字符串可以公开原始缓冲区)。此时,需要根据对象确定VARIANT类型,并将原始数据转换或转换为变量指定的数据类型,然后创建新的Object,例如串/ INT /等。来自原始数据。

因此,不要担心将VARIANT传递给C#,而是查看变量数据类型并将其在C ++中转换为实际数据类型并将其传递给C#。

例如,如果VARIANT类型是VT_INT,那么从变量获取int并使用类似的东西:

VARIANT var;

Int^ returnInt = gcnew Int(var.intVal);

returnInt可以作为Out参数从C ++ dll中的C ++函数返回,可以从C#调用。 C ++ dll需要使用/ clr选项。

功能如下: -

void ThisFunctionReturnsAnInt(Runtime::InteropServices::OutAttribute Int^ % returnIntValue)
{

    VARIANT var;
    Int^ returnInt = gcnew Int(var.intVal);
}

可以对其他数据类型使用类似的方法。它是唯一自然的,VT_INT的变体实际上就像一个整数,它不像是在进行一些重大的转换,你只是把实际价值从您对它感兴趣的VARIANT就像您将C ++的直接整数值传递给C#一样。无论如何你仍然需要做gcnew。