/// <summary>
 /// Copy a global memory block into a file
 /// </summary>
 /// <param name="hGlobal">global memory block</param>
 /// <param name="destFileName">file to copy global memory into</param>
 private void CopyHGLOBALToFile(IntPtr hGlobal, string destFileName)
 {
     HGlobalLock globalMem = new HGlobalLock(hGlobal);
     using (globalMem)
     {
         FileStream destination =
             new FileStream(destFileName, FileMode.Create, FileAccess.ReadWrite);
         using (destination)
         {
             // use Win32 WriteFile so we can blast the entire unamanged memory
             // block in a single call (if we wanted to use managed file io
             // methods we would have to copy the entire memory block into
             // unmanaged memory first)
             uint bytesWritten;
             bool success = Kernel32.WriteFile(
                 destination.SafeFileHandle, globalMem.Memory, globalMem.Size.ToUInt32(),
                 out bytesWritten, IntPtr.Zero);
             if (!success)
             {
                 throw new Win32Exception(Marshal.GetLastWin32Error(),
                                           "Error occured attempting to write file: "
                                           + destination.Name);
             }
         }
     }
 }
        /// <summary>
        /// Utility function to extract an array of file contents file descriptors from
        /// an IDataObject instnace
        /// </summary>
        /// <param name="dataObject">data object to extract descriptors from</param>
        /// <returns>array of file descriptors</returns>
        public static FileDescriptor[] GetFileDescriptors(IDataObject dataObject)
        {
            // Use an OleDataObject
            OleDataObject oleDataObject = OleDataObject.CreateFrom(dataObject);
            if (oleDataObject == null)
            {
                const string message = "DataObject not valid for FileContents!";
                Debug.Fail(message);
                throw
                    new InvalidOperationException(message);
            }

            // Try to get the data as FileGroupDescriptorW then try to get it
            // as FileGroupDescriptor
            bool bFileNameIsWide;
            OleStgMediumHGLOBAL stgMedium = (OleStgMediumHGLOBAL)oleDataObject.GetData(
                                                                      DataFormatsEx.FileGroupDescriptorWFormat, TYMED.HGLOBAL);
            if (stgMedium != null)
            {
                bFileNameIsWide = true;
            }
            else
            {
                stgMedium = (OleStgMediumHGLOBAL)oleDataObject.GetData(
                                                      DataFormatsEx.FileGroupDescriptorFormat, TYMED.HGLOBAL);

                if (stgMedium != null)
                {
                    bFileNameIsWide = false;
                }
                else
                {
                    const string message = "File group descriptor not available!";
                    Debug.Fail(message);
                    throw
                        new InvalidOperationException(message);
                }
            }

            // Copy the descriptors
            using (stgMedium)
            {
                using (HGlobalLock globalMem = new HGlobalLock(stgMedium.Handle))
                {
                    // get a pointer to the count
                    IntPtr pCount = globalMem.Memory;

                    // determine the number of file descriptors
                    Int32 count = Marshal.ReadInt32(pCount);

                    // get a pointer to the descriptors
                    IntPtr pDescriptors =
                        new IntPtr(globalMem.Memory.ToInt32() + Marshal.SizeOf(count));

                    // allocate the array of structures that will be returned
                    FileDescriptor[] descriptors = new FileDescriptor[count];

                    // determine the sizes of the various data elements
                    const int FILENAME_BUFFER_SIZE = 260;
                    int headerSize = Marshal.SizeOf(typeof(FILEDESCRIPTOR_HEADER));
                    int fileNameSize = bFileNameIsWide ? FILENAME_BUFFER_SIZE * 2 : FILENAME_BUFFER_SIZE;
                    int totalSize = headerSize + fileNameSize;

                    // iterate through the memory block copying the FILEDESCRIPTOR structures
                    for (int i = 0; i < count; i++)
                    {
                        // determine the addresses of the various data elements
                        IntPtr pAddr = new IntPtr(pDescriptors.ToInt32() + (i * totalSize));
                        IntPtr pFileNameAddr = new IntPtr(pAddr.ToInt32() + headerSize);

                        // copy the header
                        descriptors[i].header = (FILEDESCRIPTOR_HEADER)
                                                Marshal.PtrToStructure(pAddr, typeof(FILEDESCRIPTOR_HEADER));

                        // copy the file name (use Unicode or Ansi depending upon descriptor type)
                        if (bFileNameIsWide)
                            descriptors[i].fileName = Marshal.PtrToStringUni(pFileNameAddr);
                        else
                            descriptors[i].fileName = Marshal.PtrToStringAnsi(pFileNameAddr);
                    }

                    // return the descriptors
                    return descriptors;
                }
            }
        }
        /// <summary>
        /// Create a cloned copy of the the passed storage medium. This method works via
        /// a combination of actually copying underling data and incrementing reference
        /// counts on embedded objects.
        /// </summary>
        /// <param name="stgmIn">storage medium in</param>
        /// <param name="stgmOut">storage medium out</param>
        /// <returns>HRESULT.S_OK if the medium was successfully cloned, various
        /// OLE error codes if an error occurs during the clone </returns>
        private int CloneStgMedium(STGMEDIUM stgmIn, ref STGMEDIUM stgmOut)
        {
            // copy storage type
            stgmOut.tymed = stgmIn.tymed;

            // copy or add ref count to the actual data
            switch (stgmIn.tymed)
            {
                // global memory blocks get copied
                case TYMED.HGLOBAL:
                    using (HGlobalLock input = new HGlobalLock(stgmIn.contents))
                        stgmOut.contents = input.Clone();
                    break;

                // COM interfaces get copied w/ their ref-count incremented
                case TYMED.ISTREAM:
                case TYMED.ISTORAGE:
                    stgmOut.contents = stgmIn.contents;
                    Marshal.AddRef(stgmOut.contents);
                    break;

                // don't know how to clone other storage medium types (return error)
                case TYMED.ENHMF:
                case TYMED.FILE:
                case TYMED.GDI:
                case TYMED.MFPICT:
                default:
                    return DV_E.TYMED;
            }

            // copy pUnkForRelease and add a reference count on it if there is one
            stgmOut.pUnkForRelease = stgmIn.pUnkForRelease;
            if (stgmOut.pUnkForRelease != IntPtr.Zero)
                Marshal.AddRef(stgmOut.pUnkForRelease);

            // return success
            return HRESULT.S_OK;
        }