Passing managed C++ arrays to and from unmanaged C

余生颓废 提交于 2019-12-25 08:51:03

问题


I am a complete beginner at C, C++ and C++/CLI, and I don't understand much about the pointers and stuff, but I really need to create a .Net wrapper around a C library. I have tried using SWIG, but it doesn't work and I'm stuck. I'd rather not use P/Invoke because there are a few nested structs involved, and I read that P/Invoke isn't great with these.

So anyway, I have a C header looking like this:

// packer.h
typedef struct {
    int left;
    int top;
    int right;
    int bottom;
} frame_t;

typedef struct {
    /* IN */
    int width;
    int height;
    /* OUT */
    frame_t view;
    frame_t dest;
} image_t;

typedef struct {
    int page_width;
    int page_height;
    int margin;
    int nb_run;
} parameters_t;

int pack(image_t *images, int nb_images, parameters_t params);

Using various tutorials and help bits here and there, I have started writing a C++/CLI wrapper around this:

// packer_cli.cpp
#include <windows.h>
#include <vcclr.h>
#include "../../packer/packer.h"
#using <System.dll>
#using <mscorlib.dll>

using namespace System;

namespace Packer {
    // re-create the needed structs as managed classes
    public ref class Frame {
    public:
        int Left, Top, Right, Bottom;
    };
    public ref class Image {
    public:
        int Width, Height;
        Frame ^View, ^Dest;
        Image() {
            View = gcnew Frame();
            Dest = gcnew Frame();
        }
    };
    public ref class Parameters {
    public:
        int PageWidth, PageHeight, Margin, NbRun;
    };

    // wrap the main method in a class
    public ref class Packer {
    private:
        int _nb_images;
    public:
        Packer(int nb_images) {
            _nb_images = nb_images;
        }
        int Pack(array< Image^ >^ images, Parameters^ params) {
            // create a native array of image_t to send to the C library
            array< image_t * >^ nativeImages = gcnew array< image_t * >(_nb_images);
            for(int i = 0; i < _nb_images; i++) {
                image_t nativeImage;
                nativeImage.width = images[i]->Width;
                nativeImage.height = images[i]->Height;
                nativeImages[i] = &nativeImage; // is this even how it should be done?
            }

            // HALP!

            // create a native parameters_t to send to the C library
            parameters_t nativeParams;
            nativeParams.page_width = params->PageWidth;
            nativeParams.page_height = params->PageHeight;
            nativeParams.margin = params->Margin;
            nativeParams.nb_run = params->NbRun;

            // call the packing method
            int result = pack(nativeImagesToPass, _nb_images, nativeParams);

            // re-loop on images to get the values from native images, and assign them to managed images
            for(int i = 0; i < _nb_images; i++) {
                // TODO
            }

            return result;
        }
    };
}

I have trouble creating a native array of image_t to send to the pack function. I can't pass the array directly, obviously, but I can't find the way to convert it.

With pin_ptr I get this error:

pin_ptr<image_t> nativeImagesToPass = &nativeImages[0];
//C2440: 'initialization' : cannot convert from 'cli::interior_ptr<Type>' to 'cli::pin_ptr<Type>'

And with Marshal::Copy... well, I can't find how to declare my stuff:

System::Runtime::InteropServices::Marshal::Copy(IntPtr((void *)nativeImagesToPass ), nativeImages, 0, _nb_images);
// How do I declare the nativeImagesToPass variable to copy to?
// image_t nativeImagesToPass[_nb_images] yeilds a bunch of errors: C2057, C2466, C2082 and C2133

So, how do I copy an array of structs from a managed array to an unmanaged one? Or maybe there's a better, simpler solution? As I said, I know pretty much nothing about C/C++ so I'm really lost.

Thanks!

Update

Thanks to @metacubed, I have modified the Pack method such:

int Pack(array< Image^ >^% images, Parameters^ params) {
    // create a native array of image_t to send to the C library
    image_t* nativeImages = new image_t[_nb_images];
    for(int i = 0; i < _nb_images; i++) {
        image_t nativeImage = images[i]->ToNative();
    }

    // call the packing method
    int result = pack(nativeImages, _nb_images, params->ToNative());

    // re-loop on images to get the values from native images, and assign them to managed images
    for(int i = 0; i < _nb_images; i++) {
        images[i]->View = gcnew Frame(nativeImages[i].view);
        images[i]->Dest = gcnew Frame(nativeImages[i].dest);
    }

    delete[] nativeImages;

    return result;
}

but now I have these build errors (I'm not sure they're related):

LNK2028: unresolved token (0A000028) "int __cdecl pack(struct image_t *,int,struct parameters_t)" (?pack@@$$FYAHPAUimage_t@@HUparameters_t@@@Z) referenced in the function "public: int __clrcall Packer::Packer::Pack(cli::array<class Packer::Image ^ >^,class Packer::Parameters ^)" (?Pack@Packer@1@$$FQ$AAMHA$CAP$01AP$AAVImage@1@P$AAVParameters@1@@Z)
LNK2019: unresolved external symbol "int __cdecl pack(struct image_t *,int,struct parameters_t)" (?pack@@$$FYAHPAUimage_t@@HUparameters_t@@@Z) referenced in the function "public: int __clrcall Packer::Packer::Pack(cli::array<class Packer::Image ^ >^,class Packer::Parameters ^)" (?Pack@Packer@1@$$FQ$AAMHA$CAP$01AP$AAVImage@1@P$AAVParameters@1@@Z)

I feel so close and yet so far from the goal!


回答1:


One of the advantages of using C++/CLI is that you can just use native C++ constructs. So:

    int Pack(array< Image^ >^ images, Parameters^ params)
    {
        // create a native array of image_t to send to the C library
        image_t* arrNativeImages = new image_t[_nb_images];
        for(int i = 0; i < _nb_images; i++)
        {
            arrNativeImages[i].width = images[i]->Width;
            arrNativeImages[i].height = images[i]->Height;
        }

        ...

        int result = pack(parrNativeImages[0], _nb_images, nativeParams);

        // Use OUT params from arrNativeImages[i]
        ...

        // Finally don't forget to delete the created array...
        delete[] arrNativeImages;
        arrNativeImages = NULL;
    }

It's that simple. There are other ways to directly send the managed objects into native code, but they need the struct alignments to match across the managed and native structs. But for your case, this method seems quite enough.



来源:https://stackoverflow.com/questions/24548468/passing-managed-c-arrays-to-and-from-unmanaged-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!