3

I have a Blah.cs:

public unsafe static int Main()
{
  int[] ai = {1, 2, 3, 4, 5};
  UIntPtr stai = (UIntPtr) ai.Length;
  CManagedStuff obj = new CManagedStuff();
  obj.DoSomething(ai, stai);
}

Then a ManagedStuff.cpp:

void CManagedStuff::DoSomething(int^ _ai, UIntPtr _stai)
{
  // Here I should do something to marshal the int^ to an int*
  pUnmanagedStuff->DoSomething(_ai, (size_t) _stai);
}

And an UnmanagedStuff.cpp:

void CUnmanagedStuff::DoSomething(int* _ai, size_t _stai)
{
  // Walk and print the _stai ints in _ai
}

How can I pass int[] ai from Main to ManagedStuff::DoSomething? I understand there is no marshaling in that call, because all the code involved is managed.

And how can I then marshal int^ _ai in ManagedStuff::DoSomething to call UnmanagedStuff::DoSomething? If I had an int[] _ai the code in the answer for this SO question may help (C#: Marshalling a "pointer to an int array" from a SendMessage() lParam).

Alternatively, how can I avoid working with C#, C++ interop, Microsoft and Windows, and stop world suffering?

Community
  • 1
  • 1
rturrado
  • 7,699
  • 6
  • 42
  • 62
  • Apparently you're using C++/CLI, rather than straight C++, correct? – Cody Gray - on strike May 25 '11 at 11:08
  • I'm sorry but I'm so new to this C# world that I still don't understand correctly many terms and concepts. I think Blah.cs is written in C# with unsafe code (basically pointers, and addresses), CManagedStuff uses C++/CLI (pointers to managed memory, type^ style, and pointers to unmanaged memory, type* style), and CUnmanagedStuff is written entirely in C++. – rturrado May 25 '11 at 11:30
  • As you're new to C# and coming from C++, you probably should read this: [IDisposable: What Your Mother Never Told You About Resource Deallocation](http://www.codeproject.com/KB/dotnet/idisposable.aspx). Actually, it seems that you may need the IDisposable pattern exactly for this program, when you use it in a larger context. – Tamschi May 25 '11 at 12:19

3 Answers3

2

I just need to point out how broken the original idea is.

In native code, you can pass an array by passing the address of the first element, because adjacent elements can be found through pointer arithmetic.

In managed code, the elements are also stored adjacently, but passing a int^ boxes the element, making a copy outside the array. This copy will not have any other array elements stored nearby.

In fact, this also happens in native cross-process communications. The trick of using pointer arithmetic to find other elements only works in-process, and is not generally applicable.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
1

You have to pin the managed resource (your array), so the garbage collector doesn't move it while you're using the pointer.

In C#, you can do this with the fixed statement: fixed Statement (C# Reference)

Pinning in C++ works with pinning pointers, which pin a managed object while they're in scope. (A pointer to any element will pin the entire array):

// In CManagedStuff:
pin_ptr<int> _aiPinned = _ai

More info: C++/CLI in Action - Using interior and pinning pointers

Tamschi
  • 1,089
  • 7
  • 23
  • Pinning is something to take into account. But, first, I have to be able to pass an array of integers from C# code to C++/CLI (from Blah.cs to ManagedStuff). – rturrado May 25 '11 at 15:06
1

OK, I've got it working like this:

void CManagedStuff::DoSomething(array<int>^ _ai, UIntPtr _stai)
{
  // Here I should do something to marshal the int^ to an int*
  pin_ptr<int> _aiPinned = &_ai[0];
  pUnmanagedStuff->DoSomething(_aiPinned, (size_t) _stai);
}

First, passing an array<int>^.
Secondly, as Tamschi was suggesting, using a pin pointer pointing to the address of the first element in the array.

rturrado
  • 7,699
  • 6
  • 42
  • 62