private ComPointer(TComInterface comInterface)
        {
            Interface = comInterface;
            _pUnk     = RdMarshal.GetIUnknownForObject(Interface);

            ConstructorObjectPrint();
        }
Exemple #2
0
        /// <summary>
        /// Do our best to validate that the input memory address is actually a COM object
        /// </summary>
        /// <param name="comObjectPtr">the input memory address to check</param>
        /// <returns>false means definitely not a valid COM object.  true means _probably_ a valid COM object</returns>
        public static bool ValidateComObject(IntPtr comObjectPtr)
        {
            // Is it a valid memory address, with at least one accessible vTable ptr
            if (!IsValidMemoryRange(comObjectPtr, IntPtr.Size))
            {
                return(false);
            }

            var vTablePtr = RdMarshal.ReadIntPtr(comObjectPtr);

            // And for a COM object, we need a valid vtable, with at least 3 vTable entries (for IUnknown)
            if (!IsValidMemoryRange(vTablePtr, IntPtr.Size * 3))
            {
                return(false);
            }

            var firstvTableEntry = RdMarshal.ReadIntPtr(vTablePtr);

            // And lets check the first vTable entry actually points to EXECUTABLE memory
            // (we could check all 3 initial IUnknown entries, but we want to be reasonably
            // efficient and we can never 100% guarantee our result anyway.)
            if (IsValidMemoryRange(firstvTableEntry, 1, checkIsExecutable: true))
            {
                // As best as we can tell, it looks to be a valid COM object
                return(true);
            }
            else
            {
                // One of the validation checks failed.  The COM object is definitely not a valid COM object.
                return(false);
            }
        }
Exemple #3
0
        /// <summary>
        /// Takes an unmanaged memory address and reads the unmanaged memory given by its pointer,
        /// with memory address validation for added protection
        /// </summary>
        /// <typeparam name="T">the type of structure to return</typeparam>
        /// <param name="memAddress">the unamanaged memory address to read</param>
        /// <returns>the requested structure T</returns>
        public static T ReadStructureSafe <T>(IntPtr memAddress)
        {
            if (UnmanagedMemoryHelper.IsValidMemoryRange(memAddress, RdMarshal.SizeOf(typeof(T))))
            {
                return((T)RdMarshal.PtrToStructure(memAddress, typeof(T)));
            }

            throw new ArgumentException("Bad data pointer - unable to read structure data.");
        }
Exemple #4
0
 /// <summary>
 /// Takes an unmanaged memory address and reads the unmanaged memory given by its pointer
 /// </summary>
 /// <typeparam name="T">the type of structure to return</typeparam>
 /// <param name="memAddress">the unmanaged memory address to read</param>
 /// <returns>the requested structure T</returns>
 /// <remarks>use this over ReadStructureSafe for efficiency when there is no doubt about the validity of the pointed to data</remarks>
 public static T ReadStructureUnsafe <T>(IntPtr memAddress)
 {
     // We catch the most basic mistake of passing a null pointer here as it virtually costs nothing to check,
     // but no other checks are made as to the validity of the pointer.
     if (memAddress == IntPtr.Zero)
     {
         throw new ArgumentException("Unexpected null pointer.");
     }
     return((T)RdMarshal.PtrToStructure(memAddress, typeof(T)));
 }
Exemple #5
0
        // simply check if a COM object supports a particular COM interface
        // (without doing any casting by the CLR, which does much more than this under the covers)
        public static bool DoesComObjPtrSupportInterface <T>(IntPtr comObjPtr)
        {
            var iid = typeof(T).GUID;
            var hr  = RdMarshal.QueryInterface(comObjPtr, ref iid, out var outInterfacePtr);

            if (!ComHelper.HRESULT_FAILED(hr))
            {
                RdMarshal.Release(outInterfacePtr);
                return(true);
            }
            return(false);
        }
