将简单和复杂的数据类型编组到/从Object ^%/ void *

时间:2011-01-15 19:35:39

标签: c++-cli marshalling wrapper

我想这对C ++ / CLI专家来说很简单。

我正在创建一个包装器,它将高性能C ++本机类暴露给C#WinForms应用程序。 使用简单的已知对象,一切都很顺利,我还可以将回调函数包装到委托中。但现在我有点困惑。

本机C ++类有以下方法:

int GetProperty(int propId, void* propInOut)

起初我以为我可以使用void *作为IntPtr,但后来我发现我需要从C#访问它。所以我想到了一个包装器方法:

int GetProperty(int propId, Object^ propInOut)

但是当我查看C ++源代码时,我发现该方法需要修改对象。显然我需要:

int GetProperty(int propId, Object^% propInOut)

现在我无法将对象传递给本机方法,所以我需要知道如何在包装器中处理它们。由于调用者应该总是知道他/她传递/接收的数据类型,我宣布了一个包装器:

int GetProperty(int propId, int dataType, Object^% propInOut)

我想,我可以用它来传递引用和值类型,例如,像这样的int:

Object count = 100; // yeah, I know boxing is bad but this will not be real-time call anyway
myWrapper.GetProperty(Registry.PROP_SMTH, DATA_TYPE_INT, ref count);

我刚为我需要的所有数据类型添加了一堆dataType常量:

DATA_TYPE_INT, DATA_TYPE_FLOAT, DATA_TYPE_STRING, DATA_TYPE_DESCRIPTOR, DATA_TYPE_BYTE_ARRAY

(DATA_TYPE_DESCRIPTOR是一个带有两个字段的简单结构:int Id和wstring描述 - 这种类型也将被包装,所以我想编组将是简单的来回复制数据;所有本地字符串都是Unicode)。

现在,问题是 - 如何为所有这5种类型实现包装器方法? 当我可以将Object ^%转换为某个东西(是int,浮动安全来做那个?)并传递给本机方法时,我什么时候需要使用pin_ptr?当我需要一些更复杂的封送到本机和后面时?

int GetProperty(int propId, int dataType, Object^% propInOut)
{
    if(dataType == DATA_TYPE_INT)
    {
        int* marshaledPropInOut = ???
        int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut);
        // need to do anything more?
        return result;
    }
else
    if(dataType == DATA_TYPE_FLOAT)
    {
        float* marshaledPropInOut = ???
        int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut);
        // need to do anything more ?
        return result;
    }
else
    if(dataType == DATA_TYPE_STRING)
    {
        // will pin_ptr be needed or it is enough with the tracking reference in the declaration?
        // the pointers won't get stored anywhere in C++ later so I don't need AllocHGlobal
        int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut);
        // need to do anything more?
        return result;
    }
else
    if(dataType == DATA_TYPE_BYTE_ARRAY)
    {
         // need to convert form managed byte[] to native char[] and back; 
         // user has already allocated byte[] so I can get the size of array somehow

         return result;
    }
else
    if(dataType == DATA_TYPE_DESCRIPTOR)
    {
         // I guess I'll have to do a dumb copying between native and managed struct, 
         // the only problem is pinning of the string again before passing to the native

         return result;
    }

    return -1;
}

P.S。也许有一个更优雅的解决方案来包装这个带有许多可能数据类型的void *方法?

1 个答案:

答案 0 :(得分:0)

将C#对象等同于void *并不一定有意义。没有任何方法可以编组任意数据。即使有一个对象,C#仍然知道它下面是什么类型,并且为了进行编组 - 意味着从C ++世界到C#的转换,反之亦然 - 需要知道数据的类型。 void *只是指向完全未知类型的内存的指针,那么如何将其转换为必须知道类型的对象?

如果您描述的类型数量有限,可以从C#世界传入,那么最好在C ++ / CLI代码中进行多次重载,每次重载都采用其中一种类型,然后你可以固定传入的类型(如果需要),将其转换为void *,将其传递给带有void *的C ++函数,然后根据类型进行编组返回。

您可以按列出的方式实现案例陈述,但如果无法处理传入的类型,您会怎么做?从C#调用函数的人无法知道哪些类型是可接受的,编译器无法帮助你弄清楚你做错了什么。