diff --git a/OpenMcdf.Ole.Tests/2custom.doc b/OpenMcdf.Ole.Tests/2custom.doc
new file mode 100644
index 00000000..0d53d3a1
Binary files /dev/null and b/OpenMcdf.Ole.Tests/2custom.doc differ
diff --git a/OpenMcdf.Ole.Tests/CLSIDPropertyTest.file b/OpenMcdf.Ole.Tests/CLSIDPropertyTest.file
new file mode 100644
index 00000000..27b609bf
Binary files /dev/null and b/OpenMcdf.Ole.Tests/CLSIDPropertyTest.file differ
diff --git a/OpenMcdf.Ole.Tests/Issue134.cfs b/OpenMcdf.Ole.Tests/Issue134.cfs
new file mode 100644
index 00000000..f53f9b4f
Binary files /dev/null and b/OpenMcdf.Ole.Tests/Issue134.cfs differ
diff --git a/OpenMcdf.Ole.Tests/OlePropertiesExtensionsTests.cs b/OpenMcdf.Ole.Tests/OlePropertiesExtensionsTests.cs
new file mode 100644
index 00000000..6537e36a
--- /dev/null
+++ b/OpenMcdf.Ole.Tests/OlePropertiesExtensionsTests.cs
@@ -0,0 +1,440 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Diagnostics;
+
+namespace OpenMcdf.Ole.Tests;
+
+///
+/// Summary description for UnitTest1
+///
+[TestClass]
+public class OlePropertiesExtensionsTests
+{
+ [TestMethod]
+ public void ReadSummaryInformation()
+ {
+ using var cf = RootStorage.OpenRead("_Test.ppt");
+ using CfbStream stream = cf.OpenStream("\u0005SummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ foreach (OleProperty p in co.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+ }
+
+ [TestMethod]
+ public void ReadDocumentSummaryInformation()
+ {
+ using var cf = RootStorage.OpenRead("_Test.ppt");
+ using CfbStream stream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ foreach (OleProperty p in co.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+ }
+
+ [TestMethod]
+ public void ReadThenWriteDocumentSummaryInformation()
+ {
+ using var cf = RootStorage.OpenRead("_Test.ppt");
+ using CfbStream stream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ using var cf2 = RootStorage.CreateInMemory();
+ using CfbStream stream2 = cf2.CreateStream("\u0005DocumentSummaryInformation");
+ co.Save(stream2);
+ }
+
+ // Modify some document summary information properties, save to a file, and then validate the expected results
+ [TestMethod]
+ public void ModifyDocumentSummaryInformation()
+ {
+ using MemoryStream modifiedStream = new();
+ using (FileStream stream = File.OpenRead("_Test.ppt"))
+ stream.CopyTo(modifiedStream);
+
+ // Verify initial properties, and then create a modified document
+ using (var cf = RootStorage.Open(modifiedStream, StorageModeFlags.LeaveOpen))
+ {
+ using CfbStream dsiStream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(dsiStream);
+
+ // The company property should exist but be empty
+ OleProperty companyProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_COMPANY");
+ Assert.AreEqual("", companyProperty.Value);
+
+ // As a sanity check, check that the value of a property that we don't change remains the same
+ OleProperty formatProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_PRESFORMAT");
+ 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"));
+
+ OleProperty managerProp = co.CreateProperty(VTPropertyType.VT_LPSTR, 0x0000000E, "PIDDSI_MANAGER");
+ co.Add(managerProp);
+
+ companyProperty.Value = "My Company";
+ managerProp.Value = "The Boss";
+
+ co.Save(dsiStream);
+ }
+
+ using (var cf = RootStorage.Open(modifiedStream))
+ {
+ using CfbStream stream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ OleProperty companyProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_COMPANY");
+ Assert.AreEqual("My Company", companyProperty.Value);
+
+ OleProperty formatProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_PRESFORMAT");
+ Assert.AreEqual("A4 Paper (210x297 mm)", formatProperty.Value);
+
+ OleProperty managerProperty = co.Properties.First(prop => prop.PropertyName == "PIDDSI_MANAGER");
+ Assert.AreEqual("The Boss", managerProperty.Value);
+ }
+ }
+
+ [TestMethod]
+ public void ReadSummaryInformationUtf8()
+ {
+ // Regression test for #33
+ using var cf = RootStorage.Open("wstr_presets.doc", FileMode.Open);
+ using CfbStream stream = cf.OpenStream("\u0005SummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ foreach (OleProperty p in co.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+
+ using CfbStream stream2 = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co2 = new(stream2);
+
+ foreach (OleProperty p in co2.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+ }
+
+ [TestMethod]
+ public void ReadSummaryInformationUtf8Part2()
+ {
+ // Regression test for #34
+ using var cf = RootStorage.OpenRead("2custom.doc");
+ using CfbStream stream = cf.OpenStream("\u0005SummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ foreach (OleProperty p in co.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+
+ using CfbStream stream2 = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co2 = new(stream2);
+
+ foreach (OleProperty p in co2.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+
+ if (co2.UserDefinedProperties is not null)
+ {
+ foreach (OleProperty p in co2.UserDefinedProperties.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+ }
+ }
+
+ [TestMethod]
+ public void SummaryInformationReadLpwstring()
+ {
+ using var cf = RootStorage.OpenRead("english.presets.doc");
+ using CfbStream stream = cf.OpenStream("\u0005SummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ foreach (OleProperty p in co.Properties)
+ {
+ Debug.WriteLine(p);
+ }
+ }
+
+ // Test that we can modify an LPWSTR property, and the value is null terminated as required
+ [TestMethod]
+ public void SummaryInformationModifyLpwstring()
+ {
+ using MemoryStream modifiedStream = new();
+ using (FileStream stream = File.OpenRead("wstr_presets.doc"))
+ stream.CopyTo(modifiedStream);
+
+ // Modify some LPWSTR properties, and save to a new file
+ using (var cf = RootStorage.Open(modifiedStream, StorageModeFlags.LeaveOpen))
+ {
+ using CfbStream dsiStream = cf.OpenStream("\u0005SummaryInformation");
+ OlePropertiesContainer co = new(dsiStream);
+
+ OleProperty authorProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_AUTHOR");
+ Assert.AreEqual(VTPropertyType.VT_LPWSTR, authorProperty.VTType);
+ Assert.AreEqual("zkyiqpqoroxnbdwhnjfqroxlgylpbgcwuhjfifpkvycugvuecoputqgknnbs", authorProperty.Value);
+
+ OleProperty keyWordsProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_KEYWORDS");
+ Assert.AreEqual(VTPropertyType.VT_LPWSTR, keyWordsProperty.VTType);
+ Assert.AreEqual("abcdefghijk", keyWordsProperty.Value);
+
+ authorProperty.Value = "ABC";
+ keyWordsProperty.Value = "";
+ co.Save(dsiStream);
+ }
+
+ // Open the new file and check for the expected values
+ using (var cf = RootStorage.Open(modifiedStream))
+ {
+ using CfbStream stream = cf.OpenStream("\u0005SummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ OleProperty authorProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_AUTHOR");
+ Assert.AreEqual(VTPropertyType.VT_LPWSTR, authorProperty.VTType);
+ Assert.AreEqual("ABC", authorProperty.Value);
+
+ OleProperty keyWordsProperty = co.Properties.First(prop => prop.PropertyName == "PIDSI_KEYWORDS");
+ Assert.AreEqual(VTPropertyType.VT_LPWSTR, keyWordsProperty.VTType);
+ Assert.AreEqual("", keyWordsProperty.Value);
+ }
+ }
+
+ // winUnicodeDictionary.doc contains a UserProperties section with the CP_WINUNICODE codepage, and LPWSTR string properties
+ [TestMethod]
+ public void TestReadUnicodeUserPropertiesDictionary()
+ {
+ using var cf = RootStorage.OpenRead("winUnicodeDictionary.doc");
+ CfbStream dsiStream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(dsiStream);
+ OlePropertiesContainer? userProps = co.UserDefinedProperties;
+
+ // CodePage should be CP_WINUNICODE (1200)
+ Assert.AreEqual(1200, userProps.Context.CodePage);
+
+ // There should be 5 property names present, and 6 properties (the properties include the code page)
+ Assert.AreEqual(5, userProps.PropertyNames.Count);
+ Assert.AreEqual(6, userProps.Properties.Count);
+
+ // Check for expected names and values
+ OleProperty[] propArray = userProps.Properties.ToArray();
+
+ // CodePage prop
+ Assert.AreEqual(1u, propArray[0].PropertyIdentifier);
+ Assert.AreEqual("0x00000001", propArray[0].PropertyName);
+ Assert.AreEqual((short)1200, propArray[0].Value);
+
+ // String properties
+ 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);
+ }
+
+ // Test that we can add user properties of various types and then read them back
+ [TestMethod]
+ public void AddDocumentSummaryInformationCustomInfo()
+ {
+ using MemoryStream modifiedStream = new();
+ using (FileStream stream = File.OpenRead("english.presets.doc"))
+ stream.CopyTo(modifiedStream);
+
+ // Test value for a VT_FILETIME property
+ DateTime testNow = DateTime.UtcNow;
+
+ // english.presets.doc has a user defined property section, but no properties other than the codepage
+ using (var cf = RootStorage.Open(modifiedStream, StorageModeFlags.LeaveOpen))
+ {
+ using CfbStream dsiStream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(dsiStream);
+ OlePropertiesContainer? userProperties = co.UserDefinedProperties;
+
+ userProperties.PropertyNames[2] = "StringProperty";
+ userProperties.PropertyNames[3] = "BooleanProperty";
+ userProperties.PropertyNames[4] = "IntegerProperty";
+ userProperties.PropertyNames[5] = "DateProperty";
+ userProperties.PropertyNames[6] = "DoubleProperty";
+
+ OleProperty stringProperty = co.CreateProperty(VTPropertyType.VT_LPSTR, 2);
+ stringProperty.Value = "Hello";
+ userProperties.Add(stringProperty);
+
+ OleProperty booleanProperty = co.CreateProperty(VTPropertyType.VT_BOOL, 3);
+ booleanProperty.Value = true;
+ userProperties.Add(booleanProperty);
+
+ OleProperty integerProperty = co.CreateProperty(VTPropertyType.VT_I4, 4);
+ integerProperty.Value = 3456;
+ userProperties.Add(integerProperty);
+
+ OleProperty timeProperty = co.CreateProperty(VTPropertyType.VT_FILETIME, 5);
+ timeProperty.Value = testNow;
+ userProperties.Add(timeProperty);
+
+ OleProperty doubleProperty = co.CreateProperty(VTPropertyType.VT_R8, 6);
+ doubleProperty.Value = 1.234567d;
+ userProperties.Add(doubleProperty);
+
+ co.Save(dsiStream);
+ }
+
+ using (var cf = RootStorage.Open(modifiedStream))
+ {
+ using CfbStream stream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(stream);
+ OleProperty[] propArray = co.UserDefinedProperties.Properties.ToArray();
+ Assert.AreEqual(6, propArray.Length);
+
+ // CodePage prop
+ Assert.AreEqual(1u, propArray[0].PropertyIdentifier);
+ Assert.AreEqual("0x00000001", propArray[0].PropertyName);
+ Assert.AreEqual((short)-535, propArray[0].Value);
+
+ // User properties
+ Assert.AreEqual("StringProperty", propArray[1].PropertyName);
+ Assert.AreEqual("Hello", propArray[1].Value);
+ Assert.AreEqual(VTPropertyType.VT_LPSTR, propArray[1].VTType);
+ Assert.AreEqual("BooleanProperty", propArray[2].PropertyName);
+ Assert.AreEqual(true, propArray[2].Value);
+ Assert.AreEqual(VTPropertyType.VT_BOOL, propArray[2].VTType);
+ Assert.AreEqual("IntegerProperty", propArray[3].PropertyName);
+ Assert.AreEqual(3456, propArray[3].Value);
+ Assert.AreEqual(VTPropertyType.VT_I4, propArray[3].VTType);
+ Assert.AreEqual("DateProperty", propArray[4].PropertyName);
+ Assert.AreEqual(testNow, propArray[4].Value);
+ Assert.AreEqual(VTPropertyType.VT_FILETIME, propArray[4].VTType);
+ Assert.AreEqual("DoubleProperty", propArray[5].PropertyName);
+ Assert.AreEqual(1.234567d, propArray[5].Value);
+ Assert.AreEqual(VTPropertyType.VT_R8, propArray[5].VTType);
+ }
+ }
+
+ // Try to read a document which contains Vector/String properties
+ // refs https://github.com/ironfede/openmcdf/issues/98
+ [TestMethod]
+ public void ReadLpwstringVector()
+ {
+ using var cf = RootStorage.OpenRead("SampleWorkBook_bug98.xls");
+ using CfbStream stream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ OleProperty docPartsProperty = co.Properties.FirstOrDefault(property => property.PropertyIdentifier == 13); //13 == PIDDSI_DOCPARTS
+
+ var docPartsValues = docPartsProperty.Value as IList;
+ Assert.AreEqual(3, docPartsValues.Count);
+ Assert.AreEqual("Sheet1", docPartsValues[0]);
+ Assert.AreEqual("Sheet2", docPartsValues[1]);
+ Assert.AreEqual("Sheet3", docPartsValues[2]);
+ }
+
+ [TestMethod]
+ public void ReadClsidProperty()
+ {
+ Guid guid = new("15891a95-bf6e-4409-b7d0-3a31c391fa31");
+ using var cf = RootStorage.OpenRead("CLSIDPropertyTest.file");
+ using CfbStream stream = cf.OpenStream("\u0005C3teagxwOttdbfkuIaamtae3Ie");
+ OlePropertiesContainer co = new(stream);
+ OleProperty clsidProp = co.Properties.First(x => x.PropertyName == "DocumentID");
+ Assert.AreEqual(guid, clsidProp.Value);
+ }
+
+ // The test file 'report.xls' contains a DocumentSummaryInfo section, but no user defined properties.
+ // This tests adding a new user defined properties section to the existing DocumentSummaryInfo.
+ [TestMethod]
+ public void AddUserDefinedPropertiesSection()
+ {
+ using MemoryStream modifiedStream = new();
+ using (FileStream stream = File.OpenRead("report.xls"))
+ stream.CopyTo(modifiedStream);
+
+ using (var cf = RootStorage.Open(modifiedStream, StorageModeFlags.LeaveOpen))
+ {
+ using CfbStream dsiStream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(dsiStream);
+
+ Assert.IsNull(co.UserDefinedProperties);
+
+ OlePropertiesContainer newUserDefinedProperties = co.CreateUserDefinedProperties(65001); // 65001 - UTF-8
+
+ newUserDefinedProperties.PropertyNames[2] = "MyCustomProperty";
+
+ OleProperty CreateProperty = co.CreateProperty(VTPropertyType.VT_LPSTR, 2);
+ CreateProperty.Value = "Testing";
+ newUserDefinedProperties.Add(CreateProperty);
+
+ co.Save(dsiStream);
+ }
+
+ using (var cf = RootStorage.Open(modifiedStream))
+ {
+ using CfbStream stream = cf.OpenStream("\u0005DocumentSummaryInformation");
+ OlePropertiesContainer co = new(stream);
+
+ // User defined properties should be present now
+ Assert.IsNotNull(co.UserDefinedProperties);
+ Assert.AreEqual(65001, co.UserDefinedProperties.Context.CodePage);
+
+ // And the expected properties should the there
+ OleProperty[] propArray = co.UserDefinedProperties.Properties.ToArray();
+ Assert.AreEqual(propArray.Length, 2);
+
+ // CodePage prop
+ Assert.AreEqual(1u, propArray[0].PropertyIdentifier);
+ Assert.AreEqual("0x00000001", propArray[0].PropertyName);
+ Assert.AreEqual((short)-535, propArray[0].Value);
+
+ // User properties
+ Assert.AreEqual("MyCustomProperty", propArray[1].PropertyName);
+ Assert.AreEqual("Testing", propArray[1].Value);
+ Assert.AreEqual(VTPropertyType.VT_LPSTR, propArray[1].VTType);
+ }
+ }
+
+ // A test for the issue described in https://github.com/ironfede/openmcdf/issues/134 where modifying an AppSpecific stream
+ // removes any already-existing Dictionary property
+ [TestMethod]
+ public void TestRetainDictionaryPropertyInAppSpecificStreams()
+ {
+ using MemoryStream modifiedStream = new();
+ using (FileStream stream = File.OpenRead("Issue134.cfs"))
+ stream.CopyTo(modifiedStream);
+
+ Dictionary expectedPropertyNames = new()
+ {
+ [2] = "Document Number",
+ [3] = "Revision",
+ [4] = "Project Name"
+ };
+
+ using (var cf = RootStorage.Open(modifiedStream, StorageModeFlags.LeaveOpen))
+ {
+ using CfbStream testStream = cf.OpenStream("Issue134");
+ OlePropertiesContainer co = new(testStream);
+
+ CollectionAssert.AreEqual(expectedPropertyNames, co.PropertyNames);
+
+ // Write test file
+ co.Save(testStream);
+ }
+
+ // Open test file, and check that the property names are still as expected.
+ using (var cf = RootStorage.Open(modifiedStream))
+ {
+ using CfbStream testStream = cf.OpenStream("Issue134");
+ OlePropertiesContainer co = new(testStream);
+
+ CollectionAssert.AreEqual(expectedPropertyNames, co.PropertyNames);
+ }
+ }
+}
diff --git a/OpenMcdf.Ole.Tests/OpenMcdf.Ole.Tests.csproj b/OpenMcdf.Ole.Tests/OpenMcdf.Ole.Tests.csproj
new file mode 100644
index 00000000..59b58f11
--- /dev/null
+++ b/OpenMcdf.Ole.Tests/OpenMcdf.Ole.Tests.csproj
@@ -0,0 +1,54 @@
+
+
+
+ net48;net8.0
+ Exe
+ 12.0
+ enable
+ enable
+
+ false
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ PreserveNewest
+
+
+
+
diff --git a/OpenMcdf.Ole.Tests/SampleWorkBook_bug98.xls b/OpenMcdf.Ole.Tests/SampleWorkBook_bug98.xls
new file mode 100644
index 00000000..063c04f5
Binary files /dev/null and b/OpenMcdf.Ole.Tests/SampleWorkBook_bug98.xls differ
diff --git a/OpenMcdf.Ole.Tests/_Test.ppt b/OpenMcdf.Ole.Tests/_Test.ppt
new file mode 100644
index 00000000..4b5a7692
Binary files /dev/null and b/OpenMcdf.Ole.Tests/_Test.ppt differ
diff --git a/OpenMcdf.Ole.Tests/english.presets.doc b/OpenMcdf.Ole.Tests/english.presets.doc
new file mode 100644
index 00000000..3fdf7297
Binary files /dev/null and b/OpenMcdf.Ole.Tests/english.presets.doc differ
diff --git a/OpenMcdf.Ole.Tests/report.xls b/OpenMcdf.Ole.Tests/report.xls
new file mode 100644
index 00000000..7b667a50
Binary files /dev/null and b/OpenMcdf.Ole.Tests/report.xls differ
diff --git a/OpenMcdf.Ole.Tests/winUnicodeDictionary.doc b/OpenMcdf.Ole.Tests/winUnicodeDictionary.doc
new file mode 100644
index 00000000..2ba5153c
Binary files /dev/null and b/OpenMcdf.Ole.Tests/winUnicodeDictionary.doc differ
diff --git a/OpenMcdf.Ole.Tests/wstr_presets.doc b/OpenMcdf.Ole.Tests/wstr_presets.doc
new file mode 100644
index 00000000..884da6af
Binary files /dev/null and b/OpenMcdf.Ole.Tests/wstr_presets.doc differ
diff --git a/OpenMcdf.Ole/CodePages.cs b/OpenMcdf.Ole/CodePages.cs
index 1f5a22c7..fdf34886 100644
--- a/OpenMcdf.Ole/CodePages.cs
+++ b/OpenMcdf.Ole/CodePages.cs
@@ -1,4 +1,6 @@
-namespace OpenMcdf.Ole;
+using System.Text;
+
+namespace OpenMcdf.Ole;
internal static class CodePages
{
diff --git a/OpenMcdf.Ole/DocumentSummaryInfoPropertyFactory.cs b/OpenMcdf.Ole/DocumentSummaryInfoPropertyFactory.cs
index 551e55ed..828dbfa6 100644
--- a/OpenMcdf.Ole/DocumentSummaryInfoPropertyFactory.cs
+++ b/OpenMcdf.Ole/DocumentSummaryInfoPropertyFactory.cs
@@ -11,6 +11,6 @@ protected override ITypedPropertyValue CreateLpstrProperty(VTPropertyType vType,
if (propertyIdentifier is 0x0000000C or 0x0000000D)
return new VT_Unaligned_LPSTR_Property(vType, codePage, isVariant);
- return CreateLpstrProperty(vType, codePage, propertyIdentifier, isVariant);
+ return base.CreateLpstrProperty(vType, codePage, propertyIdentifier, isVariant);
}
}
diff --git a/OpenMcdf.Ole/OlePropertiesContainer.cs b/OpenMcdf.Ole/OlePropertiesContainer.cs
index 371730d0..f21fea91 100644
--- a/OpenMcdf.Ole/OlePropertiesContainer.cs
+++ b/OpenMcdf.Ole/OlePropertiesContainer.cs
@@ -1,4 +1,6 @@
-namespace OpenMcdf.Ole;
+using System.Text;
+
+namespace OpenMcdf.Ole;
public enum ContainerType
{
@@ -42,8 +44,7 @@ public OlePropertiesContainer(CfbStream cfStream)
this.cfStream = cfStream;
- cfStream.Position = 0;
- using BinaryReader reader = new(cfStream);
+ using BinaryReader reader = new(cfStream, Encoding.Unicode, true);
pStream.Read(reader);
if (pStream.FMTID0 == FormatIdentifiers.SummaryInformation)
diff --git a/OpenMcdf.Ole/PropertyFactory.cs b/OpenMcdf.Ole/PropertyFactory.cs
index f8a699f1..9a1df14a 100644
--- a/OpenMcdf.Ole/PropertyFactory.cs
+++ b/OpenMcdf.Ole/PropertyFactory.cs
@@ -6,9 +6,7 @@ internal abstract class PropertyFactory
{
static PropertyFactory()
{
-#if NETSTANDARD2_0_OR_GREATER
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
-#endif
}
public ITypedPropertyValue CreateProperty(VTPropertyType vType, int codePage, uint propertyIdentifier, bool isVariant = false)
diff --git a/OpenMcdf.Ole/PropertySetStream.cs b/OpenMcdf.Ole/PropertySetStream.cs
index 2f3de824..57caf285 100644
--- a/OpenMcdf.Ole/PropertySetStream.cs
+++ b/OpenMcdf.Ole/PropertySetStream.cs
@@ -28,6 +28,8 @@ public PropertySetStream()
public void Read(BinaryReader br)
{
+ br.BaseStream.Position = 0;
+
ByteOrder = br.ReadUInt16();
Version = br.ReadUInt16();
SystemIdentifier = br.ReadUInt32();
@@ -113,6 +115,8 @@ public void Read(BinaryReader br)
public void Write(BinaryWriter bw)
{
+ bw.BaseStream.Position = 0;
+
OffsetContainer oc0 = new();
OffsetContainer oc1 = new();
diff --git a/OpenMcdf.Tests/OpenMcdf.Tests.csproj b/OpenMcdf.Tests/OpenMcdf.Tests.csproj
index 2736a5b8..dcba798f 100644
--- a/OpenMcdf.Tests/OpenMcdf.Tests.csproj
+++ b/OpenMcdf.Tests/OpenMcdf.Tests.csproj
@@ -3,7 +3,7 @@
net48;net8.0-windows
Exe
- 11.0
+ 12.0
enable
enable
diff --git a/OpenMcdf.sln b/OpenMcdf.sln
index c241288a..9382d5ec 100644
--- a/OpenMcdf.sln
+++ b/OpenMcdf.sln
@@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenMcdf.Ole", "OpenMcdf.Ol
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StructuredStorageExplorer", "StructuredStorageExplorer\StructuredStorageExplorer.csproj", "{D5DDCC19-80C4-40D2-AEBF-2DA1CCB1D543}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.Ole.Tests", "OpenMcdf.Ole.Tests\OpenMcdf.Ole.Tests.csproj", "{34F153C4-3EFA-4D6E-B860-AEE300CCCF98}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -58,6 +60,10 @@ Global
{D5DDCC19-80C4-40D2-AEBF-2DA1CCB1D543}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5DDCC19-80C4-40D2-AEBF-2DA1CCB1D543}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5DDCC19-80C4-40D2-AEBF-2DA1CCB1D543}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34F153C4-3EFA-4D6E-B860-AEE300CCCF98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {34F153C4-3EFA-4D6E-B860-AEE300CCCF98}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34F153C4-3EFA-4D6E-B860-AEE300CCCF98}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {34F153C4-3EFA-4D6E-B860-AEE300CCCF98}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/OpenMcdf/RootStorage.cs b/OpenMcdf/RootStorage.cs
index 86e2c3fd..e6a45cd4 100644
--- a/OpenMcdf/RootStorage.cs
+++ b/OpenMcdf/RootStorage.cs
@@ -58,6 +58,8 @@ public static RootStorage Create(Stream stream, Version version = Version.V3, St
return new RootStorage(rootContextSite, flags);
}
+ public static RootStorage CreateInMemory(Version version = Version.V3) => Create(new MemoryStream(), version);
+
public static RootStorage Open(string fileName, FileMode mode, StorageModeFlags flags = StorageModeFlags.None)
{
ThrowIfLeaveOpen(flags);
@@ -91,7 +93,7 @@ public static RootStorage Open(Stream stream, StorageModeFlags flags = StorageMo
this.storageModeFlags = storageModeFlags;
}
- public void Dispose() => Context?.Dispose();
+ public void Dispose() => Context.Dispose();
public void Flush(bool consolidate = false)
{
diff --git a/StructuredStorageExplorer/StreamDataProvider.cs b/StructuredStorageExplorer/StreamDataProvider.cs
index e99c23b7..d2973128 100644
--- a/StructuredStorageExplorer/StreamDataProvider.cs
+++ b/StructuredStorageExplorer/StreamDataProvider.cs
@@ -3,7 +3,7 @@
namespace StructuredStorageExplorer;
-public class StreamDataProvider : IByteProvider
+internal sealed class StreamDataProvider : IByteProvider
{
///
/// Modifying stream