Exemple #6
0
        // The aggregation magic starts here
        public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
        {
            ppv = IntPtr.Zero;

            if (iid == typeof(T).GUID)
            {
                ppv = _outerObject;
                RdMarshal.AddRef(_outerObject);
                return(CustomQueryInterfaceResult.Handled);
            }

            return(CustomQueryInterfaceResult.Failed);
        }
        private void TraceRelease(int rcwCount, ref bool addRef)
        {
            if (!addRef)
            {
                // Temporarily add a ref so that we can safely call IUnknown::Release
                // to report the ref count in the log.
                RdMarshal.AddRef(_pUnk);
            }
            var refCount = RdMarshal.Release(_pUnk);

            Debug.Print($"ComPointer:: Disposed: _pUnk: {RdMarshal.FormatPtr(_pUnk)} _interface: {typeof(TComInterface).Name} - {Interface.GetHashCode()} addRef: {_addRef} rcwCount: {rcwCount} refCount: {refCount}");

            addRef = false;
        }
        private ComPointer(IntPtr pUnk, bool addRef)
        {
            var refCount = -1;

            _pUnk = pUnk;
            if (addRef)
            {
                _addRef  = true;
                refCount = RdMarshal.AddRef(_pUnk);
            }

            Interface = (TComInterface)RdMarshal.GetTypedObjectForIUnknown(pUnk, typeof(TComInterface));

            ConstructorPointerPrint(refCount);
        }
 /// <summary>
 /// frees all unmanaged memory assoicated with a DISPPARAMS structure
 /// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221416(v=vs.85).aspx
 /// </summary>
 /// <param name="pDispParams"></param>
 private static void UnprepareDispatchArgs(ComTypes.DISPPARAMS pDispParams)
 {
     if (pDispParams.rgvarg != IntPtr.Zero)
     {
         // free the array of COM VARIANTs
         var variantStructSize      = RdMarshal.SizeOf(typeof(VARIANT));
         var variantArgsArrayOffset = pDispParams.rgvarg;
         var argIndex = 0;
         while (argIndex < pDispParams.cArgs)
         {
             VariantClear(variantArgsArrayOffset);
             variantArgsArrayOffset += variantStructSize;
             argIndex++;
         }
         RdMarshal.FreeHGlobal(pDispParams.rgvarg);
     }
 }
        private void ReleaseUnmanagedResources()
        {
            if (_disposed)
            {
                return;
            }

            var rcwCount = RdMarshal.ReleaseComObject(Interface);
            var addRef   = _addRef;

            TraceRelease(rcwCount, ref addRef);
            if (addRef)
            {
                RdMarshal.Release(_pUnk);
            }

            _disposed = true;
        }
Exemple #11
0
        void ComTypes.ITypeInfo.CreateInstance(object pUnkOuter, ref Guid riid, out object ppvObj)
        {
            // initialize out parameters
            ppvObj = default;

            using (var outPpvObj = AddressableVariables.CreateObjectPtr <object>())
            {
                var unkOuter = RdMarshal.GetIUnknownForObject(pUnkOuter);
                var hr       = _this_Internal.CreateInstance(unkOuter, riid, outPpvObj.Address);
                RdMarshal.Release(unkOuter);
                if (ComHelper.HRESULT_FAILED(hr))
                {
                    HandleBadHRESULT(hr);
                }

                ppvObj = outPpvObj.Value;
            }
        }
