title: C# Marshal byte[] to COM SAFEARRAY parameter with "ref object" signature
body: <p>I've been going round and round in circles on Google on this, and I can find all kinds of discussion, lots of suggestions, but nothing seems to work.  I have an ActiveX component which takes an image as a byte array.  When I do a TLB import, it comes in with this signature:</p>
  
  <pre><code>int HandleImage([MarshalAs(UnmanagedType.Struct)] ref object Bitmap);
  </code></pre>
  
  <p>How do I pass a byte[] to that?</p>
  
  <p>There's another function which can return the data with a similar signature, and it works because I can pass "null" in.  The type that comes back is a byte[1..size] (non-zero bounded byte[]).  But even if I try to pass in what came back, it still gets a type mismatch exception.</p>
  
  <hr>
  
  <p>More details:</p>
  
  <p>I've been editing the method in the IDispatch interface signature (using ILSpy to extract the interface from the auto-generated interop assembly).  I've tried just about every combination of the following, it always gets Type mismatch exception:</p>
  
  <ol>
  <li>Adding and removing the "ref"</li>
  <li>Changing the parameter datatype to "byte[]" or "Array"</li>
  <li>Marshalling as [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)].  After playing around with MarshalAs quite a bit, I'm becoming convinced that IDispatch does not use those attributes.</li>
  </ol>
  
  <p>Also tried using the "ref object" interface as is, and passing it different types:  byte[], Array.CreateInstance(typeof(byte) (which I think are both identical, but I found someone suggesting it, so it couldn't hurt to try).</p>
  
  <p>Here's an example of Delphi code that creates a proper array to pass in:</p>
  
  <pre><code>var
     image: OLEVariant;
     buf: Pointer;
  
  image := VarArrayCreate([0, Stream.Size], VarByte);
  Buf  := VarArrayLock(image);
  Stream.ReadBuffer(Buf^, Stream.Size);
  VarArrayUnlock(image);
  </code></pre>
  
  <p>Here's the C code to do the same thing.  I guess if I can't get it to work from C#, I can invoke it through managed C++, although I'd rather have everything in one project:</p>
  
  <pre><code>long HandleImage(unsigned char* Bitmap, int Length)
  {
      VARIANT vBitmap;
      VariantInit (&amp;vBitmap);
      VariantClear(&amp;vBitmap);
  
      SAFEARRAYBOUND bounds[1];
      bounds[0].cElements = Length;
      bounds[0].lLbound = 1;
  
      SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
      SafeArrayLock(arr);
      memcpy(arr-&gt;pvData, Bitmap, Length);
      SafeArrayUnlock(arr);
      vBitmap.parray = arr;
      vBitmap.vt = VT_ARRAY | VT_UI1;
  
      long result;
      static BYTE parms[] = VTS_PVARIANT;
      InvokeHelper(0x5e, DISPATCH_METHOD, VT_I4, (void*)&amp;result, parms,
          &amp;vBitmap);
  
      SafeArrayDestroy(arr);
      VariantClear(&amp;vBitmap);
  
      return result;
  }
  </code></pre>