public void CreateWrapperOfType_GenericHasNoWrapperWithInterfaces_ReturnsExpected() { var comObject = new ComImportObject(); object wrapper = Marshal.CreateWrapperOfType <ComImportObject, HasNonCOMInterfaces>(comObject); Assert.Same(wrapper, Marshal.GetComObjectData(comObject, typeof(HasNonCOMInterfaces))); }
public static ComEventSinksContainer FromRuntimeCallableWrapper(object rcw, bool createIfNotFound) { // !!! Marshal.Get/SetComObjectData has a LinkDemand for UnmanagedCode which will turn into // a full demand. We need to avoid this by making this method SecurityCritical object data = Marshal.GetComObjectData(rcw, _ComObjectEventSinksKey); if (data != null || createIfNotFound == false) { return((ComEventSinksContainer)data); } lock (_ComObjectEventSinksKey) { data = Marshal.GetComObjectData(rcw, _ComObjectEventSinksKey); if (data != null) { return((ComEventSinksContainer)data); } ComEventSinksContainer comEventSinks = new ComEventSinksContainer(); if (!Marshal.SetComObjectData(rcw, _ComObjectEventSinksKey, comEventSinks)) { throw Error.SetComObjectDataFailed(); } return(comEventSinks); } }
/// <summary> /// Gets a <see cref="ComObject"/> that wraps the runtime-callable-wrapper, or creates one if none currently exists. /// </summary> /// <returns></returns> public static ComObject ObjectToComObject(object rcw) { Debug.Assert(ComBinder.IsComObject(rcw)); object data = Marshal.GetComObjectData(rcw, s_comObjectInfoKey); if (data != null) { return((ComObject)data); } lock (s_comObjectInfoKey) { data = Marshal.GetComObjectData(rcw, s_comObjectInfoKey); if (data != null) { return((ComObject)data); } ComObject comObjectInfo = CreateComObject(rcw); if (!Marshal.SetComObjectData(rcw, s_comObjectInfoKey, comObjectInfo)) { throw Error.SetComObjectDataFailed(); } return(comObjectInfo); } }
public void CreateWrapperOfType_NonGenericHasNoWrapper_ReturnsExpected() { var comObject = new ComImportObject(); object wrapper = Marshal.CreateWrapperOfType(comObject, typeof(WrapperComImportObject)); Assert.Same(wrapper, Marshal.GetComObjectData(comObject, typeof(WrapperComImportObject))); }
public static ComEventSinksContainer FromRuntimeCallableWrapper(object rcw, bool createIfNotFound) { object data = Marshal.GetComObjectData(rcw, s_comObjectEventSinksKey); if (data != null || !createIfNotFound) { return((ComEventSinksContainer)data); } lock (s_comObjectEventSinksKey) { data = Marshal.GetComObjectData(rcw, s_comObjectEventSinksKey); if (data != null) { return((ComEventSinksContainer)data); } ComEventSinksContainer comEventSinks = new ComEventSinksContainer(); if (!Marshal.SetComObjectData(rcw, s_comObjectEventSinksKey, comEventSinks)) { throw Error.SetComObjectDataFailed(); } return(comEventSinks); } }
public static ComObject ObjectToComObject(object rcw) { Debug.Assert(ComObject.IsComObject(rcw)); // Marshal.Get/SetComObjectData has a LinkDemand for UnmanagedCode which will turn into // a full demand. We could avoid this by making this method SecurityCritical object data = Marshal.GetComObjectData(rcw, _ComObjectInfoKey); if (data != null) { return((ComObject)data); } lock (_ComObjectInfoKey) { data = Marshal.GetComObjectData(rcw, _ComObjectInfoKey); if (data != null) { return((ComObject)data); } ComObject comObjectInfo = CreateComObject(rcw); if (!Marshal.SetComObjectData(rcw, _ComObjectInfoKey, comObjectInfo)) { throw Error.SetComObjectDataFailed(); } return(comObjectInfo); } }
/// <summary> /// Gets the name of the default COM interface for this object. /// </summary> /// <remarks> /// Uses the COM ITypeInfo interface to get the information. Does not throw. /// </remarks> /// <param name="obj"></param> /// <returns>The name of the interface; null on any exception</returns> /// internal static string GetDefaultCOMInterfaceName(object obj) { try { string intfName = (string)Marshal.GetComObjectData(obj, "NETLinkCOMInterface"); if (intfName == null) { UCOMIDispatch iDisp = GetIDispatch(obj); UCOMITypeInfo iTypeInfo = GetITypeInfo(obj, iDisp); string typeLibName, docStr, helpFile; int helpContext; iTypeInfo.GetDocumentation(-1, out intfName, out docStr, out helpContext, out helpFile); UCOMITypeLib iTypeLib; int index; iTypeInfo.GetContainingTypeLib(out iTypeLib, out index); iTypeLib.GetDocumentation(-1, out typeLibName, out docStr, out helpContext, out helpFile); intfName = typeLibName + "." + intfName; Marshal.SetComObjectData(obj, "NETLinkCOMInterface", intfName); // Not strictly necessary to release here, but better to force it now than rely on GC to do it later. // Cannot release iDisp or iTypeInfo, as they might be cached in the object. Marshal.ReleaseComObject(iTypeLib); } return(intfName); } catch (Exception) { return(null); } }
public void GetComObjectData_ValidObject_ReturnsExpected() { var comObject = new ComImportObject(); Assert.Null(Marshal.GetComObjectData(comObject, "key")); Marshal.SetComObjectData(comObject, "key", 1); Assert.Equal(1, Marshal.GetComObjectData(comObject, "key")); Assert.Null(Marshal.GetComObjectData(comObject, "noSuchKey")); }
// Allows exceptions to be thrown. private static UCOMITypeInfo GetITypeInfo(object obj, UCOMIDispatch iDisp) { UCOMITypeInfo iTypeInfo = (UCOMITypeInfo)Marshal.GetComObjectData(obj, "NETLinkITypeInfo"); if (iTypeInfo == null) { iDisp.GetTypeInfo(0, GetUserDefaultLCID(), out iTypeInfo); Marshal.SetComObjectData(obj, "NETLinkITypeInfo", iTypeInfo); } return(iTypeInfo); }
public void GetComObjectData_NetFramework_ReturnsExpected() { Type type = Type.GetTypeFromCLSID(new Guid("927971f5-0939-11d1-8be1-00c04fd8d503")); object comObject = Activator.CreateInstance(type); Assert.Null(Marshal.GetComObjectData(comObject, "key")); Marshal.SetComObjectData(comObject, "key", 1); Assert.Equal(1, Marshal.GetComObjectData(comObject, "key")); Assert.Null(Marshal.GetComObjectData(comObject, "noSuchKey")); }
public void SetComObjectData_NonNullValue_Sucesss() { var comObject = new ComImportObject(); Assert.True(Marshal.SetComObjectData(comObject, "key", 1)); Assert.Equal(1, Marshal.GetComObjectData(comObject, "key")); Assert.False(Marshal.SetComObjectData(comObject, "key", 2)); Assert.Equal(1, Marshal.GetComObjectData(comObject, "key")); Assert.True(Marshal.SetComObjectData(comObject, "otherKey", 2)); Assert.Equal(2, Marshal.GetComObjectData(comObject, "otherKey")); }
public static TView GetOrCreateManagedViewFromComData <T, TView>(object comObject, Func <T, TView> createCallback) { object key = typeof(TView); if (Marshal.GetComObjectData(comObject, key) is TView managedView) { return(managedView); } managedView = createCallback((T)comObject); if (!Marshal.SetComObjectData(comObject, key, managedView)) { managedView = (TView)Marshal.GetComObjectData(comObject, key); } return(managedView); }
/// <summary> /// Like Marshal.ReleaseCOMObject, but also frees IDispatch and ITypeInfo objects held in the object's cache. /// </summary> /// <param name="obj"></param> /// <returns>The new COM refcount.</returns> /// internal static int releaseCOMObject(object obj) { int newRefCount = Marshal.ReleaseComObject(obj); if (newRefCount == 0) { UCOMITypeInfo iTypeInfo = (UCOMITypeInfo)Marshal.GetComObjectData(obj, "NETLinkITypeInfo"); if (iTypeInfo != null) { Marshal.ReleaseComObject(iTypeInfo); } UCOMIDispatch iDisp = (UCOMIDispatch)Marshal.GetComObjectData(obj, "NETLinkIDispatch"); if (iDisp != null) { Marshal.ReleaseComObject(iDisp); } } return(newRefCount); }
// Returns -1 on failure. Does not throw. private static int GetMemberDispID(object obj, UCOMIDispatch iDisp, ref string memberName) { object val = Marshal.GetComObjectData(obj, "NETLinkDispID" + memberName); if (val != null) { // We also need to extract the modified (U --> _ converted) name. memberName = (string)Marshal.GetComObjectData(obj, "NETLinkModifiedName" + memberName); return((int)val); } else { int memberDispId; int defaultLCID = GetUserDefaultLCID(); string[] substrs = memberName.Split('U'); int numCombinations = (int)Math.Pow(2, substrs.Length - 1); for (int i = 0; i < numCombinations; i++) { string modifiedName = substrs[0]; // Use bit ops to take i and pick 0 -> U, 1 -> _ for replacement. for (int j = 1; j < substrs.Length; j++) { modifiedName += ((((i >> (j - 1)) & 1) == 0) ? "U" : "_") + substrs[j]; } try { iDisp.GetIDsOfNames(ref IID_NULL, new string[] { modifiedName }, 1, defaultLCID, out memberDispId); } catch (Exception) { // Try next combination. continue; } // If we get here, name was found, and is held in the variable modifiedName. Cache the correct dispID // for the unaltered name as it arrives from M, cache the modified name, and then set the ref // parameter memberName to correct name. Marshal.SetComObjectData(obj, "NETLinkDispID" + memberName, memberDispId); Marshal.SetComObjectData(obj, "NETLinkModifiedName" + memberName, modifiedName); memberName = modifiedName; return(memberDispId); } // Name not found by GetIDsOfNames(). return(-1); } }
private HandlerList GetComHandlerList(object instance) { HandlerList hl = (HandlerList)Marshal.GetComObjectData(instance, this); if (hl == null) { lock (_staticTarget) { hl = (HandlerList)Marshal.GetComObjectData(instance, this); if (hl == null) { hl = new ComHandlerList(); if (!Marshal.SetComObjectData(instance, this, hl)) { throw new COMException("Failed to set COM Object Data"); } } } } return(hl); }
/// <summary> /// Gets parameter information for the given member. /// </summary> /// <remarks> /// Uses the COM ITypeInfo interface to get the information. Does not throw. If it returns false then the out params /// will not hold useful information. /// </remarks> /// <param name="obj"></param> /// <param name="memberName"></param> /// <param name="callType"></param> /// <param name="isBeingCalledWithZeroArgs"></param> /// <param name="paramTypes"></param> /// <param name="paramFlags"></param> /// <param name="endsWithParamArray"></param> /// <returns>true if the name refers to a valid member or if no type information is available</returns> /// internal static bool GetMemberInfo(object obj, ref string memberName, int callType, bool isBeingCalledWithZeroArgs, out Type[] paramTypes, out PARAMFLAG[] paramFlags, out bool endsWithParamArray) { // There are some early returns in this method, so we initialize the out params right away. To the caller, if these values // come back null it means that the information could not be determined. paramTypes = null; paramFlags = null; endsWithParamArray = false; int defaultLCID = GetUserDefaultLCID(); try { // Throws in this outer try cause true to be returned. They mean "type info could not be obtained, so the member is not // known to be invalid." UCOMIDispatch iDisp = GetIDispatch(obj); int memberDispId = GetMemberDispID(obj, iDisp, ref memberName); if (memberDispId == -1) { // Name not found by GetIDsOfNames. return(false); } // If no args are being passed, then we can skip all the machinations below about type info. if (isBeingCalledWithZeroArgs) { return(true); } UCOMITypeInfo iTypeInfo = GetITypeInfo(obj, iDisp); IntPtr pFuncDesc; // Check to see if the func index for this member name is already cached from a previous call. FuncIndexHolder fih = (FuncIndexHolder)Marshal.GetComObjectData(obj, "NETLinkFuncIndex" + memberName); if (fih == null) { // This will be populated below. fih = new FuncIndexHolder(); Marshal.SetComObjectData(obj, "NETLinkFuncIndex" + memberName, fih); } int funcIndex = -1; if (callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_GET) { if (fih.funcIndexForCALLTYPE_FIELD_OR_SIMPLE_PROP_GET != -1) { funcIndex = fih.funcIndexForCALLTYPE_FIELD_OR_SIMPLE_PROP_GET; } } else if (callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_SET || callType == Install.CALLTYPE_PARAM_PROP_SET) { if (fih.funcIndexForCALLTYPE_FIELD_OR_ANY_PROP_SET != -1) { funcIndex = fih.funcIndexForCALLTYPE_FIELD_OR_ANY_PROP_SET; } } else if (callType == Install.CALLTYPE_METHOD || callType == Install.CALLTYPE_PARAM_PROP_GET) { // COM objects are treated by M code as if they all have indexers, so calls like obj[1] will // come in as CALLTYPE_PARAM_PROP_GET with a property name of Item. We can just treat these like // method calls. if (fih.funcIndexForCALLTYPE_METHOD != -1) { funcIndex = fih.funcIndexForCALLTYPE_METHOD; } } // Did not have the func index for this call type cached. if (funcIndex == -1) { IntPtr pTypeAttr; iTypeInfo.GetTypeAttr(out pTypeAttr); TYPEATTR typeAttr = (TYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(TYPEATTR)); int numFuncs = typeAttr.cFuncs; iTypeInfo.ReleaseTypeAttr(pTypeAttr); bool foundDispId = false; for (int thisIndex = 0; thisIndex < numFuncs; thisIndex++) { // Be aware that GetFuncDesc() can (I think) throw a cryptic "Element not found" exception, // such as for a hidden property like (Excel) _Application.ActiveDialog. iTypeInfo.GetFuncDesc(thisIndex, out pFuncDesc); FUNCDESC funcDesc = (FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(FUNCDESC)); int thisMemberId = funcDesc.memid; INVOKEKIND invokeKind = funcDesc.invkind; iTypeInfo.ReleaseFuncDesc(pFuncDesc); if (thisMemberId == memberDispId) { foundDispId = true; // Verify that it is a member of the correct call type. if (callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_GET && invokeKind == INVOKEKIND.INVOKE_PROPERTYGET) { fih.funcIndexForCALLTYPE_FIELD_OR_SIMPLE_PROP_GET = thisIndex; funcIndex = thisIndex; break; } else if ((callType == Install.CALLTYPE_FIELD_OR_SIMPLE_PROP_SET || callType == Install.CALLTYPE_PARAM_PROP_SET) && (invokeKind == INVOKEKIND.INVOKE_PROPERTYPUT || invokeKind == INVOKEKIND.INVOKE_PROPERTYPUTREF)) { fih.funcIndexForCALLTYPE_FIELD_OR_ANY_PROP_SET = thisIndex; funcIndex = thisIndex; break; } else if ((callType == Install.CALLTYPE_METHOD && (invokeKind == INVOKEKIND.INVOKE_FUNC || invokeKind == INVOKEKIND.INVOKE_PROPERTYGET)) || (callType == Install.CALLTYPE_PARAM_PROP_GET && invokeKind == INVOKEKIND.INVOKE_PROPERTYGET)) { // Parameterized prop gets, not sets, look like CALLTYPE_METHOD. Also, as discussed in an earlier comment, // indexer notation calls (obj[1]) are supported for all COM objects and come in as CALLTYPE_PARAM_PROP_GET. fih.funcIndexForCALLTYPE_METHOD = thisIndex; funcIndex = thisIndex; break; } } } if (funcIndex == -1) { // We didn't find the member in our search. This can happen in two ways. First, the member might // exist but not be the right call type. For this case we want to return false. Second, we didn't // even find the dispid. This can happen in unusual cases I don't understan An example of this // is the IWebBrowser2 interface obtained by CreateCOMObject["InternetExplorer.Application"]. // For this case we want to return true, as if there was no type info at all available, so the // call can still proceed. return(!foundDispId); } } // If we get to here, we have a valid funcIndex and memberDispId, and the member is of the correct variety // that corresponds to how it was called (e.g., it's a method and it was called as such). All that // remains is to get the parameter types. iTypeInfo.GetFuncDesc(funcIndex, out pFuncDesc); try { FUNCDESC funcDesc = (FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(FUNCDESC)); Debug.Assert(funcDesc.memid == memberDispId); int paramCount = funcDesc.cParams; // Functions that end with a VB-style ParamArray (a variable-length argument sequence) have -1 for the // cParamsOpt member. It is convenient to treat them specially, hence the 'endsWithParamArray' variable // (which is an out param for this method). The special treatment involves leaving the ParamArray arg off // the list of paramFlags and paramTypes. We just pretend there is one less arg to the function but // record that it ends with a ParamArray. We always know the type of this arg anyway: ByRef Variant[]. // To the caller, though, the individual args are sent as a sequence, not packed into an array. endsWithParamArray = funcDesc.cParamsOpt == -1; if (endsWithParamArray) { paramCount--; } paramTypes = new Type[paramCount]; paramFlags = new PARAMFLAG[paramCount]; IntPtr pElemDescArray = funcDesc.lprgelemdescParam; for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { ELEMDESC elemDesc = (ELEMDESC)Marshal.PtrToStructure((IntPtr)(pElemDescArray.ToInt64() + paramIndex * Marshal.SizeOf(typeof(ELEMDESC))), typeof(ELEMDESC)); TYPEDESC typeDesc = elemDesc.tdesc; paramFlags[paramIndex] = elemDesc.desc.paramdesc.wParamFlags; // I think I should never see a retval param here. They have been automatically converted to return types. Debug.Assert((paramFlags[paramIndex] & PARAMFLAG.PARAMFLAG_FRETVAL) == 0); VarEnum variantType = (VarEnum)typeDesc.vt; bool isArray = variantType == VarEnum.VT_SAFEARRAY; bool isPtr = variantType == VarEnum.VT_PTR; bool isOut = (paramFlags[paramIndex] & PARAMFLAG.PARAMFLAG_FOUT) != 0; // VB array params will have isPtr and !isArray, because they are always ByRef (thus ptrs). // In general (always?), out params will be VT_PTR. if (isArray || isPtr) { IntPtr pElementTypeDesc = typeDesc.lpValue; TYPEDESC elementTypeDesc = (TYPEDESC)Marshal.PtrToStructure(pElementTypeDesc, typeof(TYPEDESC)); variantType = (VarEnum)elementTypeDesc.vt; // If the arg was a ptr to an array (e.g., as in VB objects), do it again to get the element type. if (variantType == VarEnum.VT_SAFEARRAY) { isArray = true; pElementTypeDesc = elementTypeDesc.lpValue; elementTypeDesc = (TYPEDESC)Marshal.PtrToStructure(pElementTypeDesc, typeof(TYPEDESC)); variantType = (VarEnum)elementTypeDesc.vt; } } paramTypes[paramIndex] = managedTypeForVariantType(variantType, isArray, isPtr, isOut); } } finally { iTypeInfo.ReleaseFuncDesc(pFuncDesc); } return(true); } catch (Exception) { return(true); } }
/// <summary> /// Determines whether the name belongs to a COM property on the given raw COM object. /// </summary> /// <remarks> /// Looks up type information using the COM ITypeInfo interface. /// Caches the result in the object's built-in COM data area so that subsequent queries for the same name are fast. /// </remarks> /// <param name="obj"></param> /// <param name="memberName"></param> /// <returns>false if no type information is available, or for any error condition.</returns> /// internal static bool IsCOMProp(object obj, string memberName) { if (Utils.IsMono) { return(false); } // The memberName can be changed by the call to GetMemberDispID below (U to _ conversion), but we want // to cache the isComProp value for the unmodified member name, so we save it. string unmodifiedMemberName = memberName; object cachedValue = Marshal.GetComObjectData(obj, "NETLinkIsCOMProp" + unmodifiedMemberName); if (cachedValue != null) { return((bool)cachedValue); } else { UCOMIDispatch iDisp; UCOMITypeInfo iTypeInfo; try { iDisp = GetIDispatch(obj); iTypeInfo = GetITypeInfo(obj, iDisp); } catch (Exception) { Marshal.SetComObjectData(obj, "NETLinkIsCOMProp" + unmodifiedMemberName, false); return(false); } // GetMemberDispID() does not throw on failure--it returns -1. int memberDispId = GetMemberDispID(obj, iDisp, ref memberName); if (memberDispId == -1) { Marshal.SetComObjectData(obj, "NETLinkIsCOMProp" + unmodifiedMemberName, false); return(false); } // We have successfully passed GetIDsOfNames() and GetITypeInfo(), so we know that type info is available and that // the name is a member of the dispatch interface. The name may also have been modified to reflect U to _ conversion. // Previous versions of this method acquired an ITypeInfo2 from the ITypeInfo and called GetFuncIndexOfMemId() to get // a function index suitable for GetFuncDesc(). There were problems with that technique, in that the index returned // sometimes (not always) needed to have 7 added to it. Because we cache the T/F result from this method anyway, // we now just exhaustively call GetFuncDesc() on all the members, looking for one with the right memberDispId. // Then we can check its func info to see if it is a property with no args. bool result = false; IntPtr pTypeAttr; iTypeInfo.GetTypeAttr(out pTypeAttr); TYPEATTR typeAttr = (TYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(TYPEATTR)); int numFuncs = typeAttr.cFuncs; iTypeInfo.ReleaseTypeAttr(pTypeAttr); for (int i = 0; i < numFuncs; i++) { IntPtr pFuncDesc; iTypeInfo.GetFuncDesc(i, out pFuncDesc); FUNCDESC funcDesc = (FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(FUNCDESC)); int thisMemberID = funcDesc.memid; INVOKEKIND invokeKind = funcDesc.invkind; int numParams = funcDesc.cParams; iTypeInfo.ReleaseFuncDesc(pFuncDesc); if (thisMemberID == memberDispId) { result = invokeKind == INVOKEKIND.INVOKE_PROPERTYGET && numParams == 0; break; } } Marshal.SetComObjectData(obj, "NETLinkIsCOMProp" + unmodifiedMemberName, result); return(result); } }
public static ComEventsInfo?Find(object rcw) { return((ComEventsInfo?)Marshal.GetComObjectData(rcw, typeof(ComEventsInfo))); }
public void GetComObjectData_NonComObjectObj_ThrowsArgumentNullException() { AssertExtensions.Throws <ArgumentException>("obj", () => Marshal.GetComObjectData(1, 2)); }
public void GetComObjectData_NullKey_ThrowsArgumentNullException() { AssertExtensions.Throws <ArgumentNullException>("key", () => Marshal.GetComObjectData(new object(), null)); }
public void GetComObjectData_NullObj_ThrowsArgumentNullException() { AssertExtensions.Throws <ArgumentNullException>("obj", () => Marshal.GetComObjectData(null, new object())); }
public void GetComObjectData_NetCore_ThrowsPlatformNotSupportedException() { Assert.Throws <PlatformNotSupportedException>(() => Marshal.GetComObjectData(null, null)); }
public void GetComObjectData_ThrowsNotSupportedException() { Assert.Throws <NotSupportedException>(() => Marshal.GetComObjectData("key", "value")); }