Exemple #12
0
        /// <summary>
        /// Validate a memory address range
        /// </summary>
        /// <param name="memOffset">the input memory address to check</param>
        /// <param name="size">the minimum size of data we are expecting to be available next to memOffset</param>
        /// <param name="checkIsExecutable">optionally check if the memory address points to EXECUTABLE memory</param>
        public static bool IsValidMemoryRange(IntPtr memOffset, int size, bool checkIsExecutable = false)
        {
            if (memOffset == IntPtr.Zero)
            {
                return(false);
            }

            var memInfo       = new MEMORY_BASIC_INFORMATION();
            var sizeOfMemInfo = new IntPtr(RdMarshal.SizeOf(memInfo));

            // most of the time, a bad pointer will fail here
            if (VirtualQuery(memOffset, out memInfo, sizeOfMemInfo) != sizeOfMemInfo)
            {
                return(false);
            }

            // check the memory area is not a guard page, or otherwise inaccessible
            if ((memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_NOACCESS)) ||
                (memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_GUARD)))
            {
                return(false);
            }

            // We've confirmed the base memory address is valid, and is accessible.
            // Finally just check the full address RANGE is also valid (i.e. the end point of the structure we're reading)
            var validMemAddressEnd = memInfo.BaseAddress.ToInt64() + memInfo.RegionSize.ToInt64();
            var endOfStructPtr     = memOffset.ToInt64() + size;

            if (endOfStructPtr > validMemAddressEnd)
            {
                return(false);
            }

            if (checkIsExecutable)
            {
                // We've been asked to check if the memory address is marked as containing executable code
                return(memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE) ||
                       memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE_READ) ||
                       memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE_READWRITE) ||
                       memInfo.Protect.HasFlag(ALLOCATION_PROTECTION.PAGE_EXECUTE_WRITECOPY));
            }
            return(true);
        }
Exemple #13
0
        /// <summary>
        /// Takes a COM object, and reads the unmanaged memory given by its pointer, allowing us to read internal fields
        /// </summary>
        /// <typeparam name="T">the type of structure to return</typeparam>
        /// <param name="comObj">the COM object</param>
        /// <returns>the requested structure T</returns>
        public static T ReadComObjectStructure <T>(object comObj)
        {
            // Reads a COM object as a structure to copy its internal fields
            if (!RdMarshal.IsComObject(comObj))
            {
                throw new ArgumentException("Expected a COM object");
            }

            var referencesPtr = RdMarshal.GetIUnknownForObjectInContext(comObj);

            if (referencesPtr == IntPtr.Zero)
            {
                throw new InvalidOperationException("Cannot access the TypeLib API from this thread.  TypeLib API must be accessed from the main thread.");
            }
            var retVal = ReadStructureSafe <T>(referencesPtr);

            RdMarshal.Release(referencesPtr);
            return(retVal);
        }
        /// <summary>
        /// A basic helper for IDispatch::Invoke
        /// </summary>
        /// <param name="obj">The IDispatch object of which you want to invoke a member on</param>
        /// <param name="memberId">The dispatch ID of the member to invoke</param>
        /// <param name="invokeKind">See InvokeKind enumeration</param>
        /// <param name="args">Array of arguments to pass to the call, or null for no args</param>
        /// <remarks>TODO support DISPATCH_PROPERTYPUTREF (property-set) which requires special handling</remarks>
        /// <returns>An object representing the return value from the called routine</returns>
        public static object Invoke(IDispatch obj, int memberId, InvokeKind invokeKind, object[] args = null)
        {
            var pDispParams = PrepareDispatchArgs(args);
            var pExcepInfo  = new ComTypes.EXCEPINFO();

            var hr = obj.Invoke(memberId, ref _guid_null, 0, (uint)invokeKind,
                                ref pDispParams, out var pVarResult, ref pExcepInfo, out var pErrArg);

            UnprepareDispatchArgs(pDispParams);

            if (ComHelper.HRESULT_FAILED(hr))
            {
                if ((hr == (int)KnownComHResults.DISP_E_EXCEPTION) && (ComHelper.HRESULT_FAILED(pExcepInfo.scode)))
                {
                    throw RdMarshal.GetExceptionForHR(pExcepInfo.scode);
                }
                throw RdMarshal.GetExceptionForHR(hr);
            }

            return(pVarResult);
        }
