using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Vanara.Extensions; using Vanara.InteropServices; using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG; namespace Vanara.PInvoke { public static partial class Ole32 { /// /// Manages access to the running object table (ROT), a globally accessible look-up table on each workstation. A workstation's ROT /// keeps track of those objects that can be identified by a moniker and that are currently running on the workstation. When a /// client tries to bind a moniker to an object, the moniker checks the ROT to see if the object is already running; this allows the /// moniker to bind to the current instance instead of loading a new one. /// /// /// The ROT contains entries of the following form: (pmkObjectName, pUnkObject). /// /// The pmkObjectName element is a pointer to the moniker that identifies the running object. The pUnkObject element is a pointer to /// the running object itself. During the binding process, monikers consult the pmkObjectName entries in the ROT to see whether an /// object is already running. /// /// /// Objects that can be named by monikers must be registered with the ROT when they are loaded and their registration must be /// revoked when they are no longer running. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nn-objidl-irunningobjecttable [PInvokeData("objidl.h", MSDNShortId = "ff89bcb5-df6d-4325-b0e8-613217a68f42")] [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000010-0000-0000-C000-000000000046")] public interface IRunningObjectTable { /// Registers an object and its identifying moniker in the running object table (ROT). /// /// /// Specifies whether the ROT's reference to punkObject is weak or strong and controls access to the object through its entry in /// the ROT. For details, see the Remarks section. /// /// /// /// Value /// Meaning /// /// /// ROTFLAGS_REGISTRATIONKEEPSALIVE /// When set, indicates a strong registration for the object. /// /// /// ROTFLAGS_ALLOWANYCLIENT /// /// When set, any client can connect to the running object through its entry in the ROT. When not set, only clients in the /// window station that registered the object can connect to it. /// /// /// /// /// A pointer to the object that is being registered as running. /// A pointer to the moniker that identifies punkObject. /// /// An identifier for this ROT entry that can be used in subsequent calls to IRunningObjectTable::Revoke or /// IRunningObjectTable::NoteChangeTime. The caller cannot specify NULL for this parameter. If an error occurs, /// *pdwRegister is set to zero. /// /// /// /// This method registers a pointer to an object under a moniker that identifies the object. The moniker is used as the key when /// the table is searched with IRunningObjectTable::GetObject. /// /// /// When an object is registered, the ROT always calls AddRef on the object. For a weak registration /// (ROTFLAGS_REGISTRATIONKEEPSALIVE not set), the ROT will release the object whenever the last strong reference to the object /// is released. For a strong registration (ROTFLAGS_REGISTRATIONKEEPSALIVE set), the ROT prevents the object from being /// destroyed until the object's registration is explicitly revoked. /// /// /// A server registered as either LocalService or RunAs can set the ROTFLAGS_ALLOWANYCLIENT flag in its call to Register /// to allow any client to connect to it. A server setting this bit must have its executable name in the AppID section of the /// registry that refers to the AppID for the executable. An "activate as activator" server (not registered as LocalService or /// RunAs) must not set this flag in its call to Register. For details on installing services, see Installing as a /// Service Application. /// /// /// Registering a second object with the same moniker, or re-registering the same object with the same moniker, creates a second /// entry in the ROT. In this case, Register returns MK_S_MONIKERALREADYREGISTERED. Each call to Register must be /// matched by a call to IRunningObjectTable::Revoke because even duplicate entries have different pdwRegister identifiers. A /// problem with duplicate registrations is that there is no way to determine which object will be returned if the moniker is /// specified in a subsequent call to IRunningObjectTable::IsRunning. /// /// Notes to Callers /// /// If you are a moniker provider (that is, you hand out monikers identifying your objects to make them accessible to others), /// you must call the Register method to register your objects when they begin running. You must also call this method if /// you rename your objects while they are loaded. /// /// /// The most common type of moniker provider is a compound-document link source. This includes server applications that support /// linking to their documents (or portions of a document) and container applications that support linking to embeddings within /// their documents. Server applications that do not support linking can also use the ROT to cooperate with container /// applications that support linking to embeddings. /// /// /// If you are writing a server application, you should register an object with the ROT when it begins running, typically in /// your implementation of IOleObject::DoVerb. The object must be registered under its full moniker, which requires getting the /// moniker of its container document using IOleClientSite::GetMoniker. You should also revoke and re-register the object in /// your implementation of IOleObject::SetMoniker, which is called if the container document is renamed. /// /// /// If you are writing a container application that supports linking to embeddings, you should register your document with the /// ROT when it is loaded. If your document is renamed, you should revoke and re-register it with the ROT and call /// IOleObject::SetMoniker for any embedded objects in the document to give them an opportunity to re-register themselves. /// /// /// Objects registered in the ROT must be explicitly revoked when the object is no longer running or when its moniker changes. /// This revocation is important because there is no way for the system to automatically remove entries from the ROT. You must /// cache the identifier that is written through pdwRegister and use it in a call to IRunningObjectTable::Revoke to revoke the /// registration. For a strong registration, a strong reference is released when the objects registration is revoked. /// /// /// As of Windows Server 2003, if there are stale entries that remain in the ROT due to unexpected server problems, COM will /// automatically remove these stale entries from the ROT. /// /// /// The system's implementation of Register calls IMoniker::Reduce on the pmkObjectName parameter to ensure that the /// moniker is fully reduced before registration. If an object is known by more than one fully reduced moniker, it should be /// registered under all such monikers. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-irunningobjecttable-register HRESULT Register( DWORD // grfFlags, IUnknown *punkObject, IMoniker *pmkObjectName, DWORD *pdwRegister ); uint Register([In] ROTFLAGS grfFlags, [In, MarshalAs(UnmanagedType.IUnknown)] object punkObject, [In] IMoniker pmkObjectName); /// Removes an entry from the running object table (ROT) that was previously registered by a call to IRunningObjectTable::Register. /// The identifier of the ROT entry to be revoked. /// This method can return the standard return values E_INVALIDARG and S_OK. /// /// /// This method undoes the effect of a call to IRunningObjectTable::Register, removing both the moniker and the pointer to the /// object identified by that moniker. /// /// Notes to Callers /// /// A moniker provider (hands out monikers identifying its objects to make them accessible to others) must call the /// Revoke method to revoke the registration of its objects when it stops running. It must have previously called /// IRunningObjectTable::Register and stored the identifier returned by that method; it uses that identifier when calling Revoke. /// /// /// The most common type of moniker provider is a compound-document link source. This includes server applications that support /// linking to their documents (or portions of a document) and container applications that support linking to embeddings within /// their documents. Server applications that do not support linking can also use the ROT to cooperate with container /// applications that support linking to embeddings. /// /// /// If you are writing a container application, you must revoke a document's registration when the document is closed. You must /// also revoke a document's registration before re-registering it when it is renamed. /// /// /// If you are writing a server application, you must revoke an object's registration when the object is closed. You must also /// revoke an object's registration before re-registering it when its container document is renamed (see IOleObject::SetMoniker). /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-irunningobjecttable-revoke HRESULT Revoke( DWORD // dwRegister ); void Revoke([In] uint dwRegister); /// Determines whether the object identified by the specified moniker is currently running. /// A pointer to the IMoniker interface on the moniker. /// If the object is in the running state, the return value is TRUE. Otherwise, it is FALSE. /// /// /// This method simply indicates whether a object is running. To retrieve a pointer to a running object, use the /// IRunningObjectTable::GetObject method. /// /// Notes to Callers /// /// Generally, you call the IsRunning method only if you are writing your own moniker class (that is, implementing the /// IMoniker interface). You typically call this method from your implementation of IMoniker::IsRunning. However, you should do /// so only if the pmkToLeft parameter of IMoniker::IsRunning is NULL. Otherwise, you should call /// IMoniker::IsRunning on your pmkToLeft parameter instead. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-irunningobjecttable-isrunning HRESULT IsRunning( // IMoniker *pmkObjectName ); [PreserveSig] HRESULT IsRunning([In] IMoniker pmkObjectName); /// /// Determines whether the object identified by the specified moniker is running, and if it is, retrieves a pointer to that object. /// /// A pointer to the IMoniker interface on the moniker. /// /// This method can return the following values. /// /// /// Return code /// Description /// /// /// S_OK /// Indicates that pmkObjectName was found in the ROT and a pointer was retrieved. /// /// /// S_FALSE /// /// There is no entry for pmkObjectName in the ROT, or that the object it identifies is no longer running (in which case, the /// entry is revoked). /// /// /// /// /// /// /// This method checks the ROT for the moniker specified by pmkObjectName. If that moniker had previously been registered with a /// call to IRunningObjectTable::Register, this method returns the pointer that was registered at that time. /// /// Notes to Callers /// /// Generally, you call the IRunningObjectTable::GetObject method only if you are writing your own moniker class (that /// is, implementing the IMoniker interface). You typically call this method from your implementation of IMoniker::BindToObject. /// /// /// However, note that not all implementations of IMoniker::BindToObject need to call this method. If you expect your moniker to /// have a prefix (indicated by a non- NULL pmkToLeft parameter to IMoniker::BindToObject), you should not check /// the ROT. The reason for this is that only complete monikers are registered with the ROT, and if your moniker has a prefix, /// your moniker is part of a composite and thus not complete. Instead, your moniker should request services from the object /// identified by the prefix (for example, the container of the object identified by your moniker). /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-irunningobjecttable-getobject HRESULT GetObject( // IMoniker *pmkObjectName, IUnknown **ppunkObject ); [return: MarshalAs(UnmanagedType.IUnknown)] object GetObject([In] IMoniker pmkObjectName); /// /// Records the time that a running object was last modified. The object must have previously been registered with the running /// object table (ROT). This method stores the time of last change in the ROT. /// /// The identifier of the ROT entry of the changed object. This value was previously returned by IRunningObjectTable::Register. /// A pointer to a FILETIME structure containing the object's last change time. /// This method can return the standard return values E_INVALIDARG and S_OK. /// /// The time recorded by this method can be retrieved by calling IRunningObjectTable::GetTimeOfLastChange. /// Notes to Callers /// /// A moniker provider (hands out monikers identifying its objects to make them accessible to others) must call the /// NoteChangeTime method whenever its objects are modified. It must have previously called IRunningObjectTable::Register /// and stored the identifier returned by that method; it uses that identifier when calling NoteChangeTime. /// /// /// The most common type of moniker provider is a compound-document link source. This includes server applications that support /// linking to their documents (or portions of a document) and container applications that support linking to embeddings within /// their documents. Server applications that do not support linking can also use the ROT to cooperate with container /// applications that support linking to embeddings. /// /// /// When an object is first registered in the ROT, the ROT records its last change time as the value returned by calling /// IMoniker::GetTimeOfLastChange on the moniker being registered. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-irunningobjecttable-notechangetime HRESULT // NoteChangeTime( DWORD dwRegister, FILETIME *pfiletime ); void NoteChangeTime([In] uint dwRegister, in FILETIME pfiletime); /// Retrieves the time that an object was last modified. /// A pointer to the IMoniker interface on the moniker. /// A pointer to a FILETIME structure that receives the object's last change time. /// /// /// This method returns the change time that was last reported for this object by a call to IRunningObjectTable::NoteChangeTime. /// If NoteChangeTime has not been called previously, the method returns the time that was recorded when the object was registered. /// /// /// This method is provided to enable checking whether a connection between two objects (represented by one object holding a /// moniker that identifies the other) is up-to-date. For example, if one object is holding cached information about the other /// object, this method can be used to check whether the object has been modified since the cache was last updated. See IMoniker::GetTimeOfLastChange. /// /// Notes to Callers /// /// Generally, you call GetTimeOfLastChange only if you are writing your own moniker class (that is, implementing the /// IMoniker interface). You typically call this method from your implementation of IMoniker::GetTimeOfLastChange. However, you /// should do so only if the pmkToLeft parameter of IMoniker::GetTimeOfLastChange is NULL. Otherwise, you should /// call IMoniker::GetTimeOfLastChange on your pmkToLeft parameter instead. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-irunningobjecttable-gettimeoflastchange HRESULT // GetTimeOfLastChange( IMoniker *pmkObjectName, FILETIME *pfiletime ); FILETIME GetTimeOfLastChange([In] IMoniker pmkObjectName); /// /// Creates and returns a pointer to an enumerator that can list the monikers of all the objects currently registered in the /// running object table (ROT). /// /// /// A pointer to an IEnumMoniker pointer variable that receives the interface pointer to the new enumerator for the ROT. When /// successful, the implementation calls AddRef on the enumerator; it is the caller's responsibility to call Release. If an /// error occurs; the implementation sets *ppenumMoniker to NULL. /// /// /// /// IRunningObjectTable::EnumRunning must create and return a pointer to an IEnumMoniker interface on an enumerator /// object. The standard enumerator methods can then be called to enumerate the monikers currently registered in the registry. /// The enumerator cannot be used to enumerate monikers that are registered in the ROT after the enumerator has been created. /// /// /// The EnumRunning method is intended primarily for the use by the system in implementing the alert object table. Note /// that OLE 2 does not include an implementation of the alert object table. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-irunningobjecttable-enumrunning HRESULT EnumRunning( // IEnumMoniker **ppenumMoniker ); IEnumMoniker EnumRunning(); } /// /// The IStorage interface supports the creation and management of structured storage objects. Structured storage allows /// hierarchical storage of information within a single file, and is often referred to as "a file system within a file". Elements of /// a structured storage object are storages and streams. Storages are analogous to directories, and streams are analogous to files. /// Within a structured storage there will be a primary storage object that may contain substorages, possibly nested, and streams. /// Storages provide the structure of the object, and streams contain the data, which is manipulated through the IStream interface. /// /// The IStorage interface provides methods for creating and managing the root storage object, child storage objects, and stream /// objects. These methods can create, open, enumerate, move, copy, rename, or delete the elements in the storage object. /// /// /// An application must release its IStorage pointers when it is done with the storage object to deallocate memory used. There are /// also methods for changing the date and time of an element. /// /// /// There are a number of different modes in which a storage object and its elements can be opened, determined by setting values /// from STGM Constants. One aspect of this is how changes are committed. You can set direct mode, in which changes to an object are /// immediately written to it, or transacted mode, in which changes are written to a buffer until explicitly committed. The IStorage /// interface provides methods for committing changes and reverting to the last-committed version. For example, a stream can be /// opened in read-only mode or read/write mode. For more information, see STGM Constants. /// /// Other methods provide access to information about a storage object and its elements through the STATSTG structure. /// [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComConversionLoss, Guid("0000000B-0000-0000-C000-000000000046")] [PInvokeData("Objidl.h", MSDNShortId = "aa380015")] public interface IStorage { /// /// The CreateStream method creates and opens a stream object with the specified name contained in this storage object. All /// elements within a storage objects, both streams and other storage objects, are kept in the same name space. /// /// /// A string that contains the name of the newly created stream. The name can be used later to open or reopen the stream. The /// name must not exceed 31 characters in length, not including the string terminator. The 000 through 01f characters, serving /// as the first character of the stream/storage name, are reserved for use by OLE. This is a compound file restriction, not a /// structured storage restriction. /// /// /// Specifies the access mode to use when opening the newly created stream. For more information and descriptions of the /// possible values, see STGM Constants. /// /// Reserved for future use; must be zero. /// Reserved for future use; must be zero. /// On return, the new IStream interface pointer. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] IStream CreateStream([In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName, [In] STGM grfMode, [In, Optional] uint reserved1, [In, Optional] uint reserved2); /// The OpenStream method opens an existing stream object within this storage object in the specified access mode. /// /// A string that contains the name of the stream to open. The 000 through 01f characters, serving as the first character of the /// stream/storage name, are reserved for use by OLE. This is a compound file restriction, not a structured storage restriction. /// /// Reserved for future use; must be NULL. /// /// Specifies the access mode to be assigned to the open stream. For more information and descriptions of possible values, see /// STGM Constants. Other modes you choose must at least specify STGM_SHARE_EXCLUSIVE when calling this method in the compound /// file implementation. /// /// Reserved for future use; must be zero. /// A IStream interface pointer to the newly opened stream object. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] IStream OpenStream([In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName, [In, Optional] IntPtr reserved1, [In] STGM grfMode, [In, Optional] uint reserved2); /// /// The CreateStorage method creates and opens a new storage object nested within this storage object with the specified name in /// the specified access mode. /// /// /// A string that contains the name of the newly created storage object. The name can be used later to reopen the storage /// object. The name must not exceed 31 characters in length, not including the string terminator. The 000 through 01f /// characters, serving as the first character of the stream/storage name, are reserved for use by OLE. This is a compound file /// restriction, not a structured storage restriction. /// /// /// A value that specifies the access mode to use when opening the newly created storage object. For more information and a /// description of possible values, see STGM Constants. /// /// Reserved for future use; must be zero. /// Reserved for future use; must be zero. /// On return, the new IStorage interface pointer. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] IStorage CreateStorage([In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName, [In] STGM grfMode, [In, Optional] uint reserved1, [In, Optional] uint reserved2); /// The OpenStorage method opens an existing storage object with the specified name in the specified access mode. /// /// A string that contains the name of the storage object to open. The 000 through 01f characters, serving as the first /// character of the stream/storage name, are reserved for use by OLE. This is a compound file restriction, not a structured /// storage restriction. It is ignored if pstgPriority is non-NULL. /// /// Must be NULL. A non-NULL value will return STG_E_INVALIDPARAMETER. /// /// Specifies the access mode to use when opening the storage object. For descriptions of the possible values, see STGM /// Constants. Other modes you choose must at least specify STGM_SHARE_EXCLUSIVE when calling this method. /// /// Must be NULL. A non-NULL value will return STG_E_INVALIDPARAMETER. /// Reserved for future use; must be zero. /// On return, the IStorage interface pointer to the opened storage. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] IStorage OpenStorage([In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName, [In, Optional, MarshalAs(UnmanagedType.Interface)] IStorage pstgPriority, [In] STGM grfMode, [In, Optional] SNB snbExclude, [In, Optional] uint reserved); /// The CopyTo method copies the entire contents of an open storage object to another storage object. /// /// The number of elements in the array pointed to by rgiidExclude. If rgiidExclude is NULL, then ciidExclude is ignored. /// /// /// An array of interface identifiers (IIDs) that either the caller knows about and does not want copied or that the storage /// object does not support, but whose state the caller will later explicitly copy. The array can include IStorage, indicating /// that only stream objects are to be copied, and IStream, indicating that only storage objects are to be copied. An array /// length of zero indicates that only the state exposed by the IStorage object is to be copied; all other interfaces on the /// object are to be ignored. Passing NULL indicates that all interfaces on the object are to be copied. /// /// /// 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. These elements are not created at the destination. If IID_IStorage is in the rgiidExclude array, this parameter /// is ignored. This parameter may be NULL. /// /// /// A pointer to the open storage object into which this storage object is to be copied. The destination storage object can be a /// different implementation of the IStorage interface from the source storage object. Thus, IStorage::CopyTo can use only /// publicly available methods of the destination storage object. If pstgDest is open in transacted mode, it can be reverted by /// calling its IStorage::Revert method. /// [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void CopyTo([In, Optional] uint ciidExclude, [In, Optional, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] Guid[] rgiidExclude, [In] SNB snbExclude, [In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest); /// /// The MoveElementTo method copies or moves a substorage or stream from this storage object to another storage object. /// /// A string that contains the name of the element in this storage object to be moved or copied. /// IStorage pointer to the destination storage object. /// A string that contains the new name for the element in its new storage object. /// /// Specifies whether the operation should be a move (STGMOVE_MOVE) or a copy (STGMOVE_COPY). See the STGMOVE enumeration. /// [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void MoveElementTo([In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName, [In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest, [In, MarshalAs(UnmanagedType.LPWStr)] string pwcsNewName, [In] STGMOVE grfFlags); /// /// The Commit method ensures that any changes made to a storage object open in transacted mode are reflected in the parent /// storage. For nonroot storage objects in direct mode, this method has no effect. For a root storage, it reflects the changes /// in the actual device; for example, a file on disk. For a root storage object opened in direct mode, always call the /// IStorage::Commit method prior to Release. IStorage::Commit flushes all memory buffers to the disk for a root storage in /// direct mode and will return an error code upon failure. Although Release also flushes memory buffers to disk, it has no /// capacity to return any error codes upon failure. Therefore, calling Release without first calling Commit causes /// indeterminate results. /// /// /// Controls how the changes are committed to the storage object. See the STGC enumeration for a definition of these values. /// [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Commit([In] STGC grfCommitFlags); /// The Revert method discards all changes that have been made to the storage object since the last commit operation. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Revert(); /// /// The EnumElements method retrieves a pointer to an enumerator object that can be used to enumerate the storage and stream /// objects contained within this storage object. /// /// Reserved for future use; must be zero. /// Reserved for future use; must be NULL. /// Reserved for future use; must be zero. /// The interface pointer to the new enumerator object. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] IEnumSTATSTG EnumElements([In, Optional] uint reserved1, [In, Optional] IntPtr reserved2, [In, Optional] uint reserved3); /// The DestroyElement method removes the specified storage or stream from this storage object. /// A string that contains the name of the storage or stream to be removed. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void DestroyElement([In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName); /// The RenameElement method renames the specified substorage or stream in this storage object. /// A string that contains the name of the substorage or stream to be changed. /// A string that contains the new name for the specified substorage or stream. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void RenameElement([In, MarshalAs(UnmanagedType.LPWStr)] string pwcsOldName, [In, MarshalAs(UnmanagedType.LPWStr)] string pwcsNewName); /// /// The SetElementTimes method sets the modification, access, and creation times of the specified storage element, if the /// underlying file system supports this method. /// /// /// The name of the storage object element whose times are to be modified. If NULL, the time is set on the root storage rather /// than one of its elements. /// /// /// Either the new creation time as the first element of the array for the element or NULL if the creation time is not to be modified. /// /// /// Either the new access time as the first element of the array for the element or NULL if the access time is not to be modified. /// /// /// Either the new modification time as the first element of the array for the element or NULL if the modification time is not /// to be modified. /// [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetElementTimes([In, Optional, MarshalAs(UnmanagedType.LPWStr)] string pwcsName, [In, Optional, MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] FILETIME[] pctime, [In, Optional, MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] FILETIME[] patime, [In, Optional, MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] FILETIME[] pmtime); /// The SetClass method assigns the specified class identifier (CLSID) to this storage object. /// The CLSID that is to be associated with the storage object. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetClass(in Guid clsid); /// /// The SetStateBits method stores up to 32 bits of state information in this storage object. This method is reserved for future use. /// /// /// Specifies the new values of the bits to set. No legal values are defined for these bits; they are all reserved for future /// use and must not be used by applications. /// /// A binary mask indicating which bits in grfStateBits are significant in this call. [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetStateBits([In] uint grfStateBits, [In] uint grfMask); /// The Stat method retrieves the STATSTG structure for this open storage object. /// /// On return, pointer to a STATSTG structure where this method places information about the open storage object. This parameter /// is NULL if an error occurs. /// /// /// Specifies that some of the members in the STATSTG structure are not returned, thus saving a memory allocation operation. /// Values are taken from the STATFLAG enumeration. /// [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Stat(out STATSTG pstatstg, [In] STATFLAG grfStatFlag); } /// /// [Use of ISurrogateService is not recommended; use IProcessInitControl instead.] /// Used to initialize, launch, and release a COM+ application. You can also refresh the catalog and shut down the process. /// // https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-isurrogateservice [PInvokeData("objidl.h", MSDNShortId = "NN:objidl.ISurrogateService")] [ComImport, Guid("000001d4-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface ISurrogateService { /// Initializes the process server. /// The process ID of the server application. /// A pointer to an instance of the IProcessLock interface. /// Indicates whether the application is aware of the initialization. /// If the method succeeds, the return value is S_OK. Otherwise, it is E_UNEXPECTED. // https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-isurrogateservice-init HRESULT Init( REFGUID // rguidProcessID, IProcessLock *pProcessLock, BOOL *pfApplicationAware ); [PreserveSig] HRESULT Init(in Guid rguidProcessID, IProcessLock pProcessLock, [MarshalAs(UnmanagedType.Bool)] out bool pfApplicationAware); /// Launches the application. /// The application identifier. /// The application type, as described in Remarks. /// If the method succeeds, the return value is S_OK. Otherwise, it is E_UNEXPECTED. /// /// The application type is defined by the following enum. /// /// typedef enum tagApplicationType { ServerApplication, LibraryApplication } ApplicationType; /// /// // https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-isurrogateservice-applicationlaunch HRESULT // ApplicationLaunch( REFGUID rguidApplID, ApplicationType appType ); [PreserveSig] HRESULT ApplicationLaunch(in Guid rguidApplID, ApplicationType appType); /// Releases the application. /// The application identifier. /// If the method succeeds, the return value is S_OK. Otherwise, it is E_UNEXPECTED. // https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-isurrogateservice-applicationfree HRESULT // ApplicationFree( REFGUID rguidApplID ); [PreserveSig] HRESULT ApplicationFree(in Guid rguidApplID); /// Refreshes the catalog. /// This parameter is reserved. /// If the method succeeds, the return value is S_OK. Otherwise, it is E_UNEXPECTED. // https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-isurrogateservice-catalogrefresh HRESULT CatalogRefresh( // ULONG ulReserved ); [PreserveSig] HRESULT CatalogRefresh([Optional] uint ulReserved); /// Shuts down the process. /// The shutdown type, as described in Remarks. /// If the method succeeds, the return value is S_OK. Otherwise, it is E_UNEXPECTED. /// /// The shutdown type is defined by the following enum. /// /// typedef enum tagShutdownType { IdleShutdown, ForcedShutdown } ShutdownType; /// /// // https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-isurrogateservice-processshutdown HRESULT // ProcessShutdown( ShutdownType shutdownType ); [PreserveSig] HRESULT ProcessShutdown(ShutdownType shutdownType); } /// Enumerates the values in a instance. /// The instance. /// The enumerated values. public static IEnumerable Enumerate(this IEnumContextProps e) => Vanara.Collections.IEnumFromCom.Create(e); /// Enumerates the values in a instance. /// The instance. /// The enumerated values. public static IEnumerable Enumerate(this IEnumSTATSTG e) => Vanara.Collections.IEnumFromCom.Create(e); /// Enumerates the values in a instance. /// The instance. /// The enumerated values. public static IEnumerable Enumerate(this IEnumUnknown e) => Vanara.Collections.IEnumFromCom.Create(e); /// Enumerates the values in a instance. /// /// The COM interface type to query for from each item in the collection. Note that if this type cannot be retrieved, an exception /// will be thrown. /// /// The instance. /// The enumerated values. public static IEnumerable Enumerate(this IEnumUnknown e) where T : class => e.Enumerate().Select(p => p == IntPtr.Zero ? null : (T)Marshal.GetObjectForIUnknown(p)); /// Structure returned by IEnumContextProps::Enum [PInvokeData("objidl.h", MSDNShortId = "64591e45-5478-4360-8c1f-08b09b5aef8e")] // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-ienumcontextprops-next [StructLayout(LayoutKind.Sequential)] public struct ContextProperty { /// public Guid policyId; /// public uint flags; /// public IntPtr pUnk; } /// /// Specifies information about the target device for which data is being composed. DVTARGETDEVICE contains enough /// information about a Windows target device so a handle to a device context ( HDC) can be created using the CreateDC function. /// /// /// Some OLE 1 client applications incorrectly construct target devices by allocating too few bytes in the DEVMODE structure for the /// DVTARGETDEVICE. They typically only supply the number of bytes in the dmSize member of DEVMODE. The number /// of bytes to be allocated should be the sum of dmSize + dmDriverExtra. When a call is made to the CreateDC function /// with an incorrect target device, the printer driver tries to access the additional bytes and unpredictable results can occur. To /// help protect against a crash and make the additional bytes available, OLE pads the size of OLE 2 target devices created from OLE /// 1 target devices. /// // https://docs.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-dvtargetdevice typedef struct tagDVTARGETDEVICE { DWORD // tdSize; WORD tdDriverNameOffset; WORD tdDeviceNameOffset; WORD tdPortNameOffset; WORD tdExtDevmodeOffset; BYTE tdData[1]; } DVTARGETDEVICE; [PInvokeData("objidl.h", MSDNShortId = "724ff714-c170-4d06-92cb-e042e41c0af2")] [StructLayout(LayoutKind.Sequential)] public struct DVTARGETDEVICE { /// /// The size, in bytes, of the DVTARGETDEVICE structure. The initial size is included so the structure can be copied more easily. /// public uint tdSize; /// /// The offset, in bytes, from the beginning of the structure to the device driver name, which is stored as a NULL-terminated /// string in the tdData buffer. /// public ushort tdDriverNameOffset; /// /// The offset, in bytes, from the beginning of the structure to the device name, which is stored as a NULL-terminated string in /// the tdData buffer. This value can be zero to indicate no device name. /// public ushort tdDeviceNameOffset; /// /// The offset, in bytes, from the beginning of the structure to the port name, which is stored as a NULL-terminated string in /// the tdData buffer. This value can be zero to indicate no port name. /// public ushort tdPortNameOffset; /// The offset, in bytes, from the beginning of the structure to the DEVMODE structure retrieved by calling DocumentProperties. public ushort tdExtDevmodeOffset; private byte _tdData; } /// /// The StorageLayout structure describes a single block of data, including its name, location, and length. To optimize a /// compound file, an application or layout tool passes an array of StorageLayout structures in a call to ILayoutStorage::LayoutScript. /// /// /// An array of StorageLayout structures might appear as follows. /// /// Note The parameters cOffset and cBytes are LARGE_INTEGER structures, used to represent a 64-bit signed integer /// value as a union of two 32-bit members. The two 32-bit members must be represented as a LARGE_INTEGER structure with /// DWORD LowPart and LONG HighPart as the structure members. (LowPart specifies the low-order 32 bits and HighPart /// specifies the high-order 32 bits.) If your compiler has built-in support for 64-bit integers, use the QuadPart member of /// the LARGE_INTEGER structure to store the 64-bit integer. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/ns-objidl-storagelayout typedef struct tagStorageLayout { DWORD // LayoutType; OLECHAR *pwcsElementName; LARGE_INTEGER cOffset; LARGE_INTEGER cBytes; } StorageLayout; [PInvokeData("objidl.h", MSDNShortId = "1e4fb36d-077b-44bd-ab6e-8c122ec95a46")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct StorageLayout { /// /// The type of element to be written. Values are taken from the STGTY enumeration. STGTY_STREAM means read the block of /// data named by pwcsElementName. STGTY_STORAGE means open the storage specified in pwcsElementName. /// STGTY_REPEAT is used in multimedia applications to interface audio, video, text, and other elements. An opening /// STGTY_REPEAT value means that the elements that follow are to be repeated a specified number of times. The closing /// STGTY_REPEAT value marks the end of those elements that are to be repeated. Nested STGTY_REPEAT value pairs /// are permitted. /// public STGTY LayoutType; /// /// The null-terminated Unicode string name of the storage or stream. If the element is a substorage or embedded object, the /// fully qualified storage path must be specified; for example, "RootStorageName\SubStorageName\Substream". /// [MarshalAs(UnmanagedType.LPWStr)] public string pwcsElementName; /// /// /// Where the value of the LayoutType member is STGTY_STREAM, this flag specifies the beginning offset into the /// steam named in the pwscElementName member. /// /// Where LayoutType is STGTY_STORAGE, this flag should be set to zero. /// Where LayoutType is STGTY_REPEAT, this flag should be set to zero. /// public long cOffset; /// /// Length, in bytes, of the data block named in pwcsElementName. /// /// Where LayoutType is STGTY_STREAM, cBytes specifies the number of bytes to read at cOffset from /// the stream named in pwcsElementName. /// /// Where LayoutType is STGTY_STORAGE, this flag is ignored. /// /// Where LayoutType is STGTY_REPEAT, a positive cBytes specifies the beginning of a repeat block. /// STGTY_REPEAT with zero cBytes marks the end of a repeat block. /// /// /// A beginning block value of STG_TOEND specifies that elements in a following block are to be repeated after each /// stream has been completely read. /// /// public long cBytes; } /// /// Contains parameters used during a moniker-binding operation. /// The BIND_OPTS2 or BIND_OPTS3 structure can be used in place of the BIND_OPTS structure. /// /// /// /// A BIND_OPTS structure is stored in a bind context; the same bind context is used by each component of a composite moniker /// during binding, allowing the same parameters to be passed to all components of a composite moniker. See IBindCtx for more /// information about bind contexts. /// /// /// Moniker clients (use a moniker to acquire an interface pointer to an object) typically do not need to specify values for the /// members of this structure. The CreateBindCtx function creates a bind context with the bind options set to default values that /// are suitable for most situations; the BindMoniker function does the same thing when creating a bind context for use in binding a /// moniker. If you want to modify the values of these bind options, you can do so by passing a BIND_OPTS structure to the /// IBindCtx::SetBindOptions method. Moniker implementers can pass a BIND_OPTS structure to the IBindCtx::GetBindOptions /// method to retrieve the values of these bind options. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/ns-objidl-tagbind_opts typedef struct tagBIND_OPTS { DWORD cbStruct; // DWORD grfFlags; DWORD grfMode; DWORD dwTickCountDeadline; } BIND_OPTS, *LPBIND_OPTS; [PInvokeData("objidl.h", MSDNShortId = "764f09c9-ff20-4ae2-b94f-4b0a1e117e49")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class BIND_OPTS_V { /// The size of this structure, in bytes. public uint cbStruct; /// /// Flags that control aspects of moniker binding operations. This value is any combination of the bit flags in the BIND_FLAGS /// enumeration. The CreateBindCtx function initializes this member to zero. /// public BIND_FLAGS grfFlags; /// /// Flags that should be used when opening the file that contains the object identified by the moniker. Possible values are the /// STGM constants. The binding operation uses these flags in the call to IPersistFile::Load when loading the file. If the /// object is already running, these flags are ignored by the binding operation. The CreateBindCtx function initializes this /// field to STGM_READWRITE. /// public STGM grfMode; /// /// /// The clock time by which the caller would like the binding operation to be completed, in milliseconds. This member lets the /// caller limit the execution time of an operation when speed is of primary importance. A value of zero indicates that there is /// no deadline. Callers most often use this capability when calling the IMoniker::GetTimeOfLastChange method, though it can be /// usefully applied to other operations as well. The CreateBindCtx function initializes this field to zero. /// /// /// Typical deadlines allow for a few hundred milliseconds of execution. This deadline is a recommendation, not a requirement; /// however, operations that exceed their deadline by a large amount may cause delays for the end user. Each moniker /// implementation should try to complete its operation by the deadline, or fail with the error MK_E_EXCEEDEDDEADLINE. /// /// /// If a binding operation exceeds its deadline because one or more objects that it needs are not running, the moniker /// implementation should register the objects responsible in the bind context using the IBindCtx::RegisterObjectParam. The /// objects should be registered under the parameter names "ExceededDeadline", "ExceededDeadline1", "ExceededDeadline2", and so /// on. If the caller later finds the object in the running object table, the caller can retry the binding operation. /// /// /// The GetTickCount function indicates the number of milliseconds since system startup, and wraps back to zero after 2^31 /// milliseconds. Consequently, callers should be careful not to inadvertently pass a zero value (which indicates no deadline), /// and moniker implementations should be aware of clock wrapping problems. /// /// public uint dwTickCountDeadline; /// Initializes a new instance of the class. public BIND_OPTS_V() => cbStruct = (uint)Marshal.SizeOf(typeof(BIND_OPTS_V)); /// Performs an implicit conversion from to . /// The instance. /// The result of the conversion. public static implicit operator System.Runtime.InteropServices.ComTypes.BIND_OPTS(BIND_OPTS_V bo) => new System.Runtime.InteropServices.ComTypes.BIND_OPTS { cbStruct = (int)bo.cbStruct, grfFlags = (int)bo.grfFlags, grfMode = (int)bo.grfFlags, dwTickCountDeadline = (int)bo.dwTickCountDeadline }; /// /// Performs an implicit conversion from to . /// /// The instance. /// The result of the conversion. public static implicit operator BIND_OPTS_V(System.Runtime.InteropServices.ComTypes.BIND_OPTS bo) => new BIND_OPTS_V() { grfFlags = (BIND_FLAGS)bo.grfFlags, grfMode = (STGM)bo.grfFlags, dwTickCountDeadline = (uint)bo.dwTickCountDeadline }; } /// Contains parameters used during a moniker-binding operation. /// /// /// A BIND_OPTS2 structure is stored in a bind context; the same bind context is used by each component of a composite /// moniker during binding, allowing the same parameters to be passed to all components of a composite moniker. See IBindCtx for /// more information about bind contexts. /// /// /// Moniker clients (use a moniker to acquire an interface pointer to an object) typically do not need to specify values for the /// members of this structure. The CreateBindCtx function creates a bind context with the bind options set to default values that /// are suitable for most situations; the BindMoniker function does the same thing when creating a bind context for use in binding a /// moniker. If you want to modify the values of these bind options, you can do so by passing a BIND_OPTS2 structure to the /// IBindCtx::SetBindOptions method. Moniker implementers can pass a BIND_OPTS2 structure to the IBindCtx::GetBindOptions /// method to retrieve the values of these bind options. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/ns-objidl-tagbind_opts2 typedef struct tagBIND_OPTS2 { DWORD // dwTrackFlags; DWORD dwClassContext; LCID locale; COSERVERINFO *pServerInfo; base_class tagBIND_OPTS; } BIND_OPTS2, *LPBIND_OPTS2; [PInvokeData("objidl.h", MSDNShortId = "fb2aa8c1-dddc-480e-b544-61a1074125ef")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class BIND_OPTS2 : BIND_OPTS_V { /// /// /// A moniker can use this value during link tracking. If the original persisted data that the moniker is referencing has been /// moved, the moniker can attempt to reestablish the link by searching for the original data though some adequate mechanism. /// This member provides additional information on how the link should be resolved. See the documentation of the fFlags /// parameter in IShellLink::Resolve. /// /// COM's file moniker implementation uses the shell link mechanism to reestablish links and passes these flags to IShellLink::Resolve. /// public uint dwTrackFlags; /// /// The class context, taken from the CLSCTX enumeration, that is to be used for instantiating the object. Monikers typically /// pass this value to the dwClsContext parameter of CoCreateInstance. /// public CLSCTX dwClassContext; /// /// The LCID value indicating the client's preference for the locale to be used by the object to which they are binding. A /// moniker passes this value to IClassActivator::GetClassObject. /// public LCID locale; /// /// A pointer to a COSERVERINFO structure. This member allows clients calling IMoniker::BindToObject to specify server /// information. Clients may pass a BIND_OPTS2 structure to the IBindCtx::SetBindOptions method. If a server name is /// specified in the COSERVERINFO structure, the moniker bind will be forwarded to the specified computer. /// SetBindOptions only copies the struct members of BIND_OPTS2, not the COSERVERINFO structure and the /// pointers it contains. Callers may not free any of these pointers until the bind context is released. COM's new class moniker /// does not currently honor the pServerInfo flag. /// public IntPtr pServerInfo; /// Initializes a new instance of the class. public BIND_OPTS2() => cbStruct = (uint)Marshal.SizeOf(typeof(BIND_OPTS2)); } /// Contains parameters used during a moniker-binding operation. /// /// /// A BIND_OPTS3 structure is stored in a bind context; the same bind context is used by each component of a composite /// moniker during binding, allowing the same parameters to be passed to all components of a composite moniker. See IBindCtx for /// more information about bind contexts. /// /// /// Moniker clients (use a moniker to acquire an interface pointer to an object) typically do not need to specify values for the /// members of this structure. The CreateBindCtx function creates a bind context with the bind options set to default values that /// are suitable for most situations; the BindMoniker function does the same thing when creating a bind context for use in binding a /// moniker. If you want to modify the values of these bind options, you can do so by passing a BIND_OPTS3 structure to the /// IBindCtx::SetBindOptions method. Moniker implementers can pass a BIND_OPTS3 structure to the IBindCtx::GetBindOptions /// method to retrieve the values of these bind options. /// /// // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/ns-objidl-tagbind_opts3 typedef struct tagBIND_OPTS3 { HWND hwnd; // base_class tagBIND_OPTS2; } BIND_OPTS3, *LPBIND_OPTS3; [PInvokeData("objidl.h", MSDNShortId = "7e668313-229a-4d04-b8a2-d5072c87a5b5")] [StructLayout(LayoutKind.Sequential)] public class BIND_OPTS3 : BIND_OPTS2 { /// /// A handle to the window that becomes the owner of the elevation UI, if applicable. If hwnd is NULL, COM will /// call the GetActiveWindow function to find a window handle associated with the current thread. This case might occur if the /// client is a script, which cannot fill in a BIND_OPTS3 structure. In this case, COM will try to use the window /// associated with the script thread. /// public HWND hwnd; /// Initializes a new instance of the class. public BIND_OPTS3() => cbStruct = (uint)Marshal.SizeOf(typeof(BIND_OPTS3)); } /// Simple generic implementation of . /// The type to enumerate. /// /// public class IEnumUnknownImpl : IReadOnlyList, IEnumUnknown where T : class { private int current = -1; private List items; /// Initializes a new instance of the class using an existing enumeration. /// The items to enumerate. /// items public IEnumUnknownImpl(IEnumerable items) { this.items = new List(items ?? throw new ArgumentNullException(nameof(items))); } /// Gets the number of elements in the collection. /// The number of elements in the collection. public int Count => items.Count; /// Gets the element at the specified index in the read-only list. /// The element at the specified index in the read-only list. /// The zero-based index of the element to get. public T this[int index] => items[index]; /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() => items.GetEnumerator(); /// Retrieves the specified number of items in the enumeration sequence. /// /// The number of items to be retrieved. If there are fewer than the requested number of items left in the sequence, this method /// retrieves the remaining elements. /// /// /// An array of enumerated items. /// /// The enumerator is responsible for calling AddRef, and the caller is responsible for calling Release through each pointer /// enumerated. If celt is greater than 1, the caller must also pass a non-NULL pointer passed to pceltFetched to know how many /// pointers to release. /// /// /// /// The number of items that were retrieved. This parameter is always less than or equal to the number of items requested. /// /// If the method retrieves the number of items requested, the return value is S_OK. Otherwise, it is S_FALSE. /// rgelt - The length is not large enough for the requested number of items. public HRESULT Next(uint celt, IntPtr[] rgelt, out uint pceltFetched) { pceltFetched = 0; if (++current < items.Count) { pceltFetched = Math.Min((uint)(items.Count - current), celt); if (rgelt is null || rgelt.Length < pceltFetched) throw new ArgumentOutOfRangeException(nameof(rgelt), "The length is not large enough for the requested number of items."); for (int i = 0; i < pceltFetched; i++) rgelt[i] = Marshal.GetIUnknownForObject(items[current + i]); return HRESULT.S_OK; } return HRESULT.S_FALSE; } /// Resets the enumeration sequence to the beginning. /// /// There is no guarantee that the same set of objects will be enumerated after the reset operation has completed. A static /// collection is reset to the beginning, but it can be too expensive for some collections, such as files in a directory, to /// guarantee this condition. /// public void Reset() => current = -1; /// Skips over the specified number of items in the enumeration sequence. /// The number of items to be skipped. /// If the method skips the number of items requested, the return value is S_OK. Otherwise, it is S_FALSE. public HRESULT Skip(uint celt) { var temp = current + (int)celt; if (temp > items.Count - 1) return HRESULT.S_FALSE; current = temp; return HRESULT.S_OK; } /// /// Creates a new enumerator that contains the same enumeration state as the current one. /// /// This method makes it possible to record a point in the enumeration sequence in order to return to that point at a later /// time. The caller must release this new enumerator separately from the first enumerator. /// /// /// A pointer to the cloned enumerator object. IEnumUnknown IEnumUnknown.Clone() => new IEnumUnknownImpl(items); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); } /// Contains information about incoming calls. // https://docs.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-interfaceinfo typedef struct tagINTERFACEINFO { IUnknown // *pUnk; IID iid; WORD wMethod; } INTERFACEINFO, *LPINTERFACEINFO; [PInvokeData("objidl.h", MSDNShortId = "5c2c07bf-1c15-4f21-baef-103837ea24d0")] [StructLayout(LayoutKind.Sequential)] public class INTERFACEINFO { /// A pointer to the IUnknown interface on the object. [MarshalAs(UnmanagedType.IUnknown)] public object pUnk; /// The identifier of the requested interface. public Guid iid; /// The interface method. public ushort wMethod; } /// /// A string name block (SNB) is a pointer to an array of pointers to strings, that ends in a NULL pointer. String name blocks are /// used by the IStorage interface and by function calls that open storage objects. The strings point to contained storage objects /// or streams that are to be excluded in the open calls. /// /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public class SNB : IDisposable { private readonly SafeCoTaskMemHandle ptr; /// Initializes a new instance of the class. /// The list of names to associate with this instance. public SNB(IEnumerable names) => ptr = names == null ? SafeCoTaskMemHandle.Null : SafeCoTaskMemHandle.CreateFromStringList(names, StringListPackMethod.Packed, CharSet.Unicode); /// Prevents a default instance of the class from being created. private SNB() { } /// Initializes a new instance of the class. /// The native pointer. private SNB(IntPtr p) => ptr = new SafeCoTaskMemHandle(p, 0, true); /// Gets the names. /// The names. public IEnumerable Names => ptr.ToStringEnum(Count, CharSet.Unicode); private int Count => ptr.DangerousGetHandle().GetNulledPtrArrayLength(); /// Performs an implicit conversion from to . /// The native pointer to take ownership of. /// The result of the conversion. public static implicit operator SNB(IntPtr p) => new SNB(p); /// Performs an implicit conversion from to . /// The names. /// The result of the conversion. public static implicit operator SNB(string[] names) => new SNB(names); /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. void IDisposable.Dispose() => ptr?.Dispose(); } } }