diff --git a/sources/OpenMcdf.Extensions/OLEProperties/Common.cs b/sources/OpenMcdf.Extensions/OLEProperties/Common.cs
index c46b0315..4ba65449 100644
--- a/sources/OpenMcdf.Extensions/OLEProperties/Common.cs
+++ b/sources/OpenMcdf.Extensions/OLEProperties/Common.cs
@@ -36,4 +36,9 @@ public enum PropertyType
{
TypedPropertyValue = 0, DictionaryProperty = 1
}
+
+ internal static class CodePages
+ {
+ public const int CP_WINUNICODE = 0x04B0;
+ }
}
diff --git a/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs b/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs
index 9b76b754..bf1e4936 100644
--- a/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs
+++ b/sources/OpenMcdf.Extensions/OLEProperties/DictionaryEntry.cs
@@ -7,8 +7,6 @@ namespace OpenMcdf.Extensions.OLEProperties
{
public class DictionaryEntry
{
- private const int CP_WINUNICODE = 0x04B0;
-
int codePage;
public DictionaryEntry(int codePage)
@@ -27,7 +25,7 @@ public void Read(BinaryReader br)
PropertyIdentifier = br.ReadUInt32();
Length = br.ReadInt32();
- if (codePage != CP_WINUNICODE)
+ if (codePage != CodePages.CP_WINUNICODE)
{
nameBytes = br.ReadBytes(Length);
}
diff --git a/sources/OpenMcdf.Extensions/OLEProperties/DictionaryProperty.cs b/sources/OpenMcdf.Extensions/OLEProperties/DictionaryProperty.cs
index 3b8fc71d..1930cfc9 100644
--- a/sources/OpenMcdf.Extensions/OLEProperties/DictionaryProperty.cs
+++ b/sources/OpenMcdf.Extensions/OLEProperties/DictionaryProperty.cs
@@ -59,19 +59,85 @@ public void Read(BinaryReader br)
}
+ ///
+ /// Write the dictionary and all its values into the specified .
+ ///
+ ///
+ /// Based on the Microsoft specifications at https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-oleps/99127b7f-c440-4697-91a4-c853086d6b33
+ ///
+ /// A writer to write the dictionary into.
public void Write(BinaryWriter bw)
{
+ long curPos = bw.BaseStream.Position;
+
bw.Write(entries.Count);
foreach (KeyValuePair kv in entries)
{
- bw.Write(kv.Key);
- string s = kv.Value;
- if (!s.EndsWith("\0"))
- s += "\0";
- bw.Write(Encoding.GetEncoding(this.codePage).GetBytes(s));
+ WriteEntry(bw, kv.Key, kv.Value);
}
+ var size = (int)(bw.BaseStream.Position - curPos);
+ WritePaddingIfNeeded(bw, size);
+ }
+
+ // Write a single entry to the dictionary, and handle and required null termination and padding.
+ private void WriteEntry(BinaryWriter bw, uint propertyIdentifier, string name)
+ {
+ // Write the PropertyIdentifier
+ bw.Write(propertyIdentifier);
+
+ // Encode string data with the current codepage
+ var nameBytes = Encoding.GetEncoding(this.codePage).GetBytes(name);
+ uint byteLength = (uint)nameBytes.Length;
+
+ // If the code page is WINUNICODE, write the length as the number of characters and pad the length to a multiple of 4 bytes
+ // Otherwise, write the length as the number of bytes and don't pad.
+ // In either case, the string must be null terminated
+ if (codePage == CodePages.CP_WINUNICODE)
+ {
+ bool addNullTerminator =
+ byteLength == 0 || nameBytes[byteLength - 1] != '\0' || nameBytes[byteLength - 2] != '\0';
+
+ if (addNullTerminator)
+ byteLength += 2;
+
+ bw.Write((uint)byteLength / 2);
+ bw.Write(nameBytes);
+
+ if (addNullTerminator)
+ {
+ bw.Write((byte)0);
+ bw.Write((byte)0);
+ }
+
+ WritePaddingIfNeeded(bw, (int)byteLength);
+ }
+ else
+ {
+ bool addNullTerminator =
+ byteLength == 0 || nameBytes[byteLength - 1] != '\0';
+
+ if (addNullTerminator)
+ byteLength += 1;
+
+ bw.Write(byteLength);
+ bw.Write(nameBytes);
+
+ if (addNullTerminator)
+ bw.Write((byte)0);
+ }
+ }
+
+ // Write as much padding as needed to pad fieldLength to a multiple of 4 bytes
+ private void WritePaddingIfNeeded(BinaryWriter bw, int fieldLength)
+ {
+ var m = fieldLength % 4;
+
+ if (m > 0)
+ for (int i = 0; i < 4 - m; i++) // padding
+ bw.Write((byte)0);
}
}
}
+