/// <summary> /// Write the file contents to the specified path /// </summary> /// <param name="filePath">path to write file contents to</param> private void Write(string filePath) { // get the data in any format that it might be rendered in OleStgMedium storage = (OleStgMedium)oleDataObject.GetData( fileIndex, DataFormatsEx.FileContentsFormat, TYMED.ISTORAGE | TYMED.ISTREAM | TYMED.FILE | TYMED.HGLOBAL); // check for no storage if (storage == null) { throw new ApplicationException( "FileContents used unexpected storage type"); } // copy the data to a file (implemented differently for each storage type) using (storage) { // structured storage if (storage is OleStgMediumISTORAGE) { OleStgMediumISTORAGE istorage = storage as OleStgMediumISTORAGE; CopyStorageToFile(istorage.Storage, filePath); } // stream else if (storage is OleStgMediumISTREAM) { OleStgMediumISTREAM istream = storage as OleStgMediumISTREAM; CopyStreamToFile(istream.Stream, filePath); } // temporary file else if (storage is OleStgMediumFILE) { OleStgMediumFILE file = storage as OleStgMediumFILE; CopyFileToFile(file.Path, filePath); } // global memory else if (storage is OleStgMediumHGLOBAL) { OleStgMediumHGLOBAL hglobal = storage as OleStgMediumHGLOBAL; CopyHGLOBALToFile(hglobal.Handle, filePath); } else { throw new ApplicationException( "FileContents used unexpected storage type"); } } }
/// <summary> /// Retrieves the bytes representing the data in the HTML format of the HTMLData's /// IDataObject. /// </summary> /// <returns>The bytes representing the HTML in the IDataObject</returns> private byte[] GetHTMLBytes(OleStgMediumHGLOBAL storage) { // NOTE: Our theory about where/why the .NET implementation is failing // is that they probably assumed that the CF_HTML clipboard data was raw // Unicode and called Marshal.PtrToStringUni to convert it! CF_HTML is // in fact UTF8-encoded, so we need to first move it into a .NET byte // array and then UTF8-decode it. // get a non-movable pointer to the global memory IntPtr htmlBytes = Kernel32.GlobalLock(storage.Handle); if (htmlBytes == IntPtr.Zero) { Debug.Assert(false, "Failed to get the pointer for HTML Bytes"); return(null); } // move the unmanaged global memory block into a managed byte array try { // scan to see where the null terminator is byte b = 0; int byteCount = 0; // Winlive 267804: // In some instances office doesn't supply a \0 at the end of the body. // Now we add a check to make sure we don't AV int maxArraySize = (int)Kernel32.GlobalSize(htmlBytes).ToUInt32(); do { b = Marshal.ReadByte(htmlBytes, byteCount); byteCount++; } while (b != 0 && byteCount < maxArraySize); // allocate a byte array and copy the unmanged memory to it byte[] bytes = new byte[byteCount]; Marshal.Copy(htmlBytes, bytes, 0, byteCount); return(bytes); } finally { // always unlock the global memory handle Kernel32.GlobalUnlock(storage.Handle); } }
/// <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> /// Retrieves the bytes representing the data in the HTML format of the HTMLData's /// IDataObject. /// </summary> /// <returns>The bytes representing the HTML in the IDataObject</returns> private byte[] GetHTMLBytes(OleStgMediumHGLOBAL storage) { // NOTE: Our theory about where/why the .NET implementation is failing // is that they probably assumed that the CF_HTML clipboard data was raw // Unicode and called Marshal.PtrToStringUni to convert it! CF_HTML is // in fact UTF8-encoded, so we need to first move it into a .NET byte // array and then UTF8-decode it. // get a non-movable pointer to the global memory IntPtr htmlBytes = Kernel32.GlobalLock(storage.Handle); if (htmlBytes == IntPtr.Zero) { Debug.Assert(false, "Failed to get the pointer for HTML Bytes"); return null; } // move the unmanaged global memory block into a managed byte array try { // scan to see where the null terminator is byte b = 0; int byteCount = 0; // Winlive 267804: // In some instances office doesn't supply a \0 at the end of the body. // Now we add a check to make sure we don't AV int maxArraySize = (int)Kernel32.GlobalSize(htmlBytes).ToUInt32(); do { b = Marshal.ReadByte(htmlBytes, byteCount); byteCount++; } while (b != 0 && byteCount < maxArraySize); // allocate a byte array and copy the unmanged memory to it byte[] bytes = new byte[byteCount]; Marshal.Copy(htmlBytes, bytes, 0, byteCount); return bytes; } finally { // always unlock the global memory handle Kernel32.GlobalUnlock(storage.Handle); } }