From a98f49d5822e28fe74dd58c5f86ea13b752a1233 Mon Sep 17 00:00:00 2001 From: dahall Date: Sun, 12 Jan 2020 21:11:43 -0700 Subject: [PATCH] Added tests and made corrections to Opc interfaces --- PInvoke/Opc/MsOpc.cs | 76 ++++++++++++++++++++++++++++++++++++--- UnitTests/PInvoke/Opc/OpcTests.cs | 57 +++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 8 deletions(-) diff --git a/PInvoke/Opc/MsOpc.cs b/PInvoke/Opc/MsOpc.cs index 250f5f53..144cdc72 100644 --- a/PInvoke/Opc/MsOpc.cs +++ b/PInvoke/Opc/MsOpc.cs @@ -1,6 +1,10 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; +using Vanara.Extensions.Reflection; using Vanara.InteropServices; using static Vanara.PInvoke.Crypt32; using static Vanara.PInvoke.UrlMon; @@ -59,10 +63,10 @@ namespace Vanara.PInvoke public enum OPC_COMPRESSION_OPTIONS { /// Compression is turned off. - OPC_COMPRESSION_NONE, + OPC_COMPRESSION_NONE = -1, /// Compression is optimized for a balance between size and performance. - OPC_COMPRESSION_NORMAL, + OPC_COMPRESSION_NORMAL = 0, /// Compression is optimized for size. OPC_COMPRESSION_MAXIMUM, @@ -261,7 +265,7 @@ namespace Vanara.PInvoke /// The signature is not valid.Signature markup or signed package components might have been altered. Alternatively, the /// signature might not exist in the current package. /// - OPC_SIGNATURE_INVALID, + OPC_SIGNATURE_INVALID = -1, } /// Describes the read/write status of a stream. @@ -272,7 +276,7 @@ namespace Vanara.PInvoke public enum OPC_STREAM_IO_MODE { /// Creates a read-only stream for loading an existing package. - OPC_STREAM_IO_READ, + OPC_STREAM_IO_READ = 1, /// Creates a write-only stream for saving a new package. OPC_STREAM_IO_WRITE, @@ -1670,7 +1674,7 @@ namespace Vanara.PInvoke // LPCWSTR filename, OPC_STREAM_IO_MODE ioMode, LPSECURITY_ATTRIBUTES securityAttributes, DWORD dwFlagsAndAttributes, IStream // **stream ); [PreserveSig] - HRESULT CreateStreamOnFile([MarshalAs(UnmanagedType.LPWStr)] string filename, OPC_STREAM_IO_MODE ioMode, SECURITY_ATTRIBUTES securityAttributes, FileFlagsAndAttributes dwFlagsAndAttributes, out IStream stream); + HRESULT CreateStreamOnFile([MarshalAs(UnmanagedType.LPWStr)] string filename, OPC_STREAM_IO_MODE ioMode, [Optional] SECURITY_ATTRIBUTES securityAttributes, FileFlagsAndAttributes dwFlagsAndAttributes, out IStream stream); /// Creates a package object that represents an empty package. /// A pointer to the IOpcPackage interface of the package object that represents an empty package. @@ -6798,5 +6802,67 @@ namespace Vanara.PInvoke [PInvokeData("msopc.h", MSDNShortId = "0a265a0a-c109-4afc-a0ad-d3ee31757aa1")] [ComImport, ClassInterface(ClassInterfaceType.None), Guid("6B2D6BA0-9F3E-4f27-920B-313CC426A39E")] public class OpcFactory { } + + /// Creates an instance from one of the IOpcXXXEnumerator interface instances. + /// + /// The type of the enumerator interface. This interface must support the MoveNext, GetCurrent and Clone methods. + /// + /// The type of the elemement interface returned as the parameter in TElem.GetCurrent. + /// + public class OpcEnumerator : IEnumerator + { + private MethodInfo getCurrent; + private MethodInfo moveNext; + private TEnum opcEnum; + + /// Initializes a new instance of the class. + /// The opc enumerator. + /// opcEnumerator + /// The type specified for TEnum is not a valid Opc Enumerator instance. + public OpcEnumerator(TEnum opcEnumerator) + { + opcEnum = opcEnumerator ?? throw new ArgumentNullException(nameof(opcEnumerator)); + moveNext = typeof(TEnum).GetMethod("MoveNext"); + getCurrent = typeof(TEnum).GetMethod("GetCurrent"); + if (moveNext is null || getCurrent is null) throw new ArgumentException("The type specified for TEnum is not a valid Opc Enumerator instance."); + } + + /// Gets the element in the collection at the current position of the enumerator. + public TElem Current + { + get + { + var p = new object[] { default(TElem) }; + ((HRESULT)getCurrent.Invoke(opcEnum, p)).ThrowIfFailed(); + return (TElem)p[0]; + } + } + + /// Gets the element in the collection at the current position of the enumerator. + object IEnumerator.Current => Current; + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + public void Dispose() => Marshal.ReleaseComObject(opcEnum); + + /// Advances the enumerator to the next element of the collection. + /// + /// if the enumerator was successfully advanced to the next element; if the + /// enumerator has passed the end of the collection. + /// + public bool MoveNext() + { + var p = new object[] { false }; + ((HRESULT)moveNext.Invoke(opcEnum, p)).ThrowIfFailed(); + return (bool)p[0]; + } + + /// Sets the enumerator to its initial position, which is before the first element in the collection. + public void Reset() + { + var clone = opcEnum.InvokeMethod("Clone"); + Marshal.ReleaseComObject(opcEnum); + opcEnum = clone; + } + } } } \ No newline at end of file diff --git a/UnitTests/PInvoke/Opc/OpcTests.cs b/UnitTests/PInvoke/Opc/OpcTests.cs index 0489f7d5..ccf14186 100644 --- a/UnitTests/PInvoke/Opc/OpcTests.cs +++ b/UnitTests/PInvoke/Opc/OpcTests.cs @@ -1,10 +1,11 @@ using NUnit.Framework; using System; -using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; using System.Runtime.InteropServices; -using System.Text; -using System.Threading; using Vanara.Extensions; +using Vanara.Extensions.Reflection; using Vanara.InteropServices; using static Vanara.PInvoke.Opc; @@ -13,5 +14,55 @@ namespace Vanara.PInvoke.Tests [TestFixture] public class OpcTests { + public IOpcFactory factory; + + [OneTimeSetUp] + public void _Setup() => factory = new IOpcFactory(); + + [OneTimeTearDown] + public void _TearDown() => Marshal.ReleaseComObject(factory); + + [Test] + public void LoadTest() + { + Assert.That(factory.CreateStreamOnFile(TestCaseSources.WordDoc, OPC_STREAM_IO_MODE.OPC_STREAM_IO_READ, null, 0, out var sourceFileStream), ResultIs.Successful); + using var psourceFileString = ComReleaserFactory.Create(sourceFileStream); + + Assert.That(factory.ReadPackageFromStream(sourceFileStream, OPC_READ_FLAGS.OPC_CACHE_ON_ACCESS, out var outPackage), ResultIs.Successful); + using var poutPackage = ComReleaserFactory.Create(outPackage); + + IOpcPartSet pset = null; + Assert.That(() => pset = outPackage.GetPartSet(), Throws.Nothing); + using var ppset = ComReleaserFactory.Create(pset); + + IOpcPartEnumerator penum = null; + Assert.That(() => penum = pset.GetEnumerator(), Throws.Nothing); + using var ppenum = new OpcEnumerator(penum); + + while (ppenum.MoveNext()) + TestContext.WriteLine($"{ppenum.Current.GetContentType()}, {ppenum.Current.GetCompressionOptions()}"); + TestContext.WriteLine(); + + IOpcRelationshipSet rset = null; + Assert.That(() => rset = outPackage.GetRelationshipSet(), Throws.Nothing); + using var prset = ComReleaserFactory.Create(rset); + + IOpcRelationshipEnumerator renum = null; + Assert.That(() => renum = rset.GetEnumerator(), Throws.Nothing); + using var prenum = new OpcEnumerator(renum); + + while (prenum.MoveNext()) + TestContext.WriteLine($"{prenum.Current.GetId()}, {prenum.Current.GetRelationshipType()}, {prenum.Current.GetTargetMode()}"); + TestContext.WriteLine(); + } + + [Test] + public void RootTest() + { + IOpcUri rootUri = null; + Assert.That(() => rootUri = factory.CreatePackageRootUri(), Throws.Nothing); + Assert.That(rootUri, Is.Not.Null); + Marshal.ReleaseComObject(rootUri); + } } } \ No newline at end of file