【问题标题】:Marshalling simple and complex datatypes to/from Object^% / void*将简单和复杂的数据类型编组到 Object^% / void*
【发布时间】:2011-06-09 18:13:41
【问题描述】:

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

我正在创建一个包装器,它将向 C# WinForms 应用程序公开高性能 C++ 本机类。 简单的已知对象一切都很好,我还可以包装一个回调函数来委托。但是现在我有点迷茫了。

原生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,float 这样做安全吗?)并传递给本机方法时,何时需要使用 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;
}

附:也许有一个更优雅的解决方案可以用许多可能的数据类型包装这个 void* 方法?

【问题讨论】:

    标签: c++-cli marshalling wrapper


    【解决方案1】:

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

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

    你可以实现你列出的 case 语句,但是如果你不能处理传入的类型,你会怎么做?从 C# 调用函数的人无法知道哪些类型是可接受的,编译器也无法帮助您找出您做错了什么。

    【讨论】:

      猜你喜欢
      • 2019-10-30
      • 2021-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-22
      • 1970-01-01
      相关资源
      最近更新 更多