using System;
using System.Runtime.InteropServices;
using Vanara.Extensions.Reflection;
using Vanara.PInvoke;
using static Vanara.PInvoke.Ole32;
namespace Vanara.Windows.Shell
{
/// An implementation of to be used in conjunction with derivatives.
///
///
[ComVisible(true)]
public class ComClassFactory : IClassFactory, IDisposable
{
private readonly IComObject comObj;
private uint registrationId;
/// Initializes a new instance of the class.
/// The COM object that is to be registered as a class object and queried for interfaces.
/// The context within which the COM object is to be run.
/// Indicates how connections are made to the class object.
public ComClassFactory(IComObject punkObject, CLSCTX classContext, REGCLS classUse)
{
comObj = punkObject ?? throw new ArgumentNullException(nameof(punkObject));
CoRegisterClassObject(comObj.GetType().CLSID(), this, classContext, classUse, out registrationId).ThrowIfFailed();
}
///
/// Resumes activation requests for class objects using . Must use
/// in the constructor.
///
public void Resume() => CoResumeClassObjects().ThrowIfFailed();
/// Creates an uninitialized object.
///
/// If the object is being created as part of an aggregate, specify a pointer to the controlling IUnknown interface of the aggregate.
/// Otherwise, this parameter must be NULL.
///
///
/// A reference to the identifier of the interface to be used to communicate with the newly created object. If pUnkOuter is
/// NULL, this parameter is generally the IID of the initializing interface; if pUnkOuter is non- NULL,
/// must be IID_IUnknown.
///
///
/// The address of pointer variable that receives the interface pointer requested in . Upon successful return, *ppvObject
/// contains the requested interface pointer. If the object does not support the interface specified in , the implementation must
/// set *ppvObject to NULL.
///
///
///
/// This method can return the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following values.
///
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// The specified object was created.
///
/// -
/// CLASS_E_NOAGGREGATION
/// The pUnkOuter parameter was non-NULL and the object does not support aggregation.
///
/// -
/// E_NOINTERFACE
/// The object that ppvObject points to does not support the interface identified by .
///
///
///
HRESULT IClassFactory.CreateInstance(object pUnkOuter, in Guid riid, out object ppv)
{
System.Diagnostics.Debug.WriteLine($"IClassFactory.CreateInstance: riid={riid:B}");
ppv = null;
if (!(pUnkOuter is null)) return HRESULT.CLASS_E_NOAGGREGATION;
try
{
ppv = comObj.QueryInterface(riid);
System.Diagnostics.Debug.WriteLine($"IClassFactory.CreateInstance: out ppv={ppv?.GetType().Name}");
}
catch (Exception e)
{
return e.GetPropertyValue("HResult");
}
return HRESULT.S_OK;
}
///
void IDisposable.Dispose()
{
if (registrationId == 0) return;
CoRevokeClassObject(registrationId);
registrationId = 0;
}
/// Locks an object application open in memory. This enables instances to be created more quickly.
/// If TRUE, increments the lock count; if FALSE, decrements the lock count.
/// This method can return the standard return values E_OUTOFMEMORY, E_UNEXPECTED, E_FAIL, and S_OK.
///
/// IClassFactory::LockServer controls whether an object's server is kept in memory. Keeping the application alive in memory
/// allows instances to be created more quickly.
///
HRESULT IClassFactory.LockServer(bool fLock)
{
if (fLock)
{
CoAddRefServerProcess();
}
else
{
if (0 == CoReleaseServerProcess())
comObj?.QuitMessageLoop();
}
return HRESULT.S_OK;
}
}
}