/// <summary> /// Writes the property To an output stream. /// </summary> /// <param name="out1">The output stream To Write To.</param> /// <param name="codepage">The codepage To use for writing non-wide strings</param> /// <returns>the number of bytes written To the stream</returns> public int Write(Stream out1, int codepage) { int length = 0; long variantType = this.Type; /* Ensure that wide strings are written if the codepage is Unicode. */ if (codepage == (int)Constants.CP_UNICODE && variantType == Variant.VT_LPSTR) { variantType = Variant.VT_LPWSTR; } length += TypeWriter.WriteUIntToStream(out1, (uint)variantType); length += VariantSupport.Write(out1, variantType, this.Value, codepage); return(length); }
/// <summary> /// Writes the property Set To an output stream. /// </summary> /// <param name="out1">the output stream To Write the section To</param> public virtual void Write(Stream out1) { /* Write the number of sections in this property Set stream. */ int nrSections = sections.Count; int length = 0; /* Write the property Set's header. */ length += TypeWriter.WriteToStream(out1, (short)ByteOrder); length += TypeWriter.WriteToStream(out1, (short)Format); length += TypeWriter.WriteToStream(out1, OSVersion); length += TypeWriter.WriteToStream(out1, ClassID); length += TypeWriter.WriteToStream(out1, nrSections); int offset = OFFSet_HEADER; /* Write the section list, i.e. the references To the sections. Each * entry in the section list consist of the section's class ID and the * section's offset relative To the beginning of the stream. */ offset += nrSections * (ClassID.Length + LittleEndianConsts.INT_SIZE); int sectionsBegin = offset; for (IEnumerator i = sections.GetEnumerator(); i.MoveNext();) { MutableSection s = (MutableSection)i.Current; ClassID formatID = s.FormatID; if (formatID == null) { throw new NoFormatIDException(); } length += TypeWriter.WriteToStream(out1, s.FormatID); length += TypeWriter.WriteUIntToStream(out1, (uint)offset); offset += s.Size; } /* Write the sections themselves. */ offset = sectionsBegin; for (IEnumerator i = sections.GetEnumerator(); i.MoveNext();) { MutableSection s = (MutableSection)i.Current; offset += s.Write(out1); } }
/// <summary> /// Writes the section's dictionary /// </summary> /// <param name="out1">The output stream To Write To.</param> /// <param name="dictionary">The dictionary.</param> /// <param name="codepage">The codepage to be used to Write the dictionary items.</param> /// <returns>The number of bytes written</returns> /// <remarks> /// see MSDN KB: http://msdn.microsoft.com/en-us/library/aa380065(VS.85).aspx /// </remarks> private static int WriteDictionary(Stream out1, IDictionary dictionary, int codepage) { int length = TypeWriter.WriteUIntToStream(out1, (uint)dictionary.Count); for (IEnumerator i = dictionary.Keys.GetEnumerator(); i.MoveNext();) { long key = Convert.ToInt64(i.Current, CultureInfo.InvariantCulture); String value = (String)dictionary[key]; //tony qu added: some key is int32 instead of int64 if (value == null) { value = (String)dictionary[(int)key]; } if (codepage == (int)Constants.CP_UNICODE) { /* Write the dictionary item in Unicode. */ int sLength = value.Length + 1; if (sLength % 2 == 1) { sLength++; } length += TypeWriter.WriteUIntToStream(out1, (uint)key); length += TypeWriter.WriteUIntToStream(out1, (uint)sLength); byte[] ca = Encoding.GetEncoding(codepage).GetBytes(value); for (int j = 0; j < ca.Length; j += 2) //Tony qu fixed the bug { out1.WriteByte(ca[j + 1]); out1.WriteByte(ca[j]); length += 2; } sLength -= value.Length; while (sLength > 0) { out1.WriteByte(0x00); out1.WriteByte(0x00); length += 2; sLength--; } } else { /* Write the dictionary item in another codepage than * Unicode. */ length += TypeWriter.WriteUIntToStream(out1, (uint)key); length += TypeWriter.WriteUIntToStream(out1, (uint)value.Length + 1); try { byte[] ba = Encoding.GetEncoding(codepage).GetBytes(value); for (int j = 0; j < ba.Length; j++) { out1.WriteByte(ba[j]); length++; } } catch (Exception ex) { throw new IllegalPropertySetDataException(ex); } out1.WriteByte(0x00); length++; } } return(length); }
/// <summary> /// Writes this section into an output stream. /// Internally this is done by writing into three byte array output /// streams: one for the properties, one for the property list and one for /// the section as such. The two former are Appended To the latter when they /// have received all their data. /// </summary> /// <param name="out1">The stream To Write into.</param> /// <returns>The number of bytes written, i.e. the section's size.</returns> public int Write(Stream out1) { /* Check whether we have alReady generated the bytes making out the * section. */ if (!dirty && sectionBytes != null) { out1.Write(sectionBytes, 0, sectionBytes.Length); return(sectionBytes.Length); } /* The properties are written To this stream. */ using (MemoryStream propertyStream = new MemoryStream()) { /* The property list is established here. After each property that has * been written To "propertyStream", a property list entry is written To * "propertyListStream". */ using (MemoryStream propertyListStream = new MemoryStream()) { /* Maintain the current position in the list. */ int position = 0; /* Increase the position variable by the size of the property list so * that it points behind the property list and To the beginning of the * properties themselves. */ position += 2 * LittleEndianConsts.INT_SIZE + PropertyCount * 2 * LittleEndianConsts.INT_SIZE; /* Writing the section's dictionary it tricky. If there is a dictionary * (property 0) the codepage property (property 1) must be Set, Too. */ int codepage = -1; if (GetProperty(PropertyIDMap.PID_DICTIONARY) != null) { Object p1 = GetProperty(PropertyIDMap.PID_CODEPAGE); if (p1 != null) { if (!(p1 is int)) { throw new IllegalPropertySetDataException ("The codepage property (ID = 1) must be an " + "Integer object."); } } else { /* Warning: The codepage property is not Set although a * dictionary is present. In order To cope with this problem we * Add the codepage property and Set it To Unicode. */ SetProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, (int)Constants.CP_UNICODE); } codepage = Codepage; } /* Sort the property list by their property IDs: */ preprops.Sort(new PropertyComparer()); /* Write the properties and the property list into their respective * streams: */ for (int i = 0; i < preprops.Count; i++) { MutableProperty p = (MutableProperty)preprops[i]; long id = p.ID; /* Write the property list entry. */ TypeWriter.WriteUIntToStream(propertyListStream, (uint)p.ID); TypeWriter.WriteUIntToStream(propertyListStream, (uint)position); /* If the property ID is not equal 0 we Write the property and all * is fine. However, if it Equals 0 we have To Write the section's * dictionary which has an implicit type only and an explicit * value. */ if (id != 0) { /* Write the property and update the position To the next * property. */ position += p.Write(propertyStream, Codepage); } else { if (codepage == -1) { throw new IllegalPropertySetDataException ("Codepage (property 1) is undefined."); } position += WriteDictionary(propertyStream, dictionary, codepage); } } propertyStream.Flush(); propertyListStream.Flush(); /* Write the section: */ byte[] pb1 = propertyListStream.ToArray(); byte[] pb2 = propertyStream.ToArray(); /* Write the section's Length: */ TypeWriter.WriteToStream(out1, LittleEndianConsts.INT_SIZE * 2 + pb1.Length + pb2.Length); /* Write the section's number of properties: */ TypeWriter.WriteToStream(out1, PropertyCount); /* Write the property list: */ out1.Write(pb1, 0, pb1.Length); /* Write the properties: */ out1.Write(pb2, 0, pb2.Length); int streamLength = LittleEndianConsts.INT_SIZE * 2 + pb1.Length + pb2.Length; return(streamLength); } } }
/// <summary> /// Writes a variant value To an output stream. This method ensures that /// always a multiple of 4 bytes is written. /// If the codepage is UTF-16, which is encouraged, strings /// <strong>must</strong> always be written as {@link Variant#VT_LPWSTR} /// strings, not as {@link Variant#VT_LPSTR} strings. This method ensure this /// by Converting strings appropriately, if needed. /// </summary> /// <param name="out1">The stream To Write the value To.</param> /// <param name="type">The variant's type.</param> /// <param name="value">The variant's value.</param> /// <param name="codepage">The codepage To use To Write non-wide strings</param> /// <returns>The number of entities that have been written. In many cases an /// "entity" is a byte but this is not always the case.</returns> public static int Write(Stream out1, long type, Object value, int codepage) { int length = 0; switch ((int)type) { case Variant.VT_BOOL: { int trueOrFalse; if ((bool)value) { trueOrFalse = 1; } else { trueOrFalse = 0; } length = TypeWriter.WriteUIntToStream(out1, (uint)trueOrFalse); break; } case Variant.VT_LPSTR: { if (codepage == 0) { throw new ArgumentOutOfRangeException("codepage"); } byte[] bytes = (codepage == -1 ? Encoding.UTF8.GetBytes((string)value) : Encoding.GetEncoding(codepage).GetBytes((string)value)); length = TypeWriter.WriteUIntToStream(out1, (uint)bytes.Length + 1); byte[] b = new byte[bytes.Length + 1]; Array.Copy(bytes, 0, b, 0, bytes.Length); b[b.Length - 1] = 0x00; out1.Write(b, 0, b.Length); length += b.Length; break; } case Variant.VT_LPWSTR: { int nrOfChars = ((String)value).Length + 1; length += TypeWriter.WriteUIntToStream(out1, (uint)nrOfChars); char[] s = Util.Pad4((String)value); for (int i = 0; i < s.Length; i++) { int high = ((s[i] & 0x0000ff00) >> 8); int low = (s[i] & 0x000000ff); byte highb = (byte)high; byte lowb = (byte)low; out1.WriteByte(lowb); out1.WriteByte(highb); length += 2; } out1.WriteByte(0x00); out1.WriteByte(0x00); length += 2; break; } case Variant.VT_CF: { byte[] b = (byte[])value; out1.Write(b, 0, b.Length); length = b.Length; break; } case Variant.VT_EMPTY: { TypeWriter.WriteUIntToStream(out1, Variant.VT_EMPTY); length = LittleEndianConsts.INT_SIZE; break; } case Variant.VT_I2: { short x; try { x = Convert.ToInt16(value, CultureInfo.InvariantCulture); }catch (OverflowException) { x = (short)((int)value); } TypeWriter.WriteToStream(out1, x); length = LittleEndianConsts.SHORT_SIZE; break; } case Variant.VT_I4: { if (!(value is int)) { throw new Exception("Could not cast an object To " + "int" + ": " + value.GetType().Name + ", " + value.ToString()); } length += TypeWriter.WriteToStream(out1, (int)value); break; } case Variant.VT_I8: { TypeWriter.WriteToStream(out1, Convert.ToInt64(value, CultureInfo.CurrentCulture)); length = LittleEndianConsts.LONG_SIZE; break; } case Variant.VT_R8: { length += TypeWriter.WriteToStream(out1, (Double)value); break; } case Variant.VT_FILETIME: { long filetime; if (value != null) { filetime = Util.DateToFileTime((DateTime)value); } else { filetime = 0; } int high = (int)((filetime >> 32) & 0x00000000FFFFFFFFL); int low = (int)(filetime & 0x00000000FFFFFFFFL); length += TypeWriter.WriteUIntToStream (out1, (uint)(0x0000000FFFFFFFFL & low)); length += TypeWriter.WriteUIntToStream (out1, (uint)(0x0000000FFFFFFFFL & high)); break; } default: { /* The variant type is not supported yet. However, if the value * is a byte array we can Write it nevertheless. */ if (value is byte[]) { byte[] b = (byte[])value; out1.Write(b, 0, b.Length); length = b.Length; WriteUnsupportedTypeMessage (new WritingNotSupportedException(type, value)); } else { throw new WritingNotSupportedException(type, value); } break; } } return(length); }