See the question and my original answer on StackOverflow

Let's suppose you have these C# classes:

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class TestClass1
{
    public void SayHello()
    {
        Console.WriteLine("Hello 1");
    }

    public void TestMethod(ref TestClass2[] parameter)
    {
        parameter[0].SayHello();
    }
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class TestClass2
{
    public void SayHello()
    {
        Console.WriteLine("Hello 2");
    }
}

You can register it and create the type library with a command line like this:

%windir%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe ClassLibrary1.dll /codebase /tlb

If you look at the typelib created using OleView from Windows SDK, TestMethod is declared like this:

[id(0x60020005)]
HRESULT TestMethod([in, out] SAFEARRAY(_TestClass2*)* parameter);

So its a SAFEARRAY of _TestClass2 pointers (IUnknown-derived), and you can create it and use it like this (assuming Visual Studio support):

...
#import "c:\mypath\ClassLibrary1.tlb" raw_interfaces_only

using namespace ClassLibrary1;


int main()
{
  CoInitialize(NULL);
  {
    // warning: all error checks omitted

    CComPtr<_TestClass1> tc1;
    tc1.CoCreateInstance(__uuidof(TestClass1));
    tc1.SayHello(); // outputs "Hello 1"

    CComPtr<_TestClass2> tc2;
    tc2.CoCreateInstance(__uuidof(TestClass2));

    SAFEARRAY* psa = SafeArrayCreateVector(VT_UNKNOWN, 0, 1);

    LONG index = 0;
    // note this will call AddRef on tc2 so it's safe
    SafeArrayPutElement(psa, &index, (_TestClass2*)tc2);

    tc1->TestMethod(&psa); // Outputs "Hello 2"

    SafeArrayDestroy(psa);
  }

  CoUninitialize();
  return 0;
}