问题
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