| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | namespace WixToolset.Core.Native.Ole32 |
| 4 | { |
| 5 | using System; |
| 6 | using System.Runtime.InteropServices; |
| 7 | using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; |
| 8 | using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG; |
| 9 | |
| 10 | /// <summary> |
| 11 | /// Wrapper for the compound storage file APIs. |
| 12 | /// </summary> |
| 13 | internal class Storage : IDisposable |
| 14 | { |
| 15 | private readonly IStorage storage; |
| 16 | private bool disposed; |
| 17 | |
| 18 | /// <summary> |
| 19 | /// Instantiate a new Storage. |
| 20 | /// </summary> |
| 21 | /// <param name="storage">The native storage interface.</param> |
| 22 | private Storage(IStorage storage) |
| 23 | { |
| 24 | this.storage = storage; |
| 25 | } |
| 26 | |
| 27 | /// <summary> |
| 28 | /// Storage destructor. |
| 29 | /// </summary> |
| 30 | ~Storage() |
| 31 | { |
| 32 | this.Dispose(); |
| 33 | } |
| 34 | |
| 35 | /// <summary> |
| 36 | /// The IEnumSTATSTG interface enumerates an array of STATSTG structures. |
| 37 | /// </summary> |
| 38 | [ComImport, Guid("0000000d-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
| 39 | private interface IEnumSTATSTG |
| 40 | { |
| 41 | /// <summary> |
| 42 | /// Gets a specified number of STATSTG structures. |
| 43 | /// </summary> |
| 44 | /// <param name="celt">The number of STATSTG structures requested.</param> |
| 45 | /// <param name="rgelt">An array of STATSTG structures returned.</param> |
| 46 | /// <param name="pceltFetched">The number of STATSTG structures retrieved in the rgelt parameter.</param> |
| 47 | /// <returns>The error code.</returns> |
| 48 | [PreserveSig] |
| 49 | uint Next(uint celt, [MarshalAs(UnmanagedType.LPArray), Out] STATSTG[] rgelt, out uint pceltFetched); |
| 50 | |
| 51 | /// <summary> |
| 52 | /// Skips a specified number of STATSTG structures in the enumeration sequence. |
| 53 | /// </summary> |
| 54 | /// <param name="celt">The number of STATSTG structures to skip.</param> |
| 55 | void Skip(uint celt); |
| 56 | |
| 57 | /// <summary> |
| 58 | /// Resets the enumeration sequence to the beginning of the STATSTG structure array. |
| 59 | /// </summary> |
| 60 | void Reset(); |
| 61 | |
| 62 | /// <summary> |
| 63 | /// Creates a new enumerator that contains the same enumeration state as the current STATSTG structure enumerator. |
| 64 | /// </summary> |
| 65 | /// <returns>The cloned IEnumSTATSTG interface.</returns> |
| 66 | [return: MarshalAs(UnmanagedType.Interface)] |
| 67 | IEnumSTATSTG Clone(); |
| 68 | } |
| 69 | |
| 70 | /// <summary> |
| 71 | /// The IStorage interface supports the creation and management of structured storage objects. |
| 72 | /// </summary> |
| 73 | [ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
| 74 | private interface IStorage |
| 75 | { |
| 76 | /// <summary> |
| 77 | /// Creates and opens a stream object with the specified name contained in this storage object. |
| 78 | /// </summary> |
| 79 | /// <param name="pwcsName">The name of the newly created stream.</param> |
| 80 | /// <param name="grfMode">Specifies the access mode to use when opening the newly created stream.</param> |
| 81 | /// <param name="reserved1">Reserved for future use; must be zero.</param> |
| 82 | /// <param name="reserved2">Reserved for future use; must be zero.</param> |
| 83 | /// <param name="ppstm">On return, pointer to the location of the new IStream interface pointer.</param> |
| 84 | void CreateStream(string pwcsName, uint grfMode, uint reserved1, uint reserved2, out IStream ppstm); |
| 85 | |
| 86 | /// <summary> |
| 87 | /// Opens an existing stream object within this storage object using the specified access permissions in grfMode. |
| 88 | /// </summary> |
| 89 | /// <param name="pwcsName">The name of the stream to open.</param> |
| 90 | /// <param name="reserved1">Reserved for future use; must be NULL.</param> |
| 91 | /// <param name="grfMode">Specifies the access mode to be assigned to the open stream.</param> |
| 92 | /// <param name="reserved2">Reserved for future use; must be zero.</param> |
| 93 | /// <param name="ppstm">A pointer to IStream pointer variable that receives the interface pointer to the newly opened stream object.</param> |
| 94 | void OpenStream(string pwcsName, IntPtr reserved1, uint grfMode, uint reserved2, out IStream ppstm); |
| 95 | |
| 96 | /// <summary> |
| 97 | /// Creates and opens a new storage object nested within this storage object with the specified name in the specified access mode. |
| 98 | /// </summary> |
| 99 | /// <param name="pwcsName">The name of the newly created storage object.</param> |
| 100 | /// <param name="grfMode">A value that specifies the access mode to use when opening the newly created storage object.</param> |
| 101 | /// <param name="reserved1">Reserved for future use; must be zero.</param> |
| 102 | /// <param name="reserved2">Reserved for future use; must be zero.</param> |
| 103 | /// <param name="ppstg">A pointer, when successful, to the location of the IStorage pointer to the newly created storage object.</param> |
| 104 | void CreateStorage(string pwcsName, uint grfMode, uint reserved1, uint reserved2, out IStorage ppstg); |
| 105 | |
| 106 | /// <summary> |
| 107 | /// Opens an existing storage object with the specified name in the specified access mode. |
| 108 | /// </summary> |
| 109 | /// <param name="pwcsName">The name of the storage object to open.</param> |
| 110 | /// <param name="pstgPriority">Must be NULL.</param> |
| 111 | /// <param name="grfMode">Specifies the access mode to use when opening the storage object.</param> |
| 112 | /// <param name="snbExclude">Must be NULL.</param> |
| 113 | /// <param name="reserved">Reserved for future use; must be zero.</param> |
| 114 | /// <param name="ppstg">When successful, pointer to the location of an IStorage pointer to the opened storage object.</param> |
| 115 | void OpenStorage(string pwcsName, IStorage pstgPriority, uint grfMode, IntPtr snbExclude, uint reserved, out IStorage ppstg); |
| 116 | |
| 117 | /// <summary> |
| 118 | /// Copies the entire contents of an open storage object to another storage object. |
| 119 | /// </summary> |
| 120 | /// <param name="ciidExclude">The number of elements in the array pointed to by rgiidExclude.</param> |
| 121 | /// <param name="rgiidExclude">An array of interface identifiers (IIDs) that either the caller knows about and does not want |
| 122 | /// copied or that the storage object does not support, but whose state the caller will later explicitly copy.</param> |
| 123 | /// <param name="snbExclude">A string name block (refer to SNB) that specifies a block of storage or stream objects that are not to be copied to the destination.</param> |
| 124 | /// <param name="pstgDest">A pointer to the open storage object into which this storage object is to be copied.</param> |
| 125 | void CopyTo(uint ciidExclude, IntPtr rgiidExclude, IntPtr snbExclude, IStorage pstgDest); |
| 126 | |
| 127 | /// <summary> |
| 128 | /// Copies or moves a substorage or stream from this storage object to another storage object. |
| 129 | /// </summary> |
| 130 | /// <param name="pwcsName">The name of the element in this storage object to be moved or copied.</param> |
| 131 | /// <param name="pstgDest">IStorage pointer to the destination storage object.</param> |
| 132 | /// <param name="pwcsNewName">The new name for the element in its new storage object.</param> |
| 133 | /// <param name="grfFlags">Specifies whether the operation should be a move (STGMOVE_MOVE) or a copy (STGMOVE_COPY).</param> |
| 134 | void MoveElementTo(string pwcsName, IStorage pstgDest, string pwcsNewName, uint grfFlags); |
| 135 | |
| 136 | /// <summary> |
| 137 | /// Reflects changes for a transacted storage object to the parent level. |
| 138 | /// </summary> |
| 139 | /// <param name="grfCommitFlags">Controls how the changes are committed to the storage object.</param> |
| 140 | void Commit(uint grfCommitFlags); |
| 141 | |
| 142 | /// <summary> |
| 143 | /// Discards all changes that have been made to the storage object since the last commit operation. |
| 144 | /// </summary> |
| 145 | void Revert(); |
| 146 | |
| 147 | /// <summary> |
| 148 | /// Returns an enumerator object that can be used to enumerate the storage and stream objects contained within this storage object. |
| 149 | /// </summary> |
| 150 | /// <param name="reserved1">Reserved for future use; must be zero.</param> |
| 151 | /// <param name="reserved2">Reserved for future use; must be NULL.</param> |
| 152 | /// <param name="reserved3">Reserved for future use; must be zero.</param> |
| 153 | /// <param name="ppenum">Pointer to IEnumSTATSTG* pointer variable that receives the interface pointer to the new enumerator object.</param> |
| 154 | void EnumElements(uint reserved1, IntPtr reserved2, uint reserved3, out IEnumSTATSTG ppenum); |
| 155 | |
| 156 | /// <summary> |
| 157 | /// Removes the specified storage or stream from this storage object. |
| 158 | /// </summary> |
| 159 | /// <param name="pwcsName">The name of the storage or stream to be removed.</param> |
| 160 | void DestroyElement(string pwcsName); |
| 161 | |
| 162 | /// <summary> |
| 163 | /// Renames the specified storage or stream in this storage object. |
| 164 | /// </summary> |
| 165 | /// <param name="pwcsOldName">The name of the substorage or stream to be changed.</param> |
| 166 | /// <param name="pwcsNewName">The new name for the specified substorage or stream.</param> |
| 167 | void RenameElement(string pwcsOldName, string pwcsNewName); |
| 168 | |
| 169 | /// <summary> |
| 170 | /// Sets the modification, access, and creation times of the indicated storage element, if supported by the underlying file system. |
| 171 | /// </summary> |
| 172 | /// <param name="pwcsName">The name of the storage object element whose times are to be modified.</param> |
| 173 | /// <param name="pctime">Either the new creation time for the element or NULL if the creation time is not to be modified.</param> |
| 174 | /// <param name="patime">Either the new access time for the element or NULL if the access time is not to be modified.</param> |
| 175 | /// <param name="pmtime">Either the new modification time for the element or NULL if the modification time is not to be modified.</param> |
| 176 | void SetElementTimes(string pwcsName, FILETIME pctime, FILETIME patime, FILETIME pmtime); |
| 177 | |
| 178 | /// <summary> |
| 179 | /// Assigns the specified CLSID to this storage object. |
| 180 | /// </summary> |
| 181 | /// <param name="clsid">The CLSID that is to be associated with the storage object.</param> |
| 182 | void SetClass(Guid clsid); |
| 183 | |
| 184 | /// <summary> |
| 185 | /// Stores up to 32 bits of state information in this storage object. |
| 186 | /// </summary> |
| 187 | /// <param name="grfStateBits">Specifies the new values of the bits to set.</param> |
| 188 | /// <param name="grfMask">A binary mask indicating which bits in grfStateBits are significant in this call.</param> |
| 189 | void SetStateBits(uint grfStateBits, uint grfMask); |
| 190 | |
| 191 | /// <summary> |
| 192 | /// Returns the STATSTG structure for this open storage object. |
| 193 | /// </summary> |
| 194 | /// <param name="pstatstg">On return, pointer to a STATSTG structure where this method places information about the open storage object.</param> |
| 195 | /// <param name="grfStatFlag">Specifies that some of the members in the STATSTG structure are not returned, thus saving a memory allocation operation.</param> |
| 196 | void Stat(out STATSTG pstatstg, uint grfStatFlag); |
| 197 | } |
| 198 | |
| 199 | /// <summary> |
| 200 | /// The IStream interface lets you read and write data to stream objects. |
| 201 | /// </summary> |
| 202 | [ComImport, Guid("0000000c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
| 203 | private interface IStream |
| 204 | { |
| 205 | /// <summary> |
| 206 | /// Reads a specified number of bytes from the stream object into memory starting at the current seek pointer. |
| 207 | /// </summary> |
| 208 | /// <param name="pv">A pointer to the buffer which the stream data is read into.</param> |
| 209 | /// <param name="cb">The number of bytes of data to read from the stream object.</param> |
| 210 | /// <param name="pcbRead">A pointer to a ULONG variable that receives the actual number of bytes read from the stream object.</param> |
| 211 | void Read([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbRead); |
| 212 | |
| 213 | /// <summary> |
| 214 | /// Writes a specified number of bytes into the stream object starting at the current seek pointer. |
| 215 | /// </summary> |
| 216 | /// <param name="pv">A pointer to the buffer that contains the data that is to be written to the stream.</param> |
| 217 | /// <param name="cb">The number of bytes of data to attempt to write into the stream.</param> |
| 218 | /// <param name="pcbWritten">A pointer to a ULONG variable where this method writes the actual number of bytes written to the stream object.</param> |
| 219 | void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbWritten); |
| 220 | |
| 221 | /// <summary> |
| 222 | /// Changes the seek pointer to a new location relative to the beginning of the stream, the end of the stream, or the current seek pointer. |
| 223 | /// </summary> |
| 224 | /// <param name="dlibMove">The displacement to be added to the location indicated by the dwOrigin parameter.</param> |
| 225 | /// <param name="dwOrigin">The origin for the displacement specified in dlibMove.</param> |
| 226 | /// <param name="plibNewPosition">A pointer to the location where this method writes the value of the new seek pointer from the beginning of the stream.</param> |
| 227 | void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition); |
| 228 | |
| 229 | /// <summary> |
| 230 | /// Changes the size of the stream object. |
| 231 | /// </summary> |
| 232 | /// <param name="libNewSize">Specifies the new size of the stream as a number of bytes.</param> |
| 233 | void SetSize(long libNewSize); |
| 234 | |
| 235 | /// <summary> |
| 236 | /// Copies a specified number of bytes from the current seek pointer in the stream to the current seek pointer in another stream. |
| 237 | /// </summary> |
| 238 | /// <param name="pstm">A pointer to the destination stream.</param> |
| 239 | /// <param name="cb">The number of bytes to copy from the source stream.</param> |
| 240 | /// <param name="pcbRead">A pointer to the location where this method writes the actual number of bytes read from the source.</param> |
| 241 | /// <param name="pcbWritten">A pointer to the location where this method writes the actual number of bytes written to the destination.</param> |
| 242 | void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten); |
| 243 | |
| 244 | /// <summary> |
| 245 | /// Ensures that any changes made to a stream object open in transacted mode are reflected in the parent storage object. |
| 246 | /// </summary> |
| 247 | /// <param name="grfCommitFlags">Controls how the changes for the stream object are committed.</param> |
| 248 | void Commit(int grfCommitFlags); |
| 249 | |
| 250 | /// <summary> |
| 251 | /// Discards all changes that have been made to a transacted stream since the last call to IStream::Commit. |
| 252 | /// </summary> |
| 253 | void Revert(); |
| 254 | |
| 255 | /// <summary> |
| 256 | /// Restricts access to a specified range of bytes in the stream. |
| 257 | /// </summary> |
| 258 | /// <param name="libOffset">Integer that specifies the byte offset for the beginning of the range.</param> |
| 259 | /// <param name="cb">Integer that specifies the length of the range, in bytes, to be restricted.</param> |
| 260 | /// <param name="dwLockType">Specifies the restrictions being requested on accessing the range.</param> |
| 261 | void LockRegion(long libOffset, long cb, int dwLockType); |
| 262 | |
| 263 | /// <summary> |
| 264 | /// Removes the access restriction on a range of bytes previously restricted with IStream::LockRegion. |
| 265 | /// </summary> |
| 266 | /// <param name="libOffset">Specifies the byte offset for the beginning of the range.</param> |
| 267 | /// <param name="cb">Specifies, in bytes, the length of the range to be restricted.</param> |
| 268 | /// <param name="dwLockType">Specifies the access restrictions previously placed on the range.</param> |
| 269 | void UnlockRegion(long libOffset, long cb, int dwLockType); |
| 270 | |
| 271 | /// <summary> |
| 272 | /// Retrieves the STATSTG structure for this stream. |
| 273 | /// </summary> |
| 274 | /// <param name="pstatstg">Pointer to a STATSTG structure where this method places information about this stream object.</param> |
| 275 | /// <param name="grfStatFlag">Specifies that this method does not return some of the members in the STATSTG structure, thus saving a memory allocation operation.</param> |
| 276 | void Stat(out STATSTG pstatstg, int grfStatFlag); |
| 277 | |
| 278 | /// <summary> |
| 279 | /// Creates a new stream object that references the same bytes as the original stream but provides a separate seek pointer to those bytes. |
| 280 | /// </summary> |
| 281 | /// <param name="ppstm">When successful, pointer to the location of an IStream pointer to the new stream object.</param> |
| 282 | void Clone(out IStream ppstm); |
| 283 | } |
| 284 | |
| 285 | /// <summary> |
| 286 | /// Creates a new compound file storage object. |
| 287 | /// </summary> |
| 288 | /// <param name="storageFile">The compound file being created.</param> |
| 289 | /// <param name="mode">Specifies the access mode to use when opening the new storage object.</param> |
| 290 | /// <returns>The created Storage object.</returns> |
| 291 | public static Storage CreateDocFile(string storageFile, StorageMode mode) |
| 292 | { |
| 293 | var storage = NativeMethods.StgCreateDocfile(storageFile, (uint)mode, 0); |
| 294 | |
| 295 | return new Storage(storage); |
| 296 | } |
| 297 | |
| 298 | /// <summary> |
| 299 | /// Opens an existing root storage object in the file system. |
| 300 | /// </summary> |
| 301 | /// <param name="storageFile">The file that contains the storage object to open.</param> |
| 302 | /// <param name="mode">Specifies the access mode to use to open the storage object.</param> |
| 303 | /// <returns>The created Storage object.</returns> |
| 304 | public static Storage Open(string storageFile, StorageMode mode) |
| 305 | { |
| 306 | var storage = NativeMethods.StgOpenStorage(storageFile, IntPtr.Zero, (uint)mode, IntPtr.Zero, 0); |
| 307 | |
| 308 | return new Storage(storage); |
| 309 | } |
| 310 | |
| 311 | /// <summary> |
| 312 | /// Copies the entire contents of this open storage object into another Storage object. |
| 313 | /// </summary> |
| 314 | /// <param name="destinationStorage">The destination Storage object.</param> |
| 315 | public void CopyTo(Storage destinationStorage) |
| 316 | { |
| 317 | this.storage.CopyTo(0, IntPtr.Zero, IntPtr.Zero, destinationStorage.storage); |
| 318 | } |
| 319 | |
| 320 | /// <summary> |
| 321 | /// Opens an existing Storage object with the specified name according to the specified access mode. |
| 322 | /// </summary> |
| 323 | /// <param name="name">The name of the Storage object.</param> |
| 324 | /// <returns>The opened Storage object.</returns> |
| 325 | public Storage OpenStorage(string name) |
| 326 | { |
| 327 | this.storage.OpenStorage(name, null, (uint)(StorageMode.Read | StorageMode.ShareExclusive), IntPtr.Zero, 0, out var subStorage); |
| 328 | |
| 329 | return new Storage(subStorage); |
| 330 | } |
| 331 | |
| 332 | /// <summary> |
| 333 | /// Disposes the managed and unmanaged objects in this object. |
| 334 | /// </summary> |
| 335 | public void Dispose() |
| 336 | { |
| 337 | if (!this.disposed) |
| 338 | { |
| 339 | Marshal.ReleaseComObject(this.storage); |
| 340 | |
| 341 | this.disposed = true; |
| 342 | } |
| 343 | |
| 344 | GC.SuppressFinalize(this); |
| 345 | } |
| 346 | |
| 347 | /// <summary> |
| 348 | /// The native methods. |
| 349 | /// </summary> |
| 350 | private static class NativeMethods |
| 351 | { |
| 352 | /// <summary> |
| 353 | /// Creates a new compound file storage object. |
| 354 | /// </summary> |
| 355 | /// <param name="pwcsName">The name for the compound file being created.</param> |
| 356 | /// <param name="grfMode">Specifies the access mode to use when opening the new storage object.</param> |
| 357 | /// <param name="reserved">Reserved for future use; must be zero.</param> |
| 358 | /// <returns>A pointer to the location of the IStorage pointer to the new storage object.</returns> |
| 359 | [DllImport("ole32.dll", PreserveSig = false)] |
| 360 | [return: MarshalAs(UnmanagedType.Interface)] |
| 361 | internal static extern IStorage StgCreateDocfile([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, uint grfMode, uint reserved); |
| 362 | |
| 363 | /// <summary> |
| 364 | /// Opens an existing root storage object in the file system. |
| 365 | /// </summary> |
| 366 | /// <param name="pwcsName">The file that contains the storage object to open.</param> |
| 367 | /// <param name="pstgPriority">Most often NULL.</param> |
| 368 | /// <param name="grfMode">Specifies the access mode to use to open the storage object.</param> |
| 369 | /// <param name="snbExclude">If not NULL, pointer to a block of elements in the storage to be excluded as the storage object is opened.</param> |
| 370 | /// <param name="reserved">Indicates reserved for future use; must be zero.</param> |
| 371 | /// <returns>A pointer to a IStorage* pointer variable that receives the interface pointer to the opened storage.</returns> |
| 372 | [DllImport("ole32.dll", PreserveSig = false)] |
| 373 | [return: MarshalAs(UnmanagedType.Interface)] |
| 374 | internal static extern IStorage StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr pstgPriority, uint grfMode, IntPtr snbExclude, uint reserved); |
| 375 | } |
| 376 | } |
| 377 | } |