/*
   * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc)
   * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc
   * must be incremented before returning.  Caller is responsible for releasing ppenumFormatetc.
   */
  private int EnumFormatEtc(int dwDirection, long /*int*/ ppenumFormatetc) {
    // only allow getting of data - SetData is not currently supported
    if (dwDirection == COM.DATADIR_SET) return COM.E_NOTIMPL;

    // what types have been registered?
    TransferData[] allowedDataTypes = new TransferData[0];
    for (int i = 0; i < transferAgents.length; i++) {
      Transfer transferAgent = transferAgents[i];
      if (transferAgent != null) {
        TransferData[] formats = transferAgent.getSupportedTypes();
        TransferData[] newAllowedDataTypes =
            new TransferData[allowedDataTypes.length + formats.length];
        System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length);
        System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length);
        allowedDataTypes = newAllowedDataTypes;
      }
    }

    OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC();
    enumFORMATETC.AddRef();

    FORMATETC[] formats = new FORMATETC[allowedDataTypes.length];
    for (int i = 0; i < formats.length; i++) {
      formats[i] = allowedDataTypes[i].formatetc;
    }
    enumFORMATETC.setFormats(formats);

    OS.MoveMemory(ppenumFormatetc, new long /*int*/[] {enumFORMATETC.getAddress()}, OS.PTR_SIZEOF);
    return COM.S_OK;
  }
 private int SetData(long /*int*/ pFormatetc, long /*int*/ pmedium, int fRelease) {
   if (pFormatetc == 0 || pmedium == 0) return COM.E_INVALIDARG;
   FORMATETC formatetc = new FORMATETC();
   COM.MoveMemory(formatetc, pFormatetc, FORMATETC.sizeof);
   if (formatetc.cfFormat == CFSTR_PERFORMEDDROPEFFECT && formatetc.tymed == COM.TYMED_HGLOBAL) {
     STGMEDIUM stgmedium = new STGMEDIUM();
     COM.MoveMemory(stgmedium, pmedium, STGMEDIUM.sizeof);
     // TODO - this should be GlobalLock()
     long /*int*/[] ptrEffect = new long /*int*/[1];
     OS.MoveMemory(ptrEffect, stgmedium.unionField, OS.PTR_SIZEOF);
     int[] effect = new int[1];
     OS.MoveMemory(effect, ptrEffect[0], 4);
     dataEffect = osToOp(effect[0]);
   }
   if (fRelease == 1) {
     COM.ReleaseStgMedium(pmedium);
   }
   return COM.S_OK;
 }
  /* QueryInterface([in] riid, [out] ppvObject)
   * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
   * must be incremented before returning.  Caller is responsible for releasing ppvObject.
   */
  private int QueryInterface(long /*int*/ riid, long /*int*/ ppvObject) {
    if (riid == 0 || ppvObject == 0) return COM.E_INVALIDARG;
    GUID guid = new GUID();
    COM.MoveMemory(guid, riid, GUID.sizeof);

    if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDropSource)) {
      OS.MoveMemory(ppvObject, new long /*int*/[] {iDropSource.getAddress()}, OS.PTR_SIZEOF);
      AddRef();
      return COM.S_OK;
    }

    if (COM.IsEqualGUID(guid, COM.IIDIDataObject)) {
      OS.MoveMemory(ppvObject, new long /*int*/[] {iDataObject.getAddress()}, OS.PTR_SIZEOF);
      AddRef();
      return COM.S_OK;
    }

    OS.MoveMemory(ppvObject, new long /*int*/[] {0}, OS.PTR_SIZEOF);
    return COM.E_NOINTERFACE;
  }
  static Variant writeSafeArray(String string) {
    // Create a one dimensional safearray containing two VT_UI1 values
    // where VT_UI1 is an unsigned char

    // Define cDims, fFeatures and cbElements
    short cDims = 1;
    short FADF_FIXEDSIZE = 0x10;
    short FADF_HAVEVARTYPE = 0x80;
    short fFeatures = (short) (FADF_FIXEDSIZE | FADF_HAVEVARTYPE);
    int cbElements = 1;
    // Create a pointer and copy the data into it
    int count = string.length();
    char[] chars = new char[count + 1];
    string.getChars(0, count, chars, 0);
    int cchMultiByte = OS.WideCharToMultiByte(CodePage, 0, chars, -1, null, 0, null, null);
    if (cchMultiByte == 0) return null;
    long /*int*/ pvData = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, cchMultiByte);
    OS.WideCharToMultiByte(CodePage, 0, chars, -1, pvData, cchMultiByte, null, null);
    int cElements1 = cchMultiByte;
    int lLbound1 = 0;
    // Create a safearray in memory
    long /*int*/ pSafeArray = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, SAFEARRAY.sizeof);
    SAFEARRAY safeArray = new SAFEARRAY();
    safeArray.cDims = cDims;
    safeArray.fFeatures = fFeatures;
    safeArray.cbElements = cbElements;
    safeArray.pvData = pvData;
    SAFEARRAYBOUND safeArrayBound = new SAFEARRAYBOUND();
    safeArray.rgsabound = safeArrayBound;
    safeArrayBound.cElements = cElements1;
    safeArrayBound.lLbound = lLbound1;
    OS.MoveMemory(pSafeArray, safeArray, SAFEARRAY.sizeof);
    // Create a variant in memory to hold the safearray
    long /*int*/ pVariant = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, Variant.sizeof);
    short vt = (short) (OLE.VT_ARRAY | OLE.VT_UI1);
    OS.MoveMemory(pVariant, new short[] {vt}, 2);
    OS.MoveMemory(pVariant + 8, new long /*int*/[] {pSafeArray}, OS.PTR_SIZEOF);
    // Create a by ref variant
    Variant variantByRef = new Variant(pVariant, (short) (OLE.VT_BYREF | OLE.VT_VARIANT));
    return variantByRef;
  }
 static String readSafeArray(Variant variantByRef) {
   // Read a safearray that contains data of
   // type VT_UI1 (unsigned shorts) which contains
   // a text stream.
   long /*int*/ pPostData = variantByRef.getByRef();
   short[] vt_type = new short[1];
   OS.MoveMemory(vt_type, pPostData, 2);
   String result = null;
   if (vt_type[0] == (short) (OLE.VT_BYREF | OLE.VT_VARIANT)) {
     int[] pVariant = new int[1];
     OS.MoveMemory(pVariant, pPostData + 8, 4);
     vt_type = new short[1];
     OS.MoveMemory(vt_type, pVariant[0], 2);
     if (vt_type[0] == (short) (OLE.VT_ARRAY | OLE.VT_UI1)) {
       long /*int*/[] pSafearray = new long /*int*/[1];
       OS.MoveMemory(pSafearray, pVariant[0] + 8, OS.PTR_SIZEOF);
       SAFEARRAY safeArray = new SAFEARRAY();
       OS.MoveMemory(safeArray, pSafearray[0], SAFEARRAY.sizeof);
       for (int i = 0; i < safeArray.cDims; i++) {
         int cchWideChar =
             OS.MultiByteToWideChar(CodePage, OS.MB_PRECOMPOSED, safeArray.pvData, -1, null, 0);
         if (cchWideChar == 0) return null;
         char[] lpWideCharStr = new char[cchWideChar - 1];
         OS.MultiByteToWideChar(
             CodePage,
             OS.MB_PRECOMPOSED,
             safeArray.pvData,
             -1,
             lpWideCharStr,
             lpWideCharStr.length);
         result = new String(lpWideCharStr);
       }
     }
   }
   return result;
 }