You should read this article fully first: https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-arrays#unmanaged-arrays
I also found this relevant QAs: How to create and initialize SAFEARRAY of doubles in C++ to pass to C#
Your GetCppArray function only returns a pointer - it doesn't return a self-describing "safe" array, whereas arrays in .NET include length and rank (dimension) information, so you definitely need to modify the C++ code to do this correctly.
The first option is to return the array as a COM-style safe array, this is done with the SAFEARRAY( typename ) macro - and it must be passed as a parameter, not a return value.
There are two main ways of using COM Safe-Arrays in C++: using the Win32 functions like SafeArrayCreate - which are painful to use correctly, or by using the ATL CComSafeArray.
(Disclaimer: I wrote this code by looking at the API references, I haven't tested it - I don't even know if it will compile).
// C++ code for SafeArrayCreate:
#include <comdef.h>
int test_data[5] = { 12, 60, 55, 49, 26 };
extern "C" __declspec(dllexport) HRESULT GetCppArray( [out] SAFEARRAY( int )** arr )
{
SAFEARRAYBOUND bounds;
bounds.lLbound = 0;
bounds.cElements = sizeof(test_data);
*arr = SafeArrayCreate(
VT_I4, // element type
1, // dimensions
&bounds
);
if( !arr ) {
// SafeArrayCreate failed.
return E_UNEXPECTED;
}
int* arrPtr;
HRESULT hr = SafeArrayAccessData( *arr, &arrPtr );
if( !SUCCEEDED( hr ) ) {
hr = SafeArrayDestroy( arr );
// After SafeArrayDestory, if `hr != S_OK` then something is really wrong.
return E_UNEXPECTED;
}
for( size_t i = 0; i < sizeof(test_data); i++ ) {
*arrPtr[i] = test_data[i];
}
hr = SafeArrayUnaccessData( *arrPtr );
if( !SUCCEEDED( hr ) ) {
hr = SafeArrayDestroy( arr );
return E_UNEXPECTED;
}
return S_OK;
}
The C# code then needs to be updated to declare it returns a SafeArray:
// HRESULT is best represented as a UInt32 instead of Int32.
[DllImport( "CppDll.dll", CallingConvention = CallingConvention.Cdecl )]
public static extern UInt32 GetCppArray(
[MarshalAs( UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I4 )] out Int32[] arr
);