Exemple #15
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="outerObject">The object that needs interface requests filtered</param>
        /// <param name="queryForType">determines whether we call QueryInterface for the interface or not</param>
        /// <remarks>if the passed in outerObject is known to point to the correct vtable for the interface, then queryForType can be false</remarks>
        /// <returns>if outerObject is IntPtr.Zero, then a null wrapper, else an aggregated wrapper</returns>
        public RestrictComInterfaceByAggregation(IntPtr outerObject, bool queryForType = true)
        {
            if (queryForType)
            {
                var iid = typeof(T).GUID;
                if (ComHelper.HRESULT_FAILED(RdMarshal.QueryInterface(outerObject, ref iid, out _outerObject)))
                {
                    // allow null wrapping here
                    return;
                }
            }
            else
            {
                _outerObject = outerObject;
                RdMarshal.AddRef(_outerObject);
            }

            var clrAggregator = RdMarshal.CreateAggregatedObject(_outerObject, this);

            WrappedObject = (T)RdMarshal.GetObjectForIUnknown(clrAggregator); // when this CCW object gets released, it will free the aggObjInner (well, after GC)
            RdMarshal.Release(clrAggregator);                                 // _wrappedObject holds a reference to this now
        }
        /// <summary>
        /// Convert input args into a contiguous array of real COM VARIANTs for the DISPPARAMS struct used by IDispatch::Invoke
        /// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221416(v=vs.85).aspx
        /// </summary>
        /// <param name="args">An array of arguments to wrap</param>
        /// <returns><see cref="ComTypes.DISPPARAMS"/> structure ready to pass to IDispatch::Invoke</returns>
        private static ComTypes.DISPPARAMS PrepareDispatchArgs(object[] args)
        {
            var pDispParams = new ComTypes.DISPPARAMS();

            if ((args != null) && (args.Length != 0))
            {
                var variantStructSize = RdMarshal.SizeOf(typeof(VARIANT));
                pDispParams.cArgs = args.Length;

                var argsVariantLength = variantStructSize * pDispParams.cArgs;
                var variantArgsArray  = RdMarshal.AllocHGlobal(argsVariantLength);

                // In IDispatch::Invoke, arguments are passed in reverse order
                var variantArgsArrayOffset = variantArgsArray + argsVariantLength;
                foreach (var arg in args)
                {
                    variantArgsArrayOffset -= variantStructSize;
                    RdMarshal.GetNativeVariantForObject(arg, variantArgsArrayOffset);
                }
                pDispParams.rgvarg = variantArgsArray;
            }
            return(pDispParams);
        }
Exemple #17
0
        protected virtual void Dispose(bool disposing)
        {
            if (_isDisposed || !disposing)
            {
                return;
            }
            _isDisposed = true;

            if (WrappedObject != null)
            {
                RdMarshal.ReleaseComObject(WrappedObject);
            }

            if (_outerObject != IntPtr.Zero)
            {
                RdMarshal.Release(_outerObject);

                // dont set _outerObject to IntPtr.Zero here, as GetInterface() can still be called by the outer RCW
                // if it is still alive. For example, if ExtractWrappedObject was used and the outer object hasn't yet
                // been released with ReleaseComObject.  In that circumstance _outerObject will still be a valid pointer
                // due to the internally held reference, and so GetInterface() calls past this point are still OK.
            }
        }
Exemple #18
0
 private void HandleBadHRESULT(int hr)
 {
     throw RdMarshal.GetExceptionForHR(hr);
 }
 private void ConstructorObjectPrint()
 {
     Debug.Print($"ComPointer:: Created from object: pUnk: {RdMarshal.FormatPtr(_pUnk)} interface: {typeof(TComInterface).Name} - {Interface.GetHashCode()} addRef: {_addRef}");
 }
 private void ConstructorPointerPrint(int refCount)
 {
     Debug.Print($"ComPointer:: Created from pointer: pUnk: {RdMarshal.FormatPtr(_pUnk)} interface: {typeof(TComInterface).Name} - {Interface.GetHashCode()} addRef: {_addRef} refCount: {refCount}");
 }