2

I am wrapping some native C++ code in a C++/CLI .dll for use in .NET projects - mainly C#. The number of calls will be large so I am looking to do this in an effective way. The function I am wrapping takes the following arguments:

int SomeFun(
       const char* input_text,
       int* output_array,
       bool* output_value);

I know how to do an efficient System::String to const char* cast thanks to this answer. My questions are these:

  1. The function expects pointers to output_array and output_value which need to be created/cleaned-up and returned to the managed component all inside the wrapper. How do I do that?
  2. How do I return multiple values to the managed environment from the wrapper .dll - by using a struct as a return value?
  3. For now I am trying to do all the pointer handling and managed/unmanaged interaction inside the wrapper .dll but this article (Solution 3) suggests that using an "unsafe" environment inside C# is the fastest. Is this likely to be a better option for what I am trying to do? I guess it would make the wrapper less complex but also require more elaborate treatment in C#.

Thanks,

/David

Community
  • 1
  • 1
OG Dude
  • 936
  • 2
  • 12
  • 22
  • 1
    Use pin_ptr<>. This function is *very* unsafe, it doesn't have a way to tell it how large the arrays are. If you pass it an array that is too small then the function will corrupt memory. You ought to do something about that. – Hans Passant Jul 13 '11 at 17:17

1 Answers1

1

You can pin arrays the same way you pin strings, e.g. for byte[]:

pin_ptr<Byte> ptrBytes = &myArray[myArray->GetLowerBound(0)];

ptrBytes can now be used as an unsigned char*

You can pin single variables that come from C#, too, for example with a parameter that is a ref to an int called int% ival:

pin_ptr<int> pInt =   &ival;

pInt can now be used as an int*

You can create arrays with gcnew, for example to create an array of Bytes to return to C#:

array<Byte>^ streambuf = gcnew array<Byte>(bufSize+16);

You can now fill this with streambuf[index], repacking a C++ array in a loop, or you could pin it and do a memcpy before returning it to C#.

I do not usually return structs. I usually define each parameter in the C++/CLI function wrapper. For example if a struct had two ints and a string, I'd make each a parameter with ref keyword (% in C++/CLI, e.g. int% ival). if you must return a large number of values it's easiest to return a class you create with gcnew, not a struct.

Using unsafe code is the way to go if you are trying to speed up array access and your C# code will be straightforward. If you already have C++ code and it uses the STL and is very complex and you don't want to rewrite it, especially if you have lots of pointer arithmetic and casts, stick with a C++/CLI wrapper.

Ed Bayiates
  • 11,060
  • 4
  • 43
  • 62
  • AresAvatar, thank you for your reply. I will check this out. /David – OG Dude Jul 14 '11 at 11:05
  • Ok, my calls in C# look like `wrapper(input_text, ref output_array, ref output_value)`. The C++/CLI wrapper takes `wrapper(String^ input_text, array^% output_array, bool% output_value)`. Inside the wrapper I then treat input_text like [here](http://stackoverflow.com/questions/6596242) and do `pin_ptr pOutput_array = &output_array[0]; int* npOutput_array = pOutput_array` and for the boolean `pin_ptr pOutput_value = &Output_value; bool* npOutput_value = pOutput_value`. It works - is this ok? Should I destroy the pin_ptr and native pointers before exiting? Thanks. – OG Dude Jul 15 '11 at 17:23
  • That all looks correct. Do not attempt to destroy the pinned pointers -- the CLI does that for you. They are unpinned when they go out of scope, and you'd be causing memory corruption if you tried to delete them. – Ed Bayiates Jul 15 '11 at 17:27