diff --git a/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs b/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs index bf1e4936..43a211e6 100644 --- a/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs +++ b/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; namespace OpenMcdf.Extensions.OLEProperties @@ -55,7 +56,13 @@ public void Write(BinaryWriter bw) private string GetName() { - return Encoding.GetEncoding(this.codePage).GetString(nameBytes); + + var result = Encoding.GetEncoding(this.codePage).GetString(nameBytes); + + result = result.Trim('\0'); + + return result; + } diff --git a/sources/OpenMcdf.Extensions/OLEProperties/OLEProperty.cs b/sources/OpenMcdf.Extensions/OLEProperties/OLEProperty.cs index 724b94bd..4dfcfcf2 100644 --- a/sources/OpenMcdf.Extensions/OLEProperties/OLEProperty.cs +++ b/sources/OpenMcdf.Extensions/OLEProperties/OLEProperty.cs @@ -33,10 +33,30 @@ public VTPropertyType VTType internal set; } + object value; + public object Value { - get; - set; + get + { + switch (VTType) + { + case VTPropertyType.VT_LPSTR: + case VTPropertyType.VT_LPWSTR: + if (value is string str && !String.IsNullOrEmpty(str)) + return str.Trim('\0'); + break; + default: + return this.value; + + } + + return this.value; + } + set + { + this.value = value; + } } public override bool Equals(object obj) diff --git a/sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs b/sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs index e651cbb3..438cf7ae 100644 --- a/sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs +++ b/sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs @@ -412,26 +412,86 @@ public override string ReadScalarValue(System.IO.BinaryReader br) uint size = br.ReadUInt32(); data = br.ReadBytes((int)size); - return Encoding.GetEncoding(codePage).GetString(data); + var result = Encoding.GetEncoding(codePage).GetString(data); + //result = result.Trim(new char[] { '\0' }); + + //if (this.codePage == CodePages.CP_WINUNICODE) + //{ + // result = result.Substring(0, result.Length - 2); + //} + //else + //{ + // result = result.Substring(0, result.Length - 1); + //} + + return result; } public override void WriteScalarValue(BinaryWriter bw, string pValue) { - data = Encoding.GetEncoding(codePage).GetBytes(pValue); - uint dataLength = (uint)data.Length; + //bool addNullTerminator = true; + + if (String.IsNullOrEmpty(pValue)) //|| String.IsNullOrEmpty(pValue.Trim(new char[] { '\0' }))) + { + bw.Write((uint)0); + } + else if (this.codePage == CodePages.CP_WINUNICODE) + { - // The string data must be null terminated, so add a null byte if there isn't one already - bool addNullTerminator = - dataLength == 0 || data[dataLength - 1] != '\0'; + data = Encoding.GetEncoding(codePage).GetBytes(pValue); + + //if (data.Length >= 2 && data[data.Length - 2] == '\0' && data[data.Length - 1] == '\0') + // addNullTerminator = false; + + uint dataLength = (uint)data.Length; + + //if (addNullTerminator) + dataLength += 2; // null terminator \u+0000 + + // var mod = dataLength % 4; // pad to multiple of 4 bytes + + bw.Write(dataLength); // datalength of string + null char (unicode) + bw.Write(data); // string + + + //if (addNullTerminator) + //{ + bw.Write('\0'); // first byte of null unicode char + bw.Write('\0'); // second byte of null unicode char + //} + + //for (int i = 0; i < (4 - mod); i++) // padding + // bw.Write('\0'); + } + else + { + data = Encoding.GetEncoding(codePage).GetBytes(pValue); + + //if (data.Length >= 1 && data[data.Length - 1] == '\0') + // addNullTerminator = false; + + uint dataLength = (uint)data.Length; + + //if (addNullTerminator) + dataLength += 1; // null terminator \u+0000 + + var mod = dataLength % 4; // pad to multiple of 4 bytes + + bw.Write(dataLength); // datalength of string + null char (unicode) + bw.Write(data); // string + + + //if (addNullTerminator) + //{ + bw.Write('\0'); // null terminator'\0' + //} + + //for (int i = 0; i < (4 - mod); i++) // padding + // bw.Write('\0'); + } - if (addNullTerminator) - dataLength += 1; - bw.Write(dataLength); - bw.Write(data); - if (addNullTerminator) - bw.Write((byte)0); } } @@ -449,8 +509,11 @@ public VT_LPWSTR_Property(VTPropertyType vType, int codePage, bool isVariant) : public override string ReadScalarValue(System.IO.BinaryReader br) { uint nChars = br.ReadUInt32(); - data = br.ReadBytes((int)(nChars * 2)); //WChar - return Encoding.Unicode.GetString(data); + data = br.ReadBytes((int)((nChars * 2) - 2)); //WChar- null terminator + var result = Encoding.Unicode.GetString(data); + //result = result.Trim(new char[] { '\0' }); + + return result; } public override void WriteScalarValue(BinaryWriter bw, string pValue) @@ -460,20 +523,25 @@ public override void WriteScalarValue(BinaryWriter bw, string pValue) // The written data length field is the number of characters (not bytes) and must include a null terminator // add a null terminator if there isn't one already var byteLength = data.Length; - bool addNullTerminator = - byteLength == 0 || data[byteLength - 1] != '\0' || data[byteLength - 2] != '\0'; - if (addNullTerminator) - byteLength += 2; + //bool addNullTerminator = + // byteLength == 0 || data[byteLength - 1] != '\0' || data[byteLength - 2] != '\0'; + + //if (addNullTerminator) + byteLength += 2; bw.Write((uint)byteLength / 2); bw.Write(data); - if (addNullTerminator) - { - bw.Write((byte)0); - bw.Write((byte)0); - } + //if (addNullTerminator) + //{ + bw.Write((byte)0); + bw.Write((byte)0); + //} + + //var mod = byteLength % 4; // pad to multiple of 4 bytes + //for (int i = 0; i < (4 - mod); i++) // padding + // bw.Write('\0'); } } @@ -645,7 +713,7 @@ public override void WriteScalarValue(BinaryWriter bw, object pValue) } } -#endregion + #endregion } } diff --git a/sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs b/sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs index 0263517d..a0e5b9a3 100644 --- a/sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs +++ b/sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs @@ -38,4 +38,4 @@ // http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html // and Compilers should be deterministic: same inputs generate same outputs // https://stackoverflow.com/questions/43019832/auto-versioning-in-visual-studio-2017-net-core/46985624#46985624 -[assembly: AssemblyVersion("2.3.1.0")] +[assembly: AssemblyVersion("2.3.2.0")] diff --git a/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs b/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs index eaa8b5f8..928958e0 100644 --- a/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs +++ b/sources/Test/OpenMcdf.Extensions.Test/OLEPropertiesExtensionsTest.cs @@ -143,14 +143,15 @@ public void Test_DOCUMENT_SUMMARY_INFO_MODIFY() // The company property should exist but be empty var companyProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_COMPANY"); - Assert.AreEqual("\0\0\0\0", companyProperty.Value); + Assert.AreEqual("", companyProperty.Value); // As a sanity check, check that the value of a property that we don't change remains the same var formatProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_PRESFORMAT"); - Assert.AreEqual("A4 Paper (210x297 mm)\0\0\0", formatProperty.Value); + Assert.AreEqual("A4 Paper (210x297 mm)", formatProperty.Value); // The manager property shouldn't exist, and we'll add it Assert.IsFalse(co.Properties.Any(prop => prop.PropertyName == "PIDDSI_MANAGER")); + var managerProp = co.NewProperty(VTPropertyType.VT_LPSTR, 0x0000000E, "PIDDSI_MANAGER"); co.AddProperty(managerProp); @@ -159,6 +160,7 @@ public void Test_DOCUMENT_SUMMARY_INFO_MODIFY() co.Save(dsiStream); cf.SaveAs(@"test_modify_summary.ppt"); + cf.Close() ; } using (CompoundFile cf = new CompoundFile("test_modify_summary.ppt")) @@ -166,13 +168,13 @@ public void Test_DOCUMENT_SUMMARY_INFO_MODIFY() var co = cf.RootStorage.GetStream("\u0005DocumentSummaryInformation").AsOLEPropertiesContainer(); var companyProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_COMPANY"); - Assert.AreEqual("My Company\0", companyProperty.Value); + Assert.AreEqual("My Company", companyProperty.Value); var formatProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_PRESFORMAT"); - Assert.AreEqual("A4 Paper (210x297 mm)\0\0\0", formatProperty.Value); + Assert.AreEqual("A4 Paper (210x297 mm)", formatProperty.Value); var managerProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_MANAGER"); - Assert.AreEqual("The Boss\0", managerProperty.Value); + Assert.AreEqual("The Boss", managerProperty.Value); } } @@ -306,11 +308,11 @@ public void Test_SUMMARY_INFO_MODIFY_LPWSTRING() var authorProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_AUTHOR"); Assert.AreEqual(VTPropertyType.VT_LPWSTR, authorProperty.VTType); - Assert.AreEqual("zkyiqpqoroxnbdwhnjfqroxlgylpbgcwuhjfifpkvycugvuecoputqgknnbs\0", authorProperty.Value); + Assert.AreEqual("zkyiqpqoroxnbdwhnjfqroxlgylpbgcwuhjfifpkvycugvuecoputqgknnbs", authorProperty.Value); var keyWordsProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_KEYWORDS"); Assert.AreEqual(VTPropertyType.VT_LPWSTR, keyWordsProperty.VTType); - Assert.AreEqual("abcdefghijk\0", keyWordsProperty.Value); + Assert.AreEqual("abcdefghijk", keyWordsProperty.Value); authorProperty.Value = "ABC"; keyWordsProperty.Value = ""; @@ -325,11 +327,11 @@ public void Test_SUMMARY_INFO_MODIFY_LPWSTRING() var authorProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_AUTHOR"); Assert.AreEqual(VTPropertyType.VT_LPWSTR, authorProperty.VTType); - Assert.AreEqual("ABC\0", authorProperty.Value); + Assert.AreEqual("ABC", authorProperty.Value); var keyWordsProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_KEYWORDS"); Assert.AreEqual(VTPropertyType.VT_LPWSTR, keyWordsProperty.VTType); - Assert.AreEqual("\0", keyWordsProperty.Value); + Assert.AreEqual("", keyWordsProperty.Value); } } @@ -359,16 +361,16 @@ public void Test_Read_Unicode_User_Properties_Dictionary() Assert.AreEqual((short)1200, propArray[0].Value); // String properties - Assert.AreEqual("A\0", propArray[1].PropertyName); - Assert.AreEqual("\0", propArray[1].Value); - Assert.AreEqual("AB\0", propArray[2].PropertyName); - Assert.AreEqual("X\0", propArray[2].Value); - Assert.AreEqual("ABC\0", propArray[3].PropertyName); - Assert.AreEqual("XY\0", propArray[3].Value); - Assert.AreEqual("ABCD\0", propArray[4].PropertyName); - Assert.AreEqual("XYZ\0", propArray[4].Value); - Assert.AreEqual("ABCDE\0", propArray[5].PropertyName); - Assert.AreEqual("XYZ!\0", propArray[5].Value); + Assert.AreEqual("A", propArray[1].PropertyName); + Assert.AreEqual("", propArray[1].Value); + Assert.AreEqual("AB", propArray[2].PropertyName); + Assert.AreEqual("X", propArray[2].Value); + Assert.AreEqual("ABC", propArray[3].PropertyName); + Assert.AreEqual("XY", propArray[3].Value); + Assert.AreEqual("ABCD", propArray[4].PropertyName); + Assert.AreEqual("XYZ", propArray[4].Value); + Assert.AreEqual("ABCDE", propArray[5].PropertyName); + Assert.AreEqual("XYZ!", propArray[5].Value); } } @@ -426,16 +428,16 @@ public void Test_DOCUMENT_SUMMARY_INFO_ADD_CUSTOM() Assert.AreEqual((short)-535, propArray[0].Value); // User properties - Assert.AreEqual("StringProperty\0", propArray[1].PropertyName); - Assert.AreEqual("Hello\0", propArray[1].Value); + Assert.AreEqual("StringProperty", propArray[1].PropertyName); + Assert.AreEqual("Hello", propArray[1].Value); Assert.AreEqual(VTPropertyType.VT_LPSTR, propArray[1].VTType); - Assert.AreEqual("BooleanProperty\0", propArray[2].PropertyName); + Assert.AreEqual("BooleanProperty", propArray[2].PropertyName); Assert.AreEqual(true, propArray[2].Value); Assert.AreEqual(VTPropertyType.VT_BOOL, propArray[2].VTType); - Assert.AreEqual("IntegerProperty\0", propArray[3].PropertyName); + Assert.AreEqual("IntegerProperty", propArray[3].PropertyName); Assert.AreEqual(3456, propArray[3].Value); Assert.AreEqual(VTPropertyType.VT_I4, propArray[3].VTType); - Assert.AreEqual("DateProperty\0", propArray[4].PropertyName); + Assert.AreEqual("DateProperty", propArray[4].PropertyName); Assert.AreEqual(testNow, propArray[4].Value); Assert.AreEqual(VTPropertyType.VT_FILETIME, propArray[4].VTType); }