From c727cfa0552d3a8726986b1e68dcdf61a74a476a Mon Sep 17 00:00:00 2001 From: dahall Date: Thu, 16 Jul 2020 07:22:01 -0600 Subject: [PATCH] Completed work on BindContext which wraps IBindCtx --- UnitTests/Windows.Shell/BindContextTests.cs | 42 ++ UnitTests/Windows.Shell/Windows.Shell.csproj | 1 + Windows.Shell/BindContext.cs | 676 ++++++++++++++------------- 3 files changed, 386 insertions(+), 333 deletions(-) create mode 100644 UnitTests/Windows.Shell/BindContextTests.cs diff --git a/UnitTests/Windows.Shell/BindContextTests.cs b/UnitTests/Windows.Shell/BindContextTests.cs new file mode 100644 index 00000000..60f507bb --- /dev/null +++ b/UnitTests/Windows.Shell/BindContextTests.cs @@ -0,0 +1,42 @@ +using NUnit.Framework; +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using Vanara.InteropServices; +using Vanara.PInvoke; +using Vanara.PInvoke.Tests; +using static Vanara.PInvoke.Shell32; + +namespace Vanara.Windows.Shell.Tests +{ + [TestFixture] + public class BindContextTests + { + [Test] + public void BindContextTest() + { + var bc = new BindContext(); + Assert.That((int)bc.BindFlags, Is.Zero); + Assert.That(bc.Deadline, Is.EqualTo(TimeSpan.Zero)); + Assert.That(() => bc.Dispose(), Throws.Nothing); + } + + [Test] + public void BindContext2Test() + { + var bc = new BindContext(timeout: TimeSpan.FromSeconds(30), bindFlags: Ole32.BIND_FLAGS.BIND_MAYBOTHERUSER); + Assert.That(bc.BindFlags, Is.EqualTo(Ole32.BIND_FLAGS.BIND_MAYBOTHERUSER)); + Assert.That(bc.Deadline, Is.EqualTo(TimeSpan.FromSeconds(30))); + Assert.That(() => bc.Dispose(), Throws.Nothing); + } + + [Test] + public void EnumObjectParamTest() + { + using var bc = new BindContext(); + Assert.That(bc.EnumObjectParam(), Is.Not.Empty); + TestContext.Write(string.Join(", ", bc.EnumObjectParam())); + } + } +} \ No newline at end of file diff --git a/UnitTests/Windows.Shell/Windows.Shell.csproj b/UnitTests/Windows.Shell/Windows.Shell.csproj index af389808..c57d9504 100644 --- a/UnitTests/Windows.Shell/Windows.Shell.csproj +++ b/UnitTests/Windows.Shell/Windows.Shell.csproj @@ -48,6 +48,7 @@ + diff --git a/Windows.Shell/BindContext.cs b/Windows.Shell/BindContext.cs index 58e27de6..eb37efd3 100644 --- a/Windows.Shell/BindContext.cs +++ b/Windows.Shell/BindContext.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Vanara.Extensions; @@ -9,11 +10,10 @@ using static Vanara.PInvoke.Ole32; namespace Vanara.Windows.Shell { - // TODO: Fix memory problems and release BindContext /// Wraps the COM type. /// [ComVisible(true)] - class BindContext : IDisposable, IBindCtxV, IBindCtx + public class BindContext : IDisposable, IBindCtxV, IBindCtx { private readonly IBindCtxV iBindCtx; @@ -70,10 +70,10 @@ namespace Vanara.Windows.Shell /// 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 IBindCtxV::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. + /// 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 IBindCtxV::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. /// /// public TimeSpan Deadline @@ -86,16 +86,16 @@ namespace Vanara.Windows.Shell /// 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 uint Locale + public LCID Locale { get => GetOptionValue(nameof(BIND_OPTS2.locale)); - set => SetOptionValue(nameof(BIND_OPTS2.locale), value); + set => SetOptionValue(nameof(BIND_OPTS2.locale), (uint)value); } /// /// Flags that should be used when opening the file that contains the object identified by the moniker. 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 default value is STGM_READWRITE. + /// 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 default value is STGM_READWRITE. /// public STGM OpenMode { @@ -105,9 +105,9 @@ namespace Vanara.Windows.Shell /// /// - /// 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. + /// 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. /// @@ -119,8 +119,8 @@ namespace Vanara.Windows.Shell /// /// 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 + /// 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 WindowHandle @@ -132,6 +132,29 @@ namespace Vanara.Windows.Shell /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public void Dispose() => Marshal.FinalReleaseComObject(iBindCtx); + /// + /// Retrieves a pointer to an interface that can be used to enumerate the keys of the bind context's string-keyed table of pointers. + /// + /// A list of keys of the bind context's string-keyed table of pointers. + /// + /// The keys returned by the enumerator are the ones previously specified in calls to IBindCtxV::RegisterObjectParam. + /// Notes to Callers + /// + /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a + /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a + /// string known to both parties so that the other party can later retrieve it from the bind context. + /// + /// + /// In the system implementation of the IBindCtxV interface, this method is not implemented. Therefore, calling this method results + /// in a return value of E_NOTIMPL. + /// + /// + public IEnumerable EnumObjectParam() + { + ((IBindCtxV)this).EnumObjectParam(out var ppenum).ThrowIfFailed(); + return ppenum.Enum().ToArray(); + } + /// /// Retrieves an interface pointer to the object associated with the specified key in the bind context's string-keyed table of pointers. /// @@ -162,7 +185,10 @@ namespace Vanara.Windows.Shell /// /// public object GetObjectParam(string pszKey) - { ((IBindCtxV)this).GetObjectParam(pszKey, out var ppunk).ThrowIfFailed(); return ppunk; } + { + ((IBindCtxV)this).GetObjectParam(pszKey, out var ppunk).ThrowIfFailed(); + return ppunk; + } /// /// Retrieves an interface pointer to the running object table (ROT) for the computer on which this bind context is running. @@ -190,7 +216,10 @@ namespace Vanara.Windows.Shell /// /// public Ole32.IRunningObjectTable GetRunningObjectTable() - { ((IBindCtxV)this).GetRunningObjectTable(out var pprot).ThrowIfFailed(); return pprot; } + { + ((IBindCtxV)this).GetRunningObjectTable(out var pprot).ThrowIfFailed(); + return pprot; + } /// Registers an object with the bind context to ensure that the object remains active until the bind context is released. /// A pointer to the IUnknown interface on the object that is being registered as bound. @@ -203,13 +232,13 @@ namespace Vanara.Windows.Shell /// /// RegisterObjectBound calls AddRef to create an additional reference to the object. You must, however, still release your /// own copy of the pointer. Calling this method twice for the same object creates two references to that object. You can release a - /// reference obtained through a call to this method by calling IBindCtxV::RevokeObjectBound. All references held by the bind context - /// are released when the bind context itself is released. + /// reference obtained through a call to this method by calling IBindCtxV::RevokeObjectBound. All references held by the bind + /// context are released when the bind context itself is released. /// /// /// Calling RegisterObjectBound to register an object with a bind context keeps the object active until the bind context is - /// released. Reusing a bind context in a subsequent binding operation (either for another piece of the same composite moniker or for - /// a different moniker) can make the subsequent binding operation more efficient because it doesn't have to reload that object. + /// released. Reusing a bind context in a subsequent binding operation (either for another piece of the same composite moniker or + /// for a different moniker) can make the subsequent binding operation more efficient because it doesn't have to reload that object. /// This, however, improves performance only if the subsequent binding operation requires some of the same objects as the original /// one, so you need to balance the possible performance improvement of reusing a bind context against the costs of keeping objects /// activated unnecessarily. @@ -241,9 +270,9 @@ namespace Vanara.Windows.Shell /// moniker clients (those who use monikers to bind to objects). /// /// - /// In implementing a new moniker class, you call this method when an error occurs during moniker binding to inform the caller of the - /// cause of the error. The key that you would obtain with a call to this method would depend on the error condition. Following is a - /// list of common moniker binding errors, describing for each the keys that would be appropriate: + /// In implementing a new moniker class, you call this method when an error occurs during moniker binding to inform the caller of + /// the cause of the error. The key that you would obtain with a call to this method would depend on the error condition. Following + /// is a list of common moniker binding errors, describing for each the keys that would be appropriate: /// /// /// @@ -264,8 +293,8 @@ namespace Vanara.Windows.Shell /// /// E_CLASSNOTFOUND—The "ClassNotFound" key indicates a moniker whose class could not be found. (The server for the object /// identified by this moniker could not be located.) If this key is used for an OLE compound-document object, the caller can use - /// IMoniker::BindToStorage to bind to the object and then try to carry out a Treat As... or Convert To... operation to - /// associate the object with a different server. If this is successful, the caller can retry the binding operation. + /// IMoniker::BindToStorage to bind to the object and then try to carry out a Treat As... or Convert To... operation + /// to associate the object with a different server. If this is successful, the caller can retry the binding operation. /// /// /// @@ -332,12 +361,36 @@ namespace Vanara.Windows.Shell /// string known to both parties so that the other party can later retrieve it from the bind context. /// /// - /// In the system implementation of the IBindCtxV interface, this method is not implemented. Therefore, calling this method results in - /// a return value of E_NOTIMPL. + /// In the system implementation of the IBindCtxV interface, this method is not implemented. Therefore, calling this method results + /// in a return value of E_NOTIMPL. /// /// HRESULT IBindCtxV.EnumObjectParam(out IEnumString ppenum) => iBindCtx.EnumObjectParam(out ppenum); + /// + /// Retrieves a pointer to an interface that can be used to enumerate the keys of the bind context's string-keyed table of pointers. + /// + /// + /// The address of an IEnumString* pointer variable that receives the interface pointer to the enumerator. If an error occurs, + /// *ppenum is set to NULL. If *ppenum is non- NULL, the implementation calls AddRef on *ppenum; it is the caller's + /// responsibility to call Release. + /// + /// This method can return the standard return values E_OUTOFMEMORY and S_OK. + /// + /// The keys returned by the enumerator are the ones previously specified in calls to IBindCtx::RegisterObjectParam. + /// Notes to Callers + /// + /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a + /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a + /// string known to both parties so that the other party can later retrieve it from the bind context. + /// + /// + /// In the system implementation of the IBindCtx interface, this method is not implemented. Therefore, calling this method results + /// in a return value of E_NOTIMPL. + /// + /// + void IBindCtx.EnumObjectParam(out IEnumString ppenum) => iBindCtx.EnumObjectParam(out ppenum).ThrowIfFailed(); + /// Retrieves the binding options stored in this bind context. /// /// A pointer to an initialized structure that receives the current binding parameters on return. See BIND_OPTS, BIND_OPTS2, and BIND_OPTS3. @@ -345,8 +398,8 @@ namespace Vanara.Windows.Shell /// This method can return the standard return values E_UNEXPECTED and S_OK. /// /// - /// A bind context contains a block of parameters that are common to most IMoniker operations and that do not change as the operation - /// moves from piece to piece of a composite moniker. + /// A bind context contains a block of parameters that are common to most IMoniker operations and that do not change as the + /// operation moves from piece to piece of a composite moniker. /// /// Notes to Callers /// @@ -360,14 +413,36 @@ namespace Vanara.Windows.Shell /// HRESULT IBindCtxV.GetBindOptions([In, Out] BIND_OPTS_V pbindopts) => iBindCtx.GetBindOptions(pbindopts); + /// Retrieves the binding options stored in this bind context. + /// + /// A pointer to an initialized structure that receives the current binding parameters on return. See BIND_OPTS, BIND_OPTS2, and BIND_OPTS3. + /// + /// This method can return the standard return values E_UNEXPECTED and S_OK. + /// + /// + /// A bind context contains a block of parameters that are common to most IMoniker operations and that do not change as the + /// operation moves from piece to piece of a composite moniker. + /// + /// Notes to Callers + /// + /// You typically call this method if you are writing your own moniker class. (This requires that you implement the IMoniker + /// interface.) You call this method to retrieve the parameters specified by the moniker client. + /// + /// + /// You must initialize the structure that is filled in by this method. Before calling this method, you must initialize the + /// cbStruct member to the size of the structure. + /// + /// + void IBindCtx.GetBindOptions(ref System.Runtime.InteropServices.ComTypes.BIND_OPTS pbindopts) { var bo = new BIND_OPTS_V(); iBindCtx.GetBindOptions(bo).ThrowIfFailed(); pbindopts = bo; } + /// /// Retrieves an interface pointer to the object associated with the specified key in the bind context's string-keyed table of pointers. /// /// The bind context string key to be searched for. Key string comparison is case-sensitive. /// /// The address of an IUnknown* pointer variable that receives the interface pointer to the object associated with pszKey. When - /// successful, the implementation calls AddRef on *ppunk. It is the caller's responsibility to call Release. If an error occurs, the - /// implementation sets *ppunk to NULL. + /// successful, the implementation calls AddRef on *ppunk. It is the caller's responsibility to call Release. If an error occurs, + /// the implementation sets *ppunk to NULL. /// /// If the method succeeds, the return value is S_OK. Otherwise, it is E_FAIL. /// @@ -393,292 +468,14 @@ namespace Vanara.Windows.Shell /// HRESULT IBindCtxV.GetObjectParam([MarshalAs(UnmanagedType.LPWStr)] string pszKey, [MarshalAs(UnmanagedType.Interface)] out object ppunk) => iBindCtx.GetObjectParam(pszKey, out ppunk); - /// - /// Retrieves an interface pointer to the running object table (ROT) for the computer on which this bind context is running. - /// - /// - /// The address of a IRunningObjectTable* pointer variable that receives the interface pointer to the running object table. If an - /// error occurs, *pprot is set to NULL. If *pprot is non- NULL, the implementation calls AddRef on the running table - /// object; it is the caller's responsibility to call Release. - /// - /// This method can return the standard return values E_OUTOFMEMORY, E_UNEXPECTED, and S_OK. - /// - /// - /// The running object table is a globally accessible table on each computer. It keeps track of all the objects that are currently - /// running on the computer. - /// - /// Notes to Callers - /// - /// Typically, those implementing a new moniker class (through an implementation of IMoniker interface) call - /// GetRunningObjectTable. It is useful to call this method in an implementation of IMoniker::BindToObject or - /// IMoniker::IsRunning to check whether an object is currently running. You can also call this method in the implementation of - /// IMoniker::GetTimeOfLastChange to learn when a running object was last modified. - /// - /// - /// Moniker implementations should call this method instead of using the GetRunningObjectTable function. This makes it - /// possible for future implementations of IBindCtxV to modify binding behavior. - /// - /// - HRESULT IBindCtxV.GetRunningObjectTable(out Ole32.IRunningObjectTable pprot) => iBindCtx.GetRunningObjectTable(out pprot); - - /// Registers an object with the bind context to ensure that the object remains active until the bind context is released. - /// A pointer to the IUnknown interface on the object that is being registered as bound. - /// This method can return the standard return values E_OUTOFMEMORY and S_OK. - /// - /// - /// Those writing a new moniker class (through an implementation of the IMoniker interface) should call this method whenever the - /// implementation activates an object. This happens most often in the course of binding a moniker, but it can also happen while - /// retrieving a moniker's display name, parsing a display name into a moniker, or retrieving the time that an object was last modified. - /// - /// - /// RegisterObjectBound calls AddRef to create an additional reference to the object. You must, however, still release your - /// own copy of the pointer. Calling this method twice for the same object creates two references to that object. You can release a - /// reference obtained through a call to this method by calling IBindCtxV::RevokeObjectBound. All references held by the bind context - /// are released when the bind context itself is released. - /// - /// - /// Calling RegisterObjectBound to register an object with a bind context keeps the object active until the bind context is - /// released. Reusing a bind context in a subsequent binding operation (either for another piece of the same composite moniker or for - /// a different moniker) can make the subsequent binding operation more efficient because it doesn't have to reload that object. - /// This, however, improves performance only if the subsequent binding operation requires some of the same objects as the original - /// one, so you need to balance the possible performance improvement of reusing a bind context against the costs of keeping objects - /// activated unnecessarily. - /// - /// - /// IBindCtxV does not provide a method to retrieve a pointer to an object registered using RegisterObjectBound. Assuming the - /// object has registered itself with the running object table, moniker implementations can call IRunningObjectTable::GetObject to - /// retrieve a pointer to the object. - /// - /// - HRESULT IBindCtxV.RegisterObjectBound([In, MarshalAs(UnmanagedType.Interface)] object punk) => iBindCtx.RegisterObjectBound(punk); - - /// Associates an object with a string key in the bind context's string-keyed table of pointers. - /// The bind context string key under which the object is being registered. Key string comparison is case-sensitive. - /// - /// A pointer to the IUnknown interface on the object that is to be registered. - /// The method calls AddRef on the pointer. - /// - /// This method can return the standard return values E_OUTOFMEMORY and S_OK. - /// - /// - /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a - /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a - /// string known to both parties so that the other party can later retrieve it from the bind context. - /// - /// Binding operations subsequent to the use of this method can use IBindCtxV::GetObjectParam to retrieve the stored pointer. - /// Notes to Callers - /// - /// RegisterObjectParam is useful to those implementing a new moniker class (through an implementation of IMoniker) and to - /// moniker clients (those who use monikers to bind to objects). - /// - /// - /// In implementing a new moniker class, you call this method when an error occurs during moniker binding to inform the caller of the - /// cause of the error. The key that you would obtain with a call to this method would depend on the error condition. Following is a - /// list of common moniker binding errors, describing for each the keys that would be appropriate: - /// - /// - /// - /// - /// MK_E_EXCEEDEDDEADLINE—If a binding operation exceeds its deadline because a given object is not running, you should register - /// the object's moniker using the first unused key from the list: "ExceededDeadline", "ExceededDeadline1", "ExceededDeadline2", and - /// so on. If the caller later finds the moniker in the running object table, the caller can retry the binding operation. - /// - /// - /// - /// - /// MK_E_CONNECTMANUALLY—The "ConnectManually" key indicates a moniker whose binding requires assistance from the end user. To - /// request that the end user manually connect to the object, the caller can retry the binding operation after showing the moniker's - /// display name. Common reasons for this error are that a password is needed or that a floppy needs to be mounted. - /// - /// - /// - /// - /// E_CLASSNOTFOUND—The "ClassNotFound" key indicates a moniker whose class could not be found. (The server for the object - /// identified by this moniker could not be located.) If this key is used for an OLE compound-document object, the caller can use - /// IMoniker::BindToStorage to bind to the object and then try to carry out a Treat As... or Convert To... operation to - /// associate the object with a different server. If this is successful, the caller can retry the binding operation. - /// - /// - /// - /// - /// A moniker client with detailed knowledge of the implementation of the moniker can also call this method to pass private - /// information to that implementation. - /// - /// - /// You can define new strings as keys for storing pointers. By convention, you should use key names that begin with the string form - /// of the CLSID of the moniker class. (See the StringFromCLSID function.) - /// - /// - /// If the pszKey parameter matches the name of an existing key in the bind context's table, the new object replaces the existing - /// object in the table. - /// - /// When you register an object using this method, the object is not released until one of the following occurs: - /// - /// - /// It is replaced in the table by another object with the same key. - /// - /// - /// It is removed from the table by a call to IBindCtxV::RevokeObjectParam. - /// - /// - /// The bind context is released. All registered objects are released when the bind context is released. - /// - /// - /// - HRESULT IBindCtxV.RegisterObjectParam([MarshalAs(UnmanagedType.LPWStr)] string pszKey, [In, MarshalAs(UnmanagedType.Interface)] object punk) => iBindCtx.RegisterObjectParam(pszKey, punk); - - /// Releases all pointers to all objects that were previously registered by calls to RegisterObjectBound. - /// If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. - /// - /// - /// You rarely call this method directly. The system's IBindCtxV implementation calls this method when the pointer to the - /// IBindCtxV interface on the bind context is released (the bind context is released). If a bind context is not released, all - /// of the registered objects remain active. - /// - /// - /// If the same object has been registered more than once, this method calls the Release method on the object the number of times it - /// was registered. - /// - /// - HRESULT IBindCtxV.ReleaseBoundObjects() => iBindCtx.ReleaseBoundObjects(); - - /// Removes the object from the bind context, undoing a previous call to RegisterObjectBound. - /// A pointer to the IUnknown interface on the object to be removed. - /// - /// This method can return the following values. - /// - /// - /// Return code - /// Description - /// - /// - /// S_OK - /// The object was released successfully. - /// - /// - /// MK_E_NOTBOUND - /// The object was not previously registered. - /// - /// - /// - /// You would rarely call this method. It is documented primarily for completeness. - HRESULT IBindCtxV.RevokeObjectBound([In, MarshalAs(UnmanagedType.Interface)] object punk) => iBindCtx.RevokeObjectBound(punk); - - /// - /// Removes the specified key and its associated pointer from the bind context's string-keyed table of objects. The key must have - /// previously been inserted into the table with a call to RegisterObjectParam. - /// - /// The bind context string key to be removed. Key string comparison is case-sensitive. - /// - /// This method can return the following values. - /// - /// - /// Return code - /// Description - /// - /// - /// S_OK - /// The specified key was removed successfully. - /// - /// - /// S_FALSE - /// The object was not previously registered. - /// - /// - /// - /// - /// - /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a - /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a - /// string known to both parties so that the other party can later retrieve it from the bind context. - /// - /// - /// This method is used to remove an entry from the table. If the specified key is found, the bind context also releases its - /// reference to the object. - /// - /// - HRESULT IBindCtxV.RevokeObjectParam([MarshalAs(UnmanagedType.LPWStr)] string pszKey) => iBindCtx.RevokeObjectParam(pszKey); - - /// Sets new values for the binding parameters stored in the bind context. - /// A pointer to a BIND_OPTS, BIND_OPTS2, or BIND_OPTS3 structure containing the binding parameters. - /// This method can return the standard return values E_OUTOFMEMORY and S_OK. - /// - /// - /// A bind context contains a block of parameters that are common to most IMoniker operations. These parameters do not change as the - /// operation moves from piece to piece of a composite moniker. - /// - /// Subsequent binding operations can call IBindCtxV::GetBindOptions to retrieve these parameters. - /// Notes to Callers - /// This method can be called by moniker clients (those who use monikers to acquire interface pointers to objects). - /// - /// When you first create a bind context by using the CreateBindCtx function, the fields of the BIND_OPTS structure are initialized - /// to the following values: - /// - /// - /// You can use the IBindCtxV::SetBindOptions method to modify these values before using the bind context, if you want values - /// other than the defaults. - /// - /// - /// SetBindOptions copies the members of the specified structure, but not the COSERVERINFO structure and the pointers it - /// contains. Callers may not free these pointers until the bind context is released. - /// - /// - HRESULT IBindCtxV.SetBindOptions([In] BIND_OPTS_V pbindopts) => iBindCtx.SetBindOptions(pbindopts); - - /// - /// Retrieves a pointer to an interface that can be used to enumerate the keys of the bind context's string-keyed table of pointers. - /// - /// - /// The address of an IEnumString* pointer variable that receives the interface pointer to the enumerator. If an error occurs, - /// *ppenum is set to NULL. If *ppenum is non- NULL, the implementation calls AddRef on *ppenum; it is the caller's - /// responsibility to call Release. - /// - /// This method can return the standard return values E_OUTOFMEMORY and S_OK. - /// - /// The keys returned by the enumerator are the ones previously specified in calls to IBindCtx::RegisterObjectParam. - /// Notes to Callers - /// - /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a - /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a - /// string known to both parties so that the other party can later retrieve it from the bind context. - /// - /// - /// In the system implementation of the IBindCtx interface, this method is not implemented. Therefore, calling this method results in - /// a return value of E_NOTIMPL. - /// - /// - void IBindCtx.EnumObjectParam(out IEnumString ppenum) => iBindCtx.EnumObjectParam(out ppenum).ThrowIfFailed(); - - /// Retrieves the binding options stored in this bind context. - /// - /// A pointer to an initialized structure that receives the current binding parameters on return. See BIND_OPTS, BIND_OPTS2, and BIND_OPTS3. - /// - /// This method can return the standard return values E_UNEXPECTED and S_OK. - /// - /// - /// A bind context contains a block of parameters that are common to most IMoniker operations and that do not change as the operation - /// moves from piece to piece of a composite moniker. - /// - /// Notes to Callers - /// - /// You typically call this method if you are writing your own moniker class. (This requires that you implement the IMoniker - /// interface.) You call this method to retrieve the parameters specified by the moniker client. - /// - /// - /// You must initialize the structure that is filled in by this method. Before calling this method, you must initialize the - /// cbStruct member to the size of the structure. - /// - /// - void IBindCtx.GetBindOptions(ref System.Runtime.InteropServices.ComTypes.BIND_OPTS pbindopts) { var bo = new BIND_OPTS_V(); iBindCtx.GetBindOptions(bo).ThrowIfFailed(); pbindopts = bo; } - /// /// Retrieves an interface pointer to the object associated with the specified key in the bind context's string-keyed table of pointers. /// /// The bind context string key to be searched for. Key string comparison is case-sensitive. /// /// The address of an IUnknown* pointer variable that receives the interface pointer to the object associated with pszKey. When - /// successful, the implementation calls AddRef on *ppunk. It is the caller's responsibility to call Release. If an error occurs, the - /// implementation sets *ppunk to NULL. + /// successful, the implementation calls AddRef on *ppunk. It is the caller's responsibility to call Release. If an error occurs, + /// the implementation sets *ppunk to NULL. /// /// If the method succeeds, the return value is S_OK. Otherwise, it is E_FAIL. /// @@ -704,6 +501,34 @@ namespace Vanara.Windows.Shell /// void IBindCtx.GetObjectParam(string pszKey, out object ppunk) => iBindCtx.GetObjectParam(pszKey, out ppunk); + /// + /// Retrieves an interface pointer to the running object table (ROT) for the computer on which this bind context is running. + /// + /// + /// The address of a IRunningObjectTable* pointer variable that receives the interface pointer to the running object table. If an + /// error occurs, *pprot is set to NULL. If *pprot is non- NULL, the implementation calls AddRef on the running table + /// object; it is the caller's responsibility to call Release. + /// + /// This method can return the standard return values E_OUTOFMEMORY, E_UNEXPECTED, and S_OK. + /// + /// + /// The running object table is a globally accessible table on each computer. It keeps track of all the objects that are currently + /// running on the computer. + /// + /// Notes to Callers + /// + /// Typically, those implementing a new moniker class (through an implementation of IMoniker interface) call + /// GetRunningObjectTable. It is useful to call this method in an implementation of IMoniker::BindToObject or + /// IMoniker::IsRunning to check whether an object is currently running. You can also call this method in the implementation of + /// IMoniker::GetTimeOfLastChange to learn when a running object was last modified. + /// + /// + /// Moniker implementations should call this method instead of using the GetRunningObjectTable function. This makes it + /// possible for future implementations of IBindCtxV to modify binding behavior. + /// + /// + HRESULT IBindCtxV.GetRunningObjectTable(out Ole32.IRunningObjectTable pprot) => iBindCtx.GetRunningObjectTable(out pprot); + /// /// Retrieves an interface pointer to the running object table (ROT) for the computer on which this bind context is running. /// @@ -736,6 +561,127 @@ namespace Vanara.Windows.Shell pprot = (System.Runtime.InteropServices.ComTypes.IRunningObjectTable)Marshal.GetObjectForIUnknown(Marshal.GetComInterfaceForObject(rot, typeof(System.Runtime.InteropServices.ComTypes.IRunningObjectTable))); } + /// Registers an object with the bind context to ensure that the object remains active until the bind context is released. + /// A pointer to the IUnknown interface on the object that is being registered as bound. + /// This method can return the standard return values E_OUTOFMEMORY and S_OK. + /// + /// + /// Those writing a new moniker class (through an implementation of the IMoniker interface) should call this method whenever the + /// implementation activates an object. This happens most often in the course of binding a moniker, but it can also happen while + /// retrieving a moniker's display name, parsing a display name into a moniker, or retrieving the time that an object was last modified. + /// + /// + /// RegisterObjectBound calls AddRef to create an additional reference to the object. You must, however, still release your + /// own copy of the pointer. Calling this method twice for the same object creates two references to that object. You can release a + /// reference obtained through a call to this method by calling IBindCtxV::RevokeObjectBound. All references held by the bind + /// context are released when the bind context itself is released. + /// + /// + /// Calling RegisterObjectBound to register an object with a bind context keeps the object active until the bind context is + /// released. Reusing a bind context in a subsequent binding operation (either for another piece of the same composite moniker or + /// for a different moniker) can make the subsequent binding operation more efficient because it doesn't have to reload that object. + /// This, however, improves performance only if the subsequent binding operation requires some of the same objects as the original + /// one, so you need to balance the possible performance improvement of reusing a bind context against the costs of keeping objects + /// activated unnecessarily. + /// + /// + /// IBindCtxV does not provide a method to retrieve a pointer to an object registered using RegisterObjectBound. Assuming the + /// object has registered itself with the running object table, moniker implementations can call IRunningObjectTable::GetObject to + /// retrieve a pointer to the object. + /// + /// + HRESULT IBindCtxV.RegisterObjectBound([In, MarshalAs(UnmanagedType.Interface)] object punk) => iBindCtx.RegisterObjectBound(punk); + + /// Associates an object with a string key in the bind context's string-keyed table of pointers. + /// The bind context string key under which the object is being registered. Key string comparison is case-sensitive. + /// + /// A pointer to the IUnknown interface on the object that is to be registered. + /// The method calls AddRef on the pointer. + /// + /// This method can return the standard return values E_OUTOFMEMORY and S_OK. + /// + /// + /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a + /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a + /// string known to both parties so that the other party can later retrieve it from the bind context. + /// + /// Binding operations subsequent to the use of this method can use IBindCtxV::GetObjectParam to retrieve the stored pointer. + /// Notes to Callers + /// + /// RegisterObjectParam is useful to those implementing a new moniker class (through an implementation of IMoniker) and to + /// moniker clients (those who use monikers to bind to objects). + /// + /// + /// In implementing a new moniker class, you call this method when an error occurs during moniker binding to inform the caller of + /// the cause of the error. The key that you would obtain with a call to this method would depend on the error condition. Following + /// is a list of common moniker binding errors, describing for each the keys that would be appropriate: + /// + /// + /// + /// + /// MK_E_EXCEEDEDDEADLINE—If a binding operation exceeds its deadline because a given object is not running, you should register + /// the object's moniker using the first unused key from the list: "ExceededDeadline", "ExceededDeadline1", "ExceededDeadline2", and + /// so on. If the caller later finds the moniker in the running object table, the caller can retry the binding operation. + /// + /// + /// + /// + /// MK_E_CONNECTMANUALLY—The "ConnectManually" key indicates a moniker whose binding requires assistance from the end user. To + /// request that the end user manually connect to the object, the caller can retry the binding operation after showing the moniker's + /// display name. Common reasons for this error are that a password is needed or that a floppy needs to be mounted. + /// + /// + /// + /// + /// E_CLASSNOTFOUND—The "ClassNotFound" key indicates a moniker whose class could not be found. (The server for the object + /// identified by this moniker could not be located.) If this key is used for an OLE compound-document object, the caller can use + /// IMoniker::BindToStorage to bind to the object and then try to carry out a Treat As... or Convert To... operation + /// to associate the object with a different server. If this is successful, the caller can retry the binding operation. + /// + /// + /// + /// + /// A moniker client with detailed knowledge of the implementation of the moniker can also call this method to pass private + /// information to that implementation. + /// + /// + /// You can define new strings as keys for storing pointers. By convention, you should use key names that begin with the string form + /// of the CLSID of the moniker class. (See the StringFromCLSID function.) + /// + /// + /// If the pszKey parameter matches the name of an existing key in the bind context's table, the new object replaces the existing + /// object in the table. + /// + /// When you register an object using this method, the object is not released until one of the following occurs: + /// + /// + /// It is replaced in the table by another object with the same key. + /// + /// + /// It is removed from the table by a call to IBindCtxV::RevokeObjectParam. + /// + /// + /// The bind context is released. All registered objects are released when the bind context is released. + /// + /// + /// + HRESULT IBindCtxV.RegisterObjectParam([MarshalAs(UnmanagedType.LPWStr)] string pszKey, [In, MarshalAs(UnmanagedType.Interface)] object punk) => iBindCtx.RegisterObjectParam(pszKey, punk); + + /// Releases all pointers to all objects that were previously registered by calls to RegisterObjectBound. + /// If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. + /// + /// + /// You rarely call this method directly. The system's IBindCtxV implementation calls this method when the pointer to the + /// IBindCtxV interface on the bind context is released (the bind context is released). If a bind context is not released, + /// all of the registered objects remain active. + /// + /// + /// If the same object has been registered more than once, this method calls the Release method on the object the number of times it + /// was registered. + /// + /// + HRESULT IBindCtxV.ReleaseBoundObjects() => iBindCtx.ReleaseBoundObjects(); + /// Releases all pointers to all objects that were previously registered by calls to RegisterObjectBound. /// If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. /// @@ -751,6 +697,28 @@ namespace Vanara.Windows.Shell /// void IBindCtx.ReleaseBoundObjects() => iBindCtx.ReleaseBoundObjects(); + /// Removes the object from the bind context, undoing a previous call to RegisterObjectBound. + /// A pointer to the IUnknown interface on the object to be removed. + /// + /// This method can return the following values. + /// + /// + /// Return code + /// Description + /// + /// + /// S_OK + /// The object was released successfully. + /// + /// + /// MK_E_NOTBOUND + /// The object was not previously registered. + /// + /// + /// + /// You would rarely call this method. It is documented primarily for completeness. + HRESULT IBindCtxV.RevokeObjectBound([In, MarshalAs(UnmanagedType.Interface)] object punk) => iBindCtx.RevokeObjectBound(punk); + /// Removes the object from the bind context, undoing a previous call to RegisterObjectBound. /// A pointer to the IUnknown interface on the object to be removed. /// @@ -773,6 +741,41 @@ namespace Vanara.Windows.Shell /// You would rarely call this method. It is documented primarily for completeness. void IBindCtx.RevokeObjectBound([In, MarshalAs(UnmanagedType.Interface)] object punk) => iBindCtx.RevokeObjectBound(punk); + /// + /// Removes the specified key and its associated pointer from the bind context's string-keyed table of objects. The key must have + /// previously been inserted into the table with a call to RegisterObjectParam. + /// + /// The bind context string key to be removed. Key string comparison is case-sensitive. + /// + /// This method can return the following values. + /// + /// + /// Return code + /// Description + /// + /// + /// S_OK + /// The specified key was removed successfully. + /// + /// + /// S_FALSE + /// The object was not previously registered. + /// + /// + /// + /// + /// + /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a + /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a + /// string known to both parties so that the other party can later retrieve it from the bind context. + /// + /// + /// This method is used to remove an entry from the table. If the specified key is found, the bind context also releases its + /// reference to the object. + /// + /// + HRESULT IBindCtxV.RevokeObjectParam([MarshalAs(UnmanagedType.LPWStr)] string pszKey) => iBindCtx.RevokeObjectParam(pszKey); + /// /// Removes the specified key and its associated pointer from the bind context's string-keyed table of objects. The key must have /// previously been inserted into the table with a call to RegisterObjectParam. @@ -808,6 +811,32 @@ namespace Vanara.Windows.Shell /// int IBindCtx.RevokeObjectParam([MarshalAs(UnmanagedType.LPWStr)] string pszKey) => (int)iBindCtx.RevokeObjectParam(pszKey); + /// Sets new values for the binding parameters stored in the bind context. + /// A pointer to a BIND_OPTS, BIND_OPTS2, or BIND_OPTS3 structure containing the binding parameters. + /// This method can return the standard return values E_OUTOFMEMORY and S_OK. + /// + /// + /// A bind context contains a block of parameters that are common to most IMoniker operations. These parameters do not change as the + /// operation moves from piece to piece of a composite moniker. + /// + /// Subsequent binding operations can call IBindCtxV::GetBindOptions to retrieve these parameters. + /// Notes to Callers + /// This method can be called by moniker clients (those who use monikers to acquire interface pointers to objects). + /// + /// When you first create a bind context by using the CreateBindCtx function, the fields of the BIND_OPTS structure are initialized + /// to the following values: + /// + /// + /// You can use the IBindCtxV::SetBindOptions method to modify these values before using the bind context, if you want values + /// other than the defaults. + /// + /// + /// SetBindOptions copies the members of the specified structure, but not the COSERVERINFO structure and the pointers it + /// contains. Callers may not free these pointers until the bind context is released. + /// + /// + HRESULT IBindCtxV.SetBindOptions([In] BIND_OPTS_V pbindopts) => iBindCtx.SetBindOptions(pbindopts); + /// Sets new values for the binding parameters stored in the bind context. /// A pointer to a BIND_OPTS, BIND_OPTS2, or BIND_OPTS3 structure containing the binding parameters. /// This method can return the standard return values E_OUTOFMEMORY and S_OK. @@ -834,24 +863,8 @@ namespace Vanara.Windows.Shell /// void IBindCtx.SetBindOptions(ref System.Runtime.InteropServices.ComTypes.BIND_OPTS pbindopts) => iBindCtx.SetBindOptions(pbindopts).ThrowIfFailed(); - /// - /// Retrieves a pointer to an interface that can be used to enumerate the keys of the bind context's string-keyed table of pointers. - /// - /// A list of keys of the bind context's string-keyed table of pointers. - /// - /// The keys returned by the enumerator are the ones previously specified in calls to IBindCtxV::RegisterObjectParam. - /// Notes to Callers - /// - /// A bind context maintains a table of interface pointers, each associated with a string key. This enables communication between a - /// moniker implementation and the caller that initiated the binding operation. One party can store an interface pointer under a - /// string known to both parties so that the other party can later retrieve it from the bind context. - /// - /// - /// In the system implementation of the IBindCtxV interface, this method is not implemented. Therefore, calling this method results in - /// a return value of E_NOTIMPL. - /// - /// - private IEnumerable EnumObjectParam() { ((IBindCtxV)this).EnumObjectParam(out var ppenum).ThrowIfFailed(); return ppenum.Enum(); } + [DllImport(Lib.Ole32, ExactSpelling = true)] + private static extern HRESULT CreateBindCtx([Optional] uint reserved, out IBindCtxV ppbc); private BIND_OPTS_V GetBindOps() { @@ -868,8 +881,5 @@ namespace Vanara.Windows.Shell bo.SetFieldValue(fieldName, value); ((IBindCtxV)this).SetBindOptions(bo).ThrowIfFailed(); } - - [DllImport(Lib.Ole32, ExactSpelling = true)] - private static extern HRESULT CreateBindCtx([Optional] uint reserved, out IBindCtxV ppbc); } } \ No newline at end of file