Probably the best way to go here is use P/Invoke or Platform Invoke. Depending on the structure or your C++ dll interface you may want to wrap it in a pure C interface; it is easiest if your interface uses only blittable types. If you limit your dll interface to blittable types (Int32, Single, Boolean, Int32[], Single[], Double[] - the basics) you will not need to do any complex marshaling of data between the managed (C#) and unmanaged (C) memory spaces.
For example, in your c# code you define the available calls in your C/C++ dll using the DllImport attribute.
[DllImport, "ExactDllName.dll"]
static extern boolean OneOfMyCoolCRoutines([In] Double[] x, [In] Double[] y, [Out] Double result)
The little [In]'s and [Out]'s are not strictly required, but they can speed things along. Now having added your "ExactDllName.dll" as a reference to your C# project, you can call your C/C++ function from your C# code.
fixed(Double *x = &x[0], *y = &y[0] )
{
Boolean returnValue = OneOfMyCoolCRoutines(x, y, r);
}
Note that I'm essentially passing pointers back and fourth between my dll and C# code. That can cause memory bugs because the locations of those arrays may be changed by the CLR garbage collector, but the C/C++ dll will know nothing of it. So to guard against that, I've simply fixed those pointers in my C#, and now they won't move in memory while my dll is operating on those arrays. This is now unsafe code and I'll need to compile my C# code w/ that flag.
There are many fine details to language interop, but that should get you rolling. Sticking to a stateless C interface of blittable types is a great policy if possible. That will keep your language interop code the cleanest.
Good luck,
Paul