开发者

Marshalling simple and complex datatypes to/from Object^% / void*

开发者 https://www.devze.com 2023-02-04 04:56 出处:网络
I guess this will be simple for C++/CLI gurus. I am creating a wrapper which will expose high-performance C++ native classes to C# WinForms application.

I guess this will be simple for C++/CLI gurus.

I am creating a wrapper which will expose high-performance C++ native classes to C# WinForms application. Everything went fine with simple known objects and I could wrap also a callback function to delegate. But now I am a bit confused.

The native C++ class has a following method:

int GetProperty(int propId, void* propInOut)

At first I thought I could use void* as IntPtr, but then I found out that I need to access it from C#. So I thought about a wrapper method:

int GetProperty(int propId, Object^ propInOut)

but as I looked through the C++ source, I found out that the method needs to modify the objects. So obviously I need:

int GetProperty(int propId, Object^% propInOut)

Now I cannot pass Objects to native methods so I need to know how to treat them in the wrapper. As the caller should always know what kind of data he/she is passing/receiving, I declared a wrapper:

int Get开发者_JAVA百科Property(int propId, int dataType, Object^% propInOut)

I guess, I can use it to pass reference and value types, for example, an int like this:

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);

I just added a bunch of dataType constants for all the data types I need:

DATA_TYPE_INT, DATA_TYPE_FLOAT, DATA_TYPE_STRING, DATA_TYPE_DESCRIPTOR, DATA_TYPE_BYTE_ARRAY

(DATA_TYPE_DESCRIPTOR is a simple struct with two fields: int Id and wstring Description - this type will be wrapped too, so I guess marshaling will be simple copying data back and forth; all the native strings are Unicode).

Now, the question is - how to implement the wrapper method for all these 5 types? When I can just cast Object^% to something (is int, float safe to do that?) and pass to native method, when do I need to use pin_ptr and when I need some more complex marshaling to native and back?

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. Maybe there is a more elegant solution for wrapping this void* method with many possible datatypes?


It doesn't necessarily make sense to equate a C# object to a void*. There isn't any way to marshal arbitrary data. Even with an object, C# still knows what type it is underneath, and for marshaling to take place -- meaning a conversion from the C++ world to C# or vice-versa -- the type of data needs to be known. A void* is just a pointer to memory of a completely unknown type, so how would you convert it to an object, where the type has to be known?

If you have a limited number of types as you describe that could be passed in from the C# world, it is best to make several overloads in your C++/CLI code, each of which took one of those types, and then you can pin the type passed in (if necessary), convert it to a void*, pass that to your C++ function that takes a void*, and then marshal back as appropriate for the type.

You could implement a case statement as you listed, but then what do you do if you can't handle the type that was passed in? The person calling the function from C# has no way to know what types are acceptable and the compiler can't help you figure out that you did something wrong.

0

精彩评论

暂无评论...
验证码 换一张
取 消